#!/usr/bin/perl
# vim: et ts=3 sts=3 sw=3
package Ashaille;

# TODO:
#  - special forms for -illev verbs
#  - multi-word strings (spaces for multiple apostrophes)
#  - numbered suffixes
#  - numerals
#

use strict;
use warnings;
use utf8;
use charnames ":full";
use CXS;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

binmode STDOUT, ":utf8";

require Exporter;

@ISA = qw(Exporter AutoLoader);
$VERSION = '0.01';

# items to export into caller's namespace by default
@EXPORT = qw(roman kateinu kateinu_sort);

# treat CXS /tS)/ and /dZ)/ as old-style ligatures
cxs_set_cxs2ipa("tS)", 0x02a7);
cxs_set_cxs2ipa("dZ)", 0x02a4);
cxs_init();

my %ipaSkipChars = (
   0x02c8 => "primary stress",
   0x02cc => "secondary stress",
   0x02d0 => "long",
   0x02d1 => "half-long",
   0x0306 => "extra short",
   0x007c => "minor (foot) group",
   0x2016 => "major (intonation) group",
   0x002e => "syllable break",
   0x203f => "linking (absense of a break",
);

my $lexeme; # not thread-safe!
my $font_input; # not thread-safe!
my $endPunct; # not thread-safe!

# SUBROUTINES ####################################

