#!/usr/bin/gawk -f
#

#
#  Copyright (C) 1995-2002 Ricardo Ueda Karpischek and others
#
#  This is free software; you can redistribute it and/or modify
#  it under the terms of the version 2 of the GNU General Public
#  License as published by the Free Software Foundation.
#
#  This software is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this software; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
#  USA.
#

#
# Notas importantes
#
BEGIN {
notas = "\
1. Conjugue 1.1 presta-se basicamente para gerar um dicionrio\n\
para ser usado em verificadores ortogrficos. Por isso, os tempos\n\
compostos no so considerados.\n\
\n\
2. O programa chama de irregulares alguns verbos que os\n\
gramticos consideram regulares, e vice-versa. Na medida do\n\
possvel tentaremos sanar essas diferenas (ou ao menos\n\
precis-las) nas prximas verses.\n\
\n\
3. O programa conjugar qualquer verbo no pronominal da lngua\n\
portuguesa, mesmo os que no constam do banco. O paradigma,\n\
quando no  conhecido,  deduzido heuristicamente, mas esse\n\
procedimento  sujeito a erros.\n\
\n\
4. Todas as vezes em que voc tentar conjugar um verbo que no\n\
consta no banco, ele ser concatenado ao arquivo\n\
$HOME/.conjugue-novos (o programa *no* acrescenta o contedo\n\
desse arquivo ao banco quando o banco  carregado logo aps o\n\
disparo do programa)."
}

#
# Notas sobre a implementao
# ---------------------------
#
# Os verbos conhecidos pelo programa so as entradas do
# vetor associativo V. O valor da entrada pode ser a string
# nula, uma lista de tempos e/ou modos com a forma de
# flexionar o verbo nesses tempos e/ou modos ou um outro
# verbo (separador ":"). Exemplos:
#
#     V["compilar"] = ""
#     V["abrir"]    = "FN:abrir:abrindo:aberto"
#     V["amar"]     = "cantar"
#
# No primeiro caso, o verbo  flexionado segundo algum dos paradigmas
# (a ser deduzido pela terminao do verbo). No segundo caso o verbo
#  considerado paradigma nos tempos explicitados (os outros seguiro
# o paradigma regular). No terceiro o verbo ser conjugado
# segundo o paradigma indicado.
#
# A partir da verso 3.0, as formas alternativas de particpio
# passaram a ser armazenadas no vetor PA:
#
#     PA["emergir"]  = "emerso"
#     PA["frigir"]   = "frito"
#
# faltam:
# -------
#
# conjugao de verbos pronominais
# suporte para formas abundantes alm dos particpios
# leitura da lista de verbos novos aps o carregamento do banco
#

BEGIN {

#
# Lista de tempos e modos
#
abrevia = "\
FN - formas nominais: infinitivo, gerndio e particpio\n\
IP - infinitivo pessoal\n\
\n\
PI - presente do indicativo\n\
II - imperfeito do indicativo\n\
EI - perfeito do indicativo\n\
MI - mais-que-perfeito do indicativo\n\
TI - futuro do pretrito do indicativo\n\
FI - futuro do presente do indicativo\n\
\n\
PS - presente do subjuntivo\n\
IS - imperfeito do subjuntivo\n\
FS - futuro do subjuntivo\n\
\n\
IA - imperativo afirmativo\n\
IN - imperativo negativo"

#
# Flags verbais para o ispell
#
# Toda vez que um novo paradigma for includo no banco de verbos
# ser necessrio adicionar aqui uma flag para ele. As flags
# reservadas para verbos so as de a a z, inclusive. Note que
# o ispell  compilado por default com suporte para 58 flags
# (A-Z, a-z e os caracteres entre Z e A). A flag z  reservada
# para as formas alternativas de particpio.
#

#
# a. paradigmas regulares
#
FV["FN"] = "a"
FV["IP"] = "b"
FV["PI"] = "c"
FV["II"] = "d"
FV["EI"] = "e"
FV["MI"] = "f"
FV["TI"] = "g"
FV["FI"] = "h"
FV["PS"] = "i"
FV["IS"] = "j"
FV["FS"] = "k"
FV["IA"] = "l"
FV["IN"] = "m"

#
# b. primeira conjugao, fatorao por trs letras
#
FV["comunicar"]  = "n"
FV["abraar"]    = "n"
FV["saudar"]     = "n"
FV["passear"]    = "n"
FV["resfolegar"] = "n"
FV["apoiar"]     = "n"
FV["magoar"]     = "n"
FV["estar"]      = "n"
FV["aguar"]      = "n"

FV["moscar"]     = "o"
FV["dar"]        = "o"
FV["chegar"]     = "o"
FV["odiar"]      = "o"
FV["apaziguar"]  = "o"

FV["cegar"]      = "p"
FV["mobiliar"]   = "p"
FV["adequar"]    = "p"

#
# paradigmas no caracterizveis por trs letras: crer
#
FV["arruinar"]   = "t"
FV["enviuvar"]   = "u"

#
# c. segunda conjugao, fatorao por trs letras
#
FV["caber"]    = "n"
FV["conhecer"] = "n"
FV["perder"]   = "n"
FV["proteger"] = "n"
FV["valer"]    = "n"
FV["doer"]     = "n"
FV["requerer"] = "n"
FV["ser"]      = "n"
FV["ter"]      = "n"
FV["erguer"]   = "n"
FV["haver"]    = "n"
FV["jazer"]    = "n"

FV["saber"]    = "o"
FV["poder"]    = "o"
FV["querer"]   = "o"
FV["conter"]   = "o"
FV["ver"]      = "o"
FV["dizer"]    = "o"
FV["moer"]     = "o"

FV["feder"]    = "p"
FV["prover"]   = "p"
FV["fazer"]    = "p"

FV["precaver"] = "q"
FV["trazer"]   = "q"

FV["reaver"]   = "r"

FV["escrever"] = "s"

#
# paradigmas no caracterizveis por trs letras: crer
#
FV["crer"]     = "t"

#
# terceira conjugao, fatorao por trs letras
#
FV["cair"]      = "n"
FV["proibir"]   = "n"
FV["ressarcir"] = "n"
FV["pedir"]     = "n"
FV["afligir"]   = "n"
FV["engulir"]   = "n"
FV["reunir"]    = "n"
FV["abrir"]     = "n"
FV["sortir"]    = "n"
FV["seguir"]    = "n"
FV["argir"]    = "n"
FV["ouvir"]     = "n"
FV["traduzir"]  = "n"

FV["fugir"]     = "o"
FV["cobrir"]    = "o"
FV["surtir"]    = "o"
FV["construir"] = "o"
FV["vir"]       = "o"

FV["convergir"] = "p"
FV["extinguir"] = "p"

FV["ir"]        = "q"

FV["rir"]       = "r"
FV["atribuir"]  = "r"

FV["ruir"]      = "s"
FV["advir"]     = "s"


#
# paradigmas no caracterizveis por trs letras: abolir,
# adir, dormir, ferir, progredir e subir.
#
FV["progredir"] = "t"
FV["dormir"]    = "u"
FV["subir"]     = "v"
FV["ferir"]     = "w"
FV["abolir"]    = "x"
FV["adir"]      = "y"

# e. quarta conjugao

#
# Relaciona os paradigmas no caracterizveis por trs letras.
#
RP["crer"]      = 1
RP["progredir"] = 1
RP["dormir"]    = 1
RP["subir"]     = 1
RP["ferir"]     = 1
RP["abolir"]    = 1
RP["adir"]      = 1
RP["arruinar"]  = 1
RP["enviuvar"]  = 1

}

