=begin pod

=head1 NAME

Abbreviations - Provides abbreviations for an input set of one or more words

=head1 SYNOPSIS

=begin code :lang<raku>
use Abbreviations;
my $words = 'A ab Abcde';
# The main exported routine:
my %abbrevs = abbreviations $words;
say %abbrevs.gist;
# OUTPUT: «{A => A, Abcde => Ab, ab => a}␤»
=end code

=head1 DESCRIPTION

B<Abbreviations> is a module with one automatically exported
subroutine, C<abbreviations>, which takes as input a set of words and
returns the original set with added unique abbreviations for the set.
(Note the input words are also abbreviations in the context of this
module.)

Its signature:

=begin code
sub abbreviations($word-set,      #= Str, List, or Hash (Set)
                 :$out-type = HA, #= the default, HashAbbrev
                 :$lower-case,    #= convert the word st to lowercase
                 :$min-length,    #= minimum abbreviation length
                 ) is export {...}
=end code

A I<word> satisfies the Raku regex C<$word ~~ /\S+/> which is quite
loose. Using programs can of course further restrict that if need
be. For example, for use with module B<Opt::Handler> words must
satisfy this regex: C<$word ~~ /<ident>/>.

=head2 A regex alternation for single-word sets

A natural consequence of generating all the abbreviations for a
set of one word is this: the output provides a regex alternation 
which matches any partial length of the target word. For example, 
given a target word 'Args':

=begin code
use Abbreviations;
use Test;
my $target = "Args";
my $regex = abbrev $target; # OUTPUT: «"A|Ar|Arg|Args"␤»;
my $res = False;
my @w = $regex.split('|');
for @w {
    when /<$regex>/ {
        $res = True
    }
    default {
        $res = False
    }
}
is $res, True; # OUTPUT: «ok 1␤»
=end code

As shown in the example above, limiting the input set to one word
results in the output of a regex alternation string. The rest
of this description applies to sets of two or more words.

=head2 Abbreviations for multiple-word sets

The input multiple-word set can be in one of three forms: (1) a list
(recommended), (2) a string containing the words separated by spaces,
or (3) as a hash (or set) with the words being keys of the hash (set
members).  Duplicate words will be automatically and quietly
eliminated.

Note the input word set will not be modified unless the C<:lower-case>
option is used. In that case, all characters will be transformed to
lower-case and any new duplicate words deleted.

If the user wishes, he or she can restrict the minimum length of
the generated abbreviations by using the C<:$min-length> parameter.

One will normally get the result as a hash with the input words as
keys with their shortest abbreviation as values (return type HA), but
the return type can be specified via C<enum Out-type> if desired by
selecting one of the C<:$output-type> options. For example:

=begin code
my %abbrevs = abbrevs @words, :output-type(AH);
=end code

There are two shorter alias names for C<sub abbreviations> one can
use that are always exported:

=begin code :lang<raku>
 abbrevs
 abbrev
=end code

In the spirit of the module, one can C<use Abbreviations :ALL;> and
have these additional shorter alias names available:

=begin code :lang<raku>
 abbre
 abbr
 abb
 ab
 a
=end code

Each of those is individually available by adding its name as an
adverb, for example:

=begin code :lang<raku>
use Abbreviations :abb;
my %abb = abb $words;
=end code

=head3 C<enum Out-type>

=begin code
enum Out-type is export <HA H AH AL L S >;
=end code

The I<enum> C<Out-type> is exported automatically as it is required
for using C<sub abbreviations>. It has the following types:

=item C<HA> (HashAbbrev)

The default I<HashAbbrev> (C<HA>) returned will have input words as
keys whose value will be the shortest valid abbreviation.

=item C<H> (Hash)

A variant of C<HA>, the I<Hash> (C<H>) returned will have input words
as keys whose value will be a sorted list of its valid abbreviations
(sorted by length, shortest first, then by C<Str> order).

=item C<AH> (AbbrevHash)

