exercises for part 1
Dave Rolsky [Sun, 7 Jun 2009 19:48:16 +0000 (14:48 -0500)]
24 files changed:
moose-class/exercises/answers/01-classes/Employee.pm [new file with mode: 0644]
moose-class/exercises/answers/01-classes/Person.pm [new file with mode: 0644]
moose-class/exercises/lib/Employee.pm [new file with mode: 0644]
moose-class/exercises/lib/Person.pm [new file with mode: 0644]
moose-class/exercises/t/00-prereq.t [new file with mode: 0644]
moose-class/exercises/t/01-classes.t [new file with mode: 0644]
moose-class/exercises/t/lib/Lingua/EN/Inflect.pm [new file with mode: 0755]
moose-class/exercises/t/lib/MooseClass/Tests.pm [new file with mode: 0644]
moose-class/exercises/tarballs/Algorithm-C3-0.08.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Class-C3-0.21.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Class-C3-XS-0.11.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Class-MOP-0.85.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Data-OptList-0.104.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Devel-GlobalDestruction-0.02.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/List-MoreUtils-0.22.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/MRO-Compat-0.11.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Moose-0.80.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Params-Util-1.00.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Scalar-List-Utils-1.21.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Scope-Guard-0.03.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Sub-Exporter-0.982.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Sub-Install-0.925.tar.gz [new file with mode: 0644]
moose-class/exercises/tarballs/Sub-Name-0.04.tar.gz [new file with mode: 0644]
moose-class/slides/index.html