#
# Verifica se o radical  prefixo de cada conjugao da lista. Retorna
# "R" em caso afirmativo, ou "" caso contrrio (isso  uma reminiscncia
# histrica).
#
function checa_radical(verbo,r,c,n,t,i,mpc,l) {

    # determina o maior prefixo do radical comum a todas as formas
    n = split(c,t,":")
    mpc = r
    for (i=1; i <= n; ++i)
        if (t[i] != "")
            while (match(t[i],mpc) != 1) mpc = substr(mpc,1,length(mpc)-1)
    if (match(mpc,r) > 0)
        return("R")
    else
        return("")
}

#
# Essa funo recebe o radical e a conjugao como primeiro
# e segundo parmetros, e classifica o tipo de alterao
# do radical atribuindo-lhe um cdigo numrico.
#
# Por exemplo: se a ltima vogal de um radical for "o" (como
# em "dorm+ir"), ento o cdigo zero associado a esse radical
# indica que essa vogal deve ser trocada por "u" (como em
# "durm+a").
#
# Um mesmo cdigo pode ser utilizado para referir vrias
# transformaes diferentes, desde que a vogal inicial e a
# vogal resultante ocorram como tais uma nica vez sob esse
# cdigo (em outras palavras, cada cdigo deve definir uma
# bijeo).
#
# Para incluir uma nova substituio de vogais sob um cdigo j
# existente, basta adicionar o teste contendo as duas vogais,
# usando como exemplo as transformaes j relacionadas.
#
# Se no for possvel incluir a substituio sob um cdigo j
# existente, ser necessrio criar um novo cdigo. Atualmente
# s se podem usar como cdigos os dgitos decimais (0-9),
# sendo que os dgitos de 0 a 6 (inclusive) esto em uso.
#
# Em qualquer caso,  necessrio incluir a substituio
# inversa na rotina "desnormaliza". Se isso no for feito,
# a mensagem "vogal v invlida para regra n e radical r"
# ser exibida.
#
# Note que a acentuao grfica precisa ser considerada como
# substituio de vogal (proibir > probe).
#
function normaliza(r,c,t,i,j,k,l,v,w,m,n,x,x1,y,y1,u,pi,ri,pk,rk,pm,rm) {

    # quebra a conjugao nas vrias pessoas
    n = split(c,t,":")

    # tamanho e ltima letra do radical
    l = length(r);
    u = substr(r,l,1);

    # obtm em i a posio da ltima vogal do radical
    for (i=l; (i >= 1) && (!(substr(r,i,1) in VOG)); --i);

    # idem, em k, desconsiderando a ltima letra do radical
    for (k=l-1; (k >= 1) && (!(substr(r,k,1) in VOG)); --k);

    # idem, em m, desconsiderando as ltimas duas letras do radical
    for (m=l-2; (m >= 1) && (!(substr(r,m,1) in VOG)); --m);

    # se existirem essas vogais armazene-as em v e x
    if (i >= 1) {
        v = substr(r,i,1)
    }
    if (k >= 1) {
        x = substr(r,k,1)
    }
    if (m >= 1) {
        x1 = substr(r,m,1)
    }

    #
    # .. para cada pessoa tente classificar a alterao do radical
    # dentro dos casos conhecidos. A conjugao  ento alterada
    # da forma
    #     "radical alterado" "sufixo"
    # para a forma
    #     "radical" "sufixo" "cdigo"
    #
    c = ""
    for (j=1; j<=n; ++j) {
    
        w = substr(t[j],i,1)
        y = substr(t[j],k,1)
        y1 = substr(t[j],m,1)

        # prefixos e restos relativos  i-sima e k-sima letras
        pi = substr(t[j],1,i-1)
        ri = substr(t[j],i+1,l-i)
        pk = substr(t[j],1,k-1)
        rk = substr(t[j],k+1,l-k-1)
        pm = substr(t[j],1,m-1)
        rm = substr(t[j],m+1,l-m-1)

        # regra 0: substitua a ltima vogal do radical
        #     de a para e, ou
        #     de o para u (dormir > durma), ou
        #     de u para o (cuspir > cospe), ou
        #     de e para i (ferir  > fira),
        #     de i para  (proibir  > probe),
        # e verifique se se obtm o radical
        if ((i > 0) &&
        (((w == "e") && (pi "a" ri == r)) ||
        ((w == "o") && (pi "u" ri == r)) ||
        ((w == "") && (pi "i" ri == r)) ||
        ((w == "u") && (pi "o" ri == r)) ||
        ((w == "i") && (pi "e" ri == r)))) {
            t[j] = pi v substr(t[j],i+1) "0"
            #print "regra 0 aplicada para transformar " r " em " t[j]
        }

        # regra 1: substitua a ltima vogal do radical
        #     de a para i (fazer > fizer), ou
        #     de o para  (voar > vo), ou
        #     de u para  (saudar > sado),
        # e verifique se se obtm o radical
        else if ((i > 0) &&
        (((w == "i") && (pi "a" ri == r)) ||
        ((w == "") && (pi "u" ri == r)) ||
        ((w == "") && (pi "o" ri == r))))
            t[j] = pi v substr(t[j],i+1) "1"
    
        # regra 2: desconsidere a ltima letra do radical e substitua a
        # ltima vogal
        #     de a para o (trazer > trouxe), ou
        #     de i para  (mobiliar > moblio)
        #     de e para i (convergir > convirja)
        #     de o para u (moscar > musque)
        # e verifique se se obtm o radical (a menos da ltima letra).
        else if ((k > 0) &&
        (((y == "i") && (pk "e" rk u == r)) ||
        ((y == "") && (pk "i" rk u == r)) ||
        ((y == "u") && (pk "o" rk u == r)) ||
        ((y == "o") && (pk "a" rk u == r))))
            t[j] = r substr(t[j],l) "2"

        # regra 3: desconsidere a ltima letra do radical e substitua a
        # ltima vogal
        #     de a para  (haver > h)
        #     de o para  (apoiar > apio)
        # e verifique se se obtm o radical (a menos da ltima letra).
        else if ((k > 0) &&
        (((y == "") && (pk "a" rk u == r)) ||
        ((y == "") && (pk "o" rk u == r))))
            t[j] = r substr(t[j],l) "3"

        # regra 4: desconsidere a ltima letra do radical e substitua a
        # ltima vogal
        #     de a para  (haver > ho)
        # e verifique se se obtm o radical (a menos da ltima letra).
        else if ((k > 0) &&
        ((y == "") && (pk "a" rk u == r)))
            t[j] = r substr(t[j],l) "4"

        # regra 5: desconsidere a ltima letra do radical e substitua a
        # ltima vogal
        #     de a para e (saber > sei)
        # e verifique se se obtm o radical (a menos da ltima letra).
        else if ((k > 0) &&
        ((y == "e") && (pk "a" rk u == r)))
            t[j] = r substr(t[j],l) "5"

        # regra 6: desconsidere a ltima letra do radical e verifique
        # se se obtm o radical, desconsiderada a ltima letra (ouvir > oua).
        else if ((substr(t[j],1,l-1) == substr(r,1,l-1)) && (substr(t[j],1,l) != substr(r,1,l)))
            t[j] = r substr(t[j],l) "6"

        # regra 7: desconsidere as duas ltimas letras do radical e
        # substitua a ultima vogal
        #     de o para   (resfolegar > resflego)
        else if ((m > 0) &&
        ((y1 == "") && (pm "o" rm u == r)))
            t[j] = r substr(t[j],l) "7"

        # todas as regras conhecidas falharam
        else if ((t[j] != "") && (substr(t[j],1,l) != substr(r,1,l)))
            print "vogal no normalizada: w=" w ", y=" y " (" r ":" t[j] ")"
    
        # concatena em c a nova forma da conjugao da pessoa corrente
        c = c ((j>1) ? ":" : "") t[j]
    }

    return(c)
}