An I<AbbrevHash> (C<AH>) is keyed by all of the valid abbreviations
for the input word list and whose values are the word from which that
abbreviation is defined.

=item C<AL> (AbbrevList)

An I<AbbrevList> (C<AL>) is special in that the returned list is the
one, shortest abbreviation for each of the input words in input
order. For example,

=begin code
my @w = <Monday Tuesday Wednesday Thursday Friday Saturday Sunday>;
my @abb = abbrevs @w, :output-type(AL);
say @abb; # OUTPUT: «M Tu W Th F Sa Su␤»
=end code

Note that a hash (or set) input type will not reliably provide this
output as expected since the keys are not stored in order. Instead,
the ouput will be based on a list of the hash's keys. In effect, entering
C<%out = abbreviations %in> is the same as:

=begin code
my @inputlist = %in.keys.sort({.chars, .Str}';
my %out = abbreviations @inputlist;
=end code

=item C<L> (List)

A I<List> (C<L>) contains all of the valid abbreviations for the input
word list, including the words themselves, sorted by length, then character
order.

=item C<S> (String)

A I<String> (C<S>) is the string formed by joining the I<List> by a
single space between words.

=head3 Improved abbreviation search

The abbreviation algorithm has been improved from the
original (as found on L<https://rosettacode.org>) in the following way:
The input word set is formed into subgroups comprised of each input
word. Abbreviations are created for each word, abbreviations shared by
two or words are eliminated, then all those abbreviations are combined
into one set. The result will be the largest possible set of unique
abbreviations for a given input word set.

For example, given an input set consisting of the words C<A ab Abcde>,
the default output hash of abbreviations (with the original words as
keys) contains a total of seven abbreviations:

=begin code
    A     => ['A'],
    ab    => ['a', 'ab'],
    Abcde => ['Ab', 'Abc', 'Abcd', 'Abcde'],
=end code

If the C<:lower-case> option is used, we get a slightly different
result since we have fewer unique abbreviations from the lower-cased
words.  The new hash has only five abbreviations:

=begin code
my $words = 'A ab Abcde':
my %abbr = abbrevs $words, :lower-case;
=end code

The result is

=begin code
    a     => ['a'],
    ab    => ['ab],
    abcde => ['abc', 'abcd', 'abcde'],
=end code

Notice the input word B<ab> now has only one abbreviation and B<abcde>
has only three.

=head2 Other exported symbols

=head3 C<sub sort-list>

=begin code
sub sort-list(@list, :$type = SL, :$reverse --> List) is export(:sort)
{...}
=end code

By default, this routine sorts all lists by word length, then by Str
order. The order by length is by the shortest abbreviation first unless
the C<:$reverse> option is used. This is the routine used for all the
output types produced by this module I<except> the
I<AbbrevList> (C<AL>) which keeps the original word set order.

The routine's output can be modified for other uses by entering the
C<:$type> parameter to choose another of the <enum Sort-type>s.

=head3 C<enum Sort-type>

=begin code
enum Sort-type is export(:sort) < SL LS SS LL N>;
=end code

The C<Sort-type>s are:

=item SL - order by Str, then order by Length
=item LS - order by Length, then order by Str
=item SS - Str order only
=item LL - Length order only
=item N  - Numerical order only (falls back to SS if any words are not numbers)

=head1 AUTHOR

Tom Browder <tbrowder@acm.org>

=head1 CREDITS

=item Leon Timmermans (aka @Leont) for inspiration from his Raku
module C<Getopt::Long>.

=item @thundergnat, the original author of the Raku C<auto-abbreviate>
algorithm on L<Rosetta
Code|http://rosettacode.org/wiki/Abbreviations,_automatic#Raku>.

=item The Raku community for help with subroutine signatures.

=head1 COPYRIGHT and LICENSE

Copyright © 2020-2026 Tom Browder

This library is free software; you may redistribute or modify it under the Artistic License 2.0.

=end pod