sub roman($) {
   my $kateinu = shift;
   my $roman = '';
   $font_input = $kateinu;

   # remove characters that don't represent anything in the romanized
   # orthography (ie, symbols that only exist to make the font display
   # correctly, like the adjacent-right-sides separator)
   $kateinu =~ s/[\x{10de}\x{10df}]//g;

   my ($char, $stuttered);
   my $addFinalLetters;


   ##### Special processing before going letter-by-letter

   if ($kateinu eq "'") {
      return "ne";
   }


   ##### Letter-by-letter processing

   my $length = length($kateinu);
   my $i = 0;

   for (; $i < $length; $i++) {
      my $char = substr($kateinu, $i, 1);

      if ($char =~ /[AÁÄeéë]/) {
         $roman .= lc($char) . "i";
      }
      elsif ($char =~ /[EÉËOÓÖY]/) {
         $roman .= lc($char);
      }
      elsif ($char eq "I") {
         $roman .= "î";
      }
      elsif ($char eq "Í") {
         $roman .= "î́";
      }
      elsif ($char eq "Ï") {
         $roman .= "ḯ";
      }
      elsif ($char =~ /[CDGJST]/) {
         $roman .= lc($char) . "h";
      }
      elsif ($char eq "L") {
         $roman .= lc($char) . "n";
      }
      elsif ($char eq "M") {
         $roman .= lc($char) . "l";
      }
      elsif ($char eq "æ") {
         $roman .= "ae";
      }
      elsif ($char eq "Æ") {
         $roman .= "äe";
      }


      ##### Adjacent forms for barred characters
      ##
      ##    Final forms take precedent over adjacent forms

      # "mi" combined adjacent barred form
      elsif ($char eq "\x{10a0}") {
         $roman .= "mi";
      }
      # "mí" combined adjacent barred form
      elsif ($char eq "\x{10b4}") {
         $roman .= "mí";
      }
      # "mï" combined adjacent barred form
      elsif ($char eq "\x{10b5}") {
         $roman .= "mï";
      }
      # "mai" combined adjacent barred form
      elsif ($char eq "\x{10a1}") {
         $roman .= "mai";
      }
      # "mái" combined adjacent barred form
      elsif ($char eq "\x{10b6}") {
         $roman .= "mái";
      }
      # "mäi" combined adjacent barred form
      elsif ($char eq "\x{10b7}") {
         $roman .= "mäi";
      }
      # "im" combined adjacent barred form
      elsif ($char eq "\x{10a2}") {
         $roman .= "im";
      }
      # "ím" combined adjacent barred form
      elsif ($char eq "\x{10b8}") {
         $roman .= "ím";
      }
      # "ïm" combined adjacent barred form
      elsif ($char eq "\x{10b9}") {
         $roman .= "ïm";
      }
      # "ijh" combined adjacent barred form
      elsif ($char eq "\x{10a3}") {
         $roman .= "ijh";
      }
      # "íjh" combined adjacent barred form
      elsif ($char eq "\x{10ba}") {
         $roman .= "íjh";
      }
      # "ïjh" combined adjacent barred form
      elsif ($char eq "\x{10bb}") {
         $roman .= "ïjh";
      }
      # "ip" combined adjacent barred form
      elsif ($char eq "\x{10a4}") {
         $roman .= "ip";
      }
      # "íp" combined adjacent barred form
      elsif ($char eq "\x{10bc}") {
         $roman .= "íp";
      }
      # "ïp" combined adjacent barred form
      elsif ($char eq "\x{10bd}") {
         $roman .= "ïp";
      }
      # "ig" combined adjacent barred form
      elsif ($char eq "\x{10a5}") {
         $roman .= "ig";
      }
      # "íg" combined adjacent barred form
      elsif ($char eq "\x{10be}") {
         $roman .= "íg";
      }
      # "ïg" combined adjacent barred form
      elsif ($char eq "\x{10bf}") {
         $roman .= "ïg";
      }
      # "jhi" combined adjacent barred form
      elsif ($char eq "\x{10a6}") {
         $roman .= "jhi";
      }
      # "jhí" combined adjacent barred form
      elsif ($char eq "\x{10c0}") {
         $roman .= "jhí";
      }
      # "jhï" combined adjacent barred form
      elsif ($char eq "\x{10c1}") {
         $roman .= "jhï";
      }
      # "jhai" combined adjacent barred form
      elsif ($char eq "\x{10a7}") {
         $roman .= "jhai";
      }
      # "jhái" combined adjacent barred form
      elsif ($char eq "\x{10c2}") {
         $roman .= "jhái";
      }
      # "jhäi" combined adjacent barred form
      elsif ($char eq "\x{10c3}") {
         $roman .= "jhäi";
      }
      # "aim" combined adjacent barred form
      elsif ($char eq "\x{10a8}") {
         $roman .= "aim";
      }
      # "áim" combined adjacent barred form
      elsif ($char eq "\x{10c4}") {
         $roman .= "áim";
      }
      # "äim" combined adjacent barred form
      elsif ($char eq "\x{10c5}") {
         $roman .= "äim";
      }
      # "aijh" combined adjacent barred form
      elsif ($char eq "\x{10a9}") {
         $roman .= "aijh";
      }
      # "áijh" combined adjacent barred form
      elsif ($char eq "\x{10d0}") {
         $roman .= "áijh";
      }
      # "äijh" combined adjacent barred form
      elsif ($char eq "\x{10d1}") {
         $roman .= "äijh";
      }
      # "aip" combined adjacent barred form
      elsif ($char eq "\x{10aa}") {
         $roman .= "aip";
      }
      # "áip" combined adjacent barred form
      elsif ($char eq "\x{10d2}") {
         $roman .= "áip";
      }
      # "äip" combined adjacent barred form
      elsif ($char eq "\x{10d3}") {
         $roman .= "äip";
      }
      # "aig" combined adjacent barred form
      elsif ($char eq "\x{10ab}") {
         $roman .= "aig";
      }
      # "áig" combined adjacent barred form
      elsif ($char eq "\x{10d4}") {
         $roman .= "áig";
      }
      # "äig" combined adjacent barred form
      elsif ($char eq "\x{10d5}") {
         $roman .= "äig";
      }
      # "pi" combined adjacent barred form
      elsif ($char eq "\x{10ac}") {
         $roman .= "pi";
      }
      # "pí" combined adjacent barred form
      elsif ($char eq "\x{10d6}") {
         $roman .= "pí";
      }
      # "pï" combined adjacent barred form
      elsif ($char eq "\x{10d7}") {
         $roman .= "pï";
      }
      # "pai" combined adjacent barred form
      elsif ($char eq "\x{10ad}") {
         $roman .= "pai";
      }
      # "pái" combined adjacent barred form
      elsif ($char eq "\x{10d8}") {
         $roman .= "pái";
      }
      # "päi" combined adjacent barred form
      elsif ($char eq "\x{10d9}") {
         $roman .= "päi";
      }
      # "gi" combined adjacent barred form
      elsif ($char eq "\x{10ae}") {
         $roman .= "gi";
      }
      # "gí" combined adjacent barred form
      elsif ($char eq "\x{10da}") {
         $roman .= "gí";
      }
      # "gï" combined adjacent barred form
      elsif ($char eq "\x{10db}") {
         $roman .= "gï";
      }
      # "gai" combined adjacent barred form
      elsif ($char eq "\x{10af}") {
         $roman .= "gai";
      }
      # "gái" combined adjacent barred form
      elsif ($char eq "\x{10dc}") {
         $roman .= "gái";
      }
      # "gäi" combined adjacent barred form
      elsif ($char eq "\x{10dd}") {
         $roman .= "gäi";
      }


      ##### Final forms

      # check if "a" is in final form
      elsif ($char eq "\N{LATIN CAPITAL LETTER A WITH MACRON}") {
         $roman .= "ai";
      }
      # check if "á" is in final form
      elsif ($char eq "\x{10b2}") {
         $roman .= "ái";
      }
      # check if "ä" is in final form
      elsif ($char eq "\x{10b3}") {
         $roman .= "äi";
      }
      # check if "i" is in final form
      elsif ($char eq "\N{LATIN SMALL LIGATURE IJ}") {
         $roman .= "i";
      }
      # check if "í" is in final form
      elsif ($char eq "\x{10b0}") {
         $roman .= "í";
      }
      # check if "ï" is in final form
      elsif ($char eq "\x{10b1}") {
         $roman .= "ï";
      }
      # check if "m" is in final form
      elsif ($char eq "\N{GREEK CAPITAL LETTER MU}") {
         $roman .= "m";
      }
      # check if "p" is in final form
      elsif ($char eq "\N{LATIN CAPITAL LETTER P WITH HOOK}") {
         $roman .= "p";
      }
      # check if "g" is in final form
      elsif ($char eq "\N{LATIN SMALL LETTER G WITH BREVE}") {
         $roman .= "g";
      }
      # check if "jh" is in final form
      elsif ($char eq "\N{LATIN CAPITAL LIGATURE IJ}") {
         $roman .= "jh";
      }


      ##### Glottal Stop Vowels

      # "àe"
      elsif (check_font("'e", $i)) {
         $roman .= "àe";
         $i++;
      }
      # "ài"
      elsif (check_font("'A", $i)) {
         $roman .= "ài";
         $i++;
      }
      # "èi"
      elsif (check_font("'e", $i)) {
         $roman .= "èi";
         $i++;
      }
      # "à"
      elsif (check_font("'a", $i)) {
         $roman .= "à";
         $i++;
      }
      # "è"
      elsif (check_font("'E", $i)) {
         $roman .= "è";
         $i++;
      }
      # "ì"
      elsif (check_font("'i", $i)) {
         $roman .= "ì";
         $i++;
      }
      # TODO: "`í"
      # "ò"
      elsif (check_font("'o", $i)) {
         $roman .= "ò";
         $i++;
      }
      # "ù"
      elsif (check_font("'u", $i)) {
         $roman .= "ù";
         $i++;
      }


      ##### Special Morphemic Glyphs

      # "-ni" verb conjugation
      elsif ($char eq "\x{10e0}") {
         $roman .= "ni";
      }
      # "-ejh" verb conjugation
      elsif ($char eq "\x{10e1}") {
         $roman .= "ejh";
      }


      ##### Stuttered Consonants

      elsif ($stuttered = stutteredRoman($char)) {
         $roman .= $stuttered;
      }


      ##### Miscellaneous checks

      # "-te" politeness marker
      elsif ($char eq "\x{10e3}") {
         $roman .= "te";
      }
      elsif ($char eq "\N{RIGHT SINGLE QUOTATION MARK}") {
         $roman .= "'"; # contraction
      }


      else {
         $roman .= $char;
      }
   }

   if ($addFinalLetters) {
      $roman .= $addFinalLetters;
   }

   return $roman;
}