#
# desnormaliza um radical, isto , aplica a regra inversa segundo
# as definies da rotina de normalizao. Recebe em r o radical e
# em f o cdigo da transformao. Veja a documentao da funo
# "normaliza" para detalhes.
#
function desnormaliza(r,f,i,pen_ind,v,pen_vog) {

    # casos onde se trabalha com o radical inteiro
    if (f < 2) {

        # obtm em i a posio da ltima vogal do radical, e a vogal
        for (i=length(r); (i >= 1) && (!(substr(r,i,1) in VOG)); --i);
        v = substr(r,i,1)
        # substitui a ltima vogal conforme a regra
        if (f == "0") {
            if (v == "a")
                r = substr(r,1,i-1) "e" substr(r,i+1)
            else if (v == "o")
                r = substr(r,1,i-1) "u" substr(r,i+1)
            else if (v == "u")
                r = substr(r,1,i-1) "o" substr(r,i+1)
            else if (v == "i")
                r = substr(r,1,i-1) "" substr(r,i+1)
            else if (v == "e")
                r = substr(r,1,i-1) "i" substr(r,i+1)
            else
                print "vogal " v " invlida para regra " f " e radical " r
        }
        else if (f == 1) {
            if (v == "a")
                r = substr(r,1,i-1) "i" substr(r,i+1)
            else if (v == "o")
                r = substr(r,1,i-1) "" substr(r,i+1)
            else if (v == "u")
                r = substr(r,1,i-1) "" substr(r,i+1)
            else
                print "vogal " v " invlida para regra " f " e radical " r
        }
    }

    # casos onde se trabalha com o radical a menos da ltima letra
    else {

        #print "radical submetido: " r

        # remove a ltima letra do radical
        r = substr(r,1,length(r)-1);

        #print "radical sem a ltima letra: " r

        # obtm em ltima vogal do radical reduzido e sua posio
        for (i=length(r); (i >= 1) && (!(substr(r,i,1) in VOG)); --i);
        v = substr(r,i,1)

        # obtm a penltima vogal do radical reduzido e sua posio
	for (pen_ind=i-1; (pen_ind >= 1) && (!(substr(r,pen_ind,1) in VOG)); --pen_ind);
        pen_vog = substr(r,pen_ind,1)

        # substitui a ltima vogal conforme a regra
        if (f == "2") {
            if (v == "a")
                r = substr(r,1,i-1) "o" substr(r,i+1)
            else if (v == "e")
                r = substr(r,1,i-1) "i" substr(r,i+1)
            else if (v == "i")
                r = substr(r,1,i-1) "" substr(r,i+1)
            else if (v == "o")
                r = substr(r,1,i-1) "u" substr(r,i+1)
            else
                print "vogal " v " invlida para regra " f " e radical " r
        }
        if (f == "3") {
            if (v == "a")
                r = substr(r,1,i-1) "" substr(r,i+1)
            else if (v == "o")
                r = substr(r,1,i-1) "" substr(r,i+1)
            else
                print "vogal " v " invlida para regra " f " e radical " r
        }
        if (f == "4") {
            if (v == "a")
                r = substr(r,1,i-1) "" substr(r,i+1)
            else
                print "vogal " v " invlida para regra " f " e radical " r
        }
        if (f == "5") {
            if (v == "a")
                r = substr(r,1,i-1) "e" substr(r,i+1)
            else
                print "vogal " v " invlida para regra " f " e radical " r
        }
        if (f == "7") {
            if (pen_vog == "o")
                r = substr(r,1,pen_ind-1) "" substr(r,pen_ind+1)
            else
                print "vogal " v " invlida para regra " f " e radical " r
        }
    }
    return(r)
}

#
# Fatora a conjugao pelo radical, indicando para cada pessoa
# a terminao e a correo do radical, se houver.
#
function fatora_prefixos(verbo,lista,t,mpc,i,R,D,l,lf,j,n,c) {

    # transforma o mpc numa funo do radical
    R = substr(verbo,1,(l=length(verbo))-2)
    D = substr(R,1,l-3)
    mpc = checa_radical(verbo,R,lista)

    if (mpc == "") {
        lista = normaliza(R,lista)
        mpc = checa_radical(verbo,R,lista)
    }
    if (mpc == "") {
        j = 1
        print "cuidado: mpc do verbo " verbo " (" lista ") no resolvido"
    }

    # constri a lista fatorada
    n = split(lista,t,":")
    j = length(verbo)-1;
    for (i=1; i<=n; ++i) {
        if (i > 1) lf = lf ":"
        c = substr(t[i],length(t[i]));
        lf = lf ((t[i] == "") ? "-" : substr(t[i],j))
    }
    return(lf)
}

#
# A partir da conjugao do verbo, cria um modelo que permite
# aplicar a mesma conjugao em outros verbos.
#
function cria_modelo(verbo,t,i,n) {

    n = split(V[verbo],t,"\n")
    V[verbo] = ""
    for (i=1; i<=n; ++i) {
        t[i] = substr(t[i],1,3) fatora_prefixos(verbo,substr(t[i],4))
        if (i > 1) V[verbo] = V[verbo] "\n"
        V[verbo] = V[verbo] t[i]
    }
}

#
# Carrega o banco indicado criando entradas do vetor V. O
# formato do banco est descrito na man page do conjugue.
#
function carrega_banco(banco,a,b,n,l,vt,p,k) {

    # contadores de paradigmas, verbos e linhas
    n = m = l = 0

    # estado da leitura
    estado = "nulo"

    # loop de leitura de linhas
    while ((getline <banco) > 0) {

        # contabiliza a linha recm-lida
        ++l

        # despreza linhas vazias e comentrios
        if ((NF > 0) && ((a=substr($0,1,1)) != "#")) {

            # continua lendo paradigma ou inicia leitura da lista
            if (estado == "lendo_paradigma") {

                # uma parte da conjugao do paradigma atual
                if (substr($0,1,2) in TM) {
                    if (V[paradigma] == "")
                        V[paradigma] = $0
                    else
                        V[paradigma] = V[paradigma] "\n" $0
                }

                # concluda leitura do paradigma
                else {
                    cria_modelo(paradigma)
                    estado = "lendo_lista"
                }
            }

            # continua lendo lista ou volta ao estado nulo
            if (estado == "lendo_lista") {

                # verbo da lista do paradigma atual
                if (match($0,":") == 0)
                    if (substr($1,length($1),1) == "r") {
                        if ($1 in V) {
                            V[$1] = paradigma ":" V[$1]
                            print "ocorrncia mltipla de " $1 > "/dev/stderr"
                        }
                        else
                            V[$1] = paradigma
                        ++m
                    }
                    else {
                        print "conjugue: erro na linha " l " do banco"
                        print $0 " no  verbo"
                    }

                # concluda leitura da lista de verbos
                else
                    estado = "nulo"
            }

            # procura paradigma ou verbo abundante
            if (estado == "nulo") {

                # isola paradigma
                if ((a=substr($0,1,9)) == "paradigma") {
                    if ((k=split($0,p,":")) > 1)
                        LP[p[2]] = p[3]
                    paradigma = (k > 1) ? p[2] : ""
                    if (paradigma in V) {
                        print "conjugue: erro fatal na linha " l " do banco"
                        print "paradigma " paradigma "j ocorreu antes."
                        exit
                    }
                    ++n
                    estado = "lendo_paradigma"

                    # o primeiro paradigma de cada conjugao  o regular
                    vt = substr(paradigma,length(paradigma)-1,1)
                    if ((paradigma != "") && (pr[vt] == "")) {
                        pr[vt] = paradigma
                    }
                }

                # isola verbo abundante
                else if (a == "abundante") {

                    if (split($0,p,":") != 3) {
                        print "conjugue: erro fatal na linha " l " do banco"
                        print "sintaxe incorreta para forma abundante"
                        exit 1
	            }

                    if ((p[2] in PA) && (PA[p[2]] != p[3])) {
                        print "conjugue: erro fatal na linha " l " do banco"
                        print "no estou preparado para verbos com duas"
                        print "formas alternativas do particpio."
                        exit 1
                    }
                    else
                        PA[p[2]] = p[3]

                }

                # erro no banco
                else {
                    print "conjugue: erro fatal na linha " l " do banco"
                    print "esperado \"paradigma\" ou \"abundante\""
                    exit 1
                }
            }
        }
    }

    if ((FORMATO !~ /^(c|aa|b)/) && (CMD == "")) {
        print "lidos " n " paradigmas"
        print "lidos " m+n " verbos"
    }
}