diff --git a/moose-class/exercises/answers/01-classes/Employee.pm b/moose-class/exercises/answers/01-classes/Employee.pm
new file mode 100644 (file)
index 0000000..6c301fc
--- /dev/null
@@ -0,0 +1,21 @@
+package Employee;
+
+use Moose;
+
+extends 'Person';
+
+has position => ( is => 'rw' );
+has salary   => ( is => 'rw' );
+has ssn      => ( is => 'ro' );
+
+override full_name => sub {
+    my $self = shift;
+
+    return super() . q[ (] . $self->position . q[)];
+};
+
+no Moose;
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/moose-class/exercises/answers/01-classes/Person.pm b/moose-class/exercises/answers/01-classes/Person.pm
new file mode 100644 (file)
index 0000000..5e79748
--- /dev/null
@@ -0,0 +1,18 @@
+package Person;
+
+use Moose;
+
+has first_name => ( is => 'rw' );
+has last_name  => ( is => 'rw' );
+
+sub full_name {
+    my $self = shift;
+
+    return join q{ }, $self->first_name, $self->last_name;
+}
+
+no Moose;
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/moose-class/exercises/lib/Employee.pm b/moose-class/exercises/lib/Employee.pm
new file mode 100644 (file)
index 0000000..0f67f08
--- /dev/null
@@ -0,0 +1,4 @@
+package Employee;
+
+
+1;
diff --git a/moose-class/exercises/lib/Person.pm b/moose-class/exercises/lib/Person.pm
new file mode 100644 (file)
index 0000000..9ec8c1b
--- /dev/null
@@ -0,0 +1,4 @@
+package Person;
+
+
+1;
diff --git a/moose-class/exercises/t/00-prereq.t b/moose-class/exercises/t/00-prereq.t
new file mode 100644 (file)
index 0000000..92a507f
--- /dev/null
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+
+my %prereqs = (
+    'Test::More' => '0',
+    'Moose'      => '0.80',
+    'Class::MOP' => '0.85',
+);
+
+my @missing;
+for my $mod ( keys %prereqs ) {
+    eval "require $mod";
+
+    if ($@) {
+        push @missing, "$mod is not installed";
+        next;
+    }
+
+    if ( $mod->VERSION < $prereqs{$mod} ) {
+        push @missing, "$mod must be version $prereqs{$mod} or greater";
+    }
+}
+
+if (@missing) {
+    warn "\n# ***********************************************************\n";
+    warn "#\n";
+    warn "# Found the following prereq problems ...\n";
+    warn "#   $_\n" for @missing;
+    warn "#\n";
+    warn "# ***********************************************************\n";
+
+    exit 255;
+}
+
+Test::More->import;
+
+plan( tests => 1 );
+ok( 'Looks like you have all the prereqs' );
+
diff --git a/moose-class/exercises/t/01-classes.t b/moose-class/exercises/t/01-classes.t
new file mode 100644 (file)
index 0000000..2e71127
--- /dev/null
@@ -0,0 +1,41 @@
+# Your task ...
+#
+# Create a Person class in lib/Person.pm
+#
+# A Person has the following attributes:
+#
+# * first_name - read-write
+# * last_name - read-write
+#
+# This class should also have a method named "printable_name". This
+# method should return the first and last name separated by a string
+# ("Jon Smith").
+#
+#
+# Create an Employee class in lib/Employee.pm
+#
+# The Employee class is a subclass of Person
+#
+# An Employee has the following read-write attributes:
+#
+# * position - read-write
+# * salary - read-write
+# * ssn - read-only
+#
+# The Employee class should override the "printable_name" method to
+# append the employee's title in parentheses ("Jon Smith
+# (Programmer)"). Use override() and super() for this.
+#
+# Finally, both classes should be free of Moose droppings, and should be
+# immutable.
+
+use strict;
+use warnings;
+
+use lib 't/lib';
+use MooseClass::Tests;
+
+use Person;
+use Employee;
+
+MooseClass::Tests::tests01();
diff --git a/moose-class/exercises/t/lib/Lingua/EN/Inflect.pm b/moose-class/exercises/t/lib/Lingua/EN/Inflect.pm
new file mode 100755 (executable)
index 0000000..72c65fd
--- /dev/null
@@ -0,0 +1,2661 @@
+package Lingua::EN::Inflect;
+
+use strict;
+use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS @ISA);
+use Env;
+
+require Exporter;
+@ISA = qw(Exporter);
+
+$VERSION = '1.89';
+
+%EXPORT_TAGS =
+(
+       ALL =>          [ qw( classical inflect
+                             PL PL_N PL_V PL_ADJ NO NUM A AN
+                             PL_eq PL_N_eq PL_V_eq PL_ADJ_eq
+                             PART_PRES
+                             ORD
+                             NUMWORDS
+                             def_noun def_verb def_adj def_a def_an )],
+
+       INFLECTIONS =>  [ qw( classical inflect
+                             PL PL_N PL_V PL_ADJ PL_eq
+                             NO NUM A AN PART_PRES )],
+
+       PLURALS =>      [ qw( classical inflect
+                             PL PL_N PL_V PL_ADJ NO NUM
+                             PL_eq PL_N_eq PL_V_eq PL_ADJ_eq )],
+
+       COMPARISONS =>  [ qw( classical 
+                             PL_eq PL_N_eq PL_V_eq PL_ADJ_eq )],
+
+       ARTICLES =>     [ qw( classical inflect NUM A AN )],
+
+       NUMERICAL =>    [ qw( ORD NUMWORDS )],
+
+       USER_DEFINED => [ qw( def_noun def_verb def_adj def_a def_an )],
+);
+
+Exporter::export_ok_tags(qw( ALL ));
+
+# SUPPORT CLASSICAL PLURALIZATIONS
+
+my %def_classical = (
+    all      => 0,
+    zero     => 0,
+    herd     => 0,
+    names    => 1,
+    persons  => 0,
+    ancient  => 0,
+);
+
+my %all_classical = (
+    all      => 1,
+    zero     => 1,
+    herd     => 1,
+    names    => 1,
+    persons  => 1,
+    ancient  => 1,
+);
+
+my %classical = %def_classical;
+
+my $classical_mode = join '|', keys %all_classical;
+   $classical_mode = qr/^(?:$classical_mode)$/;
+
+sub classical
+{
+    if (!@_) {
+        %classical = %all_classical;
+        return;
+    }
+    if (@_==1 && $_[0] !~ $classical_mode) {
+        %classical = $_[0] ? %all_classical : ();
+        return;
+    }
+    while (@_) {
+        my $arg = shift;
+        if ($arg !~ $classical_mode) {
+            die "Unknown classical mode ($arg)\n";
+        }
+        if (@_ && $_[0] !~ $classical_mode) { $classical{$arg} = shift; }
+        else                                { $classical{$arg} = 1;     }
+
+        if ($arg eq 'all') {
+            %classical = $classical{all} ? %all_classical : ();
+        }
+    }
+}
+
+my $persistent_count;
+
+sub NUM                # (;$count,$show)
+{
+       if (defined $_[0])
+       {
+               $persistent_count = $_[0];
+               return $_[0] if !defined($_[1]) || $_[1];
+       }
+       else
+       {
+               $persistent_count = undef;
+       }
+       return '';
+}
+
+
+# 0. PERFORM GENERAL INFLECTIONS IN A STRING
+
+sub enclose { "(?:$_[0])" }
+
+sub inflect
+{
+       my $save_persistent_count = $persistent_count;
+       my @sections = split /(NUM\([^)]*\))/, $_[0];
+       my $inflection = "";
+
+       foreach ( @sections )
+       {
+               unless (s/NUM\(\s*?(?:([^),]*)(?:,([^)]*))?)?\)/ NUM($1,$2) /xe)
+               {
+                   1 while
+                      s/\bPL   \( ([^),]*) (, ([^)]*) )? \)  / PL($1,$3)   /xeg
+                   || s/\bPL_N \( ([^),]*) (, ([^)]*) )? \)  / PL_N($1,$3) /xeg
+                   || s/\bPL_V \( ([^),]*) (, ([^)]*) )? \)  / PL_V($1,$3) /xeg
+                   || s/\bPL_ADJ \( ([^),]*) (, ([^)]*) )? \)  / PL_ADJ($1,$3) /xeg
+                   || s/\bAN?  \( ([^),]*) (, ([^)]*) )? \)  / A($1,$3)    /xeg
+                   || s/\bNO   \( ([^),]*) (, ([^)]*) )? \)  / NO($1,$3)   /xeg
+                   || s/\bORD  \( ([^)]*) \)                 / ORD($1)   /xeg
+                   || s/\bNUMWORDS  \( ([^)]*) \)            / NUMWORDS($1)   /xeg
+                   || s/\bPART_PRES  \( ([^)]*) \)            / PART_PRES($1)   /xeg
+               }
+
+               $inflection .= $_;
+       }
+
+       $persistent_count = $save_persistent_count;
+       return $inflection;
+}
+
+
+# 1. PLURALS
+
+my %PL_sb_irregular_s = 
+(
+       "corpus"        => "corpuses|corpora",
+       "opus"          => "opuses|opera",
+       "genus"         => "genera",
+       "mythos"        => "mythoi",
+       "penis"         => "penises|penes",
+       "testis"        => "testes",
+       "atlas"         => "atlases|atlantes",
+);
+
+my %PL_sb_irregular =
+(
+       "child"         => "children",
+       "brother"       => "brothers|brethren",
+       "loaf"          => "loaves",
+       "hoof"          => "hoofs|hooves",
+       "beef"          => "beefs|beeves",
+       "money"         => "monies",
+       "mongoose"      => "mongooses",
+       "ox"            => "oxen",
+       "cow"           => "cows|kine",
+       "soliloquy"     => "soliloquies",
+       "graffito"      => "graffiti",
+       "prima donna"   => "prima donnas|prime donne",
+       "octopus"       => "octopuses|octopodes",
+       "genie"         => "genies|genii",
+       "ganglion"      => "ganglions|ganglia",
+       "trilby"        => "trilbys",
+       "turf"          => "turfs|turves",
+       "numen"         => "numina",
+    "atman"     => "atmas",
+       "occiput"       => "occiputs|occipita",
+
+       %PL_sb_irregular_s,
+);
+
+my $PL_sb_irregular = enclose join '|', keys %PL_sb_irregular;
+
+# CLASSICAL "..is" -> "..ides"
+
+my @PL_sb_C_is_ides = 
+(
+# GENERAL WORDS...
+
+       "ephemeris", "iris", "clitoris",
+       "chrysalis", "epididymis",
+
+# INFLAMATIONS...
+
+       ".*itis", 
+
+);
+
+my $PL_sb_C_is_ides = enclose join "|", map { substr($_,0,-2) } @PL_sb_C_is_ides;
+
+# CLASSICAL "..a" -> "..ata"
+
+my @PL_sb_C_a_ata = 
+(
+       "anathema", "bema", "carcinoma", "charisma", "diploma",
+       "dogma", "drama", "edema", "enema", "enigma", "lemma",
+       "lymphoma", "magma", "melisma", "miasma", "oedema",
+       "sarcoma", "schema", "soma", "stigma", "stoma", "trauma",
+       "gumma", "pragma",
+);
+
+my $PL_sb_C_a_ata = enclose join "|", map { substr($_,0,-1) } @PL_sb_C_a_ata;
+
+# UNCONDITIONAL "..a" -> "..ae"
+
+my $PL_sb_U_a_ae = enclose join "|", 
+(
+       "alumna", "alga", "vertebra", "persona"
+);
+
+# CLASSICAL "..a" -> "..ae"
+
+my $PL_sb_C_a_ae = enclose join "|", 
+(
+       "amoeba", "antenna", "formula", "hyperbola",
+       "medusa", "nebula", "parabola", "abscissa",
+       "hydra", "nova", "lacuna", "aurora", ".*umbra",
+       "flora", "fauna",
+);
+
+# CLASSICAL "..en" -> "..ina"
+
+my $PL_sb_C_en_ina = enclose join "|", map { substr($_,0,-2) }
+(
+       "stamen", "foramen", "lumen"
+);
+
+# UNCONDITIONAL "..um" -> "..a"
+
+my $PL_sb_U_um_a = enclose join "|", map { substr($_,0,-2) }
+(
+       "bacterium",    "agendum",      "desideratum",  "erratum",
+       "stratum",      "datum",        "ovum",         "extremum",
+       "candelabrum",
+);
+
+# CLASSICAL "..um" -> "..a"
+
+my $PL_sb_C_um_a = enclose join "|", map { substr($_,0,-2) }
+(
+       "maximum",      "minimum",        "momentum",   "optimum",
+       "quantum",      "cranium",        "curriculum", "dictum",
+       "phylum",       "aquarium",       "compendium", "emporium",
+       "enconium",     "gymnasium",  "honorarium",     "interregnum",
+       "lustrum",      "memorandum", "millennium",     "rostrum", 
+       "spectrum",     "speculum",       "stadium",    "trapezium",
+       "ultimatum",    "medium",       "vacuum",       "velum", 
+       "consortium",
+);
+
+# UNCONDITIONAL "..us" -> "i"
+
+my $PL_sb_U_us_i = enclose join "|", map { substr($_,0,-2) }
+(
+       "alumnus",      "alveolus",     "bacillus",     "bronchus",
+       "locus",        "nucleus",      "stimulus",     "meniscus",
+);
+
+# CLASSICAL "..us" -> "..i"
+
+my $PL_sb_C_us_i = enclose join "|", map { substr($_,0,-2) }
+(
+       "focus",        "radius",       "genius",
+       "incubus",      "succubus",     "nimbus",
+       "fungus",       "nucleolus",    "stylus",
+       "torus",        "umbilicus",    "uterus",
+       "hippopotamus",
+);
+
+# CLASSICAL "..us" -> "..us"  (ASSIMILATED 4TH DECLENSION LATIN NOUNS)
+
+my $PL_sb_C_us_us = enclose join "|",
+(
+       "status", "apparatus", "prospectus", "sinus",
+       "hiatus", "impetus", "plexus",
+);
+
+# UNCONDITIONAL "..on" -> "a"
+
+my $PL_sb_U_on_a = enclose join "|", map { substr($_,0,-2) }
+(
+       "criterion",    "perihelion",   "aphelion",
+       "phenomenon",   "prolegomenon", "noumenon",
+       "organon",      "asyndeton",    "hyperbaton",
+);
+
+# CLASSICAL "..on" -> "..a"
+
+my $PL_sb_C_on_a = enclose join "|", map { substr($_,0,-2) }
+(
+       "oxymoron",
+);
+
+# CLASSICAL "..o" -> "..i"  (BUT NORMALLY -> "..os")
+
+my @PL_sb_C_o_i = 
+(
+       "solo",         "soprano",      "basso",        "alto",
+       "contralto",    "tempo",        "piano",        "virtuoso",
+);
+my $PL_sb_C_o_i = enclose join "|", map { substr($_,0,-1) } @PL_sb_C_o_i;
+
+# ALWAYS "..o" -> "..os"
+
+my $PL_sb_U_o_os = enclose join "|",
+(
+       "albino",       "archipelago",  "armadillo",
+       "commando",     "crescendo",    "fiasco",
+       "ditto",        "dynamo",       "embryo",
+       "ghetto",       "guano",        "inferno",
+       "jumbo",        "lumbago",      "magneto",
+       "manifesto",    "medico",       "octavo",
+       "photo",        "pro",          "quarto",       
+       "canto",        "lingo",        "generalissimo",
+       "stylo",        "rhino",        "casino",
+       "auto",     "macro",    'zero',
+
+       @PL_sb_C_o_i,
+);
+
+
+# UNCONDITIONAL "..[ei]x" -> "..ices"
+
+my $PL_sb_U_ex_ices = enclose join "|", map { substr($_,0,-2) }
+(
+       "codex",        "murex",        "silex",
+);
+
+my $PL_sb_U_ix_ices = enclose join "|", map { substr($_,0,-2) }
+(
+       "radix",        "helix",
+);
+
+# CLASSICAL "..[ei]x" -> "..ices"
+
+my $PL_sb_C_ex_ices = enclose join "|", map { substr($_,0,-2) }
+(
+       "vortex",       "vertex",       "cortex",       "latex",
+       "pontifex",     "apex",         "index",        "simplex",
+);
+
+my $PL_sb_C_ix_ices = enclose join "|", map { substr($_,0,-2) }
+(
+       "appendix",
+);
+
+# ARABIC: ".." -> "..i"
+
+my $PL_sb_C_i = enclose join "|", 
+(
+       "afrit",        "afreet",       "efreet",
+);
+
+# HEBREW: ".." -> "..im"
+
+my $PL_sb_C_im = enclose join "|",
+(
+       "goy",          "seraph",       "cherub",
+);
+
+# UNCONDITIONAL "..man" -> "..mans"
+
+my $PL_sb_U_man_mans = enclose join "|", 
+qw(
+       human
+       Alabaman Bahaman Burman German
+       Hiroshiman Liman Nakayaman Oklahoman
+       Panaman Selman Sonaman Tacoman Yakiman
+       Yokohaman Yuman
+);
+
+my @PL_sb_uninflected_s =
+(
+# PAIRS OR GROUPS SUBSUMED TO A SINGULAR...
+    "breeches", "britches", "clippers", "gallows", "hijinks",
+       "headquarters", "pliers", "scissors", "testes", "herpes",
+       "pincers", "shears", "proceedings", "trousers",
+
+# UNASSIMILATED LATIN 4th DECLENSION
+
+       "cantus", "coitus", "nexus",
+
+# RECENT IMPORTS...
+       "contretemps", "corps", "debris",
+       ".*ois", "siemens",
+       
+# DISEASES
+       ".*measles", "mumps",
+
+# MISCELLANEOUS OTHERS...
+       "diabetes", "jackanapes", "series", "species", "rabies",
+       "chassis", "innings", "news", "mews",
+);
+
+my $PL_sb_uninflected_herd = enclose join "|",
+# DON'T INFLECT IN CLASSICAL MODE, OTHERWISE NORMAL INFLECTION
+(
+       "wildebeest", "swine", "eland", "bison", "buffalo",
+       "elk", "moose", "rhinoceros",
+);
+
+my $PL_sb_uninflected = enclose join "|",
+(
+# SOME FISH AND HERD ANIMALS
+       ".*fish", "tuna", "salmon", "mackerel", "trout",
+       "bream", "sea[- ]bass", "carp", "cod", "flounder", "whiting", 
+
+       ".*deer", ".*sheep", 
+
+# ALL NATIONALS ENDING IN -ese
+        "Portuguese", "Amoyese", "Borghese", "Congoese", "Faroese",
+       "Foochowese", "Genevese", "Genoese", "Gilbertese", "Hottentotese",
+       "Kiplingese", "Kongoese", "Lucchese", "Maltese", "Nankingese",
+       "Niasese", "Pekingese", "Piedmontese", "Pistoiese", "Sarawakese",
+       "Shavese", "Vermontese", "Wenchowese", "Yengeese",
+       ".*[nrlm]ese",
+
+# SOME WORDS ENDING IN ...s (OFTEN PAIRS TAKEN AS A WHOLE)
+
+       @PL_sb_uninflected_s,
+
+# DISEASES
+       ".*pox",
+
+
+# OTHER ODDITIES
+       "graffiti", "djinn"
+);
+
+# SINGULAR WORDS ENDING IN ...s (ALL INFLECT WITH ...es)
+
+my $PL_sb_singular_s = enclose join '|',
+(
+       ".*ss",
+        "acropolis", "aegis", "alias", "asbestos", "bathos", "bias",
+       "bronchitis", "bursitis", "caddis", "cannabis",
+        "canvas", "chaos", "cosmos", "dais", "digitalis",
+        "epidermis", "ethos", "eyas", "gas", "glottis", 
+       "hubris", "ibis", "lens", "mantis", "marquis", "metropolis",
+        "pathos", "pelvis", "polis", "rhinoceros",
+        "sassafras", "trellis", ".*us", "[A-Z].*es",
+       
+       @PL_sb_C_is_ides,
+);
+
+my $PL_v_special_s = enclose join '|',
+(
+       $PL_sb_singular_s,
+       @PL_sb_uninflected_s,
+       keys %PL_sb_irregular_s,
+       '(.*[csx])is',
+       '(.*)ceps',
+       '[A-Z].*s',
+);
+
+my %PL_sb_postfix_adj = (
+       'general' => ['(?!major|lieutenant|brigadier|adjutant)\S+'],
+       'martial' => [qw(court)],
+);
+
+foreach (keys %PL_sb_postfix_adj) {
+       $PL_sb_postfix_adj{$_} = enclose
+                                enclose(join('|', @{$PL_sb_postfix_adj{$_}}))
+                              . "(?=(?:-|\\s+)$_)";
+}
+
+my $PL_sb_postfix_adj = '(' . join('|', values %PL_sb_postfix_adj) . ')(.*)';
+
+my $PL_sb_military = 'major|lieutenant|brigadier|adjutant|quartermaster';
+my $PL_sb_general = '((?!'.$PL_sb_military.').*?)((-|\s+)general)';
+
+my $PL_prep = enclose join '|', qw (
+        about above across after among around at athwart before behind
+        below beneath beside besides between betwixt beyond but by
+        during except for from in into near of off on onto out over
+        since till to under until unto upon with
+);
+
+my $PL_sb_prep_dual_compound = '(.*?)((?:-|\s+)(?:'.$PL_prep.'|d[eu])(?:-|\s+))a(?:-|\s+)(.*)';
+
+my $PL_sb_prep_compound = '(.*?)((-|\s+)('.$PL_prep.'|d[eu])((-|\s+)(.*))?)';
+
+
+my %PL_pron_nom =
+(
+#      NOMINATIVE              REFLEXIVE
+
+"i"    => "we",        "myself"   =>   "ourselves",
+"you"  => "you",       "yourself" =>   "yourselves",
+"she"  => "they",      "herself"  =>   "themselves",
+"he"   => "they",      "himself"  =>   "themselves",
+"it"   => "they",      "itself"   =>   "themselves",
+"they" => "they",      "themself" =>   "themselves",
+
+#      POSSESSIVE
+
+"mine"  => "ours",
+"yours"         => "yours",
+"hers"  => "theirs",
+"his"   => "theirs",
+"its"   => "theirs",
+"theirs" => "theirs",
+);
+
+my %PL_pron_acc =
+(
+#      ACCUSATIVE              REFLEXIVE
+
+"me"   => "us",        "myself"   =>   "ourselves",
+"you"  => "you",       "yourself" =>   "yourselves",
+"her"  => "them",      "herself"  =>   "themselves",
+"him"  => "them",      "himself"  =>   "themselves",
+"it"   => "them",      "itself"   =>   "themselves",
+"them" => "them",      "themself" =>   "themselves",
+);
+
+my $PL_pron_acc = enclose join '|', keys %PL_pron_acc;
+
+my %PL_v_irregular_pres =
+(
+#      1st PERS. SING.         2ND PERS. SING.         3RD PERS. SINGULAR
+#                              3RD PERS. (INDET.)      
+
+"am"   => "are",       "are"   => "are",       "is"     => "are",
+"was"  => "were",      "were"  => "were",      "was"    => "were",
+"have"  => "have",     "have"  => "have",      "has"    => "have",
+"do"    => "do",       "do"    => "do",        "does"   => "do",
+);
+
+my $PL_v_irregular_pres = enclose join '|', keys %PL_v_irregular_pres;
+
+my %PL_v_ambiguous_pres =
+(
+#      1st PERS. SING.         2ND PERS. SING.         3RD PERS. SINGULAR
+#                              3RD PERS. (INDET.)      
+
+"act"  => "act",       "act"   => "act",       "acts"    => "act",
+"blame"        => "blame",     "blame" => "blame",     "blames"  => "blame",
+"can"  => "can",       "can"   => "can",       "can"     => "can",
+"must" => "must",      "must"  => "must",      "must"    => "must",
+"fly"  => "fly",       "fly"   => "fly",       "flies"   => "fly",
+"copy" => "copy",      "copy"  => "copy",      "copies"  => "copy",
+"drink"        => "drink",     "drink" => "drink",     "drinks"  => "drink",
+"fight"        => "fight",     "fight" => "fight",     "fights"  => "fight",
+"fire" => "fire",      "fire"  => "fire",      "fires"   => "fire",
+"like" => "like",      "like"  => "like",      "likes"   => "like",
+"look" => "look",      "look"  => "look",      "looks"   => "look",
+"make" => "make",      "make"  => "make",      "makes"   => "make",
+"reach"        => "reach",     "reach" => "reach",     "reaches" => "reach",
+"run"  => "run",       "run"   => "run",       "runs"    => "run",
+"sink" => "sink",      "sink"  => "sink",      "sinks"   => "sink",
+"sleep"        => "sleep",     "sleep" => "sleep",     "sleeps"  => "sleep",
+"view" => "view",      "view"  => "view",      "views"   => "view",
+);
+
+my $PL_v_ambiguous_pres = enclose join '|', keys %PL_v_ambiguous_pres;
+
+
+my $PL_v_irregular_non_pres = enclose join '|',
+(
+"did", "had", "ate", "made", "put", 
+"spent", "fought", "sank", "gave", "sought",
+"shall", "could", "ought", "should",
+);
+
+my $PL_v_ambiguous_non_pres = enclose join '|',
+(
+"thought", "saw", "bent", "will", "might", "cut",
+);
+
+# "..oes" -> "..oe" (the rest are "..oes" -> "o")
+
+my $PL_v_oes_oe = enclose join "|",
+qw(
+       .*shoes  .*hoes  .*toes
+       canoes   floes   oboes  roes  throes  woes
+);
+
+my $PL_count_zero = enclose join '|',
+(
+0, "no", "zero", "nil"
+);
+
+my $PL_count_one = enclose join '|',
+(
+1, "a", "an", "one", "each", "every", "this", "that",
+);
+
+my %PL_adj_special =
+(
+"a"    => "some",      "an"   =>  "some",
+"this" => "these",     "that" => "those",
+);
+my $PL_adj_special = enclose join '|', keys %PL_adj_special;
+
+my %PL_adj_poss =
+(
+"my"    => "our",
+"your" => "your",
+"its"  => "their",
+"her"  => "their",
+"his"  => "their",
+"their"        => "their",
+);
+my $PL_adj_poss = enclose join '|', keys %PL_adj_poss;
+
+
+sub checkpat
+{
+local $SIG{__WARN__} = sub {0};
+do {$@ =~ s/at.*?$//;
+    die "\nBad user-defined singular pattern:\n\t$@\n"}
+       if (!eval "'' =~ m/$_[0]/; 1;" or $@);
+return @_;
+}
+
+sub checkpatsubs
+{
+checkpat($_[0]);
+if (defined $_[1])
+{
+       local $SIG{__WARN__} = sub {0};
+       do {$@ =~ s/at.*?$//;
+           die "\nBad user-defined plural string: '$_[1]'\n\t$@\n"}
+               if (!eval "qq{$_[1]}; 1;" or $@);
+}
+return @_;
+}
+
+my @PL_sb_user_defined = ();
+my @PL_v_user_defined  = ();
+my @PL_adj_user_defined  = ();
+my @A_a_user_defined   = ();
+
+sub def_noun
+{
+       unshift @PL_sb_user_defined, checkpatsubs(@_);
+       return 1;
+}
+
+sub def_verb
+{
+       unshift @PL_v_user_defined, checkpatsubs(@_[4,5]);
+       unshift @PL_v_user_defined, checkpatsubs(@_[2,3]);
+       unshift @PL_v_user_defined, checkpatsubs(@_[0,1]);
+       return 1;
+}
+
+sub def_adj
+{
+       unshift @PL_adj_user_defined, checkpatsubs(@_);
+       return 1;
+}
+
+sub def_a
+{
+unshift @A_a_user_defined, checkpat(@_,'a');
+return 1;
+}
+
+sub def_an
+{
+unshift @A_a_user_defined, checkpat(@_,'an');
+return 1;
+}
+
+sub ud_match
+{
+my $word = shift;
+for (my $i=0; $i < @_; $i+=2)
+{
+       if ($word =~ /^(?:$_[$i])$/i)
+       {
+               last unless defined $_[$i+1];
+               return eval '"'.$_[$i+1].'"';
+       }
+}
+return undef;
+}
+
+do
+{
+local $SIG{__WARN__} = sub {0};
+my $rcfile;
+
+$rcfile = $INC{'Lingua//EN/Inflect.pm'} || '';
+$rcfile =~ s/Inflect.pm$/.inflectrc/;
+do $rcfile or die "\nBad .inflectrc file ($rcfile):\n\t$@\n"
+if $rcfile && -r $rcfile && -s $rcfile;
+
+$rcfile = "$ENV{HOME}/.inflectrc" || '';
+do $rcfile or die "\nBad .inflectrc file ($rcfile):\n\t$@\n"
+if $rcfile && -r $rcfile && -s $rcfile;
+};
+
+sub postprocess                # FIX PEDANTRY AND CAPITALIZATION :-)
+{
+my ($orig, $inflected) = @_;
+$inflected =~ s/([^|]+)\|(.+)/ $classical{all}?$2:$1 /e;
+return $orig =~ /^I$/  ? $inflected
+ : $orig =~ /^[A-Z]+$/ ? uc $inflected
+ : $orig =~ /^[A-Z]/   ? ucfirst $inflected
+ :                       $inflected;
+}
+
+sub PL
+#   PL($word,$number)
+{
+my ($str, $count) = @_;
+my ($pre, $word, $post) = ($str =~ m/\A(\s*)(.+?)(\s*)\Z/);
+return $str unless $word;
+my $plural = postprocess $word,  _PL_special_adjective($word,$count)
+                         || _PL_special_verb($word,$count)
+                         || _PL_noun($word,$count);
+return $pre.$plural.$post;
+}
+
+sub PL_N
+#   PL_N($word,$number)
+{
+my ($str, $count) = @_;
+my ($pre, $word, $post) = ($str =~ m/\A(\s*)(.+?)(\s*)\Z/);
+return $str unless $word;
+my $plural = postprocess $word, _PL_noun($word,$count);
+return $pre.$plural.$post;
+}
+
+sub PL_V
+#   PL_V($word,$number)
+{
+my ($str, $count) = @_;
+my ($pre, $word, $post) = ($str =~ m/\A(\s*)(.+?)(\s*)\Z/);
+return $str unless $word;
+my $plural = postprocess $word, _PL_special_verb($word,$count)
+                         || _PL_general_verb($word,$count);
+return $pre.$plural.$post;
+}
+
+sub PL_ADJ
+#   PL_ADJ($word,$number)
+{
+my ($str, $count) = @_;
+my ($pre, $word, $post) = ($str =~ m/\A(\s*)(.+?)(\s*)\Z/);
+return $str unless $word;
+my $plural = postprocess $word, _PL_special_adjective($word,$count)
+                         || $word;
+return $pre.$plural.$post;
+}
+
+sub PL_eq        { _PL_eq(@_, \&PL); }
+sub PL_N_eq      { _PL_eq(@_, \&PL_N); }
+sub PL_V_eq      { _PL_eq(@_, \&PL_V); }
+sub PL_ADJ_eq    { _PL_eq(@_, \&PL_ADJ); }
+
+sub _PL_eq
+{
+my ( $word1, $word2, $PL ) = @_;
+my %classval = %classical;
+%classical = %all_classical;
+my $result = "";
+$result = "eq" if !$result && $word1 eq $word2;
+$result = "p:s" if !$result && $word1 eq &$PL($word2);
+$result = "s:p" if !$result && &$PL($word1) eq $word2;
+%classical = ();
+$result = "p:s" if !$result && $word1 eq &$PL($word2);
+$result = "s:p" if !$result && &$PL($word1) eq $word2;
+%classical = %classval;
+
+if ($PL == \&PL || $PL == \&PL_N)
+{
+       $result = "p:p" 
+               if !$result && _PL_check_plurals_N($word1,$word2);
+       $result = "p:p" 
+               if !$result && _PL_check_plurals_N($word2,$word1);
+}
+if ($PL == \&PL || $PL == \&PL_ADJ)
+{
+       $result = "p:p" 
+               if !$result && _PL_check_plurals_ADJ($word1,$word2,$PL);
+}
+
+return $result;
+}
+
+sub _PL_reg_plurals
+{
+       $_[0] =~ /($_[1])($_[2]\|\1$_[3]|$_[3]\|\1$_[2])/
+}
+
+sub _PL_check_plurals_N
+{
+my $pair = "$_[0]|$_[1]";
+foreach ( values %PL_sb_irregular_s )  { return 1 if $_ eq $pair; }
+foreach ( values %PL_sb_irregular )    { return 1 if $_ eq $pair; }
+
+return 1 if _PL_reg_plurals($pair, $PL_sb_C_a_ata,   "as","ata")
+        || _PL_reg_plurals($pair, $PL_sb_C_is_ides, "is","ides")
+        || _PL_reg_plurals($pair, $PL_sb_C_a_ae,    "s","e")
+        || _PL_reg_plurals($pair, $PL_sb_C_en_ina,  "ens","ina")
+        || _PL_reg_plurals($pair, $PL_sb_C_um_a,    "ums","a")
+        || _PL_reg_plurals($pair, $PL_sb_C_us_i,    "uses","i")
+        || _PL_reg_plurals($pair, $PL_sb_C_on_a,    "ons","a")
+        || _PL_reg_plurals($pair, $PL_sb_C_o_i,     "os","i")
+        || _PL_reg_plurals($pair, $PL_sb_C_ex_ices, "exes","ices")
+        || _PL_reg_plurals($pair, $PL_sb_C_ix_ices, "ixes","ices")
+        || _PL_reg_plurals($pair, $PL_sb_C_i,       "s","i")
+        || _PL_reg_plurals($pair, $PL_sb_C_im,      "s","im")
+
+        || _PL_reg_plurals($pair, '.*eau',       "s","x")
+        || _PL_reg_plurals($pair, '.*ieu',       "s","x")
+        || _PL_reg_plurals($pair, '.*tri',       "xes","ces")
+        || _PL_reg_plurals($pair, '.{2,}[yia]n', "xes","ges");
+
+
+return 0;
+}
+
+sub _PL_check_plurals_ADJ
+{
+my ( $word1a, $word2a ) = @_;
+my ( $word1b, $word2b ) = @_;
+
+$word1a = '' unless $word1a =~ s/'s?$//;
+$word2a = '' unless $word2a =~ s/'s?$//;
+$word1b = '' unless $word1b =~ s/s'$//;
+$word2b = '' unless $word2b =~ s/s'$//;
+
+if ($word1a)
+{
+       return 1 if $word2a && ( _PL_check_plurals_N($word1a, $word2a)
+                               || _PL_check_plurals_N($word2a, $word1a) );
+       return 1 if $word2b && ( _PL_check_plurals_N($word1a, $word2b)
+                               || _PL_check_plurals_N($word2b, $word1a) );
+}
+if ($word1b)
+{
+       return 1 if $word2a && ( _PL_check_plurals_N($word1b, $word2a)
+                               || _PL_check_plurals_N($word2a, $word1b) );
+       return 1 if $word2b && ( _PL_check_plurals_N($word1b, $word2b)
+                               || _PL_check_plurals_N($word2b, $word1b) );
+}
+
+
+return "";
+}
+
+sub _PL_noun
+{
+my ( $word, $count ) = @_;
+my $value;                             # UTILITY VARIABLE
+
+# DEFAULT TO PLURAL
+
+$count = $persistent_count
+       if !defined($count) && defined($persistent_count);
+
+$count = (defined $count and $count=~/^($PL_count_one)$/io
+         or defined $count and $classical{zero}
+         and $count=~/^($PL_count_zero)$/io)
+            ? 1  
+            : 2;
+
+return $word if $count==1;
+
+# HANDLE USER-DEFINED NOUNS
+
+return $value if defined($value = ud_match($word, @PL_sb_user_defined));
+
+
+# HANDLE EMPTY WORD, SINGULAR COUNT AND UNINFLECTED PLURALS
+
+$word eq ''                    and return $word;
+
+$word =~ /^($PL_sb_uninflected)$/i
+                               and return $word;
+
+$classical{herd} and $word =~ /^($PL_sb_uninflected_herd)$/i
+                               and return $word;
+
+
+# HANDLE COMPOUNDS ("Governor General", "mother-in-law", "aide-de-camp", ETC.)
+
+$word =~ /^(?:$PL_sb_postfix_adj)$/i
+                               and $value = $2
+                               and return _PL_noun($1,2)
+                                          . $value;
+
+$word =~ /^(?:$PL_sb_prep_dual_compound)$/i
+                               and $value = [$2,$3] 
+                               and return _PL_noun($1,2)
+                                          . $value->[0]
+                                          . _PL_noun($value->[1]);
+
+$word =~ /^(?:$PL_sb_prep_compound)$/i
+                               and $value = $2 
+                               and return _PL_noun($1,2)
+                                          . $value;
+
+# HANDLE PRONOUNS
+
+$word =~ /^((?:$PL_prep)\s+)($PL_pron_acc)$/i
+                               and return $1.$PL_pron_acc{lc($2)};
+
+$value = $PL_pron_nom{lc($word)}
+                               and return $value;
+
+$word =~ /^($PL_pron_acc)$/i
+                               and return $PL_pron_acc{lc($1)};
+
+# HANDLE ISOLATED IRREGULAR PLURALS 
+
+$word =~ /(.*)\b($PL_sb_irregular)$/i
+                               and return $1 . $PL_sb_irregular{lc $2};
+$word =~ /($PL_sb_U_man_mans)$/i
+                               and return "$1s";
+$word =~ /(\S*)(person)$/i and return $classical{persons}?"$1persons":"$1people";
+
+# HANDLE FAMILIES OF IRREGULAR PLURALS 
+
+$word =~ /(.*)man$/i           and return "$1men";
+$word =~ /(.*[ml])ouse$/i      and return "$1ice";
+$word =~ /(.*)goose$/i         and return "$1geese";
+$word =~ /(.*)tooth$/i         and return "$1teeth";
+$word =~ /(.*)foot$/i          and return "$1feet";
+
+# HANDLE UNASSIMILATED IMPORTS
+
+$word =~ /(.*)ceps$/i          and return $word;
+$word =~ /(.*)zoon$/i          and return "$1zoa";
+$word =~ /(.*[csx])is$/i       and return "$1es";
+$word =~ /($PL_sb_U_ex_ices)ex$/i      and return "$1ices";
+$word =~ /($PL_sb_U_ix_ices)ix$/i      and return "$1ices";
+$word =~ /($PL_sb_U_um_a)um$/i and return "$1a";
+$word =~ /($PL_sb_U_us_i)us$/i and return "$1i";
+$word =~ /($PL_sb_U_on_a)on$/i and return "$1a";
+$word =~ /($PL_sb_U_a_ae)$/i   and return "$1e";
+
+# HANDLE INCOMPLETELY ASSIMILATED IMPORTS
+
+if ($classical{ancient})
+{
+    $word =~ /(.*)trix$/i              and return "$1trices";
+    $word =~ /(.*)eau$/i               and return "$1eaux";
+    $word =~ /(.*)ieu$/i               and return "$1ieux";
+    $word =~ /(.{2,}[yia])nx$/i                and return "$1nges";
+    $word =~ /($PL_sb_C_en_ina)en$/i   and return "$1ina";
+    $word =~ /($PL_sb_C_ex_ices)ex$/i  and return "$1ices";
+    $word =~ /($PL_sb_C_ix_ices)ix$/i  and return "$1ices";
+    $word =~ /($PL_sb_C_um_a)um$/i     and return "$1a";
+    $word =~ /($PL_sb_C_us_i)us$/i     and return "$1i";
+    $word =~ /($PL_sb_C_us_us)$/i      and return "$1";
+    $word =~ /($PL_sb_C_a_ae)$/i       and return "$1e";
+    $word =~ /($PL_sb_C_a_ata)a$/i     and return "$1ata";
+    $word =~ /($PL_sb_C_is_ides)is$/i  and return "$1ides";
+    $word =~ /($PL_sb_C_o_i)o$/i       and return "$1i";
+    $word =~ /($PL_sb_C_on_a)on$/i     and return "$1a";
+    $word =~ /$PL_sb_C_im$/i           and return "${word}im";
+    $word =~ /$PL_sb_C_i$/i            and return "${word}i";
+}
+
+
+# HANDLE SINGULAR NOUNS ENDING IN ...s OR OTHER SILIBANTS
+
+$word =~ /^($PL_sb_singular_s)$/i      and return "$1es";
+$word =~ /^([A-Z].*s)$/                        and $classical{names} and return "$1es";
+$word =~ /(.*)([cs]h|[zx])$/i          and return "$1$2es";
+# $word =~ /(.*)(us)$/i                        and return "$1$2es";
+
+# HANDLE ...f -> ...ves
+
+$word =~ /(.*[eao])lf$/i       and return "$1lves"; 
+$word =~ /(.*[^d])eaf$/i       and return "$1eaves";
+$word =~ /(.*[nlw])ife$/i      and return "$1ives";
+$word =~ /(.*)arf$/i           and return "$1arves";
+
+# HANDLE ...y
+
+$word =~ /(.*[aeiou])y$/i      and return "$1ys";
+$word =~ /([A-Z].*y)$/         and $classical{names} and return "$1s";
+$word =~ /(.*)y$/i             and return "$1ies";
+
+# HANDLE ...o
+
+$word =~ /$PL_sb_U_o_os$/i     and return "${word}s";
+$word =~ /[aeiou]o$/i          and return "${word}s";
+$word =~ /o$/i                 and return "${word}es";
+
+
+# OTHERWISE JUST ADD ...s
+
+return "${word}s";
+}
+
+
+sub _PL_special_verb
+{
+my ( $word, $count ) = @_;
+$count = $persistent_count
+       if !defined($count) && defined($persistent_count);
+$count = (defined $count and $count=~/^($PL_count_one)$/io or
+         defined $count and $classical{zero} and $count=~/^($PL_count_zero)$/io) ? 1  
+       : 2;
+
+return undef if $count=~/^($PL_count_one)$/io;
+
+my $value;                             # UTILITY VARIABLE
+
+# HANDLE USER-DEFINED VERBS
+
+return $value if defined($value = ud_match($word, @PL_v_user_defined));
+
+# HANDLE IRREGULAR PRESENT TENSE (SIMPLE AND COMPOUND)
+
+$word =~ /^($PL_v_irregular_pres)((\s.*)?)$/i
+               and return $PL_v_irregular_pres{lc $1}.$2;
+
+# HANDLE IRREGULAR FUTURE, PRETERITE AND PERFECT TENSES 
+
+$word =~ /^($PL_v_irregular_non_pres)((\s.*)?)$/i
+               and return $word;
+
+# HANDLE PRESENT NEGATIONS (SIMPLE AND COMPOUND)
+
+$word =~ /^($PL_v_irregular_pres)(n't(\s.*)?)$/i
+               and return $PL_v_irregular_pres{lc $1}.$2;
+
+$word =~ /^\S+n't\b/i
+               and return $word;
+
+# HANDLE SPECIAL CASES
+
+$word =~ /^($PL_v_special_s)$/         and return undef;
+$word =~ /\s/                          and return undef;
+
+# HANDLE STANDARD 3RD PERSON (CHOP THE ...(e)s OFF SINGLE WORDS)
+
+$word =~ /^(.*)([cs]h|[x]|zz|ss)es$/i  and return "$1$2";
+
+$word =~ /^(..+)ies$/i                 and return "$1y";
+
+$word =~ /($PL_v_oes_oe)$/             and return substr($1,0,-1);
+$word =~ /^(.+)oes$/i                  and return "$1o";
+
+$word =~ /^(.*[^s])s$/i                        and return $1;
+
+# OTHERWISE, A REGULAR VERB (HANDLE ELSEWHERE)
+
+return undef;
+}  
+
+sub _PL_general_verb
+{
+my ( $word, $count ) = @_;
+$count = $persistent_count
+       if !defined($count) && defined($persistent_count);
+$count = (defined $count and $count=~/^($PL_count_one)$/io or
+         defined $count and $classical{zero} and $count=~/^($PL_count_zero)$/io) ? 1  
+       : 2;
+
+return $word if $count=~/^($PL_count_one)$/io;
+
+# HANDLE AMBIGUOUS PRESENT TENSES  (SIMPLE AND COMPOUND)
+
+$word =~ /^($PL_v_ambiguous_pres)((\s.*)?)$/i
+               and return $PL_v_ambiguous_pres{lc $1}.$2;
+
+# HANDLE AMBIGUOUS PRETERITE AND PERFECT TENSES 
+
+$word =~ /^($PL_v_ambiguous_non_pres)((\s.*)?)$/i
+               and return $word;
+
+# OTHERWISE, 1st OR 2ND PERSON IS UNINFLECTED
+
+return $word;
+
+}
+
+sub _PL_special_adjective
+{
+my ( $word, $count ) = @_;
+$count = $persistent_count
+       if !defined($count) && defined($persistent_count);
+$count = (defined $count and $count=~/^($PL_count_one)$/io or
+         defined $count and $classical{zero} and $count=~/^($PL_count_zero)$/io) ? 1  
+       : 2;
+
+return $word if $count=~/^($PL_count_one)$/io;
+
+
+# HANDLE USER-DEFINED ADJECTIVES
+
+my $value;
+return $value if defined($value = ud_match($word, @PL_adj_user_defined));
+
+# HANDLE KNOWN CASES
+
+$word =~ /^($PL_adj_special)$/i
+                       and return $PL_adj_special{lc $1};
+
+# HANDLE POSSESSIVES
+
+$word =~ /^($PL_adj_poss)$/i
+                       and return $PL_adj_poss{lc $1};
+
+$word =~ /^(.*)'s?$/   and do { my $pl = PL_N($1);
+                                return "$pl'" . ($pl =~ m/s$/ ? "" : "s");
+                              };
+
+# OTHERWISE, NO IDEA
+
+return undef;
+
+}
+
+
+# 2. INDEFINITE ARTICLES
+
+# THIS PATTERN MATCHES STRINGS OF CAPITALS STARTING WITH A "VOWEL-SOUND"
+# CONSONANT FOLLOWED BY ANOTHER CONSONANT, AND WHICH ARE NOT LIKELY
+# TO BE REAL WORDS (OH, ALL RIGHT THEN, IT'S JUST MAGIC!)
+
+my $A_abbrev = q{
+(?! FJO | [HLMNS]Y.  | RY[EO] | SQU
+  | ( F[LR]? | [HL] | MN? | N | RH? | S[CHKLMNPTVW]? | X(YL)?) [AEIOU])
+[FHLMNRSX][A-Z]
+};
+
+# THIS PATTERN CODES THE BEGINNINGS OF ALL ENGLISH WORDS BEGINING WITH A
+# 'y' FOLLOWED BY A CONSONANT. ANY OTHER Y-CONSONANT PREFIX THEREFORE
+# IMPLIES AN ABBREVIATION.
+
+my $A_y_cons = 'y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)';
+
+# EXCEPTIONS TO EXCEPTIONS
+
+my $A_explicit_an = enclose join '|',
+(
+"euler",
+"hour(?!i)", "heir", "honest", "hono",
+);
+
+sub A
+{
+my ($str, $count) = @_;
+my ($pre, $word, $post) = ( $str =~ m/\A(\s*)(?:an?\s+)?(.+?)(\s*)\Z/i );
+return $str unless $word;
+my $result = _indef_article($word,$count);
+return $pre.$result.$post;
+}
+
+sub AN { goto &A }
+
+sub _indef_article
+{
+my ( $word, $count ) = @_;
+
+$count = $persistent_count
+       if !defined($count) && defined($persistent_count);
+
+return "$count $word"
+       if defined $count && $count!~/^($PL_count_one)$/io;
+
+# HANDLE USER-DEFINED VARIANTS
+
+my $value;
+return $value if defined($value = ud_match($word, @A_a_user_defined));
+
+# HANDLE SPECIAL CASES
+
+$word =~ /^($A_explicit_an)/i          and return "an $word";
+
+# HANDLE ABBREVIATIONS
+
+$word =~ /^($A_abbrev)/ox              and return "an $word";
+$word =~ /^[aefhilmnorsx][.-]/i                and return "an $word";
+$word =~ /^[a-z][.-]/i                 and return "a $word";
+
+# HANDLE CONSONANTS
+
+$word =~ /^[^aeiouy]/i         and return "a $word";
+
+# HANDLE SPECIAL VOWEL-FORMS
+
+$word =~ /^e[uw]/i                     and return "a $word";
+$word =~ /^onc?e\b/i                   and return "a $word";
+$word =~ /^uni([^nmd]|mo)/i            and return "a $word";
+$word =~ /^u[bcfhjkqrst][aeiou]/i      and return "a $word";
+
+# HANDLE SPECIAL CAPITALS
+
+$word =~ /^U[NK][AIEO]?/               and return "a $word";
+
+# HANDLE VOWELS
+
+$word =~ /^[aeiou]/i           and return "an $word";
+
+# HANDLE y... (BEFORE CERTAIN CONSONANTS IMPLIES (UNNATURALIZED) "i.." SOUND)
+
+$word =~ /^($A_y_cons)/io      and return "an $word";
+
+# OTHERWISE, GUESS "a"
+                                   return "a $word";
+}
+
+# 2. TRANSLATE ZERO-QUANTIFIED $word TO "no PL($word)"
+
+sub NO
+{
+my ($str, $count) = @_;
+my ($pre, $word, $post) = ($str =~ m/\A(\s*)(.+?)(\s*)\Z/);
+
+$count = $persistent_count
+       if !defined($count) && defined($persistent_count);
+$count = 0 unless $count;
+
+return "$pre$count " . PL($word,$count) . $post
+       unless $count =~ /^$PL_count_zero$/;
+return "${pre}no ". PL($word,0) . $post ;
+}
+
+
+# PARTICIPLES
+
+sub PART_PRES
+{
+        local $_ = PL_V(shift,2);
+           s/ie$/y/
+        or s/ue$/u/
+        or s/([auy])e$/$1/
+        or s/ski$/ski/
+        or s/i$//
+        or s/([^e])e$/$1/
+        or m/er$/
+        or s/([^aeiou][aeiouy]([bdgmnprst]))$/$1$2/;
+        return "${_}ing";
+}
+
+
+
+# NUMERICAL INFLECTIONS
+
+my %nth =
+(
+       0 => 'th',
+       1 => 'st',
+       2 => 'nd',
+       3 => 'rd',
+       4 => 'th',
+       5 => 'th',
+       6 => 'th',
+       7 => 'th',
+       8 => 'th',
+       9 => 'th',
+       11 => 'th',
+       12 => 'th',
+       13 => 'th',
+);
+
+
+my %ordinal;
+@ordinal{qw(ty    one   two    three five  eight  nine  twelve )}=
+         qw(tieth first second third fifth eighth ninth twelfth);
+
+my $ordinal_suff = join '|', keys %ordinal, "";
+
+$ordinal{""} = 'th';
+
+sub ORD($)
+{
+       my $num = shift;
+       if ($num =~ /\d/) {
+               return $num . ($nth{$num%100} || $nth{$num%10});
+       }
+       else {
+               $num =~ s/($ordinal_suff)\Z/$ordinal{$1}/;
+               return $num;
+       }
+}
+
+
+my %default_args = 
+(
+       'group'   => 0,
+       'comma'   => ',',
+       'and'     => 'and',
+       'zero'    => 'zero',
+       'one'     => 'one',
+       'decimal' => 'point',
+);
+
+my @unit = ('',qw(one two three four five six seven eight nine));
+my @teen = qw(ten eleven twelve thirteen fourteen
+             fifteen sixteen seventeen eighteen nineteen);
+my @ten  = ('','',qw(twenty thirty forty fifty sixty seventy eighty ninety));
+my @mill = map { (my $val=$_) =~ s/_/illion/; " $val" }
+          ('',qw(thousand m_ b_ tr_ quadr_ quint_ sext_ sept_ oct_ non_ dec_));
+
+
+sub mill { my $ind = $_[0]||0;
+          die "Number out of range\n" if $ind > $#mill;
+          return $ind<@mill ? $mill[$ind] : ' ???illion'; }
+
+sub unit { return $unit[$_[0]]. mill($_[1]); }
+
+sub ten
+{
+       return $ten[$_[0]] . ($_[0]&&$_[1]?'-':'') . $unit[$_[1]] . mill($_[2])
+               if $_[0] ne '1';
+       return $teen[$_[1]]. $mill[$_[2]||0];
+}
+
+sub hund
+{
+       return unit($_[0]) . " hundred" . ($_[1] || $_[2] ? " $_[4] " : '')
+            . ten($_[1],$_[2]) . mill($_[3]) . ', ' if $_[0];
+       return ten($_[1],$_[2]) . mill($_[3]) . ', ' if $_[1] || $_[2];
+       return '';
+}
+
+
+sub enword
+{
+       my ($num,$group,$zero,$one,$comma,$and) = @_;
+
+       if ($group==1)
+       {
+               $num =~ s/(\d)/ ($1==1 ? " $one" : $1 ? unit($1) :" $zero")."$comma " /eg;
+       }
+       elsif ($group==2)
+       {
+               $num =~ s/(\d)(\d)/ ($1 ? ten($1,$2) : $2 ? " $zero " . unit($2) : " $zero $zero") . "$comma " /eg;
+               $num =~ s/(\d)/ ($1 ? unit($1) :" $zero")."$comma " /e;
+       }
+       elsif ($group==3)
+       {
+               $num =~ s/(\d)(\d)(\d)/ ($1==1 ? " $one" : $1 ? unit($1) :" $zero")." ".($2 ? ten($2,$3) : $3 ? " $zero " . unit($3) : " $zero $zero") . "$comma " /eg;
+               $num =~ s/(\d)(\d)/ ($1 ? ten($1,$2) : $2 ? " $zero " . unit($2) : " $zero $zero") . "$comma " /e;
+               $num =~ s/(\d)/ ($1==1 ? " $one" : $1 ? unit($1) :" $zero")."$comma " /e;
+       }
+       elsif ($num+0==0) {
+               $num = $zero;
+       }
+       elsif ($num+0==1) {
+               $num = $one;
+       }
+       else {
+               $num =~ s/\A\s*0+//;
+               my $mill = 0;
+               1 while $num =~ s/(\d)(\d)(\d)(?=\D*\Z)/ hund($1,$2,$3,$mill++,$and) /e;
+               $num =~ s/(\d)(\d)(?=\D*\Z)/ ten($1,$2,$mill)."$comma " /e;
+               $num =~ s/(\d)(?=\D*\Z)/ unit($1,$mill) . "$comma "/e;
+       }
+       return $num;
+}
+
+sub NUMWORDS
+{
+       my $num = shift;
+       my %arg = ( %default_args, @_ );
+       my $group = $arg{group};
+
+       die "Bad chunking option: $group\n" unless $group =~ /\A[0-3]\Z/;
+       my $sign = ($num =~ /\A\s*\+/) ? "plus"
+                : ($num =~ /\A\s*\-/) ? "minus"
+                : '';
+
+       my ($zero, $one) = @arg{'zero','one'};
+       my $comma = $arg{comma};
+       my $and = $arg{'and'};
+
+       my $ord = $num =~ s/(st|nd|rd|th)\Z//;
+       my @chunks = ($arg{decimal})
+                       ? $group ? split(/\./, $num) : split(/\./, $num, 2)
+                       : ($num);
+
+       my $first = 1;
+
+       if ($chunks[0] eq '') { $first=0; shift @chunks; }
+
+       foreach ( @chunks )
+       {
+               s/\D//g;
+               $_ = '0' unless $_;
+
+               if (!$group && !$first) { $_ = enword($_,1,$zero,$one,$comma,$and) }
+               else                    { $_ = enword($_,$group,$zero,$one,$comma,$and) }
+
+               s/, \Z//;
+               s/\s+,/,/g;
+               s/, (\S+)\s+\Z/ $and $1/ if !$group and $first;
+               s/\s+/ /g;
+               s/(\A\s|\s\Z)//g;
+               $first = '' if $first;
+       }
+
+       my @numchunks = ();
+       if ($first =~ /0/)
+       {
+               unshift @chunks, '';
+       }
+       else
+       {
+               @numchunks = split /\Q$comma /, $chunks[0];
+       }
+
+       $numchunks[-1] =~ s/($ordinal_suff)\Z/$ordinal{$1}/
+               if $ord and @numchunks;
+
+       foreach (@chunks[1..$#chunks])
+       {
+               push @numchunks, $arg{decimal};
+               push @numchunks, split /\Q$comma /;
+       }
+
+       if (wantarray)
+       {
+               unshift @numchunks, $sign if $sign;
+               return @numchunks
+       }
+       elsif ($group)
+       {
+               return ($sign?"$sign ":'') .  join ", ", @numchunks;
+       }
+       else
+       {
+               $num = ($sign?"$sign ":'') . shift @numchunks;
+               $first = ($num !~ /$arg{decimal}\Z/);
+               foreach ( @numchunks )
+               {
+                       if (/\A$arg{decimal}\Z/)
+                       {
+                               $num .= " $_";
+                               $first = 0;
+                       }
+                       elsif ($first)
+                       {
+                               $num .= "$comma $_";
+                       }
+                       else
+                       {
+                               $num .= " $_";
+                       }
+               }
+               return $num;
+       }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Lingua::EN::Inflect - Convert singular to plural. Select "a" or "an".
+
+=head1 VERSION
+
+This document describes version 1.86 of Lingua::EN::Inflect,
+released October 20, 2000.
+
+=head1 SYNOPSIS
+
+ use Lingua::EN::Inflect qw ( PL PL_N PL_V PL_ADJ NO NUM
+                             PL_eq PL_N_eq PL_V_eq PL_ADJ_eq
+                             A AN
+                             PART_PRES
+                             ORD NUMWORDS
+                             inflect classical
+                             def_noun def_verb def_adj def_a def_an ); 
+
+
+ # UNCONDITIONALLY FORM THE PLURAL
+
+      print "The plural of ", $word, " is ", PL($word), "\n";
+
+
+ # CONDITIONALLY FORM THE PLURAL
+
+      print "I saw $cat_count ", PL("cat",$cat_count), "\n";
+
+
+ # FORM PLURALS FOR SPECIFIC PARTS OF SPEECH
+
+      print PL_N("I",$N1), PL_V("saw",$N1),
+           PL_ADJ("my",$N2), PL_N("saw",$N2), "\n";
+
+
+ # DEAL WITH "0/1/N" -> "no/1/N" TRANSLATION:
+
+      print "There ", PL_V("was",$errors), NO(" error",$errors), "\n";
+
+
+ # USE DEFAULT COUNTS:
+
+      print NUM($N1,""), PL("I"), PL_V(" saw"), NUM($N2), PL_N(" saw");
+      print "There ", NUM($errors,''), PL_V("was"), NO(" error"), "\n";
+
+
+ # COMPARE TWO WORDS "NUMBER-INSENSITIVELY":
+
+      print "same\n"      if PL_eq($word1, $word2);
+      print "same noun\n" if PL_eq_N($word1, $word2);
+      print "same verb\n" if PL_eq_V($word1, $word2);
+      print "same adj.\n" if PL_eq_ADJ($word1, $word2);
+
+
+ # ADD CORRECT "a" OR "an" FOR A GIVEN WORD:
+
+      print "Did you want ", A($thing), " or ", AN($idea), "\n";
+
+
+ # CONVERT NUMERALS INTO ORDINALS (i.e. 1->1st, 2->2nd, 3->3rd, etc.)
+
+      print "It was", ORD($position), " from the left\n";
+
+ # CONVERT NUMERALS TO WORDS (i.e. 1->"one", 101->"one hundred and one", etc.)
+ # IN A SCALAR CONTEXT: GET BACK A SINGLE STRING...
+
+    $words = NUMWORDS(1234);     # "one thousand, two hundred and thirty-four"
+    $words = NUMWORDS(ORD(1234)); # "one thousand, two hundred and thirty-fourth"
+
+
+ # IN A LIST CONTEXT: GET BACK A LIST OF STRINGSi, ONE FOR EACH "CHUNK"...
+
+    @words = NUMWORDS(1234);   # ("one thousand","two hundred and thirty-four")
+
+
+ # OPTIONAL PARAMETERS CHANGE TRANSLATION:
+
+    $words = NUMWORDS(12345, group=>1);
+                               # "one, two, three, four, five"
+
+    $words = NUMWORDS(12345, group=>2);
+                               # "twelve, thirty-four, five"
+
+    $words = NUMWORDS(12345, group=>3);
+                               # "one twenty-three, forty-five"
+
+    $words = NUMWORDS(1234, 'and'=>'');
+                               # "one thousand, two hundred thirty-four"
+
+    $words = NUMWORDS(1234, 'and'=>', plus');
+                               # "one thousand, two hundred, plus thirty-four"
+
+    $words = NUMWORDS(555_1202, group=>1, zero=>'oh');
+                               # "five, five, five, one, two, oh, two"
+
+    $words = NUMWORDS(555_1202, group=>1, one=>'unity');
+                               # "five, five, five, unity, two, oh, two"
+
+    $words = NUMWORDS(123.456, group=>1, decimal=>'mark');
+                               # "one two three mark four five six"
+
+
+ # REQUIRE "CLASSICAL" PLURALS (EG: "focus"->"foci", "cherub"->"cherubim")
+
+      classical;             # USE ALL CLASSICAL PLURALS
+
+      classical 1;              #  USE ALL CLASSICAL PLURALS
+      classical 0;              #  USE ALL MODERN PLURALS (DEFAULT)
+
+      classical 'zero';      #  "no error" INSTEAD OF "no errors"
+      classical zero=>1;     #  "no error" INSTEAD OF "no errors"
+      classical zero=>0;     #  "no errors" INSTEAD OF "no error" 
+
+      classical 'herd';      #  "2 buffalo" INSTEAD OF "2 buffalos"
+      classical herd=>1;     #  "2 buffalo" INSTEAD OF "2 buffalos"
+      classical herd=>0;     #  "2 buffalos" INSTEAD OF "2 buffalo"
+
+      classical 'persons';   # "2 chairpersons" INSTEAD OF "2 chairpeople"
+      classical persons=>1;  # "2 chairpersons" INSTEAD OF "2 chairpeople"
+      classical persons=>0;  # "2 chairpeople" INSTEAD OF "2 chairpersons"
+
+      classical 'ancient';   # "2 formulae" INSTEAD OF "2 formulas"
+      classical ancient=>1;  # "2 formulae" INSTEAD OF "2 formulas"
+      classical ancient=>0;  # "2 formulas" INSTEAD OF "2 formulae"
+
+
+
+ # INTERPOLATE "PL()", "PL_N()", "PL_V()", "PL_ADJ()", A()", "AN()"
+ # "NUM()" AND "ORD()" WITHIN STRINGS:
+
+      print inflect("The plural of $word is PL($word)\n");
+      print inflect("I saw $cat_count PL("cat",$cat_count)\n");
+      print inflect("PL(I,$N1) PL_V(saw,$N1) PL(a,$N2) PL_N(saw,$N2)");
+      print inflect("NUM($N1,)PL(I) PL_V(saw) NUM($N2,)PL(a) PL_N(saw)");
+      print inflect("I saw NUM($cat_count) PL("cat")\nNUM()");
+      print inflect("There PL_V(was,$errors) NO(error,$errors)\n");
+      print inflect("There NUM($errors,) PL_V(was) NO(error)\n";
+      print inflect("Did you want A($thing) or AN($idea)\n");
+      print inflect("It was ORD($position) from the left\n");
+
+
+ # ADD USER-DEFINED INFLECTIONS (OVERRIDING INBUILT RULES):
+
+      def_noun  "VAX"  => "VAXen";     # SINGULAR => PLURAL
+
+      def_verb  "will" => "shall",     # 1ST PERSON SINGULAR => PLURAL
+                "will" => "will",      # 2ND PERSON SINGULAR => PLURAL
+                "will" => "will",      # 3RD PERSON SINGULAR => PLURAL
+
+      def_adj   "hir"  => "their",     # SINGULAR => PLURAL
+
+      def_a    "h"                     # "AY HALWAYS SEZ 'HAITCH'!"
+
+      def_an   "horrendous.*"          # "AN HORRENDOUS AFFECTATION"
+
+
+=head1 DESCRIPTION
+
+The exportable subroutines of Lingua::EN::Inflect provide plural
+inflections, "a"/"an" selection for English words, and manipulation
+of numbers as words
+
+Plural forms of all nouns, most verbs, and some adjectives are
+provided. Where appropriate, "classical" variants (for example: "brother" ->
+"brethren", "dogma" -> "dogmata", etc.) are also provided.
+
+Pronunciation-based "a"/"an" selection is provided for all English
+words, and most initialisms.
+
+It is also possible to inflect numerals (1,2,3) to ordinals (1st, 2nd, 3rd)
+and to english words ("one", "two", "three).
+
+In generating these inflections, Lingua::EN::Inflect follows the Oxford
+English Dictionary and the guidelines in Fowler's Modern English
+Usage, preferring the former where the two disagree.
+
+The module is built around standard British spelling, but is designed
+to cope with common American variants as well. Slang, jargon, and
+other English dialects are I<not> explicitly catered for.
+
+Where two or more inflected forms exist for a single word (typically a
+"classical" form and a "modern" form), Lingua::EN::Inflect prefers the
+more common form (typically the "modern" one), unless "classical"
+processing has been specified
+(see L<"MODERN VS CLASSICAL INFLECTIONS">).
+
+=head1 FORMING PLURALS
+
+=head2 Inflecting Plurals
+
+All of the C<PL_...> plural inflection subroutines take the word to be
+inflected as their first argument and return the corresponding inflection.
+Note that all such subroutines expect the I<singular> form of the word. The
+results of passing a plural form are undefined (and unlikely to be correct).
+
+The C<PL_...> subroutines also take an optional second argument,
+which indicates the grammatical "number" of the word (or of another word
+with which the word being inflected must agree). If the "number" argument is
+supplied and is not C<1> (or C<"one"> or C<"a">, or some other adjective that
+implies the singular), the plural form of the word is returned. If the
+"number" argument I<does> indicate singularity, the (uninflected) word
+itself is returned. If the number argument is omitted, the plural form
+is returned unconditionally.
+
+The various subroutines are:
+
+=over 8
+
+=item C<PL_N($;$)>
+
+The exportable subroutine C<PL_N()> takes a I<singular> English noun or
+pronoun and returns its plural. Pronouns in the nominative ("I" ->
+"we") and accusative ("me" -> "us") cases are handled, as are
+possessive pronouns ("mine" -> "ours").
+
+
+=item C<PL_V($;$)>
+
+The exportable subroutine C<PL_V()> takes the I<singular> form of a
+conjugated verb (that is, one which is already in the correct "person"
+and "mood") and returns the corresponding plural conjugation.
+
+
+=item C<PL_ADJ($;$)>
+
+The exportable subroutine C<PL_ADJ()> takes the I<singular> form of
+certain types of adjectives and returns the corresponding plural form.
+Adjectives that are correctly handled include: "numerical" adjectives
+("a" -> "some"), demonstrative adjectives ("this" -> "these", "that" ->
+"those"), and possessives ("my" -> "our", "cat's" -> "cats'", "child's"
+-> "childrens'", etc.)
+
+
+=item C<PL($;$)>
+
+The exportable subroutine C<PL()> takes a I<singular> English noun,
+pronoun, verb, or adjective and returns its plural form. Where a word
+has more than one inflection depending on its part of speech (for
+example, the noun "thought" inflects to "thoughts", the verb "thought"
+to "thought"), the (singular) noun sense is preferred to the (singular)
+verb sense.
+
+Hence C<PL("knife")> will return "knives" ("knife" having been treated
+as a singular noun), whereas C<PL("knifes")> will return "knife"
+("knifes" having been treated as a 3rd person singular verb).
+
+The inherent ambiguity of such cases suggests that,
+where the part of speech is known, C<PL_N>, C<PL_V>, and
+C<PL_ADJ> should be used in preference to C<PL>.
+
+=back
+
+Note that all these subroutines ignore any whitespace surrounding the
+word being inflected, but preserve that whitespace when the result is
+returned. For example, C<S<PL(" cat  ")>> returns S<" cats  ">.
+
+
+=head2 Numbered plurals
+
+The C<PL_...> subroutines return only the inflected word, not the count that
+was used to inflect it. Thus, in order to produce "I saw 3 ducks", it
+is necessary to use:
+
+       print "I saw $N ", PL_N($animal,$N), "\n";
+
+Since the usual purpose of producing a plural is to make it agree with
+a preceding count, Lingua::EN::Inflect provides an exportable subroutine
+(C<NO($;$)>) which, given a word and a(n optional) count, returns the
+count followed by the correctly inflected word. Hence the previous
+example can be rewritten:
+
+       print "I saw ", NO($animal,$N), "\n";
+
+In addition, if the count is zero (or some other term which implies
+zero, such as C<"zero">, C<"nil">, etc.) the count is replaced by the
+word "no". Hence, if C<$N> had the value zero, the previous example
+would print the somewhat more elegant:
+
+       I saw no animals
+
+rather than:
+
+       I saw 0 animals
+
+Note that the name of the subroutine is a pun: the subroutine
+returns either a number (a I<No.>) or a C<"no">, in front of the
+inflected word.
+
+
+=head2 Reducing the number of counts required
+
+In some contexts, the need to supply an explicit count to the various
+C<PL_...> subroutines makes for tiresome repetition. For example:
+
+       print PL_ADJ("This",$errors), PL_N(" error",$errors),
+             PL_V(" was",$errors), " fatal.\n";
+
+Lingua::EN::Inflect therefore provides an exportable subroutine
+(C<NUM($;$)>) which may be used to set a persistent "default number"
+value. If such a value is set, it is subsequently used whenever an
+optional second "number" argument is omitted. The default value thus set 
+can subsequently be removed by calling C<NUM()> with no arguments.
+Hence we could rewrite the previous example:
+
+       NUM($errors);
+       print PL_ADJ("This"), PL_N(" error"), PL_V(" was"), "fatal.\n";
+       NUM();
+
+Normally, C<NUM()> returns its first argument, so that it may also
+be "inlined" in contexts like:
+
+       print NUM($errors), PL_N(" error"), PL_V(" was"), " detected.\n"
+       print PL_ADJ("This"), PL_N(" error"), PL_V(" was"), "fatal.\n"
+               if $severity > 1;
+
+However, in certain contexts (see L<"INTERPOLATING INFLECTIONS IN STRINGS">)
+it is preferable that C<NUM()> return an empty string. Hence C<NUM()>
+provides an optional second argument. If that argument is supplied (that is, if
+it is defined) and evaluates to false, C<NUM> returns an empty string
+instead of its first argument. For example:
+
+       print NUM($errors,0), NO("error"), PL_V(" was"), " detected.\n";
+       print PL_ADJ("This"), PL_N(" error"), PL_V(" was"), "fatal.\n"
+               if $severity > 1;
+       
+
+
+=head2 Number-insensitive equality
+
+Lingua::EN::Inflect also provides a solution to the problem
+of comparing words of differing plurality through the exportable subroutines
+C<PL_eq($$)>, C<PL_N_eq($$)>, C<PL_V_eq($$)>, and C<PL_ADJ_eq($$)>.
+Each  of these subroutines takes two strings, and  compares them
+using the corresponding plural-inflection subroutine (C<PL()>, C<PL_N()>,
+C<PL_V()>, and C<PL_ADJ()> respectively).
+
+The comparison returns true if:
+
+=over 8
+
+=item *
+
+the strings are C<eq>-equal, or
+
+=item *
+
+one string is C<eq>-equal to a plural form of the other, or
+
+=item *
+
+the strings are two different plural forms of the one word.
+
+=back
+
+Hence all of the following return true:
+
+       PL_eq("index","index")          # RETURNS "eq"
+       PL_eq("index","indexes")        # RETURNS "s:p"
+       PL_eq("index","indices")        # RETURNS "s:p"
+       PL_eq("indexes","index")        # RETURNS "p:s"
+       PL_eq("indices","index")        # RETURNS "p:s"
+       PL_eq("indices","indexes")      # RETURNS "p:p"
+       PL_eq("indexes","indices")      # RETURNS "p:p"
+       PL_eq("indices","indices")      # RETURNS "eq"
+
+As indicated by the comments in the previous example, the actual value
+returned by the various C<PL_eq_...> subroutines encodes which of the
+three equality rules succeeded: "eq" is returned if the strings were
+identical, "s:p" if the strings were singular and plural respectively,
+"p:s" for plural and singular, and "p:p" for two distinct plurals.
+Inequality is indicated by returning an empty string.
+
+It should be noted that two distinct singular words which happen to take
+the same plural form are I<not> considered equal, nor are cases where
+one (singular) word's plural is the other (plural) word's singular.
+Hence all of the following return false:
+
+       PL_eq("base","basis")       # ALTHOUGH BOTH -> "bases"
+       PL_eq("syrinx","syringe")   # ALTHOUGH BOTH -> "syringes"
+       PL_eq("she","he")           # ALTHOUGH BOTH -> "they"
+
+       PL_eq("opus","operas")      # ALTHOUGH "opus" -> "opera" -> "operas"
+       PL_eq("taxi","taxes")       # ALTHOUGH "taxi" -> "taxis" -> "taxes"
+
+Note too that, although the comparison is "number-insensitive" it is I<not>
+case-insensitive (that is, C<PL("time","Times")> returns false. To obtain
+both number and case insensitivity, prefix both arguments with C<lc>
+(that is, C<PL(lc "time", lc "Times")> returns true).
+
+
+=head1 OTHER VERB FORMS
+
+=head2 Present participles
+
+C<Lingua::EN::Inflect> also provides the C<PART_PRES> subroutine,
+which can take a 3rd person singular verb and
+correctly inflect it to its present participle:
+
+       PART_PRES("runs")       # "running"
+       PART_PRES("loves")      # "loving"
+       PART_PRES("eats")       # "eating"
+       PART_PRES("bats")       # "batting"
+       PART_PRES("spies")      # "spying"
+
+
+=head1 PROVIDING INDEFINITE ARTICLES
+
+=head2 Selecting indefinite articles
+
+Lingua::EN::Inflect provides two exportable subroutines (C<A($;$)> and
+C<AN($;$)>) which will correctly prepend the appropriate indefinite
+article to a word, depending on its pronunciation. For example:
+
+       A("cat")                # -> "a cat"
+       AN("cat")               # -> "a cat"
+       A("euphemism")          # -> "a euphemism"
+       A("Euler number")       # -> "an Euler number"
+       A("hour")               # -> "an hour"
+       A("houri")              # -> "a houri"
+
+The two subroutines are I<identical> in function and may be used
+interchangeably. The only reason that two versions are provided is to
+enhance the readability of code such as:
+
+       print "That is ", AN($errortype), " error\n;
+       print "That is ", A($fataltype), " fatal error\n;
+
+Note that in both cases the actual article provided depends I<only> on
+the pronunciation of the first argument, I<not> on the name of the
+subroutine.
+
+C<A()> and C<AN()> will ignore any indefinite article that already
+exists at the start of the string. Thus:
+
+       @half_arked = (
+               "a elephant",
+               "a giraffe",
+               "an ewe",
+               "a orangutan",
+       );
+
+       print A($_), "\n" for @half_arked;
+
+       # prints:
+       #     an elephant
+       #     a giraffe
+       #     a ewe
+       #     an orangutan
+
+
+C<A()> and C<AN()> both take an optional second argument. As with the
+C<PL_...> subroutines, this second argument is a "number" specifier. If
+its value is C<1> (or some other value implying singularity), C<A()> and
+C<AN()> insert "a" or "an" as appropriate. If the number specifier 
+implies plurality, (C<A()> and C<AN()> insert the actual second argument instead.
+For example:
+
+       A("cat",1)              # -> "a cat"
+       A("cat",2)              # -> "2 cat"
+       A("cat","one")          # -> "one cat"
+       A("cat","no")           # -> "no cat"
+
+Note that, as implied by the previous examples, C<A()> and
+C<AN()> both assume that their job is merely to provide the correct
+qualifier for a word (that is: "a", "an", or the specified count).
+In other words, they assume that the word they are given has
+already been correctly inflected for plurality. Hence, if C<$N> 
+has the value 2, then:
+
+      print A("cat",$N);
+
+prints "2 cat", instead of "2 cats". The correct approach is to use:
+
+      print A(PL("cat",$N),$N);
+
+or, better still:
+
+      print NO("cat",$N);
+
+Note too that, like the various C<PL_...> subroutines, whenever C<A()>
+and C<AN()> are called with only one argument they are subject to the
+effects of any preceding call to C<NUM()>. Hence, another possible
+solution is:
+
+      NUM($N);
+      print A(PL("cat"));
+       
+
+=head2 Indefinite articles and initialisms
+
+"Initialisms" (sometimes inaccurately called "acronyms") are terms which
+have been formed from the initial letters of words in a phrase (for
+example, "NATO", "NBL", "S.O.S.", "SCUBA", etc.)
+
+Such terms present a particular challenge when selecting between "a"
+and "an", since they are sometimes pronounced as if they were a single
+word ("nay-tow", "sku-ba") and sometimes as a series of letter names
+("en-eff-ell", "ess-oh-ess").
+
+C<A()> and C<AN()> cope with this dichotomy using a series of inbuilt
+rules, which may be summarized as:
+
+=over 8
+
+=item 1.
+
+If the word starts with a single letter, followed by a period or dash
+(for example, "R.I.P.", "C.O.D.", "e-mail", "X-ray", "T-square"), then
+choose the appropriate article for the I<sound> of the first letter
+("an R.I.P.", "a C.O.D.", "an e-mail", "an X-ray", "a T-square").
+
+=item 2.
+
+If the first two letters of the word are capitals,
+consonants, and do not appear at the start of any known English word,
+(for example, "LCD", "XML", "YWCA"), then once again choose "a" or
+"an" depending on the I<sound> of the first letter ("an LCD", "an
+XML", "a YWCA").
+
+=item 3.
+
+Otherwise, assume the string is a capitalized word or a
+pronounceable initialism (for example, "LED", "OPEC", "FAQ", "UNESCO"), and
+therefore takes "a" or "an" according to the (apparent) pronunciation of
+the entire word ("a LED", "an OPEC", "a FAQ", "a UNESCO").
+
+=back
+
+Note that rules 1 and 3 together imply that the presence or absence of
+punctuation may change the selection of indefinite article for a
+particular initialism (for example, "a FAQ" but "an F.A.Q.").
+
+
+=head2 Indefinite articles and "soft H's"
+
+Words beginning in the letter 'H' present another type of difficulty
+when selecting a suitable indefinite article. In a few such words
+(for example, "hour", "honour", "heir") the 'H' is not voiced at
+all, and so such words inflect with "an". The remaining cases
+("voiced H's") may be divided into two categories:
+"hard H's" (such as "hangman", "holograph", "hat", etc.) and
+"soft H's" (such as "hysterical", "horrendous", "holy", etc.)
+
+Hard H's always take "a" as their indefinite article, and soft
+H's normally do so as well. But I<some> English speakers prefer
+"an" for soft H's (although the practice is now generally considered an
+affectation, rather than a legitimate grammatical alternative).
+
+At present, the C<A()> and C<AN()> subroutines ignore soft H's and use
+"a" for any voiced 'H'. The author would, however, welcome feedback on
+this decision (envisaging a possible future "soft H" mode).
+
+
+=head1 INFLECTING ORDINALS
+
+Occasionally it is useful to present an integer value as an ordinal
+rather than as a numeral. For example:
+
+       Enter password (1st attempt): ********
+       Enter password (2nd attempt): *********
+       Enter password (3rd attempt): *********
+       No 4th attempt. Access denied.
+
+To this end, Lingua::EN::Inflect provides the C<ORD()> subroutine.
+<ORD()> takes a single argument and forms its ordinal equivalent.
+If the argument isn't a numerical integer, it just adds "-th".
+
+
+=head1 CONVERTING NUMBERS TO WORDS
+
+The exportable subroutine C<NUMWORDS> takes a number (cardinal or ordinal)
+and returns an English representation of that number. In a scalar context 
+a string is returned. Hence:
+
+       use Lingua::EN::Inflect qw( NUMWORDS );
+
+       $words = NUMWORDS(1234567);
+
+puts the string:
+
+       "one million, two hundred and thirty-four thousand, five hundred and sixty-seven"
+       
+into $words.
+
+In a list context each comma-separated chunk is returned as a separate element.
+Hence:
+
+       @words = NUMWORDS(1234567);
+
+puts the list:
+
+       ("one million",
+        "two hundred and thirty-four thousand",
+        "five hundred and sixty-seven")
+
+into @words.
+
+Non-digits (apart from an optional leading plus or minus sign,
+any decimal points, and ordinal suffixes -- see below) are silently
+ignored, so the following all produce identical results:
+
+        NUMWORDS(5551202);
+        NUMWORDS(5_551_202);
+        NUMWORDS("5,551,202");
+        NUMWORDS("555-1202");
+
+That last case is a little awkward since it's almost certainly a phone number,
+and "five million, five hundred and fifty-one thousand, two hundred and two"
+probably isn't what's wanted.
+
+To overcome this, C<NUMWORDS()> takes an optional named argument, 'group',
+which changes how numbers are translated. The argument must be a
+positive integer less than four, which indicated how the digits of the
+number are to be grouped. If the argument is C<1>, then each digit is
+translated separately. If the argument is C<2>, pairs of digits
+(starting from the I<left>) are grouped together. If the argument is
+C<3>, triples of numbers (again, from the I<left>) are grouped. Hence:
+
+        NUMWORDS("555-1202", group=>1)
+
+returns C<"five, five, five, one, two, zero, two">, whilst:
+
+        NUMWORDS("555-1202", group=>2)
+
+returns C<"fifty-five, fifty-one, twenty, two">, and:
+
+        NUMWORDS("555-1202", group=>3)
+
+returns C<"five fifty-five, one twenty, two">.
+
+Phone numbers are often written in words as
+C<"five..five..five..one..two..zero..two">, which is also easy to
+achieve:
+
+        join '..', NUMWORDS("555-1202", group=>1)
+
+C<NUMWORDS> also handles decimal fractions. Hence:
+
+        NUMWORDS("1.2345")
+
+returns C<"one point two three four five"> in a scalar context
+and C<("one","point","two","three","four","five")>) in an array context.
+Exponent form (C<"1.234e56">) is not yet handled.
+
+Multiple decimal points are only translated in one of the "grouping" modes.
+Hence:
+
+        NUMWORDS(101.202.303)
+
+returns C<"one hundred and one point two zero two three zero three">,
+whereas:
+
+        NUMWORDS(101.202.303, group=>1)
+
+returns C<"one zero one point two zero two point three zero three">.
+
+The digit C<'0'> is unusual in that in may be translated to English as "zero",
+"oh", or "nought". To cater for this diversity, C<NUMWORDS> may be passed
+a named argument, 'zero', which may be set to
+the desired translation of C<'0'>. For example:
+
+        print join "..", NUMWORDS("555-1202", group=>3, zero=>'oh')
+
+prints C<"five..five..five..one..two..oh..two">.
+By default, zero is rendered as "zero".
+
+Likewise, the digit C<'1'> may be rendered as "one" or "a/an" (or very
+occasionally other variants), depending on the context. So there is a
+C<'one'> argument as well:
+
+        print NUMWORDS($_, one=>'a solitary', zero=>'no more'),
+              PL(" bottle of beer on the wall\n", $_)
+                   for (3,2,1,0);
+
+        # prints:
+        #     three bottles of beer on the wall
+        #     two bottles of beer on the wall
+        #     a solitary bottle of beer on the wall
+        #     no more bottles of beer on the wall
+              
+Care is needed if the word "a/an" is to be used as a C<'one'> value.
+Unless the next word is known in advance, it's almost always necessary
+to use the C<A> function as well:
+
+        print A( NUMWORDS(1, one=>'a') . " $_\n")
+            for qw(cat aardvark ewe hour);   
+
+       # prints:
+       #     a cat
+       #     an aardvark
+       #     a ewe
+       #     an hour
+
+Another major regional variation in number translation is the use of
+"and" in certain contexts. The named argument 'and'
+allows the programmer to specify how "and" should be handled. Hence:
+
+        print scalar NUMWORDS("765", 'and'=>'')
+
+prints "seven hundred sixty-five", instead of "seven hundred and sixty-five".
+By default, the "and" is included.
+
+The translation of the decimal point is also subject to variation
+(with "point", "dot", and "decimal" being the favorites).
+The named argument 'decimal' allows the
+programmer to how the decimal point should be rendered. Hence:
+
+        print scalar NUMWORDS("666.124.64.101", group=>3, decimal=>'dot')
+
+prints "six sixty-six, dot, one twenty-four, dot, sixty-four, dot, one zero one"
+By default, the decimal point is rendered as "point".
+
+C<NUMWORDS> also handles the ordinal forms of numbers. So:
+
+        print scalar NUMWORDS('1st');
+        print scalar NUMWORDS('3rd');
+        print scalar NUMWORDS('202nd');
+        print scalar NUMWORDS('1000000th');
+
+print:
+
+        first
+        third
+        two hundred and twenty-second
+        one millionth
+
+Two common idioms in this regard are:
+
+        print scalar NUMWORDS(ORD($number));
+
+and:
+
+        print scalar ORD(NUMWORDS($number));
+
+These are identical in effect, except when $number contains a decimal:
+
+        $number = 99.09;
+        print scalar NUMWORDS(ORD($number));    # ninety-ninth point zero nine
+        print scalar ORD(NUMWORDS($number));    # ninety-nine point zero ninth
+
+Use whichever you feel is most appropriate.
+
+
+=head1 INTERPOLATING INFLECTIONS IN STRINGS
+
+By far the commonest use of the inflection subroutines is to
+produce message strings for various purposes. For example:
+
+        print NUM($errors), PL_N(" error"), PL_V(" was"), " detected.\n";
+        print PL_ADJ("This"), PL_N(" error"), PL_V(" was"), "fatal.\n"
+                if $severity > 1;
+
+Unfortunately the need to separate each subroutine call detracts
+significantly from the readability of the resulting code. To ameliorate
+this problem, Lingua::EN::Inflect provides an exportable string-interpolating
+subroutine (C<inflect($)>), which recognizes calls to the various inflection
+subroutines within a string and interpolates them appropriately.
+
+Using C<inflect> the previous example could be rewritten:
+
+        print inflect "NUM($errors) PL_N(error) PL_V(was) detected.\n";
+        print inflect "PL_ADJ(This) PL_N(error) PL_V(was) fatal.\n"
+                if $severity > 1;
+
+Note that C<inflect> also correctly handles calls to the C<NUM()> subroutine
+(whether interpolated or antecedent). The C<inflect()> subroutine has
+a related extra feature, in that it I<automatically> cancels any "default
+number" value before it returns its interpolated string. This means that
+calls to C<NUM()> which are embedded in an C<inflect()>-interpolated
+string do not "escape" and interfere with subsequent inflections.
+
+
+=head1 MODERN VS CLASSICAL INFLECTIONS
+
+Certain words, mainly of Latin or Ancient Greek origin, can form
+plurals either using the standard English "-s" suffix, or with 
+their original Latin or Greek inflections. For example:
+
+        PL("stigma")            # -> "stigmas" or "stigmata"
+        PL("torus")             # -> "toruses" or "tori"
+        PL("index")             # -> "indexes" or "indices"
+        PL("millennium")        # -> "millenniums" or "millennia"
+        PL("ganglion")          # -> "ganglions" or "ganglia"
+        PL("octopus")           # -> "octopuses" or "octopodes"
+
+
+Lingua::EN::Inflect caters to such words by providing an
+"alternate state" of inflection known as "classical mode".
+By default, words are inflected using their contemporary English
+plurals, but if classical mode is invoked, the more traditional 
+plural forms are returned instead.
+
+The exportable subroutine C<classical()> controls this feature.
+If C<classical()> is called with no arguments, it unconditionally
+invokes classical mode. If it is called with a single argument, it
+turns all classical inflects on or off (depending on whether the argument is
+true or false). If called with two or more arguments, those arguments 
+specify which aspects of classical behaviour are to be used.
+
+Thus:
+
+        classical;                  # SWITCH ON CLASSICAL MODE
+        print PL("formula");        # -> "formulae"
+
+        classical 0;                # SWITCH OFF CLASSICAL MODE
+        print PL("formula");        # -> "formulas"
+
+        classical $cmode;           # CLASSICAL MODE IFF $cmode
+        print PL("formula");        # -> "formulae" (IF $cmode)
+                                    # -> "formulas" (OTHERWISE)
+
+        classical herd=>1;          # SWITCH ON CLASSICAL MODE FOR "HERD" NOUNS
+        print PL("wilderbeest");    # -> "wilderbeest"
+
+        classical names=>1;         # SWITCH ON CLASSICAL MODE FOR NAMES
+        print PL("sally");          # -> "sallies"
+        print PL("Sally");          # -> "Sallys"
+
+Note however that C<classical()> has no effect on the inflection of words which
+are now fully assimilated. Hence:
+
+        PL("forum")             # ALWAYS -> "forums"
+        PL("criterion")         # ALWAYS -> "criteria"
+
+LEI assumes that a capitalized word is a person's name. So it forms the
+plural according to the rules for names (which is that you don't
+inflect, you just add -s or -es). You can choose to turn that behaviour
+off (it's on by the default, even when the module isn't in classical
+mode) by calling C< classical(names=>0) >;
+
+=head1 USER-DEFINED INFLECTIONS
+
+=head2 Adding plurals at run-time
+
+Lingua::EN::Inflect provides five exportable subroutines which allow
+the programmer to override the module's behaviour for specific cases:
+
+=over 8
+
+=item C<def_noun($$)>
+
+The C<def_noun> subroutine takes a pair of string arguments: the singular and
+plural forms of the noun being specified. The singular form 
+specifies a pattern to be interpolated (as C<m/^(?:$first_arg)$/i>).
+Any noun matching this pattern is then replaced by the string in the
+second argument. The second argument specifies a string which is
+interpolated after the match succeeds, and is then used as the plural
+form. For example:
+
+      def_noun  'cow'        => 'kine';
+      def_noun  '(.+i)o'     => '$1i';
+      def_noun  'spam(mer)?' => '\\$\\%\\@#\\$\\@#!!';
+
+Note that both arguments should usually be specified in single quotes,
+so that they are not interpolated when they are specified, but later (when
+words are compared to them). As indicated by the last example, care
+also needs to be taken with certain characters in the second argument,
+to ensure that they are not unintentionally interpolated during comparison.
+
+The second argument string may also specify a second variant of the plural
+form, to be used when "classical" plurals have been requested. The beginning
+of the second variant is marked by a '|' character:
+
+      def_noun  'cow'        => 'cows|kine';
+      def_noun  '(.+i)o'     => '$1os|$1i';
+      def_noun  'spam(mer)?' => '\\$\\%\\@#\\$\\@#!!|varmints';
+
+If no classical variant is given, the specified plural form is used in
+both normal and "classical" modes.
+
+If the second argument is C<undef> instead of a string, then the
+current user definition for the first argument is removed, and the
+standard plural inflection(s) restored.
+
+Note that in all cases, later plural definitions for a particular
+singular form replace earlier definitions of the same form. For example:
+
+      # FIRST, HIDE THE MODERN FORM....
+      def_noun  'aviatrix' => 'aviatrices';
+
+      # LATER, HIDE THE CLASSICAL FORM...
+      def_noun  'aviatrix' => 'aviatrixes';
+
+      # FINALLY, RESTORE THE DEFAULT BEHAVIOUR...
+      def_noun  'aviatrix' => undef;
+
+
+Special care is also required when defining general patterns and
+associated specific exceptions: put the more specific cases I<after>
+the general pattern. For example:
+
+      def_noun  '(.+)us' => '$1i';      # EVERY "-us" TO "-i"
+      def_noun  'bus'    => 'buses';    # EXCEPT FOR "bus"
+
+This "try-most-recently-defined-first" approach to matching
+user-defined words is also used by C<def_verb>, C<def_a> and C<def_an>.
+
+
+=item C<def_verb($$$$$$)>
+
+The C<def_verb> subroutine takes three pairs of string arguments (that is, six
+arguments in total), specifying the singular and plural forms of the three
+"persons" of verb. As with C<def_noun>, the singular forms are specifications of
+run-time-interpolated patterns, whilst the plural forms are specifications of
+(up to two) run-time-interpolated strings:
+
+       def_verb 'am'       => 'are',
+                'are'      => 'are|art",
+                'is'       => 'are';
+
+       def_verb 'have'     => 'have',
+                'have'     => 'have",
+                'ha(s|th)' => 'have';
+
+Note that as with C<def_noun>, modern/classical variants of plurals
+may be separately specified, subsequent definitions replace previous
+ones, and C<undef>'ed plural forms revert to the standard behaviour.
+
+
+=item C<def_adj($$)>
+
+The C<def_adj> subroutine takes a pair of string arguments, which specify
+the singular and plural forms of the adjective being defined.
+As with C<def_noun> and C<def_adj>, the singular forms are specifications of
+run-time-interpolated patterns, whilst the plural forms are specifications of
+(up to two) run-time-interpolated strings:
+
+       def_adj  'this'     => 'these',
+       def_adj  'red'      => 'red|gules',
+
+As previously, modern/classical variants of plurals
+may be separately specified, subsequent definitions replace previous
+ones, and C<undef>'ed plural forms revert to the standard behaviour.
+
+
+=item C<def_a($)> and C<def_an($)>
+
+The C<def_a> and C<def_an> subroutines each take a single argument, which
+specifies a pattern. If a word passed to C<A()> or C<AN()> matches this
+pattern, it will be prefixed (unconditionally) with the corresponding indefinite
+article. For example:
+
+      def_a  'error';
+      def_a  'in.+';
+
+      def_an 'mistake';
+      def_an 'error';
+
+As with the other C<def_...> subroutines, such redefinitions are sequential
+in effect so that, after the above example, "error" will be inflected with "an".
+
+=back
+
+=head2 The F<$HOME/.inflectrc> file
+
+When it is imported, Lingua::EN::Inflect executes (as Perl code)
+the contents of any file named F<.inflectrc> which it finds in the
+in the directory where F<Lingua/EN/Inflect.pm> is installed,
+or in the current home directory (C<$ENV{HOME}>), or in both.
+Note that the code is executed within the Lingua::EN::Inflect
+namespace.
+
+Hence the user or the local Perl guru can make appropriate calls to
+C<def_noun>, C<def_verb>, etc. in one of these F<.inflectrc> files, to
+permanently and universally modify the behaviour of the module. For example
+
+      > cat /usr/local/lib/perl5/Text/Inflect/.inflectrc
+
+      def_noun  "UNIX"  => "UN*X|UNICES";
+
+      def_verb  "teco"  => "teco",      # LITERALLY: "to edit with TECO"
+                "teco"  => "teco",
+                "tecos" => "teco";
+
+      def_a     "Euler.*";              # "Yewler" TURNS IN HIS GRAVE
+
+
+Note that calls to the C<def_...> subroutines from within a program
+will take precedence over the contents of the home directory
+F<.inflectrc> file, which in turn takes precedence over the system-wide
+F<.inflectrc> file.
+
+
+=head1 DIAGNOSTICS
+
+On loading, if the Perl code in a F<.inflectrc> file is invalid
+(syntactically or otherwise), an appropriate fatal error is issued.
+A common problem is not ending the file with something that
+evaluates to true (as the five C<def_...> subroutines do).
+
+Using the five C<def_...> subroutines directly in a program may also
+result in fatal diagnostics, if a (singular) pattern or an interpolated
+(plural) string is somehow invalid.
+
+Specific diagnostics related to user-defined inflections are:
+
+=over 8
+
+=item C<"Bad user-defined singular pattern:\n\t %s">
+
+The singular form of a user-defined noun or verb
+(as defined by a call to C<def_noun>, C<def_verb>, C<def_adj>,
+C<def_a> or C<def_an>) is not a valid Perl regular expression. The
+actual Perl error message is also given.
+
+=item C<"Bad user-defined plural string: '%s'">
+
+The plural form(s) of a user-defined noun or verb
+(as defined by a call to C<def_noun>, C<def_verb> or C<def_adj>)
+is not a valid Perl interpolated string (usually because it 
+interpolates some undefined variable).
+
+=item C<"Bad .inflectrc file (%s):\n %s">
+
+Some other problem occurred in loading the named local 
+or global F<.inflectrc> file. The Perl error message (including
+the line number) is also given.
+
+=back
+
+There are I<no> diagnosable run-time error conditions for the actual
+inflection subroutines, except C<NUMWORDS> and hence no run-time
+diagnostics. If the inflection subroutines are unable to form a plural
+via a user-definition or an inbuilt rule, they just "guess" the
+commonest English inflection: adding "-s" for nouns, removing "-s" for
+verbs, and no inflection for adjectives.
+
+C<Lingua::EN::Inflect::NUMWORDS()> can C<die> with the following messages:
+
+=over 8
+
+=item C<"Bad grouping option: %s">
+
+The optional argument to C<NUMWORDS()> wasn't 1, 2 or 3.
+
+=item C<"Number out of range">
+
+C<NUMWORDS()> was passed a number larger than
+999,999,999,999,999,999,999,999,999,999,999,999 (that is: nine hundred
+and ninety-nine decillion, nine hundred and ninety-nine nonillion, nine
+hundred and ninety-nine octillion, nine hundred and ninety-nine
+septillion, nine hundred and ninety-nine sextillion, nine hundred and
+ninety-nine quintillion, nine hundred and ninety-nine quadrillion, nine
+hundred and ninety-nine trillion, nine hundred and ninety-nine billion,
+nine hundred and ninety-nine million, nine hundred and ninety-nine
+thousand, nine hundred and ninety-nine :-) 
+
+The problem is that C<NUMWORDS> doesn't know any
+words for number components bigger than "decillion".
+
+
+=head1 OTHER ISSUES
+
+=head2 2nd Person precedence
+
+If a verb has identical 1st and 2nd person singular forms, but
+different 1st and 2nd person plural forms, then when its plural is
+constructed, the 2nd person plural form is always preferred.
+
+The author is not currently aware of any such verbs in English, but is
+not quite arrogant enough to assume I<ipso facto> that none exist.
+
+
+=head2 Nominative precedence
+
+The singular pronoun "it" presents a special problem because its plural form
+can vary, depending on its "case". For example:
+
+        It ate my homework       ->  They ate my homework
+        It ate it                ->  They ate them
+        I fed my homework to it  ->  I fed my homework to them
+
+As a consequence of this ambiguity, C<PL()> or C<PL_N> have been implemented
+so that they always return the I<nominative> plural (that is, "they").
+
+However, when asked for the plural of an unambiguously I<accusative>
+"it" (namely, C<PL("to it")>, C<PL_N("from it")>, C<PL("with it")>,
+etc.), both subroutines will correctly return the accusative plural
+("to them", "from them", "with them", etc.)
+
+
+=head2 The plurality of zero
+
+The rules governing the choice between:
+
+      There were no errors.
+
+and
+
+      There was no error.
+
+are complex and often depend more on I<intent> rather than I<content>.
+Hence it is infeasible to specify such rules algorithmically.
+
+Therefore, Lingua::EN::Text contents itself with the following compromise: If
+the governing number is zero, inflections always return the plural form
+unless the appropriate "classical" inflection is in effect, in which case the
+singular form is always returned.
+
+Thus, the sequence:
+
+      NUM(0);
+      print inflect "There PL(was) NO(choice)";
+
+produces "There were no choices", whereas:
+
+      classical 'zero';     # or: classical(zero=>1);
+      NUM(0);
+      print inflect "There PL(was) NO(choice)";
+
+it will print "There was no choice".
+
+
+=head2 Homographs with heterogeneous plurals
+
+Another context in which intent (and not content) sometimes determines
+plurality is where two distinct meanings of a word require different
+plurals. For example:
+
+      Three basses were stolen from the band's equipment trailer.
+      Three bass were stolen from the band's aquarium.
+
+      I put the mice next to the cheese.
+      I put the mouses next to the computers.
+
+      Several thoughts about leaving crossed my mind.
+      Several thought about leaving across my lawn.
+
+Lingua::EN::Inflect handles such words in two ways:
+
+=over 8
+
+=item *
+
+If both meanings of the word are the I<same> part of speech (for
+example, "bass" is a noun in both sentences above), then one meaning
+is chosen as the "usual" meaning, and only that meaning's plural is
+ever returned by any of the inflection subroutines.
+
+=item *
+
+If each meaning of the word is a different part of speech (for
+example, "thought" is both a noun and a verb), then the noun's
+plural is returned by C<PL()> and C<PL_N()> and the verb's plural is
+returned only by C<PL_V()>.
+
+=back
+
+Such contexts are, fortunately, uncommon (particularly
+"same-part-of-speech" examples). An informal study of nearly 600
+"difficult plurals" indicates that C<PL()> can be relied upon to "get
+it right" about 98% of the time (although, of course, ichthyophilic
+guitarists or cyber-behaviouralists may experience higher rates of
+confusion).
+
+If the choice of a particular "usual inflection" is considered
+inappropriate, it can always be reversed with a preliminary call
+to the corresponding C<def_...> subroutine.
+
+=head1 NOTE
+
+I'm not taking any further correspondence on:
+
+=over
+
+=item "octopi".
+
+Despite the populist pandering of certain New World dictionaries, the
+plural is "octopuses" or (for the pendantic classicist) "octopodes". The
+suffix "-pus" is Greek, not Latin, so the plural is "-podes", not "pi".
+
+
+=item "virus".
+
+Had no plural in Latin (possibly because it was a mass noun).
+The only plural is the Anglicized "viruses".
+
+=back
+
+=head1 AUTHORS
+
+Damian Conway (damian@conway.org)
+Matthew Persico (ORD inflection)
+
+
+=head1 BUGS AND IRRITATIONS
+
+The endless inconsistencies of English.
+
+(I<Please> report words for which the correct plural or
+indefinite article is not formed, so that the reliability
+of Lingua::EN::Inflect can be improved.)
+
+
+
+=head1 COPYRIGHT
+
+ Copyright (c) 1997-2000, Damian Conway. All Rights Reserved.
+ This module is free software. It may be used, redistributed
+     and/or modified under the same terms as Perl itself.
diff --git a/moose-class/exercises/t/lib/MooseClass/Tests.pm b/moose-class/exercises/t/lib/MooseClass/Tests.pm
new file mode 100644 (file)
index 0000000..8bc112d
--- /dev/null
@@ -0,0 +1,153 @@
+package MooseClass::Tests;
+
+use strict;
+use warnings;
+
+use Lingua::EN::Inflect qw( PL_N );
+use Test::More 'no_plan';
+
+sub tests01 {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    has_meta('Person');
+
+    check_isa( 'Person', ['Moose::Object'] );
+
+    count_attrs( 'Person', 2 );
+
+    has_rw_attr( 'Person', $_ ) for qw( first_name last_name );
+
+    has_method( 'Person', 'full_name' );
+
+    no_droppings('Person');
+    is_immutable('Person');
+
+    person01();
+
+    has_meta('Employee');
+
+    check_isa( 'Employee', [ 'Person', 'Moose::Object' ] );
+
+    count_attrs( 'Employee', 3 );
+
+    has_rw_attr( 'Employee', $_ ) for qw( position salary );
+    has_ro_attr( 'Employee', 'ssn' );
+
+    has_overridden_method( 'Employee', 'full_name' );
+
+    employee01();
+}
+
+sub has_meta {
+    my $class = shift;
+
+    ok( $class->can('meta'), "$class has a meta() method" )
+        or BAIL_OUT("Cannot run tests against a class without a meta!");
+}
+
+sub check_isa {
+    my $class   = shift;
+    my $parents = shift;
+
+    my @isa = $class->meta->linearized_isa;
+    shift @isa;    # returns $class as the first entry
+
+    my $count = scalar @{$parents};
+    my $noun = PL_N( 'parent', $count );
+
+    is( scalar @isa, $count, "$class has $count $noun" );
+
+    for ( my $i = 0; $i < @{$parents}; $i++ ) {
+        is( $isa[$i], $parents->[$i], "parent[$i] is $parents->[$i]" );
+    }
+}
+
+sub count_attrs {
+    my $class = shift;
+    my $count = shift;
+
+    my $noun = PL_N( 'attribute', $count );
+    is( scalar $class->meta->get_attribute_list, $count,
+        "$class defines $count $noun" );
+}
+
+sub has_rw_attr {
+    my $class = shift;
+    my $name  = shift;
+
+    ok( $class->meta->has_attribute($name), "$class has attribute - $name" );
+
+    my $attr = $class->meta->get_attribute($name);
+
+    is( $attr->get_read_method, $name,
+        "$name attribute has a reader accessor - $name()" );
+    is( $attr->get_write_method, $name,
+        "$name attribute has a writer accessor - $name()" );
+}
+
+sub has_ro_attr {
+    my $class = shift;
+    my $name  = shift;
+
+    ok( $class->meta->has_attribute($name), "$class has attribute - $name" );
+
+    my $attr = $class->meta->get_attribute($name);
+
+    is( $attr->get_read_method, $name,
+        "$name attribute has a reader accessor - $name()" );
+    is( $attr->get_write_method, undef,
+        "$name attribute does not have a writer" );
+}
+
+sub has_method {
+    my $class = shift;
+    my $name  = shift;
+
+    ok( $class->meta->has_method($name), "$class has a $name method" );
+}
+
+sub has_overridden_method {
+    my $class = shift;
+    my $name  = shift;
+
+    ok( $class->meta->has_method($name), "$class has a $name method" );
+
+    my $meth = $class->meta->get_method($name);
+    isa_ok( $meth, 'Moose::Meta::Method::Overridden' );
+}
+
+sub no_droppings {
+    my $class = shift;
+
+    ok( !$class->can('has'), "no Moose droppings in $class" );
+}
+
+sub is_immutable {
+    my $class = shift;
+
+    ok( $class->meta->is_immutable, "$class has been made immutable" );
+}
+
+sub person01 {
+    my $person = Person->new(
+        first_name => 'Bilbo',
+        last_name  => 'Baggins',
+    );
+
+    is( $person->full_name, 'Bilbo Baggins',
+        'full_name() is correctly implemented' );
+}
+
+sub employee01 {
+    my $employee = Employee->new(
+        first_name => 'Amanda',
+        last_name  => 'Palmer',
+        position   => 'Singer',
+    );
+
+    is( $employee->full_name, 'Amanda Palmer (Singer)',
+        'full_name() is properly overriden in Employee' );
+}
+
+
+1;
diff --git a/moose-class/exercises/tarballs/Algorithm-C3-0.08.tar.gz b/moose-class/exercises/tarballs/Algorithm-C3-0.08.tar.gz
new file mode 100644 (file)
index 0000000..4529dc7
Binary files /dev/null and b/moose-class/exercises/tarballs/Algorithm-C3-0.08.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Class-C3-0.21.tar.gz b/moose-class/exercises/tarballs/Class-C3-0.21.tar.gz
new file mode 100644 (file)
index 0000000..d44d41f
Binary files /dev/null and b/moose-class/exercises/tarballs/Class-C3-0.21.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Class-C3-XS-0.11.tar.gz b/moose-class/exercises/tarballs/Class-C3-XS-0.11.tar.gz
new file mode 100644 (file)
index 0000000..01adf48
Binary files /dev/null and b/moose-class/exercises/tarballs/Class-C3-XS-0.11.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Class-MOP-0.85.tar.gz b/moose-class/exercises/tarballs/Class-MOP-0.85.tar.gz
new file mode 100644 (file)
index 0000000..fb93993
Binary files /dev/null and b/moose-class/exercises/tarballs/Class-MOP-0.85.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Data-OptList-0.104.tar.gz b/moose-class/exercises/tarballs/Data-OptList-0.104.tar.gz
new file mode 100644 (file)
index 0000000..c507e79
Binary files /dev/null and b/moose-class/exercises/tarballs/Data-OptList-0.104.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Devel-GlobalDestruction-0.02.tar.gz b/moose-class/exercises/tarballs/Devel-GlobalDestruction-0.02.tar.gz
new file mode 100644 (file)
index 0000000..dce3a18
Binary files /dev/null and b/moose-class/exercises/tarballs/Devel-GlobalDestruction-0.02.tar.gz differ
diff --git a/moose-class/exercises/tarballs/List-MoreUtils-0.22.tar.gz b/moose-class/exercises/tarballs/List-MoreUtils-0.22.tar.gz
new file mode 100644 (file)
index 0000000..3daa285
Binary files /dev/null and b/moose-class/exercises/tarballs/List-MoreUtils-0.22.tar.gz differ
diff --git a/moose-class/exercises/tarballs/MRO-Compat-0.11.tar.gz b/moose-class/exercises/tarballs/MRO-Compat-0.11.tar.gz
new file mode 100644 (file)
index 0000000..7952561
Binary files /dev/null and b/moose-class/exercises/tarballs/MRO-Compat-0.11.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Moose-0.80.tar.gz b/moose-class/exercises/tarballs/Moose-0.80.tar.gz
new file mode 100644 (file)
index 0000000..2d7fd31
Binary files /dev/null and b/moose-class/exercises/tarballs/Moose-0.80.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Params-Util-1.00.tar.gz b/moose-class/exercises/tarballs/Params-Util-1.00.tar.gz
new file mode 100644 (file)
index 0000000..d8c90c0
Binary files /dev/null and b/moose-class/exercises/tarballs/Params-Util-1.00.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Scalar-List-Utils-1.21.tar.gz b/moose-class/exercises/tarballs/Scalar-List-Utils-1.21.tar.gz
new file mode 100644 (file)
index 0000000..1fc8642
Binary files /dev/null and b/moose-class/exercises/tarballs/Scalar-List-Utils-1.21.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Scope-Guard-0.03.tar.gz b/moose-class/exercises/tarballs/Scope-Guard-0.03.tar.gz
new file mode 100644 (file)
index 0000000..f9f96fd
Binary files /dev/null and b/moose-class/exercises/tarballs/Scope-Guard-0.03.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Sub-Exporter-0.982.tar.gz b/moose-class/exercises/tarballs/Sub-Exporter-0.982.tar.gz
new file mode 100644 (file)
index 0000000..056050a
Binary files /dev/null and b/moose-class/exercises/tarballs/Sub-Exporter-0.982.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Sub-Install-0.925.tar.gz b/moose-class/exercises/tarballs/Sub-Install-0.925.tar.gz
new file mode 100644 (file)
index 0000000..f7abc26
Binary files /dev/null and b/moose-class/exercises/tarballs/Sub-Install-0.925.tar.gz differ
diff --git a/moose-class/exercises/tarballs/Sub-Name-0.04.tar.gz b/moose-class/exercises/tarballs/Sub-Name-0.04.tar.gz
new file mode 100644 (file)
index 0000000..34c7d7e
Binary files /dev/null and b/moose-class/exercises/tarballs/Sub-Name-0.04.tar.gz differ
index dcae330..10d0339 100644 (file)
@@ -1059,5 +1059,16 @@ use Moose;
   </ul>
 </div>
 
+<div class="slide">
+  <h1>Classes - Exercises</h1>
+
+  <pre>$ cd exercises
+$ prove -lv t/00-prereq.t
+
+Missing anything? Install it. (see tarballs/)
+
+# prove -lv t/01-classes.t
+# 
+
 </body>
 </html>