/// <summary>
 /// Calcula el paradigma per a paraules amb singular i plural.
 /// </summary>
 private ParadigmaCat Id2(string arrel, MorfoGram morfoGram, Dictionary<string, string> dades, 
     Dictionary<string, string> exc)
 {
     // PERFER: mirar "dijous"
     bool admetArticle = (exc == null || !exc.ContainsKey("NOART"));
     string arrel0, arrel1;
     arrel0 = ParadigmaCat.Arrel0(arrel, out arrel1);
     if (arrel1 != arrel0)
     {
         // PER_FER: unificar amb Precalc()
         // Tenim una paraula amb trossos
         PC_multi par = new PC_multi();
         string[] trossos = arrel.Split(' ');
         for (int i = 0; i < trossos.Length; i++)
         {
             if (trossos.Length == 0)
                 continue;
             // PER_FER: la paraula pot tenir articles i preposicions
             Paraula ptros = new Paraula(trossos[i]);
             if (i == 0)
                 par.Add(new PC_formaFixada("Entrada amb espais, primer tros", morfoGram, ptros,
                     ptros.PotApostrofar(), ptros.PotApostrofar(morfoGram.Gen == MorfoGram.eGen.F)));
             else
                 par.Add(new PC_formaFixada("Entrada amb espais, tros interior", morfoGram, ptros, false, false));
         }
         return par;
     }
     Paraula paraula = new Paraula(arrel0);
     Debug.Assert(!paraula.Forma.Contains(" "), "No hi ha d'haver espais a l'arrel");
     if (exc != null)
     {
         // suposam que el singular i el plural tenen el mateix valor de VocalInicial
         string plural = Dades(exc, "PLU");
         if (plural != null)
         {
             if (plural.Contains(" "))
             {
                 // Hi ha més d'un plural
                 string[] trossos = plural.Split(' ');
                 PC_multi par = new PC_multi();
                 foreach (string tros in trossos)
                     par.Add(new PC_plural_precalc(morfoGram, paraula, tros, paraula.VocalInicial,
                         paraula.VocalInicial && admetArticle));
                 return par;
             }
             return new PC_plural_precalc(morfoGram, paraula,
                 plural, paraula.VocalInicial, paraula.VocalInicial && admetArticle);
         }
     }
     string admet = String.Format("D{0}, L{1}", paraula.VocalInicial ? "+" : "-",
         paraula.VocalInicial && admetArticle ? "+" : "-");
     if (morfoGram.Cat == MorfoGram.eCat.ADJ)
     {
         string id = null;
         if (paraula.VocalFinal && paraula.Aguda)
             id = "A2T, -ns, " + admet;
         else if (paraula.SxcFinal && paraula.Aguda)
             id = "A2T, -os, " + admet;
         else
             id = "A2T, -s, " + admet;
         return paradigmes[id];
     }
     else // (no és un adjectiu, ho tractam com un nom)
     {
         string id = null;
         if (morfoGram.Gen == MorfoGram.eGen.F)
         {
             // exemples: mà, casa
             if (paraula.VocalFinal && paraula.Aguda)
                 id = "NF, -ns, " + admet;
             else if (paraula.SxcFinal && paraula.Aguda)
                 id = "NF, -s, " + admet;
             // PERFER: comprovar que el plural de "falç" és "falçs"
             else
                 id = "NF, -s, " + admet;
         }
         else // (la paraula és masculina o no té gènere)
         {
             // exemples: ca, moix, peu
             if (paraula.VocalFinal && paraula.Aguda)
                 id = "NM, -ns, " + admet;
             else if (paraula.SxcFinal && paraula.Aguda)
                 id = "NM, -os, " + admet;
             else
                 id = "NM, -s, " + admet;
         }
         return paradigmes[id];
     }
     //throw new Exception("No sé què fer amb " + arrel);
 }
 /// <summary>
 /// Calcula el paradigma per a adjectius i masculins/femenins.
 /// El resultat pot tenir dues terminacions (abacial), tres (feliç) o quatre (blanc).
 /// </summary>
 private ParadigmaCat Id4(string arrel, MorfoGram morfoGram, Dictionary<string, string> dades, Dictionary<string, string> exc)
 {
     bool admetArticle = (exc == null || !exc.ContainsKey("NOART"));
     string fem;
     string masc = ParadigmaCat.Arrel0(arrel, out fem);
     if (exc != null && exc.ContainsKey("PLU") && exc["PLU"].Contains(" "))
     {
         string[] trossos = exc["PLU"].Split(' ');
         if (trossos.Length != 2) throw new Exception("S'esperaven dos trossos: " + exc["PLU"]);
         Dictionary<string, string> exc2 = new Dictionary<string, string>(exc);
         exc2["PLU"] = trossos[0];
         ParadigmaCat parMasc = Id2(masc, morfoGram | mgMasc, dades, exc2);
         exc2["PLU"] = trossos[1];
         ParadigmaCat parFem = Id2(fem, morfoGram | mgFem, dades, exc2);
         return new PC_multi(parMasc, parFem);
     }
     if (fem == masc)
     {
         // Si hi ha una forma, vol dir que és igual per al masculí i el femení, almenys en singular
         ParadigmaCat par = Id2(arrel, morfoGram, dades, exc);
         if (arrel.EndsWith("ç"))
         {
             // Si termina en 'ç', hi ha dues formes per al plural
             PC_multi pars = new PC_multi(par);
             Paraula fpl = new Paraula(arrel.Substring(0, arrel.Length - 1) + "ces");
             pars.Add(new PC_formaFixada("plural en -ces", new MorfoGram(morfoGram.Cat, MorfoGram.eGen.F, MorfoGram.eNbre.PL),
                 fpl, fpl.VocalInicial, false));
             return pars;
         }
         else
             return par;
     }
     else // (fem != null)
     {
         Paraula pMasc = new Paraula(masc);
         foreach (char id in "FJBHKL")
         {
             if (id == 'H' && (!pMasc.Aguda || !pMasc.VocalFinal))
                 continue;
             Regla regla = regles.Llista[id.ToString()];
             List<Mot> femenins = regla.Genera(masc, mgFemSg, regles, Marques.totes, true);
             if (femenins.Count == 0)
                 continue;
             if (femenins.Exists(delegate(Mot mot) { return mot.Forma == fem; }))
             {
                 string idPar = String.Format("{0}, MFSP, {1}, D{2}, L{3}",
                     morfoGram.Cat == MorfoGram.eCat.ADJ ? "A" : "N",
                     id.ToString(),
                     pMasc.VocalInicial ? "+" : "-",
                     pMasc.VocalInicial && admetArticle ? "+" : "-");
                 ParadigmaCat par = paradigmes[idPar];
                 Paraula pFem = new Paraula(fem);
                 if (pFem.PotApostrofar(true)&& admetArticle)
                 {
                     MorfoGram mgFS = new MorfoGram(mgFemSg);
                     mgFS.Cat = morfoGram.Cat;
                     par = new PC_multi(par, new PC_formaFixada("femení singular, D+, L+", mgFS, pFem, true, true));
                     // PERFER: No hauríem d'admetre "d'" ja que està inclòs dins l'altre paradigma
                 }
                 return par;
             }
         }
         return new PC_multi(Id2(masc, mgMascSg, dades, exc), Id2(fem, mgFemSg, dades, exc));
     }
 }
 private void test_03(GrupTest arrel)
 {
     GrupTest grup = arrel.NouGrup("Eines per al català");
     grup.NouTest("Comparacions simples", delegate(RTest resultat)
     {
         string[] mots = "a al alça alt cant canta cantà cantam cella cel·la celles cel·les".Split(" ".ToCharArray());
         for (int i = 0; i < mots.Length; i++)
         {
             string m1 = mots[i];
             resultat.Assert(Cat.Cmp(m1, m1) == 0, "{0} == {1}", m1, m1);
             for (int j = i + 1; j < mots.Length; j++)
             {
                 string m2 = mots[j];
                 int cmp1 = Cat.Cmp(m1, m2);
                 int cmp2 = Cat.Cmp(m2, m1);
                 resultat.Assert((cmp1 < 0) && (cmp1 + cmp2 == 0), "{0} < {1} ({2}, {3})", m1, m2, cmp1, cmp2);
             }
         }
     });
     grup.NouTest("Més comparacions I", delegate(RTest resultat)
     {
         string[] mots = "a1 a2 a3 a10 a30".Split(" ".ToCharArray());
         for (int i = 0; i < mots.Length; i++)
         {
             string m1 = mots[i];
             resultat.Assert(Cat.Cmp(m1, m1) == 0, "{0} == {1}", m1, m1);
             for (int j = i + 1; j < mots.Length; j++)
             {
                 string m2 = mots[j];
                 int cmp1 = Cat.Cmp(m1, m2);
                 int cmp2 = Cat.Cmp(m2, m1);
                 resultat.Assert((cmp1 < 0) && (cmp1 + cmp2 == 0), "{0} < {1} ({2}, {3})", m1, m2, cmp1, cmp2);
             }
         }
     });
     grup.NouTest("Més comparacions II", delegate(RTest resultat)
     {
         string str1 = "abstinguin/C";
         string str2 = "abstinguin/Z";
         resultat.Assert(Cat.Cmp(str1, str2) < 0, "{0} < {1}", str1, str2);
     });
     grup.NouTest("Conversió a minúscules", delegate(RTest resultat)
     {
         string maj = "AÀBCÇDEÈÉFGHIÍÏJKLMNOÒÓPQRSTUÚÜVXYZaàbcçdeèéfghiíïjklmnoòópqrstuúüvxyz";
         string min = "aàbcçdeèéfghiíïjklmnoòópqrstuúüvxyzaàbcçdeèéfghiíïjklmnoòópqrstuúüvxyz";
         resultat.Esperat(min, Cat.Min(maj));
     });
     grup.NouTest("Divisió sil·làbica", delegate(RTest resultat)
     {
         string[] paraules = { "es/què/ieu", "hie/na", "ca/nya", "psi/cò/legs", "Àus/tri/a", "cui/nar", "xxx", "ha", "any", "i/ó", "i/ons", "i/o/nit/za/ci/ó", "" };
         foreach (string s in paraules)
         {
             string forma = s.Replace("/", "");
             Paraula sillabes = new Paraula(forma);
             string sil = String.Join("/", sillabes.Sillabes);
             resultat.Nota("\"{0}\" => \"{1}\", {2}", sillabes.Forma, sillabes.Prototip, sil);
             resultat.Esperat(s, sil);
         }
     });
     grup.NouTest("Vocal inicial", delegate(RTest resultat)
     {
         string[] si = { "ara", "àrab", "humitat", "indi", "Henedina", "ARA", "ÀRAB", "HUMITAT", "INDI" };
         foreach (string forma in si)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(Paraula.TeVocalInicial(forma) && paraula.VocalInicial, String.Format("\"{0}\" comença per vocal", forma));
         }
         string[] no = { "casa", "hiena", "iode", "CASA", "HIENA", "IODE" };
         foreach (string forma in no)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(!Paraula.TeVocalInicial(forma) && !paraula.VocalInicial, String.Format("\"{0}\" no comença per vocal", forma));
         }
     });
     grup.NouTest("Vocal final", delegate(RTest resultat)
     {
         string[] si = { "ara", "indi", "pagui", "ARA", "INDI", "PAGUI" };
         foreach (string forma in si)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(paraula.VocalFinal, String.Format("\"{0}\" acaba per vocal", forma));
         }
         string[] no = { "cau", "moix", "seguiu", "adscriu", "virrei", "CAU", "MOIX", "SEGUIU" };
         foreach (string forma in no)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(!paraula.VocalFinal, String.Format("\"{0}\" no acaba per vocal", forma));
         }
     });
     grup.NouTest("Aguda", delegate(RTest resultat)
     {
         string[] si = { "pa", "ratolí", "forat", "PA", "RATOLÍ", "FORAT" };
         foreach (string forma in si)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(paraula.Aguda, String.Format("\"{0}\" és aguda", forma));
         }
         string[] no = { "panera", "rata", "pàmpol", "mèrlera", "PANERA", "RATA", "PÀMPOL", "MÈRLERA" };
         foreach (string forma in no)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(!paraula.Aguda, String.Format("\"{0}\" no és aguda", forma));
         }
     });
     grup.NouTest("Síl·laba tònica", delegate(RTest resultat)
     {
         string[] paraules = { "aguiÀveu", "PA", "PÀMpol", "CAsa", "coLOR", "MÀquina" };
         foreach (string forma in paraules)
         {
             Paraula paraula = new Paraula(forma.ToLower());
             int tonica = paraula.Tonica;
             StringBuilder sb = new StringBuilder();
             for (int i = 0; i < paraula.Sillabes.Length; i++)
             {
                 string s = paraula.Sillabes[i];
                 if (i == tonica)
                     sb.Append(s.ToUpper());
                 else
                     sb.Append(s);
             }
             resultat.Nota("{0} => {1}", forma, sb);
             resultat.Esperat(forma, sb.ToString());
         }
     });
     grup.NouTest("Abans de la vocal tònica", delegate(RTest resultat)
     {
         string[] paraules = { "aguiÀVEU", "pA", "pÀMPOL", "cASA", "colOR", "mÀQUINA" };
         foreach (string forma in paraules)
         {
             string minuscula = forma.ToLower();
             Paraula paraula = new Paraula(minuscula);
             string preTonica = paraula.PreTonica;
             int mida = preTonica.Length;
             string nova = minuscula.Substring(0, mida) + minuscula.Substring(mida).ToUpper();
             resultat.Nota("{0} => {1}", forma, nova);
             resultat.Esperat(forma, nova);
         }
     });
     grup.NouTest("Es poden apostrofar", delegate(RTest resultat)
     {
         string[] noFemeninesSi = { "ara", "àrab", "indi", "unir" };
         foreach (string forma in noFemeninesSi)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(paraula.PotApostrofar(), String.Format("\"{0}\" es pot apostrofar", forma));
         }
         string[] noFemeninesNo = { "casa", "iode", "hiatus" };
         foreach (string forma in noFemeninesNo)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(!paraula.PotApostrofar(), String.Format("\"{0}\" no es pot apostrofar", forma));
         }
         string[] femeninesSi = { "índia", "alba", "hora", "ungla" };
         foreach (string forma in femeninesSi)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(paraula.PotApostrofar(true), String.Format("\"{0}\" es pot apostrofar", forma));
         }
         string[] femeninesNo = { "humitat", "casa", "il·lícita" };
         foreach (string forma in femeninesNo)
         {
             Paraula paraula = new Paraula(forma);
             resultat.Assert(!paraula.PotApostrofar(true), String.Format("\"{0}\" no es pot apostrofar", forma));
         }
     });
     grup.NouTest("EsMin()", delegate(RTest resultat)
     {
         string[] si = { "un", "dos", "tres", "quatre-cinc", "" };
         string[] no = { "Un", "DOS", "treS", "Quatre-cinc" };
         foreach (string str in si)
             resultat.Assert(Cat.EsMin(str), "És minúscula");
         foreach (string str in no)
             resultat.Assert(!Cat.EsMin(str), "No és minúscula");
     });
 }
 /// <summary>
 /// Llegeix un fitxer com el generat pel web de tractament de propostes.
 /// Conté entrades en diversos formats: DIEC, topònims, antropònims, etc.
 /// </summary>
 private void LlegeixEntradesWeb(int mod, List<string> log, DateTime horaInici, Regles regles, 
     List<Identificador> identificadors, List<Entrada> entrades)
 {
     var idDiec = new IdentificadorDIEC("web", regles, null);
     var idToponims = new IdentificadorToponims("web", regles);
     var idAntroponims = new IdentificadorAntroponims("web", regles, null);
     var idDiversos = new IdentificadorDiversos("web", regles);
     AfegeixLiniaLog("Llegeix les propostes del web", horaInici, log);
     var sr = new StreamReader(DirEntrades("web.txt"), Encoding.Default);
     var linies = new List<string>();
     while (!sr.EndOfStream)
         linies.Add(sr.ReadLine());
     var temp = DirEntrades("_part_web.txt");
     // llista de topònims
     linies = TriaLinies(linies, temp, delegate(string lin)
     {
         Match m;
         if ((m = reTopo.Match(lin)).Success)
             return m.Groups["nom"].Value;
         else
             return "";
     });
     idToponims.LlegeixEntrades(temp, entrades, mod);
     // llista d'antropònims
     linies = TriaLinies(linies, temp, delegate(string lin)
     {
         Match m;
         if ((m = reAntro.Match(lin)).Success)
         {
             if (m.Groups["gen"].Value == "m")
                 return m.Groups["nom"].Value;
             else
             {
                 var nom = m.Groups["nom"].Value;
                 var p = new Paraula(nom);
                 return string.Format("{0}{1}", nom, p.PotApostrofar(true) ? " f" : "");
             }
         }
         else
             return "";
     });
     idAntroponims.LlegeixEntrades(temp, entrades, mod);
     // llista de diversos
     linies = TriaLinies(linies, temp, delegate(string lin)
     {
         Match m;
         if ((m = reDivers.Match(lin)).Success)
             return m.Groups["nom"].Value;
         else
             return "";
     });
     idDiversos.LlegeixEntrades(temp, entrades, mod);
     // la resta, té el format DIEC
     TriaLinies(linies, temp, lin => lin);
     idDiec.LlegeixEntrades(temp, entrades, mod);
     if (File.Exists(temp))
         File.Delete(temp);
     identificadors.Add(idToponims);
     identificadors.Add(idAntroponims);
     identificadors.Add(idDiversos);
     identificadors.Add(idDiec);
 }
 /// <summary>
 /// Afegeix la forma femenina singular del participi amb el flag que permet posar-li l'article "l'",
 /// sempre que unaForma no comenci en "i" o "u" àtones.
 /// El paràmetre unaForma no ha de ser necessàriament el participi, sinó una paraula que es comporti 
 /// igual que el el participi femení singular pel que fa a l'apostrofació.
 /// Si s'ha d'afegir la forma (femení singular del participi) a la llista, s'agafa dels ítems que ja hi
 /// a la llista.
 /// </summary>
 protected void AfegeixParFemSg(List<ItemDic> items, string unaForma, Marques filtre)
 {
     Paraula pForma = new Paraula(unaForma);
     if (pForma.PotApostrofar(true))
     {
         List<Mot> parFem = GeneraMots(items, mgPartFemSg, filtre, true);
         foreach (Mot mot in parFem)
             AfegeixItem(items, mot.Forma, mgVerb, mot.Info, "V", "Y");
     }
 }
 public override void Genera(Dictionary<string, string> dades, Dictionary<string, string> excepcions, Marques filtre, List<ItemDic> items)
 {
     string arrel = dades["arrel"];
     if (masc)
     {
         if (Paraula.TeVocalInicial(arrel))
             AfegeixItem(items, arrel, mgMascSg, mgMascSg, "V", "Y");
         else
             AfegeixItem(items, arrel, mgMascSg, mgMascSg);
     }
     else // (femení)
     {
         Paraula pArrel = new Paraula(arrel);
         if (pArrel.VocalInicial)
         {
             if (pArrel.PotApostrofar(true))
                 AfegeixItem(items, arrel, mgFemSg, mgFemSg, "V", "Y");
             else
                 AfegeixItem(items, arrel, mgFemSg, mgFemSg, "Y");
         }
         else
             AfegeixItem(items, arrel, mgFemSg, mgFemSg);
     }
 }
 public override void Genera(Dictionary<string, string> dades, Dictionary<string, string> excepcions, Marques filtre, List<ItemDic> items)
 {
     string arrel0, arrel1;
     arrel0 = Arrel0(dades["arrel"], out arrel1);
     int numArrel = (mgComuna.Gen == MorfoGram.eGen.F) ? 1 : 0;
     ItemDic item = new ItemDic((numArrel == 1) ? arrel1 : arrel0, regla);
     item.MesMorfoGram(mgComuna, mgArrel);
     if (admetD)
         item.MesFlags("Y");
     if (admetL)
     {
         if (numArrel == 0)
             item.MesFlags("V");
         else
         {
             Paraula paraula = new Paraula(arrel1);
             if (paraula.PotApostrofar(true))
                 item.MesFlags("V");
         }
     }
     items.Add(item);
 }