#
# Expande uma string abreviada. Isso significa basicamente
# prefixar cada componente de c (as componentes so
# separadas por ":") pelo radical r, alterado segundo a
# regra indicada.
#
function expande(c,r,v,i,j,l,p,q,z,f) {

#    Acho que aqui nao est fazendo nada:
#    p = substr(p,2,i-2)

    i = split(substr(c,1),z,":")
    c = ""
    for (j=1; j <= i; ++j) {
        f = substr(z[j],length(z[j]))
        if (("0" <= f) && (f <= "9"))
            c = c ":" desnormaliza(r,f) p substr(z[j],1,length(z[j])-1)
        else
            c = c ":" ((z[j] == "-") ? "" : r p z[j])
    }
    c = substr(c,2,length(c)-1)
    return c
}

#
# cria uma regra de sufixo
#
function regra(lema,forma,a,R,k)
{
    # antecedente
    gsub(""," ",a)
    R = "   " a "> "

    k = length(forma) - 1
    while ((k>0) && (substr(lema,1,k) != substr(forma,1,k))) --k
    if (k < length(lema))
        R = R "-" substr(lema,k+1) ","
    R = R substr(forma,k+1)
    
    # embeleze
    R = toupper(R);
    for (k=length(R); (k < 33); ++k)
        R = R " "
    
    return(R)
}