sub kateinu($;$$) {
   $lexeme = lc(shift);
   $endPunct = 0;

   # needed to correctly distinguish /o O/
   my $cxs = ''; # default
   $cxs = shift if @_;

   # include ligatures and other font-rendering requirements
   my $font = 1; # default
   $font = shift if @_;

   my $kateinu = '';
   my $ipa = $cxs ? cxs2ipa($cxs) : '';
   my @chars = unpack('U0U*', $ipa);
   my $length = length($lexeme);

   my ($char, $next, $nextNext, $ipaChar, $stuttered);
   my $ipaNdx = 0;
   my $i = 0;


   ##### Special processing before going letter-by-letter

   if ($lexeme eq "ne" && $font) {
      return "'";
   }

   # ignore punctuation at end of word
   while (substr($lexeme, $length - 1, 1) =~ /[,.?!]/)
   {
      $length--;
      $endPunct++;
      last if $endPunct >= length($lexeme);
   }


   ##### Letter-by-letter processing

   for (; $i < $length; $i++) {
      $char = substr($lexeme, $i, 1);
      $next = substr($lexeme, $i + 1, 1) unless $i + 1 > $length;
      $nextNext = substr($lexeme, $i + 2, 1) unless $i + 2 > $length;

      while ($ipaNdx < scalar @chars && $ipaSkipChars{$chars[$ipaNdx]}) {
         $ipaNdx++;
      }
      $ipaChar = $chars[$ipaNdx];


      ##### Special Morphemic Glyphs

      # "-ni" verb conjugation
      if (check("vni", $i) && $font) {
         $kateinu .= "v\x{10e0}";
         $i += 2;
      }
      # "-ejh" verb conjugation
      elsif (check("vejh", $i) && $font) {
         $kateinu .= "v\x{10e1}";
         $i += 3;
      }


      ##### Stuttered Consonants

      elsif ($char eq $next) {
         # check for 3-character strings: ggh, jjh, ssh, tth, ddh, cch, lle$
         my $endOfWord = ($i + 3 == $length);
         my $isLle = ($char eq "l" && $nextNext eq "e" && $endOfWord);
         if ($nextNext eq "h" || $isLle) {
            $char .= $nextNext;
         }

         $stuttered = stutteredKateinu($char);
         if ($stuttered) { $kateinu .= $stuttered; $i += length($char) }
         else { $kateinu .= $char }
      }


      ##### Adjacent forms for barred characters
      ##
      ##    Final forms take precedent over adjacent forms

      # "mi" combined adjacent barred form
      elsif (check("mi", $i, 0) && $font) {
         $kateinu .= "\x{10a0}";
         $i++;
      }
      # "mí" combined adjacent barred form
      elsif (check("mí", $i, 0) && $font) {
         $kateinu .= "\x{10b4}";
         $i++;
      }
      # "mï" combined adjacent barred form
      elsif (check("mï", $i, 0) && $font) {
         $kateinu .= "\x{10b5}";
         $i++;
      }
      # "mai" combined adjacent barred form
      elsif (check("mai", $i, 0) && $font) {
         $kateinu .= "\x{10a1}";
         $i += 2;
      }
      # "mái" combined adjacent barred form
      elsif (check("mái", $i, 0) && $font) {
         $kateinu .= "\x{10b6}";
         $i += 2;
      }
      # "mäi" combined adjacent barred form
      elsif (check("mäi", $i, 0) && $font) {
         $kateinu .= "\x{10b7}";
         $i += 2;
      }
      # "im" combined adjacent barred form
      elsif (check("im", $i, 0) && $font) {
         $kateinu .= "\x{10a2}";
         $i++;
      }
      # "ím" combined adjacent barred form
      elsif (check("ím", $i, 0) && $font) {
         $kateinu .= "\x{10b8}";
         $i++;
      }
      # "ïm" combined adjacent barred form
      elsif (check("ïm", $i, 0) && $font) {
         $kateinu .= "\x{10b9}";
         $i++;
      }
      # "ijh" combined adjacent barred form
      elsif (check("ijh", $i, 0) && $font) {
         $kateinu .= "\x{10a3}";
         $i += 2;
      }
      # "íjh" combined adjacent barred form
      elsif (check("íjh", $i, 0) && $font) {
         $kateinu .= "\x{10ba}";
         $i += 2;
      }
      # "ïjh" combined adjacent barred form
      elsif (check("ïjh", $i, 0) && $font) {
         $kateinu .= "\x{10bb}";
         $i += 2;
      }
      # "ip" combined adjacent barred form
      elsif (check("ip", $i, 0) && $font) {
         $kateinu .= "\x{10a4}";
         $i++;
      }
      # "íp" combined adjacent barred form
      elsif (check("íp", $i, 0) && $font) {
         $kateinu .= "\x{10bc}";
         $i++;
      }
      # "ïp" combined adjacent barred form
      elsif (check("ïp", $i, 0) && $font) {
         $kateinu .= "\x{10bd}";
         $i++;
      }
      # "ig" combined adjacent barred form
      elsif (check("ig", $i, 0) && $font) {
         $kateinu .= "\x{10a5}";
         $i++;
      }
      # "íg" combined adjacent barred form
      elsif (check("íg", $i, 0) && $font) {
         $kateinu .= "\x{10be}";
         $i++;
      }
      # "ïg" combined adjacent barred form
      elsif (check("ïg", $i, 0) && $font) {
         $kateinu .= "\x{10bf}";
         $i++;
      }
      # "jhi" combined adjacent barred form
      elsif (check("jhi", $i, 0) && $font) {
         $kateinu .= "\x{10a6}";
         $i += 2;
      }
      # "jhí" combined adjacent barred form
      elsif (check("jhí", $i, 0) && $font) {
         $kateinu .= "\x{10c0}";
         $i += 2;
      }
      # "jhï" combined adjacent barred form
      elsif (check("jhï", $i, 0) && $font) {
         $kateinu .= "\x{10c1}";
         $i += 2;
      }
      # "jhai" combined adjacent barred form
      elsif (check("jhai", $i, 0) && $font) {
         $kateinu .= "\x{10a7}";
         $i += 3;
      }
      # "jhái" combined adjacent barred form
      elsif (check("jhái", $i, 0) && $font) {
         $kateinu .= "\x{10c2}";
         $i += 3;
      }
      # "jhäi" combined adjacent barred form
      elsif (check("jhäi", $i, 0) && $font) {
         $kateinu .= "\x{10c3}";
         $i += 3;
      }
      # "aim" combined adjacent barred form
      elsif (check("aim", $i, 0) && $font) {
         $kateinu .= "\x{10a8}";
         $i += 2;
      }
      # "áim" combined adjacent barred form
      elsif (check("áim", $i, 0) && $font) {
         $kateinu .= "\x{10c4}";
         $i += 2;
      }
      # "äim" combined adjacent barred form
      elsif (check("äim", $i, 0) && $font) {
         $kateinu .= "\x{10c5}";
         $i += 2;
      }
      # "aijh" combined adjacent barred form
      elsif (check("aijh", $i, 0) && $font) {
         $kateinu .= "\x{10a9}";
         $i += 3;
      }
      # "áijh" combined adjacent barred form
      elsif (check("áijh", $i, 0) && $font) {
         $kateinu .= "\x{10d0}";
         $i += 3;
      }
      # "äijh" combined adjacent barred form
      elsif (check("äijh", $i, 0) && $font) {
         $kateinu .= "\x{10d1}";
         $i += 3;
      }
      # "aip" combined adjacent barred form
      elsif (check("aip", $i, 0) && $font) {
         $kateinu .= "\x{10aa}";
         $i += 2;
      }
      # "áip" combined adjacent barred form
      elsif (check("áip", $i, 0) && $font) {
         $kateinu .= "\x{10d2}";
         $i += 2;
      }
      # "äip" combined adjacent barred form
      elsif (check("äip", $i, 0) && $font) {
         $kateinu .= "\x{10d3}";
         $i += 2;
      }
      # "aig" combined adjacent barred form
      elsif (check("aig", $i, 0) && $font) {
         $kateinu .= "\x{10ab}";
         $i += 2;
      }
      # "áig" combined adjacent barred form
      elsif (check("áig", $i, 0) && $font) {
         $kateinu .= "\x{10d4}";
         $i += 2;
      }
      # "äig" combined adjacent barred form
      elsif (check("äig", $i, 0) && $font) {
         $kateinu .= "\x{10d5}";
         $i += 2;
      }
      # "pi" combined adjacent barred form
      elsif (check("pi", $i, 0) && $font) {
         $kateinu .= "\x{10ac}";
         $i++;
      }
      # "pí" combined adjacent barred form
      elsif (check("pí", $i, 0) && $font) {
         $kateinu .= "\x{10d6}";
         $i++;
      }
      # "pï" combined adjacent barred form
      elsif (check("pï", $i, 0) && $font) {
         $kateinu .= "\x{10d7}";
         $i++;
      }
      # "pai" combined adjacent barred form
      elsif (check("pai", $i, 0) && $font) {
         $kateinu .= "\x{10ad}";
         $i += 2;
      }
      # "pái" combined adjacent barred form
      elsif (check("pái", $i, 0) && $font) {
         $kateinu .= "\x{10d8}";
         $i += 2;
      }
      # "päi" combined adjacent barred form
      elsif (check("päi", $i, 0) && $font) {
         $kateinu .= "\x{10d9}";
         $i += 2;
      }
      # "gi" combined adjacent barred form
      elsif (check("gi", $i, 0) && $font) {
         $kateinu .= "\x{10ae}";
         $i++;
      }
      # "gí" combined adjacent barred form
      elsif (check("gí", $i, 0) && $font) {
         $kateinu .= "\x{10da}";
         $i++;
      }
      # "gï" combined adjacent barred form
      elsif (check("gï", $i, 0) && $font) {
         $kateinu .= "\x{10db}";
         $i++;
      }
      # "gai" combined adjacent barred form
      elsif (check("gai", $i, 0) && $font) {
         $kateinu .= "\x{10af}";
         $i += 2;
      }
      # "gái" combined adjacent barred form
      elsif (check("gái", $i, 0) && $font) {
         $kateinu .= "\x{10dc}";
         $i += 2;
      }
      # "gäi" combined adjacent barred form
      elsif (check("gäi", $i, 0) && $font) {
         $kateinu .= "\x{10dd}";
         $i += 2;
      }


      ##### Spacer between right-side consonants

      # "kr" with spacer
      elsif (check("kr", $i) && $font) {
         $kateinu .= "k\x{10de}r";
         $i++;
      }
      # "kl" with spacer
      elsif (check("kl", $i) && $font) {
         $kateinu .= "k\x{10de}l";
         $i++;
      }
      # "tr" with spacer
      elsif (check("tr", $i) && $font) {
         $kateinu .= "t\x{10de}r";
         $i++;
      }
      # "rl" with spacer
      elsif (check("rl", $i) && $font) {
         $kateinu .= "r\x{10de}l";
         $i++;
      }
      # "rk" with spacer
      elsif (check("rk", $i) && $font) {
         $kateinu .= "r\x{10de}k";
         $i++;
      }
      # "rv" with spacer
      elsif (check("rv", $i) && $font) {
         $kateinu .= "r\x{10de}v";
         $i++;
      }
      # "rn" with spacer
      elsif (check("rn", $i) && $font) {
         $kateinu .= "r\x{10de}n";
         $i++;
      }
      # "rsh" with spacer
      elsif (check("rsh", $i) && $font) {
         $kateinu .= "r\x{10de}S";
         $i += 2;
      }
      # "rdh" with spacer
      elsif (check("rdh", $i) && $font) {
         $kateinu .= "r\x{10de}D";
         $i += 2;
      }


      ##### Spacer between left-side glyphs

      # "aln" with spacer
      elsif (check("aln", $i) && $font) {
         $kateinu .= "a\x{10df}L";
         $i += 2;
      }


      ##### Glottal Stop Vowels

      # "àe"
      elsif (check("àe", $i)) {
         $kateinu .= "'e";
         $i++;
      }
      # "ài"
      elsif (check("ài", $i)) {
         $kateinu .= "'A";
         $i++;
      }
      # "èi"
      elsif (check("èi", $i)) {
         $kateinu .= "'e";
         $i++;
      }
      # "à"
      elsif (check("à", $i)) {
         $kateinu .= "'a";
      }
      # "è"
      elsif (check("è", $i)) {
         $kateinu .= "'E";
      }
      # "ì"
      elsif (check("ì", $i)) {
         $kateinu .= "'i";
      }
      # TODO: "`í"
      # "ò"
      elsif (check("ò", $i)) {
         $kateinu .= "'o";
      }
      # "ù"
      elsif (check("ù", $i)) {
         $kateinu .= "'u";
      }


      ##### Digraphs

      # "ae" digraph
      elsif (check("ae", $i)) {
         $kateinu .= "\N{LATIN SMALL LETTER AE}";
         $i++;
      }
      # "äe" digraph
      elsif (check("äe", $i)) {
         $kateinu .= "\N{LATIN CAPITAL LETTER AE}";
         $i++;
      }
      # "ai" digraph
      elsif (is_vowel($char, "a") && $next eq "i") {
         # check if "ai" should be the final form (but not standalone)
         if ($i + 2 == $length && $i != 0 && $font) {
            if ($char eq "a") { $kateinu .= "\N{LATIN CAPITAL LETTER A WITH MACRON}" }
            elsif ($char eq "á") { $kateinu .= "\x{10b2}" }
            elsif ($char eq "ä") { $kateinu .= "\x{10b3}" }
         }
         else {
            $kateinu .= uc($char);
         }
         $i++;
      }
      # "ei" digraph
      elsif (is_vowel($char, "e") && $next eq "i") {
         $kateinu .= $char;
         $i++;
      }
      # "gh" digraph
      elsif (check("gh", $i)) {
         $kateinu .= "G";
         $i++;
      }
      # "jh" digraph
      elsif (check("jh", $i)) {
         # check if "jh" should be the final form (but not standalone)
         if ($i + 2 == $length && $i != 0 && $font) {
            $kateinu .= "\N{LATIN CAPITAL LIGATURE IJ}";
         }
         else { $kateinu .= "J" }
         $i++;
      }
      # "ln" digraph
      elsif (check("ln", $i)) {
         $kateinu .= "L";
         $i++;
      }
      # "sh" digraph
      elsif (check("sh", $i)) {
         $kateinu .= "S";
         $i++;
      }
      # "th" digraph
      elsif (check("th", $i)) {
         $kateinu .= "T";
         $i++;
      }
      # "dh" digraph
      elsif (check("dh", $i)) {
         $kateinu .= "D";
         $i++;
      }
      # "ml" digraph
      elsif (check("ml", $i)) {
         $kateinu .= "M";
         $i++;
      }
      # "ch" digraph
      elsif (check("ch", $i)) {
         $kateinu .= "C";
         $i++;
      }


      ##### Final forms of barred characters

      # check if "m" should be the final form
      elsif ($char eq "m" && $i + 1 == $length && $i != 0 && $font) {
         $kateinu .= "\N{GREEK CAPITAL LETTER MU}";
      }
      # check if "p" should be the final form
      elsif ($char eq "p" && $i + 1 == $length && $i != 0 && $font) {
         $kateinu .= "\N{LATIN CAPITAL LETTER P WITH HOOK}";
      }
      # check if "g" should be the final form
      elsif ($char eq "g" && $i + 1 == $length && $i != 0 && $font) {
         $kateinu .= "\N{LATIN SMALL LETTER G WITH BREVE}";
      }


      ##### Final forms for barred character "i" as well
      ##
      ##

      # disambiguate /i/ from /I/
      elsif ($char eq "î") {
         $kateinu .= "I";
      }
      elsif (is_vowel($char, "i")) {
         # check if "i" should be the final form (but not standalone)
         if ($i + 1 == $length && $i != 0 && $font) {
            if ($char eq "i") { $kateinu .= "\N{LATIN SMALL LIGATURE IJ}" }
            elsif ($char eq "í") { $kateinu .= "\x{10b0}" }
            elsif ($char eq "ï") { $kateinu .= "\x{10b1}" }
         }
         else {
            $kateinu .= $char;
         }
      }


      ##### Miscellaneous checks

      # "-te" politeness marker
      elsif (check("te", $i) && $i + 2 == $length && $font) {
         $kateinu .= "\x{10e3}";
         $i += 1;
      }
      # "e" as /E/
      elsif (is_vowel($char, "e")) {
         $kateinu .= uc($char);
      }
      # "y" following a consonant
      elsif ($char eq "y" && $i != 0 && !is_vowel(substr($lexeme, $i - 1, 1)) && $font) {
         $kateinu .= "Y";
      }
      # "'" as contraction
      elsif ($char eq "'" && ($i == 0 || $i + 1 == $length)) {
         $kateinu .= "\N{RIGHT SINGLE QUOTATION MARK}";
      }
      else {
         $kateinu .= $char;
      }

      $ipaNdx++;
   }

   # add back any ending punctuation
   $kateinu .= substr($lexeme, $i, $endPunct);

   return $kateinu;
}

