001package squidpony; 002 003import regexodus.*; 004import squidpony.squidmath.CrossHash; 005import squidpony.squidmath.GapShuffler; 006import squidpony.squidmath.RNG; 007import squidpony.squidmath.StatefulRNG; 008 009import java.io.Serializable; 010import java.util.ArrayList; 011import java.util.Collection; 012import java.util.LinkedHashMap; 013import java.util.Map; 014 015import static squidpony.Maker.*; 016 017/** 018 * A text processing class that can swap out occurrences of words and replace them with their synonyms. 019 * Created by Tommy Ettinger on 5/23/2016. 020 */ 021public class Thesaurus implements Serializable{ 022 private static final long serialVersionUID = 3387639905758074640L; 023 protected static final Pattern wordMatch = Pattern.compile("([\\pL`]+)"); 024 public LinkedHashMap<String, GapShuffler<String>> mappings; 025 protected StatefulRNG rng; 026 027 /** 028 * Constructs a new Thesaurus with an unseeded RNG used to shuffle word order. 029 */ 030 public Thesaurus() 031 { 032 mappings = new LinkedHashMap<>(256); 033 rng = new StatefulRNG(); 034 } 035 036 /** 037 * Constructs a new Thesaurus, seeding its RNG (used to shuffle word order) with the next long from the given RNG. 038 * @param rng an RNG that will only be used to get one long (for seeding this class' RNG) 039 */ 040 public Thesaurus(RNG rng) 041 { 042 mappings = new LinkedHashMap<>(256); 043 this.rng = new StatefulRNG(rng.nextLong()); 044 } 045 046 /** 047 * Constructs a new Thesaurus, seeding its RNG (used to shuffle word order) with shuffleSeed. 048 * @param shuffleSeed a long for seeding this class' RNG 049 */ 050 public Thesaurus(long shuffleSeed) 051 { 052 mappings = new LinkedHashMap<>(256); 053 this.rng = new StatefulRNG(shuffleSeed); 054 } 055 056 057 /** 058 * Constructs a new Thesaurus, seeding its RNG (used to shuffle word order) with shuffleSeed. 059 * @param shuffleSeed a String for seeding this class' RNG 060 */ 061 public Thesaurus(String shuffleSeed) 062 { 063 mappings = new LinkedHashMap<>(256); 064 this.rng = new StatefulRNG(shuffleSeed); 065 } 066 067 /** 068 * Allows this Thesaurus to find the exact words in synonyms and, when requested, replace each occurrence with a 069 * different word from the same Collection. Each word in synonyms should have the same part of speech, so "demon" 070 * and "devils" should not be in the same list of synonyms (singular noun and plural noun), but "demon" and "devil" 071 * could be (each is a singular noun). The Strings in synonyms should all be lower-case, since case is picked up 072 * from the text as it is being replaced and not from the words themselves. Proper nouns should normally not be used 073 * as synonyms, since this could get very confusing if it changed occurrences of "Germany" to "France" at random and 074 * a character's name, like "Dorothy", to "Anne", "Emily", "Cynthia", etc. in the middle of a section about Dorothy. 075 * The word matching pattern this uses only matches all-letter words, not words that contain hyphens, apostrophes, 076 * or other punctuation. 077 * @param synonyms a Collection of lower-case Strings with similar meaning and the same part of speech 078 * @return this for chaining 079 */ 080 public Thesaurus addSynonyms(Collection<String> synonyms) 081 { 082 if(synonyms.isEmpty()) 083 return this; 084 rng.setState(CrossHash.hash64(synonyms)); 085 GapShuffler<String> shuffler = new GapShuffler<>(synonyms, rng); 086 for(String syn : synonyms) 087 { 088 mappings.put(syn, shuffler); 089 } 090 return this; 091 } 092 093 /** 094 * Allows this Thesaurus to replace a specific keyword, typically containing multiple backtick characters ('`') so 095 * it can't be confused with a "real word," with one of the words in synonyms (chosen in shuffled order). The 096 * backtick is the only punctuation character that this class' word matcher considers part of a word, both for this 097 * reason and because it is rarely used in English text. 098 * @param keyword a word (typically containing backticks, '`') that will be replaced by a word from synonyms 099 * @param synonyms a Collection of lower-case Strings with similar meaning and the same part of speech 100 * @return this for chaining 101 */ 102 public Thesaurus addCategory(String keyword, Collection<String> synonyms) 103 { 104 if(synonyms.isEmpty()) 105 return this; 106 rng.setState(CrossHash.hash64(synonyms)); 107 GapShuffler<String> shuffler = new GapShuffler<>(synonyms, rng); 108 mappings.put(keyword, shuffler); 109 return this; 110 } 111 112 /** 113 * Adds several pre-made categories to this Thesaurus' known categories, but won't cause it to try to replace normal 114 * words with synonyms (only categories, which contain backticks in the name). The keywords this currently knows, 115 * and the words it will replace those keywords with, are: 116 * <br> 117 * <ul> 118 * <li>"calm`adj`": harmonious, peaceful, pleasant, serene, placid, tranquil, calm</li> 119 * <li>"calm`noun`": harmony, peace, kindness, serenity, tranquility, calmn</li> 120 * <li>"org`noun`": fraternity, brotherhood, order, group, foundation</li> 121 * <li>"org`nouns`": fraternities, brotherhoods, orders, groups, foundations</li> 122 * <li>"empire`adj`": imperial, princely, kingly, regal, dominant, dynastic, royal, hegemonic, monarchic, ascendant</li> 123 * <li>"empire`noun`": empire, emirate, kingdom, sultanate, dominion, dynasty, imperium, hegemony, triumvirate, ascendancy</li> 124 * <li>"empire`nouns`": empires, emirates, kingdoms, sultanates, dominions, dynasties, imperia, hegemonies, triumvirates, ascendancies</li> 125 * <li>"duke`noun`": duke, earl, baron, fief, lord, shogun</li> 126 * <li>"duke`nouns`": dukes, earls, barons, fiefs, lords, shoguns</li> 127 * <li>"duchy`noun`": duchy, earldom, barony, fiefdom, lordship, shogunate</li> 128 * <li>"duchy`nouns`": duchies, earldoms, baronies, fiefdoms, lordships, shogunates</li> 129 * <li>"magical`adj`": arcane, enchanted, sorcerous, ensorcelled, magical, mystical</li> 130 * <li>"holy`adj`": auspicious, divine, holy, sacred, prophetic, blessed, godly</li> 131 * <li>"unholy`adj`": bewitched, occult, unholy, macabre, accursed, foul, vile</li> 132 * <li>"forest`adj`": natural, primal, verdant, lush, fertile, bountiful</li> 133 * <li>"forest`noun`": nature, forest, greenery, jungle, woodland, grove, copse</li> 134 * </ul> 135 * Capitalizing the first letter in the keyword where it appears in text you call process() on will capitalize the 136 * first letter of the produced fake word. Capitalizing the second letter will capitalize the whole produced fake 137 * word. This applies only per-instance of each keyword; it won't change the internally-stored list of words. 138 * @return this for chaining 139 */ 140 public Thesaurus addKnownCategories() 141 { 142 for(Map.Entry<String, ArrayList<String>> kv : categories.entrySet()) 143 { 144 addCategory(kv.getKey(), kv.getValue()); 145 } 146 return this; 147 } 148 149 /** 150 * Adds a large list of words pre-generated by FakeLanguageGen and hand-picked for fitness, and makes them 151 * accessible with a keyword based on the language and any tweaks made to it. The keywords this currently knows: 152 * <br> 153 * <ul> 154 * <li>"jp`gen`": Imitation Japanese</li> 155 * <li>"fr`gen`": Imitation French; contains some accented chars</li> 156 * <li>"gr`gen`": Imitation Greek (romanized)</li> 157 * <li>"ru`gen`": Imitation Russian (romanized)</li> 158 * <li>"sw`gen`": Imitation Swahili</li> 159 * <li>"so`gen`": Imitation Somali</li> 160 * <li>"en`gen`": Imitation English (not very good on its own)</li> 161 * <li>"ar`gen`": Imitation Arabic (better); doesn't have accents and should be more readable</li> 162 * <li>"ar`acc`gen`": Imitation Arabic (worse); has special accents and uses two Greek letters as well</li> 163 * <li>"hi`gen`": Imitation Hindi (romanized and with accents removed)</li> 164 * <li>"fn`gen`": Fantasy Names; styled after the possibly-Europe-like names common in fantasy books</li> 165 * <li>"fn`acc`gen`": Fancy Fantasy Names; the same as "fn`gen`", but with lots of accented chars</li> 166 * <li>"lc`gen`": Lovecraft; styled after the names of creatures from H.P. Lovecraft's Cthulhu Mythos</li> 167 * <li>"ru`so`gen`": Mix of imitation Russian (75%) and Somali (25%)</li> 168 * <li>"gr`hi`gen`": Mix of imitation Greek (50%) and Hindi (accents removed, 50%)</li> 169 * <li>"sw`fr`gen`": Mix of imitation Swahili (70%) and French (30%)</li> 170 * <li>"ar`jp`gen`": Mix of imitation Arabic (accents removed, 60%) and Japanese (40%)</li> 171 * <li>"sw`gr`gen`": Mix of imitation Swahili (60%) and Greek (40%)</li> 172 * <li>"gr`so`gen`": Mix of imitation Greek (60%) and Somali (40%)</li> 173 * <li>"en`hi`gen`": Mix of imitation English (60%) and Hindi (accents removed, 40%)</li> 174 * <li>"en`jp`gen`": Mix of imitation English (60%) and Japanese (40%)</li> 175 * <li>"so`hi`gen`": Mix of imitation Somali (60%) and Hindi (accents removed, 40%)</li> 176 * <li>"ru`gr`gen`": Mix of imitation Russian (60%) and Greek (40%)</li> 177 * <li>"lc`gr`gen`": Mix of Lovecraft-styled names (60%) and imitation Russian (40%)</li> 178 * <li>"fr`mod`gen`": Imitation French; modified to replace doubled consonants like "gg" with "gsh" or similar</li> 179 * <li>"jp`mod`gen`": Imitation Japanese; modified to sometimes double vowels from "a" to "aa" or similar</li> 180 * <li>"so`mod`gen`": Imitation Somali (not really); modified beyond recognition and contains accents</li> 181 * </ul> 182 * Capitalizing the first letter in the keyword where it appears in text you call process() on will capitalize the 183 * first letter of the produced fake word, which is often desirable for things like place names. Capitalizing the 184 * second letter will capitalize the whole produced fake word. This applies only per-instance of each keyword; it 185 * won't change the internally-stored list of words. 186 * @return this for chaining 187 */ 188 public Thesaurus addFakeWords() 189 { 190 for(Map.Entry<String, ArrayList<String>> kv : languages.entrySet()) 191 { 192 addCategory(kv.getKey(), kv.getValue()); 193 } 194 return this; 195 } 196 197 /** 198 * Given a String, StringBuilder, or other CharSequence that should contain words this knows synonyms for, this 199 * replaces each occurrence of such a known word with one of its synonyms, leaving unknown words untouched. Words 200 * that were learned together as synonyms with addSynonyms() will be replaced in such a way that an individual 201 * replacement word should not occur too close to a previous occurrence of the same word; that is, replacing the 202 * text "You fiend! You demon! You despoiler of creation; devil made flesh!", where "fiend", "demon", and "devil" 203 * are all synonyms, would never produce a string that contained "fiend" as the replacement for all three of those. 204 * @param text a CharSequence, such as a String, that contains words in the source language 205 * @return a String of the translated text. 206 */ 207 public String process(CharSequence text) 208 { 209 Replacer rep = wordMatch.replacer(new SynonymSubstitution()); 210 return rep.replace(text); 211 } 212 213 public String lookup(String word) 214 { 215 if(word.isEmpty()) 216 return word; 217 String word2 = word.toLowerCase(); 218 if(mappings.containsKey(word2)) 219 { 220 String nx = mappings.get(word2).getNext(); 221 if(nx.isEmpty()) 222 return nx; 223 if(word.length() > 1 && Character.isUpperCase(word.charAt(1))) 224 return nx.toUpperCase(); 225 if(Character.isUpperCase(word.charAt(0))) 226 { 227 return Character.toUpperCase(nx.charAt(0)) + nx.substring(1, nx.length()); 228 } 229 return nx; 230 } 231 return word; 232 } 233 234 private class SynonymSubstitution implements Substitution 235 { 236 @Override 237 public void appendSubstitution(MatchResult match, TextBuffer dest) { 238 dest.append(lookup(match.group(0))); 239 } 240 } 241 242 public static LinkedHashMap<String, ArrayList<String>> categories = makeLHM( 243 "calm`adj`", 244 makeList("harmonious", "peaceful", "pleasant", "serene", "placid", "tranquil", "calm"), 245 "calm`noun`", 246 makeList("harmony", "peace", "kindness", "serenity", "tranquility", "calm"), 247 "org`noun`", 248 makeList("fraternity", "brotherhood", "order", "group", "foundation", "association", "guild", "fellowship", "partnership"), 249 "org`nouns`", 250 makeList("fraternities", "brotherhoods", "orders", "groups", "foundations", "associations", "guilds", "fellowships", "partnerships"), 251 "empire`adj`", 252 makeList("imperial", "princely", "kingly", "regal", "dominant", "dynastic", "royal", "hegemonic", "monarchic", "ascendant"), 253 "empire`noun`", 254 makeList("empire", "emirate", "kingdom", "sultanate", "dominion", "dynasty", "imperium", "hegemony", "triumvirate", "ascendancy"), 255 "empire`nouns`", 256 makeList("empires", "emirates", "kingdoms", "sultanates", "dominions", "dynasties", "imperia", "hegemonies", "triumvirates", "ascendancies"), 257 "union`noun`", 258 makeList("union", "alliance", "coalition", "confederation", "federation", "congress", "confederacy", "league", "faction"), 259 "union`nouns`", 260 makeList("unions", "alliances", "coalitions", "confederations", "federations", "congresses", "confederacies", "leagues", "factions"), 261 "militia`noun`", 262 makeList("rebellion", "resistance", "militia", "liberators", "warriors", "fighters", "militants", "front", "irregulars"), 263 "militia`nouns`", 264 makeList("rebellions", "resistances", "militias", "liberators", "warriors", "fighters", "militants", "fronts", "irregulars"), 265 "gang`noun`", 266 makeList("gang", "syndicate", "mob", "crew", "posse", "mafia", "cartel"), 267 "gang`nouns`", 268 makeList("gangs", "syndicates", "mobs", "crews", "posses", "mafias", "cartels"), 269 "duke`noun`", 270 makeList("duke", "earl", "baron", "fief", "lord", "shogun"), 271 "duke`nouns`", 272 makeList("dukes", "earls", "barons", "fiefs", "lords", "shoguns"), 273 "duchy`noun`", 274 makeList("duchy", "earldom", "barony", "fiefdom", "lordship", "shogunate"), 275 "duchy`nouns`", 276 makeList("duchies", "earldoms", "baronies", "fiefdoms", "lordships", "shogunates"), 277 "magical`adj`", 278 makeList("arcane", "enchanted", "sorcerous", "ensorcelled", "magical", "mystical"), 279 "holy`adj`", 280 makeList("auspicious", "divine", "holy", "sacred", "prophetic", "blessed", "godly"), 281 "unholy`adj`", 282 makeList("bewitched", "occult", "unholy", "macabre", "accursed", "profane", "vile"), 283 "forest`adj`", 284 makeList("natural", "primal", "verdant", "lush", "fertile", "bountiful"), 285 "forest`noun`", 286 makeList("nature", "forest", "greenery", "jungle", "woodland", "grove", "copse"), 287 "fancy`adj`", 288 makeList("grand", "glorious", "magnificent", "magnanimous", "majestic", "great", "powerful"), 289 "evil`adj`", 290 makeList("heinous", "scurrilous", "terrible", "horrible", "debased", "wicked", "evil", "malevolent", "nefarious", "vile"), 291 "good`adj`", 292 makeList("righteous", "moral", "good", "pure", "compassionate", "flawless", "perfect"), 293 "sinister`adj`", 294 makeList("shadowy", "silent", "lethal", "deadly", "fatal", "venomous", "cutthroat", "murderous", "bloodstained"), 295 "sinister`noun`", 296 makeList("shadow", "silence", "assassin", "ninja", "venom", "poison", "snake", "murder", "blood", "razor"), 297 "blade`noun`", 298 makeList("blade", "knife", "sword", "axe", "stiletto", "katana", "scimitar", "hatchet", "spear", "glaive", "halberd", 299 "hammer", "maul", "flail", "mace", "sickle", "scythe", "whip", "lance", "nunchaku", "saber", "cutlass", "trident"), 300 "bow`noun`", 301 makeList("bow", "longbow", "shortbow", "crossbow", "sling", "atlatl", "bolas", "javelin", "net", "shuriken", "dagger"), 302 "weapon`noun`", 303 makeList("blade", "knife", "sword", "axe", "stiletto", "katana", "scimitar", "hatchet", "spear", "glaive", "halberd", 304 "hammer", "maul", "flail", "mace", "sickle", "scythe", "whip", "lance", "nunchaku", "saber", "cutlass", "trident", 305 "bow", "longbow", "shortbow", "crossbow", "sling", "atlatl", "bolas", "javelin", "net", "shuriken", "dagger"), 306 "musket`noun`", 307 makeList("arquebus", "blunderbuss", "musket", "matchlock", "flintlock", "wheellock", "cannon"), 308 "grenade`noun`", 309 makeList("rocket", "grenade", "missile", "bomb", "warhead", "explosive", "flamethrower"), 310 "rifle`noun`", 311 makeList("pistol", "rifle", "handgun", "firearm", "longarm", "shotgun"), 312 "blade`nouns`", 313 makeList("blades", "knives", "swords", "axes", "stilettos", "katana", "scimitars", "hatchets", "spears", "glaives", "halberds", 314 "hammers", "mauls", "flails", "maces", "sickles", "scythes", "whips", "lances", "nunchaku", "sabers", "cutlasses", "tridents"), 315 "bow`nouns`", 316 makeList("bows", "longbows", "shortbows", "crossbows", "slings", "atlatls", "bolases", "javelins", "nets", "shuriken", "daggers"), 317 "weapon`nouns`", 318 makeList("blades", "knives", "swords", "axes", "stilettos", "katana", "scimitars", "hatchets", "spears", "glaives", "halberds", 319 "hammers", "mauls", "flails", "maces", "sickles", "scythes", "whips", "lances", "nunchaku", "sabers", "cutlasses", "tridents", 320 "bows", "longbows", "shortbows", "crossbows", "slings", "atlatls", "bolases", "javelins", "nets", "shuriken", "daggers"), 321 "musket`nouns`", 322 makeList("arquebusses", "blunderbusses", "muskets", "matchlocks", "flintlocks", "wheellocks", "cannons"), 323 "grenade`nouns`", 324 makeList("rockets", "grenades", "missiles", "bombs", "warheads", "explosives", "flamethrowers"), 325 "rifle`nouns`", 326 makeList("pistols", "rifles", "handguns", "firearms", "longarms", "shotguns"), 327 "tech`adj`", 328 makeList("cyber", "digital", "electronic", "techno", "hacker", "crypto", "turbo", "mechanical", "servo"), 329 "sole`adj`", 330 makeList("sole", "true", "singular", "total", "ultimate", "final"), 331 "light`adj`", 332 makeList("bright", "glowing", "solar", "stellar", "lunar", "radiant", "luminous", "shimmering"), 333 "light`noun`", 334 makeList("light", "glow", "sun", "star", "moon", "radiance", "dawn", "torch"), 335 "light`nouns`", 336 makeList("lights", "glimmers", "suns", "stars", "moons", "torches"), 337 "smart`adj`", 338 makeList("brilliant", "smart", "genius", "wise", "clever", "cunning", "mindful", "aware"), 339 "smart`noun`", 340 makeList("genius", "wisdom", "cunning", "awareness", "mindfulness", "acumen", "smarts", "knowledge"), 341 "bandit`noun`", 342 makeList("thief", "raider", "bandit", "rogue", "brigand", "highwayman", "pirate"), 343 "bandit`nouns`", 344 makeList("thieves", "raiders", "bandits", "rogues", "brigands", "highwaymen", "pirates"), 345 "guard`noun`", 346 makeList("protector", "guardian", "warden", "defender", "guard", "shield", "sentinel", "watchman", "knight"), 347 "guard`nouns`", 348 makeList("protectors", "guardians", "wardens", "defenders", "guards", "shields", "sentinels", "watchmen", "knights"), 349 "rage`noun`", 350 makeList("rage", "fury", "anger", "wrath", "frenzy", "vengeance") 351 ), 352 languages = makeLHM( 353 "lc`gen`", 354 makeList("lahatos", "iatsiltak", "hmimrekaarl", "yixaltaishk", "cthupaxa", "zvroggamraa", "ixaakran"), 355 "jp`gen`", 356 makeList("naimoken", "kishigu", "houdaibo", "souchaya", "aijake", "hyazuran", "pajokke", "sokkimou"), 357 "fr`gen`", 358 makeList("devive", "esiggoi", "afaddouille", "roiquide", "obaploui", "baîmefi", "vêggrôste", "blaçeglè", "bamissecois"), 359 "gr`gen`", 360 makeList("lemabus", "ithonxeum", "etoneoch", "eirkuirstes", "taunonkos", "krailozes", "amarstei", "psorsteomium"), 361 "ru`gen`", 362 makeList("belyvia", "tiuzhiskit", "dazyved", "dabrisazetsky", "shaskianyr", "goskabad", "deblieskib", "neskagre"), 363 "sw`gen`", 364 makeList("mzabandu", "nzaloi", "tamzamda", "anzibo", "jamsala", "latazi", "faazaza", "uzoge", "mbomuta", "nbasonga"), 365 "so`gen`", 366 makeList("daggidda", "xabuumaq", "naadhana", "goquusad", "baxiltuu", "qooddaddut", "mosumyuuc", "uggular", "jaabacyut"), 367 "en`gen`", 368 makeList("thabbackion", "joongipper", "urbigsus", "otsaffet", "pittesely", "ramesist", "elgimmac", "genosont", "bessented"), 369 "fn`gen`", 370 makeList("kemosso", "venzendo", "tybangue", "evendi", "ringamye", "drayusta", "acleutos", "nenizo", "ifelle", "rytoudo"), 371 "fn`acc`gen`", 372 makeList("tánzeku", "nìāfőshi", "ñoffêfès", "áfŏmu", "drĕstishű", "pyeryĕquı", "bėdĕbǽ", "nęìjônne", "mainűthî"), 373 "ar`acc`gen`", 374 makeList("azawiq", "al-ahaluq", "isabzīz", "zūrżuhikari", "īrālať", "ījīqab", "qizifih", "ibn-āħūkū", "šulilfas"), 375 "ar`gen`", 376 makeList("iibaatuu", "wiilnalza", "ulanzha", "jaliifa", "iqaddiz", "waatufaa", "lizhuqa", "qinzaamju", "zuzuri"), 377 "hi`gen`", 378 makeList("maghubadhit", "bhunasu", "ipruja", "dhuevasam", "nubudho", "ghasaibi", "virjorghu", "khlindairai", "irsinam"), 379 "ru`so`gen`", 380 makeList("tserokyb", "zhieziufoj", "bisaskug", "nuriesyv", "gybared", "bableqa", "pybadis", "wiuskoglif", "zakalieb"), 381 "gr`hi`gen`", 382 makeList("takhada", "bepsegos", "ovukhrim", "sinupiam", "nabogon", "umianum", "dhainukotron", "muisaithi", "aerpraidha"), 383 "sw`fr`gen`", 384 makeList("nchaleûja", "soëhusi", "nsavarço", "fambofai", "namyàmse", "mfonsapa", "zalasha", "hiplaîpu", "hœumyemza"), 385 "ar`jp`gen`", 386 makeList("jukkaizhi", "hibiikkiiz", "shomela", "qhabohuz", "isiikya", "akkirzuh", "jalukhmih", "uujajon", "ryaataibna"), 387 "sw`gr`gen`", 388 makeList("ozuxii", "muguino", "nauteicha", "mjixazi", "yataya", "pomboirki", "achuiga", "maleibe", "psizeso", "njameichim"), 389 "gr`so`gen`", 390 makeList("xaaxoteum", "basaalii", "azaibe", "oupeddom", "pseiqigioh", "garkame", "uddoulis", "jobegos", "eqisol"), 391 "en`hi`gen`", 392 makeList("promolchi", "dhontriso", "gobhamblom", "hombangot", "sutsidalm", "dhindhinaur", "megsesa", "skaghinma", "thacebha"), 393 "en`jp`gen`", 394 makeList("nyintazu", "haxinsen", "kedezorp", "angapec", "donesalk", "ranepurgy", "laldimyi", "ipprijain", "bizinni"), 395 "so`hi`gen`", 396 makeList("yiteevadh", "omithid", "qugadhit", "nujagi", "nidogish", "danurbha", "sarojik", "cigafo", "tavodduu", "huqoyint"), 397 "fr`mod`gen`", 398 makeList("egleidô", "glaiemegchragne", "veçebun", "aubudaî", "peirquembrut", "eglecque", "marçoimeaux", "jêmbrégshre"), 399 "jp`mod`gen`", 400 makeList("dotobyu", "nikinaan", "gimoummee", "aanzaro", "ryasheeso", "aizaizo", "nyaikkashaa", "kitaani", "maabyopai"), 401 "so`mod`gen`", 402 makeList("sanata", "ájisha", "soreeggár", "quágeleu", "abaxé", "tedora", "bloxajac", "tiblarxo", "oodagí", "jélebi"), 403 "ru`gr`gen`", 404 makeList("zydievov", "pyplerta", "gaupythian", "kaustybre", "larkygagda", "metuskiev", "vuvidzhian", "ykadzhodna", "paziutso"), 405 "lc`gr`gen`", 406 makeList("fesiagroigor", "gledzhiggiakh", "saghiathask", "sheglerliv", "hmepobor", "riagarosk", "kramrufot", "glonuskiub")); 407}