#
# Conjuga o verbo dado em todos os modos e tempos
#
function conjugue_todos(verbo,r,vt,tmr,tme,i,j,p,q,a,k,l,t,reg,C,cs,pp,R,TE,RA,LT,O,nr) {

    # Obteno da raiz e da vogal temtica
    l = length(verbo)
    if (l < 2)
        return
    r = substr(verbo,1,l-2)
    vt = substr(verbo,l-1,1)

    # Obteno do paradigma regular
    cpr = V[pr[vt]]

    # O verbo conta com uma lista especfica de tempos/modos
    if (substr(V[verbo],1,2) in TM) {
        esp = V[verbo]
	if (pr[vt] == verbo) {
            if (FORMATO !~ /^(c|a|b)/)
                print "# paradigma regular"
            reg = 1;
        }
        else {
            if (FORMATO !~ /^(c|a|b)/)
                print "# paradigma irregular"
            reg = 0
        }
        p = verbo
    }

    # O verbo  conjugado segundo algum dos paradigmas
    else {

        p = V[verbo];

        # Temos que deduzir um paradigma
        if (p == "") {
            t = 0
            for (i in LP) {
                k = length(LP[i])
                if ((LP[i] != "") && (k > t) && (substr(verbo,l-k+1) == LP[i])) {
                    p = i
                    t = k
                }
            }
            if (pr[vt] == p) {
                if (FORMATO !~ /^(c|a|b)/)
                    print "# paradigma deduzido: " p " (regular)"
                reg = 1
            }
            else {
                if (FORMATO !~ /^(c|a|b)/)
                    print "# paradigma deduzido: " p " (irregular)"
                reg = 0
            }
        }

        # o paradigma foi explicitado
        else {
            if (pr[vt] == p) {
                if (FORMATO !~ /^(c|a|b)/)
                    print "# paradigma: " p " (regular)"
                reg = 1
            }
            else {
                if (FORMATO !~ /^(c|a|b)/)
                    print "# paradigma: " p " (irregular)"
                reg = 0
            }
        }

        # obtm regras especficas do paradigma
        esp = V[p]
    }

    # Correo do regular pelo especfico
    n = split(cpr,tmr,"\n")
    m = split(esp,tme,"\n")
    if ((m < n) && (FORMATO !~ /^(c|a|b)/) && (CMD == ""))
        print "# regular em " n-m " casos"
    for (i=1; i<=n; ++i)
        tmr[substr(tmr[i],1,2)] = tmr[i]
    for (i=1; i<=m; ++i) {

        #
        # o banco de verbos pode incluir regras de conjugao
        # desnecessrias (isto , que seriam dedutveis a partir
        # do paradigma regular). A fim delas no gerarem regras de
        # afixos desnecessrias, elas so detetadas e descartadas
        # aqui.
        #
        j = substr(tme[i],1,2)
        if (tmr[j] == tme[i])
            delete tme[i]

        #
        # a regra no  desnecessria: troque o caso regular pelo
        # explicitado.
        #
        else
            tmr[j] = tme[i]
    }

    #
    # Adio das formas alternativas do particpio. Isso fica
    # inibido no caso de estarmos gerando os afixos para no
    # contaminar os paradigmas com particularidades. Estas
    # sero tratadas  parte pela flag z.
    #
    # Isso realmente est um pouco confuso porque os particpios
    # alternativos esto sendo tratados de forma diferenciada
    # no caso do formato ser "aa" ou "ci".
    #
    if (("FN" in tmr) && (FORMATO != "aa") && (FORMATO != "ci") && (verbo in PA))
        tmr["FN"] = tmr["FN"] "," PA[verbo]

    # formato curto com flags do br.ispell (obsoleto)
    if (FORMATO=="old_ci") {

        if (reg == 1)
            print verbo "/R/T/F/N/C"

        else {

            #
            # Neste ponto o conjugue gera algumas formas
            # enclticas. Note que aqui s so tratados verbos
            # irregulares. De fato, o caso regular  tratado
            # no br.aff pela flag C. A documentao da flag C
            # no br.aff contm muitas informaes adicionais.
            #

            #
            # formas enclticas dos tempos compostos
            #
            #   mago (-la)
            #   ro (-lo)
            #   proibi (-lo) (desnecessrio acrescentar)
            #   sup (-lo)
            #
            if (vt == "a")
                print r ""
            else if (vt == "e")
                print r ""
            else if (vt == "o")
                print r ""

            #
            # formas enclticas da primeira do plural, vrios tempos
            #

            # magoamo (-la)
            C = expande(substr(tmr["PI"],4),r,verbo)
            n = split(C,pp,":")
            if (pp[4] != "")
                print substr(pp[4],0,length(pp[4])-1);

            # magoemo (-la)
            C = expande(substr(tmr["PS"],4),r,verbo)
            n = split(C,pp,":")
            if (pp[4] != "")
                print substr(pp[4],0,length(pp[4])-1);

            # magoarmo (-la)
            C = expande(substr(tmr["FS"],4),r,verbo)
            n = split(C,pp,":")
            if (pp[4] != "")
                print substr(pp[4],0,length(pp[4])-1);

            # magoaremo (-la)
            C = expande(substr(tmr["FI"],4),r,verbo)
            n = split(C,pp,":")
            if (pp[4] != "")
                print substr(pp[4],0,length(pp[4])-1);

            if ((verbo == "passear") || (p == "passear"))
                print verbo "/R/S/F"

            else if ((verbo == "conhecer") || (p == "conhecer"))
                print verbo "/R/S/F"

            #
            # O paradigma ferir congrega verbos com terminaes variadas,
            # o que torna complexa a alterao do radical no ispell.
            #
            # Por exemplo, a regra simples
            #
            #     I R > -ERIR,IRO    # ferir > firo
            #
            # no funciona para
            #
            #     COMPELIR > COMPILO
            #     ADVERTIR > ADVIRTO
            #     MENTIR   > MINTO
            #     REPETIR  > REPITO
            #     VESTIR   > REVISTO
            #     SERVIR   > SIRVO
            #     SERZIR   > SIRZO
            #
            # Assim, vrias outras esto presentes no br.aff (veja a
            # flag S).
            #
            else if ((verbo ~ "ferir") || (p == "ferir"))
                print verbo "/R/S/F"

            else if ((verbo == "comunicar") || (p == "comunicar"))
                print verbo "/R/Q/N"

            else if ((verbo == "cegar") || (p == "cegar"))
                print verbo "/R/A/N"

            else if ((verbo == "abraar") || (p == "abraar"))
                print verbo "/R/B/N"

            else if ((verbo == "magoar") || (p == "magoar"))
                print verbo "/R/M/F"

            else if ((verbo == "abolir") || (p == "abolir"))
                print verbo "/R/M/F"

            else {
                for (i in TM) {
                    C = expande(substr(tmr[i],4),r,verbo)
                    gsub(":","\n",C)
                    print C
                }
            }
        }
    }

    # formato curto
    else if (FORMATO=="c") {

        for (i in TM) {
            C = expande(substr(tmr[i],4),r,verbo)
            gsub(":","\n",C)
            print C
        }
    }

    # formato normal
    else if (FORMATO ~ /^n/) {

        for (i in TM) {
            C = expande(substr(tmr[i],4),r,verbo)
            print i ":" C
        }
    }

    # formato para debugao (exibe a regra de formao)
    else if (FORMATO ~ /^d/) {

        for (i in TM) {
            print i ":" substr(tmr[i],4)
        }
    }

    # formato longo (default)
    else if ((FORMATO == "l") || (FORMATO == "")) {

        for (i in TM) {
            C = expande(substr(tmr[i],4),r,verbo)
            print TM[i] ":" C
        }
    }

    # formato muito longo
    else if (FORMATO == "ll") {

        # formas nominais
        print TM["FN"]
        C = expande(substr(tmr["FN"],4),r,verbo)
        split(C,cs,":");
        print "   infinitivo: " cs[1]
        print "   gerndio: " cs[2]
        print "   particpio: " cs[3]

        # presente do indicativo
        print TM["PI"]
        C = expande(substr(tmr["PI"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   " P[j] " " cs[j]
        }

        # imperfeito do indicativo
        print TM["II"]
        C = expande(substr(tmr["II"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   " P[j] " " cs[j]
        }

        # perfeito do indicativo
        print TM["EI"]
        C = expande(substr(tmr["EI"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   " P[j] " " cs[j]
        }

        # mais-que-perfeito do indicativo
        print TM["MI"]
        C = expande(substr(tmr["MI"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   " P[j] " " cs[j]
        }

        # futuro do pretrito do indicativo
        print TM["TI"]
        C = expande(substr(tmr["TI"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   " P[j] " " cs[j]
        }

        # futuro do presente do indicativo
        print TM["FI"]
        C = expande(substr(tmr["FI"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   " P[j] " " cs[j]
        }

        # no presente do subjuntivo adicionamos "que"
        print TM["PS"]
        C = expande(substr(tmr["PS"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   que " P[j] " " cs[j]
        }
	
        # no imperfeito do subjuntivo adicionamos "se"
        print TM["IS"]
        C = expande(substr(tmr["IS"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   se " P[j] " " cs[j]
        }
	
        # no futuro do subjuntivo adicionamos "quando"
        print TM["FS"]
        C = expande(substr(tmr["FS"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   quando " P[j] " " cs[j]
        }
	
        # imperativo afirmativo
        print TM["IA"]
        C = expande(substr(tmr["IA"],4),r,verbo)
        split(C,cs,":");
        for (j=2; j<=6; ++j) {
            if (cs[j-1] != "")
                print "   " cs[j-1] " " P[j]
        }
	
        # imperativo negativo
        print TM["IN"]
        C = expande(substr(tmr["IN"],4),r,verbo)
        split(C,cs,":");
        for (j=2; j<=6; ++j) {
            if (cs[j-1] != "")
                print "   no " cs[j-1] " " P[j]
        }
	
        # no infinitivo pessoal adicionamos "por"
        print TM["IP"]
        C = expande(substr(tmr["IP"],4),r,verbo)
        split(C,cs,":");
        for (j=1; j<=6; ++j) {
            if (cs[j] != "")
                print "   por " cs[j] " " P[j]
        }
    }

    # gera lista para o buildhash
    else if (FORMATO == "ci") {

        #
        # Alguns paradigmas no so caracterizveis pelas trs ltimas
        # letras. Para eles, temos que reservar uma flag especfica.
        #
        # Se a execuo do conjugue durante a gerao do v.ispell
        # for interrompida neste ponto,  necessrio no primeiro
        # bloco BEGIN do programa alocar uma flag especfica para
        # o paradigma problemtico, e inclui-lo no hash RP.
        #
        if ((reg == 0) &&
            (RP[p] == 0) &&
            (substr(verbo,length(verbo)-2) != substr(p,length(p)-2))) {

            print "erro fatal: verbo "  verbo " segue o paradigma " p > "/dev/stderr"
            print "mas diferem nas trs ltimas letras" > "/dev/stderr"
            exit 1
        }

        # verbo com forma alternativa de particpio
        if (verbo in PA)
            print verbo PF[p] "/z"

        # outros verbos
        else
            print verbo PF[p]
    }

    # gera dicionrio base
    else if (FORMATO == "b") {

        if (FVN[p] == "") {
            print "paradigma " p " no tem ndice" > "/dev/stderr"
            exit 1
        }

        # verbo com forma alternativa de particpio
        if (verbo in PA)
            print verbo " v 1 1|par=" FVN[p] " 1|PA 1"

        # outros verbos
        else
            print verbo " v 1 1|par=" FVN[p] " 1"
    }

    #
    # cria as regras de sufixao verbal
    #
    else if (FORMATO == "aa") {

        #if ((verbo != pr[vt]) && (FV[verbo] == "")) {
        #    print verbo " no consta na tabela de paradigmas!"
        #    exit 1
        #}

        if (reg == 0) {
            O = "";
            PF[verbo] = "/" FV[verbo]
        }
        else {
            PF[verbo] = ""
        }

        # obtm os tempos especficos no vetor TE
        if ((verbo == p) && (reg)) {
            for (i in TM) {
                TE[i] = 1
                PF[verbo] = PF[verbo] "/" FV[i]
            }
        }
        else {
            for (i=1; i<=m; ++i) {
                if (tme[i] != "") {
                    j = substr(tme[i],1,2)
                    TE[j] = 1
                }
            }
            if (verbo==p) {
                for (i in TM)
                    if (TE[i] == 0)
                        PF[verbo] = PF[verbo] "/" FV[i]
            }
        }

        if (reg == 0) {
            #O = O sprintf("#\n# VERBO " toupper(verbo) "\n# ")
            #for (k=6+length(verbo); k>0; --k)
            #    O = O sprintf("-")
            #O = O "\n"
            nr = 0
        }

        #
        # prepare uma lista para percorrer os tempos em dois passos
        #
        LT[0]  = LT[13] = "FN"
        LT[1]  = LT[14] = "IP"
        LT[2]  = LT[15] = "PI"
        LT[3]  = LT[16] = "II"
        LT[4]  = LT[17] = "EI"
        LT[5]  = LT[18] = "MI"
        LT[6]  = LT[19] = "TI"
        LT[7]  = LT[20] = "FI"
        LT[8]  = LT[21] = "PS"
        LT[9]  = LT[22] = "IS"
        LT[10] = LT[23] = "FS"
        LT[11] = LT[24] = "IA"
        LT[12] = LT[25] = "IN"

        # gera as regras para cada tempo e pessoa
        for (q=0; q<26; ++q) {

            i = LT[q]

            #
            # No primeiro passo processe os tempos que seguem o paradigma
            # regular a fim de bufferizar as regras herdadas daquele
            # paradigma. No segundo passo processe os tempos especficos.
            #
            if (q < 13) {
                if (TE[i]!=0)
                    continue
            }
            else if (TE[i]==0)
                continue

            if (reg) {
                #O = sprintf("#\n# VERBO " toupper(verbo) "\n# ")
                #for (k=6+length(verbo); k>0; --k)
                #    O = O sprintf("-")
                #O = O "\n"
                O = ""
                delete RA
                nr = 0
            }

            C = expande(substr(tmr[i],4),r,verbo)
            split(C,cs,":");

            #
            # elimine a forma alternativa do particpio se ela
            # for idntica a alguma das formas pessoais. Note que o
            # formato aa deve ser utilizado apenas para gerar os
            # afixos, assim esse mtodo abrupto no ter
            # nenhum efeito colateral alm de evitar a criao
            # de regras desnecessrias na flag z.
            #
            # As forma alternativas correspondem sempre s pessoas
            # do singular (ex. expulso, expulsas, expulsa), mas
            # mesmo assim permanece o masculino plural no coberto
            # (expulsos). Assim se a concidncia ocorrem no presente
            # do indicativo, mantemos o particpio no plural.
            #
            # Entretanto se a coincidncia ocorrer no presente do
            # subjuntivo (ex. entregue, entregues) ento eliminamos
            # a forma alternativa pois as flexes j esto todas
            # cobertas.
            #
            if ((i == "PI") && (verbo in PA) && (cs[1] == PA[verbo])) {
                PA[verbo] = PA[verbo] "s"
            }
            else if ((i == "PS") && (verbo in PA) && (cs[1] == PA[verbo])) {
                delete PA[verbo]
            }

            # flexione o particpio
            if (i == "FN") {
                j = substr(cs[3],1,length(cs[3])-1)
                cs[4] = j "a"
                cs[5] = j "os"
                cs[6] = j "as"
            }

            #
            # acrescente as formas enclticas
            #

            # ex. cant-la, vend-la, antep-la
            if ((i == "FN") && (cs[1] != "")) {
                if (vt == "a")
                    cs[7] = substr(cs[1],0,length(cs[1])-2) ""
                else if (vt == "e")
                    cs[7] = substr(cs[1],0,length(cs[1])-2) ""
                else if ((vt == "o") || (vt == ""))
                    cs[7] = substr(cs[1],0,length(cs[1])-2) ""
            }

            # ex. magoamo-la
            if ((i == "PI") && (cs[4] != ""))
                cs[7] = substr(cs[4],0,length(cs[4])-1)

            # ex. magoemo-la
            if ((i == "PS") && (cs[4] != ""))
                cs[7] = substr(cs[4],0,length(cs[4])-1)

            # ex. magoarmo-la
            if ((i == "FS") && (cs[4] != ""))
                cs[7] = substr(cs[4],0,length(cs[4])-1)

            # ex. magoaremo-la
            if ((i == "FI") && (cs[4] != ""))
                cs[7] = substr(cs[4],0,length(cs[4])-1)

            # ex. compusemo-la
            if ((i == "EI") && (cs[4] != ""))
                cs[7] = substr(cs[4],0,length(cs[4])-1)

            # documente o tempo
            #O = O "# " TM[i] ":\n"

            #
            # O paradigma ferir  difcil de tratar. Alguns verbos que
            # o adotam por paradigma necessitam de regras particulares
            # que seguem hardcoded.
            #
            # No chegamos a verificar com cuidado se essas regras
            # includas  mo no esto gerando falsos positivos.
            # Por exemplo, se existe um verbo que ao mesmo tempo segue
            # o paradigma ferir, termina com -elir mas no segue as
            # particularidades do verbo compelir.
            #
            if ((verbo == "ferir") && (i == "PS")) {
                O = O "    E R I R     > -ERIR,IRA      # fira\n"
                O = O "    E L I R     > -ELIR,ILA      # compila (compelir)\n"
                O = O "    E R T I R   > -ERTIR,IRTA    # advirta\n"
                O = O "    E N T I R   > -ENTIR,INTA    # minta\n"
                O = O "    E T I R     > -ETIR,ITA      # repita\n"
                O = O "    E S T I R   > -ESTIR,ISTA    # revista (revestir)\n"
                O = O "    E R V I R   > -ERVIR,IRVA    # sirva\n"
                O = O "    E R Z I R   > -ERZIR,IRZA    # sirza\n"
                O = O "    E R I R     > -ERIR,IRAS     # firas\n"
                O = O "    E L I R     > -ELIR,ILAS     # compilas (compelir)\n"
                O = O "    E R T I R   > -ERTIR,IRTAS   # advirtas\n"
                O = O "    E N T I R   > -ENTIR,INTAS   # mintas\n"
                O = O "    E T I R     > -ETIR,ITAS     # repitas\n"
                O = O "    E S T I R   > -ESTIR,ISTAS   # revistas (revestir)\n"
                O = O "    E R V I R   > -ERVIR,IRVAS   # sirvas\n"
                O = O "    E R Z I R   > -ERZIR,IRZAS   # sirzas\n"
                O = O "    E R I R     > -ERIR,IRAMOS   # firamos\n"
                O = O "    E L I R     > -ELIR,ILAMOS   # compilamos (compelir)\n"
                O = O "    E R T I R   > -ERTIR,IRTAMOS # advirtamos\n"
                O = O "    E N T I R   > -ENTIR,INTAMOS # mintamos\n"
                O = O "    E T I R     > -ETIR,ITAMOS   # repitamos\n"
                O = O "    E S T I R   > -ESTIR,ISTAMOS # revistamos (revestir)\n"
                O = O "    E R V I R   > -ERVIR,IRVAMOS # sirvamos\n"
                O = O "    E R Z I R   > -ERZIR,IRZAMOS # sirzamos\n"
                O = O "    E R I R     > -ERIR,IRAIS    # firais\n"
                O = O "    E L I R     > -ELIR,ILAIS    # compilais (compelir)\n"
                O = O "    E R T I R   > -ERTIR,IRTAIS  # advirtais\n"
                O = O "    E N T I R   > -ENTIR,INTAIS  # mintais\n"
                O = O "    E T I R     > -ETIR,ITAIS    # repitais\n"
                O = O "    E S T I R   > -ESTIR,ISTAIS  # revistais (revestir)\n"
                O = O "    E R V I R   > -ERVIR,IRVAIS  # sirvais\n"
                O = O "    E R Z I R   > -ERZIR,IRZAIS  # sirzais\n"
                O = O "    E R I R     > -ERIR,IRAM     # firam\n"
                O = O "    E L I R     > -ELIR,ILAM     # compilam (compelir)\n"
                O = O "    E R T I R   > -ERTIR,IRTAM   # advirtam\n"
                O = O "    E N T I R   > -ENTIR,INTAM   # mintam\n"
                O = O "    E T I R     > -ETIR,ITAM     # repitam\n"
                O = O "    E S T I R   > -ESTIR,ISTAM   # revistam (revestir)\n"
                O = O "    E R V I R   > -ERVIR,IRVAM   # sirvam\n"
                O = O "    E R Z I R   > -ERZIR,IRZAM   # sirzam\n"
                nr = 1
            }

            else for (j=1; j<=7; ++j) {

                # veja o comentrio logo acima sobre o paradigma ferir.
                if ((verbo == "ferir") && (i == "PI") && (j == 1)) {
                    O = O "    E R I R     > -ERIR,IRO      # firo\n"
                    O = O "    E L I R     > -ELIR,ILO      # compilo (compelir)\n"
                    O = O "    E R T I R   > -ERTIR,IRTO    # advirto\n"
                    O = O "    E N T I R   > -ENTIR,INTO    # minto\n"
                    O = O "    E T I R     > -ETIR,ITO      # repito\n"
                    O = O "    E S T I R   > -ESTIR,ISTO    # revisto (revestir)\n"
                    O = O "    E R V I R   > -ERVIR,IRVO    # sirvo\n"
                    O = O "    E R Z I R   > -ERZIR,IRZO    # sirzo\n"
                    nr = 1
                }

                # paradigmas fceis para o ispell
                else if (cs[j] != "") {

                    #
                    # construa a regra com duas letras (casos regulares
                    # e paradigmas no caracterizveis por trs letras,
                    # incluindo o infinitivo "ir", que corresponde ao
                    # teste (r == "")), ou com trs letras (demais casos).
                    #
                    if (reg || (r == "") || (RP[verbo] == 1))
                        R = regra(verbo,cs[j],vt "R")
                    else
                        R = regra(verbo,cs[j],substr(r,length(r)) vt "R")

                    # explicite as formas enclticas
                    if (j == 7)
                        k = cs[j] "-la"
                    else
                        k = cs[j]

                    # casos regulares j esto cobertos
                    if (TE[i] == 0) {
                        #O = O "#" substr(R,2) "# " k " (regular)\n"
                    }

                    # acrescente a regra se ela no estiver coberta
                    else if ((cs[j] != verbo) && (RA[R] != 1)) {
                        RA[R] = 1
                        O = O R "# " k "\n"
                        nr = 1
                    }
                    else {
                        #O = O "#" substr(R,2) "# " k " (coberto)\n"
                    }
                }
            }

            if ((nr == 1) && (reg) && (verbo == p))
                RULES[FV[i]] = RULES[FV[i]] O
        }

        if ((nr == 1) && (reg == 0) && (verbo == p))
            RULES[FV[verbo]] = RULES[FV[verbo]] O
    }

    # anlise do paradigma
    else if (FORMATO == "ap") {

        k = ""

        # verifica se o radical sofre alterao
        for (i in TM) {
            if (tmr[i] ~ /[0-9]/)
                k = " T"
        }

        # obtm e informa os tempos especficos
        k = k " ("
        for (i=1; i<=m; ++i) {
            if (tme[i] != "") {
                j = substr(tme[i],1,2)
                TE[j] = 1
                k = k " " j
            }
        }
        k = k ") ("

        informa os tempos regulares
        for (i in TM) {
            if (TE[i] != 1) {
                k = k " " i
            }
        }
        k = k ") "

        print p k
    }
}

#
# Inicializa alguns conjuntos
#
function inicializa_tm() {

    TM["FN"] = "Formas Nominais"
    TM["IP"] = "Infinitivo Pessoal"
    TM["PI"] = "Presente do Indicativo"
    TM["II"] = "Imperfeito do Indicativo"
    TM["EI"] = "Perfeito do Indicativo"
    TM["MI"] = "Mais-que-perfeito do Indicativo"
    TM["TI"] = "Futuro do Pretrito do Indicativo"
    TM["FI"] = "Futuro do Presente do Indicativo"
    TM["PS"] = "Presente do Subjuntivo"
    TM["IS"] = "Imperfeito do Subjuntivo"
    TM["FS"] = "Futuro do Subjuntivo"
    TM["IA"] = "Imperativo Afirmativo"
    TM["IN"] = "Imperativo Negativo"
    VOG["a"] = ""
    VOG["e"] = ""
    VOG["i"] = ""
    VOG["o"] = ""
    VOG["u"] = ""
    VOG[""] = ""
    VOG[""] = ""
    VOG[""] = ""
    VOG[""] = ""
    VOG[""] = ""
    VOG[""] = ""
    VOG[""] = ""
    VOG[""] = ""
    P["1"] = "eu"
    P["2"] = "tu"
    P["3"] = "ele"
    P["4"] = "ns"
    P["5"] = "vs"
    P["6"] = "eles"
}

#
# Cria as regras de afixos para as formas alternativas do particpio.
#
function crie_z(n,i,R,j,k,m,a,rc) {

    # gere as regras da flag z flexionando cada forma
    n = 0
    for (i in PA) {
        a = regra(i,PA[i],i)
        sub("^.*>",i " >",a)
        R[++n] = a " # " PA[i]
    }

    # elimines letras  esquerda na flag z
    RULES["z"] = ""
    delete RA
    for (j=1; j<=n; ++j) {
    
        split(R[j],MRF," +")
        k = 1
        for (a=1; (k<n+2) && (a<=length(MRF[1])); ++a) {
            for (k=1; k<=n; ++k) {
                split(R[k],TRF," +")
                m = length(TRF[1]) - length(MRF[1])
    
                if ((MRF[3] != TRF[3]) &&
                    (m+a >= 1) &&
                    (substr(MRF[1],a) == substr(TRF[1],m+a))) {
    
                    k = n+2
                }
            }
        }
    
        if (--a == 1) {
            print " impossvel gerar as regras da flag z."
            print MRF[1] "  sufixo de " TRF[1]
            exit 1
        }
        else {
            k = substr(MRF[1],a-1)
            gsub(""," ",k)
            a = "   " toupper(k) MRF[2] " " MRF[3]
            rc = a " "
            for (k=length(rc); k<33; ++k)
                rc = rc " "
            if (RA[rc] != 1) {
                RA[rc] = 1
                RULES["z"] = RULES["z"] rc MRF[4] " " MRF[5] "\n"
    
                # gere as flexes se necessrio
                if (a ~ /O$/) {
                    sub("O$","A",a)
                    RULES["z"] = RULES["z"] a "\n"
                    sub("A$","OS",a)
                    RULES["z"] = RULES["z"] a "\n"
                    sub("OS$","AS",a)
                    RULES["z"] = RULES["z"] a "\n"
                }
                else if (a ~ /E$/) {
                    a = a "S"
                    RULES["z"] = RULES["z"] a "\n"
                }
            }
        }
    }
}

#
# fatora e lista as regras de sufixos verbais.
#
function liste_regras(n,i,f,R,j,k,m,a,rc) {

    # os verbos usam as flags de "a" (97) a "z" (122)
    for (i=97; i<=122; ++i) {
        f = sprintf("%c",i);
        if (RULES[f] == "")
            continue
    
        # cabealho do bloco
        printf("\n#\n# FLAG " f "\n");
        if (i <= 82) {
            printf("# paradigma regular:")
            for (j in FV) {
                if (FV[j] == f)
                    printf(" " TM[j])
            }
        }
        else if (i == 122) {
            printf("# (particpios alternativos)")
        }
        else {
            printf("# verbos:")
            for (j in FV) {
                if (FV[j] == f)
                    printf(" " j)
            }
        }
        print "\n#\nflag " f ":\n"
    
        #
        # fatora e escreve as regras
        #
        n = split(RULES[f],R,"\n");
        for (j=1; j<=n; ++j) {
    
            # regra j foi fatorada junto com alguma antecedente
            if (R[j] == "")
                continue
    
            # tente fatorar com cada uma das outras
            split(R[j],MRF," +")
            for (k=j+1; k<=n; ++k) {
                split(R[k],TRF," +")

                # fatora regras de dois antecedentes j e k
                if ((MRF[4] == ">") &&
                    (TRF[4] == ">") &&
                    (MRF[6] == "#") &&
                    (TRF[6] == "#") &&
                    (MRF[5] == TRF[5])) {
                    if (index(MRF[2],TRF[2]) == 0)
                        MRF[2] = MRF[2] TRF[2]
                    MRF[7] = MRF[7] ", " TRF[7]
                    R[k] = ""
                }

                # fatora as regras de trs antecedentes j e k
                else if ((MRF[5] == ">") &&
                         (TRF[5] == ">") &&
                         (MRF[7] == "#") &&
                         (TRF[7] == "#") &&
                         (MRF[3] == TRF[3]) &&
                         (MRF[6] == TRF[6])) {

                    if (index(MRF[2],TRF[2]) == 0)
                        MRF[2] = MRF[2] TRF[2]
                    MRF[8] = MRF[8] ", " TRF[8]
                    R[k] = ""
                }
            }
    
            # regra fatorada
            if (length(MRF[2]) > 1) {
                MRF[2] = "[" MRF[2] "]"
                if (MRF[6] == "#") {
                    rc = "    " MRF[2] " " MRF[3] " " MRF[4] " " MRF[5] " "
                    for (k=length(rc); k<33; ++k)
                        rc = rc " "
                    print rc "# " MRF[7]
                }
                else {
                    rc = "    " MRF[2] " " MRF[3] " " MRF[4] " " MRF[5] " " MRF[6] " "
                    for (k=length(rc); k<33; ++k)
                        rc = rc " "
                    print rc "# " MRF[8]
                }
            }
    
            # regra no fatorada
            else
                print R[j]
        }
    }

    # divisor
    print "\n#\n# FINAL DAS REGRAS VERBAIS\n#\n";
}

#
# Consiste a distribuio das flags pelos paradigmas verbais.
#
function consiste_fv(i,j,ti,tj)
{
    for (i in FV) {

        if (index("abcdefghijklmnopqrstuvwxy",FV[i]) == 0) {
            print "Falha interna: flag verbal " FV[i] " invlida" > "/dev/stderr"
            exit 1
        }
        if (length(i) <= 2)
            continue
        ti = substr(i,length(i)-2)
        for (j in FV) {
            if ((i == j) || (length(j) <= 2))
                continue
            tj = substr(j,length(j)-2)

            if ((FV[i] == FV[j]) && (ti == tj)) {
                print "Falha interna: " i " e " j " colidem em FV" > "/dev/stderr"
                exit 1
            }
        }
    }
}

#
# O programa comea aqui
#
BEGIN {

    #
    # comando "T" subentende formato "c".
    # o formato default  "n".
    #
    if (FORMATO == "")
        if (CMD == "T")
            FORMATO = "c"
        else
            FORMATO = "n"

    # mensagem de abertura
    if ((FORMATO !~ /^(c|aa|b)/) && (CMD == "")) {
        print "Conjugue -- conjugador de verbos para a lngua portuguesa"
        print "verso 1.1 (outubro de 99) por Ricardo Ueda Karpischek"
        print "envie correes, crticas ou sugestes para ueda@ime.usp.br."
        print ""
        print "Use por sua prpria conta e risco."
        print ""
        print "Tanto o programa quanto o banco de verbos que o acompanha"
        print "so distribudos sob os termos da licena GNU GPL. Isso"
        print "significa que podem ser livremente copiados e que trabalhos"
        print "derivados devem tambm ser disponibilizados atravs dessa"
        print "mesma licena."
        print ""
        print "\"?\" exibe um pequeno guia de utilizao."
        print "\"n\" exibe algumas notas importantes."
        print ""
        #print "A atual verso no  capaz de conjugar os verbos conter"
        #print "haver, seguir, conseguir, perseguir e engulir, progredir,"
        #print "agredir, transgredir, prevenir e denegrir. Ela tambm no"
        #print "trata as formas abundantes, alm de outros provveis problemas."
        #print ""
    }

    # inicializa as flags e estruturas
    fim = 0
    inicializa_tm()
    consiste_fv()
    pr["a"] = pr["e"] = pr["i"] = pr["o"] = pr[""] = ""
    novos = (NOVOS == "") ? ENVIRON["HOME"] "/.conjugue-novos" : NOVOS

    # carga do banco
    if ((FORMATO !~ /^(c|aa|b)/) && (CMD == "")) {
        print "aguarde o trmino da leitura do banco..."
    }
    carrega_banco((BANCO=="") ? "/usr/local/lib/verbos" : BANCO)

    # prompt
    if (FORMATO ~ /^(c|aa|b)/)
        PR = ""
    else
        PR = ":"

    # lista os paradigmas
    if (FORMATO == "b") {
        FVN[pr["a"]] = 1
        FVN[pr["e"]] = 2
        FVN[pr["i"]] = 3
        FVN[pr["o"]] = 4
        FVN[pr[""]] = 5
        if (CMD != "T") {
            print "1 " pr["a"]
            print "2 " pr["e"]
            print "3 " pr["i"]
            print "4 " pr["o"]
            print "5 " pr[""]
        }
        j = 6;
        for (i in FV) {
            if (i ~ /r$/) {
                FVN[i] = j
                if (CMD != "T") {
                    print j " " i
                }
                ++j
            }
        }
        if (CMD != "T") {
            exit
        }
    }

    # loop principal
    while (fim == 0) {

        # prompt e leitura do comando
        if (FORMATO ~ /^(c|aa|b)/)
            ORS = ""
        else
            ORS = " "
        if ((FORMATO !~ /^(c|aa|b)/) && (CMD == ""))
            print PR
        ORS = "\n"
        if (CMD != "") {
            $1 = CMD
            fim = 1
        }

        # thanks Ralf Baechle
        else {
            if (getline <= 0)
                exit
        }

        # tempos e modos
        if ($1 == "a") {
            print abrevia
        }

        # comando de abandono
        if ($1 == "f") {
            fim = 1;
        }

        # conjuga o verbo dado
        if (length($1) > 1) {

            # conjuga o verbo
            if ($1 in V)
                conjugue_todos($1)

            else if (substr($1,length($1),1) != "r")
                print "# No sou capaz de conjugar " $1

            else {
                print "# " $1 " no consta do banco de verbos"
                if (novos != "")
                    print $1 >>novos
                conjugue_todos($1)
            }
        }

        # notas sobre o programa
        if ($1 == "n") {
            print notas
        }

        # lista dos verbos
        if ($1 == "V") {
            for (i in V) print i
        }

        # conjuga todos os verbos conhecidos
        if ($1 == "T") {

            #
            # O formato aa cria os blocos de regras para cada um dos
            # paradigmas verbais. As flags so pr-definidas pelo vetor FV.
            # Em seguida aos blocos de regras, so
            # enviados os verbos, cada um rotulado com a flag correspondente
            # ao seu paradigma.  necessrio dividir a sada,
            # e prefixar os blocos de regras com um cabealho adequado
            # no formato do ispell(4).
            #
            if ((FORMATO == "aa") || (FORMATO == "ci")) {

                FORMATO_MESMO = FORMATO
                FORMATO = "aa"

                # Gera os blocos de regras.
                for (i in LP)
                    conjugue_todos(i)

                # remove os particpios alternativos que so
                # idnticos a formas da primeira pessoa.
                for (i in PA)
                    conjugue_todos(i)

                FORMATO = FORMATO_MESMO

                if (FORMATO == "aa") {

                    # crie as regras para os particpios alternativos
                    crie_z()

                    # fatore e liste as regras de sufixos verbais
                    liste_regras()
                }

                # lista os verbos
                else if (FORMATO == "ci") {
                    for (i in V)
                        conjugue_todos(i)
                }
            }

            else {
                for (i in V)
                    conjugue_todos(i)
            }
        }

        # percorre (e analisa) os paradigmas
        if ($1 == "P") {
            for (i in LP)
                conjugue_todos(i)
        }

        # lista dos comandos disponveis
        if ($1 == "?") {
            print "a: exibe as abreviaes"
            print "<verbo>: conjuga o verbo dado"
            print "f: abandona"
            print "n: exibe algumas notas importantes"
        }
    }
}