sub kateinu_sort($;$) {
   $lexeme = shift;
   my $cxs = ''; # default
   $cxs = shift if @_;
   return kateinu($lexeme, $cxs, 0);
}

sub stutteredRoman($) {
   my $char = shift;

   if    ($char eq "\N{LATIN SMALL LETTER K WITH CEDILLA}")      { return "kk" }
   elsif ($char eq "\N{LATIN SMALL LETTER T WITH CARON}")        { return "tt" }
   elsif ($char eq "\N{LATIN SMALL LETTER N WITH ACUTE}")        { return "nn" }
   elsif ($char eq "\N{LATIN SMALL LETTER R WITH CARON}")        { return "rr" }
   elsif ($char eq "\N{LATIN SMALL LETTER B WITH STROKE}")       { return "bb" }
   elsif ($char eq "\N{LATIN SMALL LETTER S WITH ACUTE}")        { return "ss" }
   elsif ($char eq "\N{GREEK SMALL LETTER MU}")                  { return "mm" }
   elsif ($char eq "\N{LATIN CAPITAL LETTER G WITH CIRCUMFLEX}") { return "ggh" }
   elsif ($char eq "\N{LATIN SMALL LETTER Z WITH ACUTE}")        { return "zz" }
   elsif ($char eq "\N{LATIN SMALL LETTER L WITH ACUTE}")        { return "ll" }
   elsif ($char eq "\N{LATIN CAPITAL LETTER J WITH CIRCUMFLEX}") { return "jjh" }
   elsif ($char eq "\N{LATIN CAPITAL LETTER V WITH HOOK}")       { return "vv" }
   elsif ($char eq "\N{LATIN SMALL LETTER ETH}")                 { return "dd" }
   elsif ($char eq "\N{LATIN SMALL LETTER F WITH HOOK}")         { return "ff" }
   elsif ($char eq "\N{LATIN SMALL LETTER P WITH HOOK}")         { return "pp" }
   elsif ($char eq "\N{LATIN SMALL LETTER G WITH CIRCUMFLEX}")   { return "gg" }
   elsif ($char eq "\N{LATIN CAPITAL LETTER S WITH ACUTE}")      { return "ssh" }
   elsif ($char eq "\N{LATIN CAPITAL LETTER T WITH CARON}")      { return "tth" }
   elsif ($char eq "\N{LATIN CAPITAL LETTER ETH}")               { return "ddh" }
   elsif ($char eq "\N{LATIN CAPITAL LETTER C WITH CEDILLA}")    { return "cch" }
   elsif ($char eq "\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}")   { return "jj" }
   elsif ($char eq "\N{LATIN SMALL LETTER L WITH CARON}")        { return "lle" }
   else { return 0 }
}

sub stutteredKateinu($) {
   my $char = shift;

   if    ($char eq "k")  { return "\N{LATIN SMALL LETTER K WITH CEDILLA}" }
   elsif ($char eq "t")  { return "\N{LATIN SMALL LETTER T WITH CARON}" }
   elsif ($char eq "n")  { return "\N{LATIN SMALL LETTER N WITH ACUTE}" }
   elsif ($char eq "r")  { return "\N{LATIN SMALL LETTER R WITH CARON}" }
   elsif ($char eq "b")  { return "\N{LATIN SMALL LETTER B WITH STROKE}" }
   elsif ($char eq "s")  { return "\N{LATIN SMALL LETTER S WITH ACUTE}" }
   elsif ($char eq "m")  { return "\N{GREEK SMALL LETTER MU}" }
   elsif ($char eq "gh") { return "\N{LATIN CAPITAL LETTER G WITH CIRCUMFLEX}" }
   elsif ($char eq "z")  { return "\N{LATIN SMALL LETTER Z WITH ACUTE}" }
   elsif ($char eq "l")  { return "\N{LATIN SMALL LETTER L WITH ACUTE}" }
   elsif ($char eq "jh") { return "\N{LATIN CAPITAL LETTER J WITH CIRCUMFLEX}" }
   elsif ($char eq "v")  { return "\N{LATIN CAPITAL LETTER V WITH HOOK}" }
   elsif ($char eq "d")  { return "\N{LATIN SMALL LETTER ETH}" }
   elsif ($char eq "f")  { return "\N{LATIN SMALL LETTER F WITH HOOK}" }
   elsif ($char eq "p")  { return "\N{LATIN SMALL LETTER P WITH HOOK}" }
   elsif ($char eq "g")  { return "\N{LATIN SMALL LETTER G WITH CIRCUMFLEX}" }
   elsif ($char eq "sh") { return "\N{LATIN CAPITAL LETTER S WITH ACUTE}" }
   elsif ($char eq "th") { return "\N{LATIN CAPITAL LETTER T WITH CARON}" }
   elsif ($char eq "dh") { return "\N{LATIN CAPITAL LETTER ETH}" }
   elsif ($char eq "ch") { return "\N{LATIN CAPITAL LETTER C WITH CEDILLA}" }
   elsif ($char eq "j")  { return "\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}" }
   elsif ($char eq "le") { return "\N{LATIN SMALL LETTER L WITH CARON}" } # -lle
   else { return 0 }
}

sub is_vowel($;$) {
   my $test_char = lc(shift);
   my $restriction = shift;
   my $regex_str = "aáäàǎeéëèěiíïìǐîoóöòǒuúüùǔ";

   if (defined $restriction) {
      $restriction = lc($restriction);
      SWITCH: {
         if ($restriction eq "a") { $regex_str = "aáäàǎ" }
         if ($restriction eq "e") { $regex_str = "eéëèě" }
         if ($restriction eq "i") { $regex_str = "iíïìǐ" }
         if ($restriction eq "î") { $regex_str = "î" }
         if ($restriction eq "o") { $regex_str = "oóöòǒ" }
         if ($restriction eq "u") { $regex_str = "uúüùǔ" }
      }
   }

   if ($test_char =~ /[$regex_str]/) {
      return 1;
   }
}

sub is_lax($) {
   my $ipaChar = shift;
   if (defined $ipaChar && ($ipaChar == 0x026a || $ipaChar == 0x0254)) {
      return 1;
   }
}

sub check($$;$) {
   return check_string($lexeme, @_);
}

sub check_font($$;$) {
   return check_string($font_input, @_);
}

sub check_string($$;$) {
   my $input = shift;
   my $search = shift;
   my $ndx = shift;
   my $final_ok = shift;

   if (not defined $final_ok) {
      $final_ok = 1;
   }

   my $search_length = length($search);
   my $substr = substr($input, $ndx, $search_length);
   my $not_final = ($ndx + $search_length != length($input) - $endPunct);
   if ($substr eq $search && ($final_ok || $not_final)) {
      return 1;
   }
}

1;

__END__
