001package squidpony;
002
003import regexodus.MatchResult;
004import regexodus.Matcher;
005import regexodus.Pattern;
006import regexodus.Replacer;
007import squidpony.squidmath.CrossHash;
008import squidpony.squidmath.RNG;
009import squidpony.squidmath.StatefulRNG;
010
011import java.io.Serializable;
012import java.util.*;
013
014/**
015 * A text generator for producing sentences and/or words in nonsense languages that fit a theme. This does not use an
016 * existing word list as a basis for its output, so it may or may not produce existing words occasionally, but you can
017 * safely assume it won't generate a meaningful sentence except in the absolute unlikeliest of cases.
018 * Created by Tommy Ettinger on 11/29/2015.
019 * @author Tommy Ettinger
020 */
021
022public class FakeLanguageGen implements Serializable {
023    private static final long serialVersionUID = -2396642435461186352L;
024    public final String[] openingVowels, midVowels, openingConsonants, midConsonants, closingConsonants,
025            vowelSplitters, closingSyllables;
026    public boolean clean;
027    public final LinkedHashMap<Integer, Double> syllableFrequencies;
028    protected double totalSyllableFrequency = 0.0;
029    public final double vowelStartFrequency, vowelEndFrequency, vowelSplitFrequency, syllableEndFrequency;
030    public final Pattern[] sanityChecks;
031    public ArrayList<Modifier> modifiers;
032    public static final StatefulRNG srng = new StatefulRNG();
033    static final Pattern repeats = Pattern.compile("(.)\\1+");
034    //, diacritics = Pattern.compile("[\u0300-\u036F\u1DC0-\u1DFF]+");
035    static final Pattern[]
036            vulgarChecks = new Pattern[]
037            {
038                    Pattern.compile("[SsξCcсςС][hнН].*[dtтτТΤ]"),
039                    Pattern.compile("([PpрρРΡ][hнН])|[KkкκКΚFfDdCcсςСQq].{1,4}[KkкκКΚCcсςСxхжχХЖΧQq]"), // lots of these end in a 'k' sound, huh
040                    Pattern.compile("[BbъыбвβЪЫБВΒ]..?.?[cсςС][hнН]"),
041                    Pattern.compile("[WwшщψШЩHhнН]..?[rяЯ]"),
042                    Pattern.compile("[TtтτТΤ]..?[tтτТΤ]"),
043                    Pattern.compile("([PpрρРΡ][hнН])|[Ff]..?[rяЯ][tтτТΤ]"),
044                    Pattern.compile("([Ssξ][hнН])[iτιΙ].?[sξzΖ]"),
045                    Pattern.compile("[AaаαАΑΛ][NnийИЙΝ]..?[SsξlιζzΖ]"),
046                    Pattern.compile("[AaаαАΑΛ][sξ][sξ]"),
047                    Pattern.compile(".[uμυν][hнН]?[nийИЙΝ]+[tтτТΤ]"),
048                    Pattern.compile("[NnFf]..?g"), // might as well remove two possible slurs with one check
049                    Pattern.compile("[PpрρРΡ][eеёзξεЕЁЗΞΕΣioоюσοОЮΟuμυν][eеёзξεЕЁЗΞΕΣoоюσοОЮΟs]"), // the grab bag of juvenile words
050                    Pattern.compile("[MmмМΜ]..?[rяЯ].?d"), // should pick up the #1 obscenity from Spanish and French
051                    Pattern.compile("[Gg][HhнН]?[aаαАΑΛeеёзξεЕЁЗΞΕΣ][yуλγУΥeеёзξεЕЁЗΞΕΣ]") // could be inappropriate for random text
052            },
053            englishSanityChecks = new Pattern[]
054                    {
055                            Pattern.compile("[AEIOUaeiou]{3}"),
056                            Pattern.compile("(\\w)\\1\\1"),
057                            Pattern.compile("(.)\\1(.)\\2"),
058                            Pattern.compile("[Aa][ae]"),
059                            Pattern.compile("[Uu][umlkj]"),
060                            Pattern.compile("[Ii][iyqkhrl]"),
061                            Pattern.compile("[Oo][c]"),
062                            Pattern.compile("[Yy][aeiou]{2}"),
063                            Pattern.compile("[Rr][aeiouy]+[xrhp]"),
064                            Pattern.compile("[Qq]u[yu]"),
065                            Pattern.compile("[^oai]uch"),
066                            Pattern.compile("[^tcsz]hh"),
067                            Pattern.compile("[Hh][tcszi]h"),
068                            Pattern.compile("[Tt]t[^aeiouy]{2}"),
069                            Pattern.compile("[IYiy]h[^aeiouy ]"),
070                            Pattern.compile("[szSZrlRL][^aeiou][rlsz]"),
071                            Pattern.compile("[UIuiYy][wy]"),
072                            Pattern.compile("^[UIui][ae]"),
073                            Pattern.compile("q$")
074                    },
075            japaneseSanityChecks  = new Pattern[]
076                    {
077                            Pattern.compile("[AEIOUaeiou]{3}"),
078                            Pattern.compile("(\\w)\\1\\1"),
079                            Pattern.compile("[Tt]s[^u]"),
080                            Pattern.compile("[Ff][^u]"),
081                            Pattern.compile("[Yy][^auo]"),
082                            Pattern.compile("[Tt][ui]"),
083                            Pattern.compile("[SsZzDd]i"),
084                            Pattern.compile("[Hh]u"),
085                    },
086            arabicSanityChecks  = new Pattern[]
087                    {
088                            Pattern.compile("(\\w)\\1\\1"),
089                            Pattern.compile("-[^aeiou]{2}"),
090                    };
091    static final Replacer[]
092            accentFinders = new Replacer[]
093            {
094                    Pattern.compile("[àáâãäåæāăąǻǽ]").replacer("a"),
095                    Pattern.compile("[èéêëēĕėęě]").replacer("e"),
096                    Pattern.compile("[ìíîïĩīĭįı]").replacer("i"),
097                    Pattern.compile("[òóôõöøōŏőœǿ]").replacer("o"),
098                    Pattern.compile("[ùúûüũūŭůűų]").replacer("u"),
099                    Pattern.compile("[ÀÁÂÃÄÅÆĀĂĄǺǼ]").replacer("A"),
100                    Pattern.compile("[ÈÉÊËĒĔĖĘĚ]").replacer("E"),
101                    Pattern.compile("[ÌÍÎÏĨĪĬĮI]").replacer("I"),
102                    Pattern.compile("[ÒÓÔÕÖØŌŎŐŒǾ]").replacer("O"),
103                    Pattern.compile("[ÙÚÛÜŨŪŬŮŰŲ]").replacer("U"),
104                    Pattern.compile("[çćĉċč]").replacer("c"),
105                    Pattern.compile("[þðďđḍ]").replacer("d"),
106                    Pattern.compile("[ĝğġģ]").replacer("g"),
107                    Pattern.compile("[ĥħḥ]").replacer("h"),
108                    Pattern.compile("[ĵȷ]").replacer("j"),
109                    Pattern.compile("ķ").replacer("k"),
110                    Pattern.compile("[ĺļľŀłḷḹļ]").replacer("l"),
111                    Pattern.compile("ṃ").replacer("m"),
112                    Pattern.compile("[ñńņňŋṅṇ]").replacer("n"),
113                    Pattern.compile("[ŕŗřṛṝŗŕ]").replacer("r"),
114                    Pattern.compile("[śŝşšșṣ]").replacer("s"),
115                    Pattern.compile("[ţťŧțṭ]").replacer("t"),
116                    Pattern.compile("[ŵẁẃẅ]").replacer("w"),
117                    Pattern.compile("[ýÿŷỳ]").replacer("y"),
118                    Pattern.compile("[źżž]").replacer("z"),
119                    Pattern.compile("[ÇĆĈĊČ]").replacer("C"),
120                    Pattern.compile("[ÞÐĎĐḌ]").replacer("D"),
121                    Pattern.compile("[ĜĞĠĢ]").replacer("G"),
122                    Pattern.compile("[ĤĦḤ]").replacer("H"),
123                    Pattern.compile("Ĵ").replacer("J"),
124                    Pattern.compile("Ķ").replacer("K"),
125                    Pattern.compile("[ĹĻĽĿŁḶḸĻ]").replacer("L"),
126                    Pattern.compile("Ṃ").replacer("M"),
127                    Pattern.compile("[ÑŃŅŇŊṄṆ]").replacer("N"),
128                    Pattern.compile("[ŔŖŘṚṜŖŔ]").replacer("R"),
129                    Pattern.compile("[ŚŜŞŠȘṢ]").replacer("S"),
130                    Pattern.compile("[ŢŤŦȚṬ]").replacer("T"),
131                    Pattern.compile("[ŴẀẂẄ]").replacer("W"),
132                    Pattern.compile("[ÝŸŶỲ]").replacer("Y"),
133                    Pattern.compile("[ŹŻŽ]").replacer("Z"),
134
135            };
136
137    static final char[][] accentedVowels = new char[][]{
138            new char[]{
139                    'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ā', 'ă', 'ą', 'ǻ', 'ǽ'
140            },
141            new char[]{
142                    'è', 'é', 'ê', 'ë', 'ē', 'ĕ', 'ė', 'ę', 'ě'
143            },
144            new char[]{
145                    'ì', 'í', 'î', 'ï', 'ĩ', 'ī', 'ĭ', 'į', 'ı',
146            },
147            new char[]{
148                    'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ō', 'ŏ', 'ő', 'œ', 'ǿ'
149            },
150            new char[]{
151                    'ù', 'ú', 'û', 'ü', 'ũ', 'ū', 'ŭ', 'ů', 'ű', 'ų'
152            }
153    },
154            accentedConsonants = new char[][]
155                    {
156                            new char[]{
157                                    'b'
158                            },
159                            new char[]{
160                                    'c', 'ç', 'ć', 'ĉ', 'ċ', 'č',
161                            },
162                            new char[]{
163                                    'd', 'þ', 'ð', 'ď', 'đ',
164                            },
165                            new char[]{
166                                    'f'
167                            },
168                            new char[]{
169                                    'g', 'ĝ', 'ğ', 'ġ', 'ģ',
170                            },
171                            new char[]{
172                                    'h', 'ĥ', 'ħ',
173                            },
174                            new char[]{
175                                    'j', 'ĵ', 'ȷ',
176                            },
177                            new char[]{
178                                    'k', 'ķ',
179                            },
180                            new char[]{
181                                    'l', 'ĺ', 'ļ', 'ľ', 'ŀ', 'ł',
182                            },
183                            new char[]{
184                                    'm',
185                            },
186                            new char[]{
187                                    'n', 'ñ', 'ń', 'ņ', 'ň', 'ŋ',
188                            },
189                            new char[]{
190                                    'p',
191                            },
192                            new char[]{
193                                    'q',
194                            },
195                            new char[]{
196                                    'r', 'ŕ', 'ŗ', 'ř',
197                            },
198                            new char[]{
199                                    's', 'ś', 'ŝ', 'ş', 'š', 'ș',
200                            },
201                            new char[]{
202                                    't', 'ţ', 'ť', 'ț',
203                            },
204                            new char[]{
205                                    'v',
206                            },
207                            new char[]{
208                                    'w', 'ŵ', 'ẁ', 'ẃ', 'ẅ',
209                            },
210                            new char[]{
211                                    'x',
212                            },
213                            new char[]{
214                                    'y', 'ý', 'ÿ', 'ŷ', 'ỳ',
215                            },
216                            new char[]{
217                                    'z', 'ź', 'ż', 'ž',
218                            },
219                    };
220
221    /*
222     * Removes accented characters from a string; if the "base" characters are non-English anyway then the result won't
223     * be an ASCII string, but otherwise it probably will be.
224     * <br>
225     * Credit to user hashable from http://stackoverflow.com/a/1215117
226     *
227     * @param str a string that may contain accented characters
228     * @return a string with all accented characters replaced with their (possibly ASCII) counterparts
229     *
230    public String removeAccents(String str) {
231        String alteredString = Normalizer.normalize(str, Normalizer.Form.NFD);
232        alteredString = diacritics.matcher(alteredString).replaceAll("");
233        alteredString = alteredString.replace('æ', 'a');
234        alteredString = alteredString.replace('œ', 'o');
235        alteredString = alteredString.replace('Æ', 'A');
236        alteredString = alteredString.replace('Œ', 'O');
237        return alteredString;
238    }*/
239
240    /**
241     * Removes accented Latin-script characters from a string; if the "base" characters are non-English anyway then the
242     * result won't be an ASCII string, but otherwise it probably will be.
243     *
244     * @param str a string that may contain accented Latin-script characters
245     * @return a string with all accented characters replaced with their (possibly ASCII) counterparts
246     */
247    public CharSequence removeAccents(CharSequence str) {
248        CharSequence alteredString = str;
249        for (int i = 0; i < accentFinders.length; i++) {
250            alteredString = accentFinders[i].replace(alteredString);
251        }
252        return alteredString;
253    }
254
255
256    /**
257     * Ia! Ia! Cthulhu Rl'yeh ftaghn! Useful for generating cultist ramblings or unreadable occult texts.
258     * <br>
259     * Zvrugg pialuk, ya'as irlemrugle'eith iposh hmo-es nyeighi, glikreirk shaivro'ei!
260     */
261    public static final FakeLanguageGen LOVECRAFT = new FakeLanguageGen(
262            new String[]{"a", "i", "o", "e", "u", "a", "i", "o", "e", "u", "ia", "ai", "aa", "ei"},
263            new String[]{},
264            new String[]{"s", "t", "k", "n", "y", "p", "k", "l", "g", "gl", "th", "sh", "ny", "ft", "hm", "zvr", "cth"},
265            new String[]{"h", "gl", "gr", "nd", "mr", "vr", "kr"},
266            new String[]{"l", "p", "s", "t", "n", "k", "g", "x", "rl", "th", "gg", "gh", "ts", "lt", "rk", "kh", "sh", "ng", "shk"},
267            new String[]{"aghn", "ulhu", "urath", "oigor", "alos", "'yeh", "achtal", "urath", "ikhet", "adzek"},
268            new String[]{"'", "-"}, new int[]{1, 2, 3}, new double[]{6, 7, 2}, 0.4, 0.31, 0.07, 0.04, null, true);
269    /**
270     * Imitation English; may seem closer to Dutch in some generated text, and is not exactly the best imitation.
271     * Should seem pretty fake to many readers; does not filter out dictionary words but does perform basic vulgarity
272     * filtering. If you want to avoid generating other words, you can subclass FakeLanguageGen and modify word() .
273     * <br>
274     * Mont tiste frot; mousation hauddes?
275     * Lily wrely stiebes; flarrousseal gapestist.
276     */
277    public static final FakeLanguageGen ENGLISH = new FakeLanguageGen(
278            new String[]{
279                    "a", "a", "a", "a", "o", "o", "o", "e", "e", "e", "e", "e", "i", "i", "i", "i", "u",
280                    "a", "a", "a", "a", "o", "o", "o", "e", "e", "e", "e", "e", "i", "i", "i", "i", "u",
281                    "a", "a", "a", "o", "o", "e", "e", "e", "i", "i", "i", "u",
282                    "a", "a", "a", "o", "o", "e", "e", "e", "i", "i", "i", "u",
283                    "au", "ai", "ai", "ou", "ea", "ie", "io", "ei",
284            },
285            new String[]{"u", "u", "oa", "oo", "oo", "oo", "ee", "ee", "ee", "ee",},
286            new String[]{
287                    "b", "bl", "br", "c", "cl", "cr", "ch", "d", "dr", "f", "fl", "fr", "g", "gl", "gr", "h", "j", "k", "l", "m", "n",
288                    "p", "pl", "pr", "qu", "r", "s", "sh", "sk", "st", "sp", "sl", "sm", "sn", "t", "tr", "th", "thr", "v", "w", "y", "z",
289                    "b", "bl", "br", "c", "cl", "cr", "ch", "d", "dr", "f", "fl", "fr", "g", "gr", "h", "j", "k", "l", "m", "n",
290                    "p", "pl", "pr", "r", "s", "sh", "st", "sp", "sl", "t", "tr", "th", "w", "y",
291                    "b", "br", "c", "ch", "d", "dr", "f", "g", "h", "j", "l", "m", "n",
292                    "p", "r", "s", "sh", "st", "sl", "t", "tr", "th",
293                    "b", "d", "f", "g", "h", "l", "m", "n",
294                    "p", "r", "s", "sh", "t", "th",
295                    "b", "d", "f", "g", "h", "l", "m", "n",
296                    "p", "r", "s", "sh", "t", "th",
297                    "r", "s", "t", "l", "n",
298                    "str", "spr", "spl", "wr", "kn", "kn", "gn",
299            },
300            new String[]{"x", "cst", "bs", "ff", "lg", "g", "gs",
301                    "ll", "ltr", "mb", "mn", "mm", "ng", "ng", "ngl", "nt", "ns", "nn", "ps", "mbl", "mpr",
302                    "pp", "ppl", "ppr", "rr", "rr", "rr", "rl", "rtn", "ngr", "ss", "sc", "rst", "tt", "tt", "ts", "ltr", "zz"
303            },
304            new String[]{"b", "rb", "bb", "c", "rc", "ld", "d", "ds", "dd", "f", "ff", "lf", "rf", "rg", "gs", "ch", "lch", "rch", "tch",
305                    "ck", "ck", "lk", "rk", "l", "ll", "lm", "m", "rm", "mp", "n", "nk", "nch", "nd", "ng", "ng", "nt", "ns", "lp", "rp",
306                    "p", "r", "rn", "rts", "s", "s", "s", "s", "ss", "ss", "st", "ls", "t", "t", "ts", "w", "wn", "x", "ly", "lly", "z",
307                    "b", "c", "d", "f", "g", "k", "l", "m", "n", "p", "r", "s", "t", "w",
308            },
309            new String[]{"ate", "ite", "ism", "ist", "er", "er", "er", "ed", "ed", "ed", "es", "es", "ied", "y", "y", "y", "y",
310                    "ate", "ite", "ism", "ist", "er", "er", "er", "ed", "ed", "ed", "es", "es", "ied", "y", "y", "y", "y",
311                    "ate", "ite", "ism", "ist", "er", "er", "er", "ed", "ed", "ed", "es", "es", "ied", "y", "y", "y", "y",
312                    "ay", "ay", "ey", "oy", "ay", "ay", "ey", "oy",
313                    "ough", "aught", "ant", "ont", "oe", "ance", "ell", "eal", "oa", "urt", "ut", "iom", "ion", "ion", "ision", "ation", "ation", "ition",
314                    "ough", "aught", "ant", "ont", "oe", "ance", "ell", "eal", "oa", "urt", "ut", "iom", "ion", "ion", "ision", "ation", "ation", "ition",
315                    "ily", "ily", "ily", "adly", "owly", "oorly", "ardly", "iedly",
316            },
317            new String[]{}, new int[]{1, 2, 3, 4}, new double[]{7, 8, 4, 1}, 0.22, 0.1, 0.0, 0.25, englishSanityChecks, true);
318    /**
319     * Imitation ancient Greek, romanized to use the Latin alphabet. Likely to seem pretty fake to many readers.
320     * <br>
321     * Psuilas alor; aipeomarta le liaspa...
322     */
323    public static final FakeLanguageGen GREEK_ROMANIZED = new FakeLanguageGen(
324            new String[]{"a", "a", "a", "o", "o", "o", "e", "e", "i", "i", "i", "au", "ai", "ai", "oi", "oi", "ia", "io", "ou", "ou", "eo", "ei"},
325            new String[]{"ui", "ei"},
326            new String[]{"rh", "s", "z", "t", "t", "k", "ch", "n", "th", "kth", "m", "p", "ps", "b", "l", "kr", "g", "phth"},
327            new String[]{"lph", "pl", "l", "l", "kr", "nch", "nx", "ps"},
328            new String[]{"s", "p", "t", "ch", "n", "m", "s", "p", "t", "ch", "n", "m", "b", "g", "st", "rst", "rt", "sp", "rk", "ph", "x", "z", "nk", "ng", "th"},
329            new String[]{"os", "os", "is", "us", "um", "eum", "ium", "iam", "us", "um", "es", "anes", "eros", "or", "ophon", "on", "otron"},
330            new String[]{}, new int[]{1, 2, 3}, new double[]{5, 7, 4}, 0.45, 0.45, 0.0, 0.3, null, true);
331    /**
332     * Imitation ancient Greek, using the original Greek alphabet. People may try to translate it and get gibberish.
333     * Make sure the font you use to render this supports the Greek alphabet! In the GDX display module, the "smooth"
334     * fonts support all the Greek you need for this.
335     * <br>
336     * Ψυιλασ αλορ; αιπεομαρτα λε λιασπα...
337     */
338    public static final FakeLanguageGen GREEK_AUTHENTIC = new FakeLanguageGen(
339            new String[]{"α", "α", "α", "ο", "ο", "ο", "ε", "ε", "ι", "ι", "ι", "αυ", "αι", "αι", "οι", "οι", "ια", "ιο", "ου", "ου", "εο", "ει"},
340            new String[]{"υι", "ει"},
341            new String[]{"ρ", "σ", "ζ", "τ", "τ", "κ", "χ", "ν", "θ", "κθ", "μ", "π", "ψ", "β", "λ", "κρ", "γ", "φθ"},
342            new String[]{"λφ", "πλ", "λ", "λ", "κρ", "γχ", "γξ", "ψ"},
343            new String[]{"σ", "π", "τ", "χ", "ν", "μ", "σ", "π", "τ", "χ", "ν", "μ", "β", "γ", "στ", "ρστ", "ρτ", "σπ", "ρκ", "φ", "ξ", "ζ", "γκ", "γγ", "θ"},
344            new String[]{"ος", "ος", "ις", "υς", "υμ", "ευμ", "ιυμ", "ιαμ", "υς", "υμ", "ες", "ανες", "ερος", "ορ", "οφον", "ον", "οτρον"},
345            new String[]{}, new int[]{1, 2, 3}, new double[]{5, 7, 4}, 0.45, 0.45, 0.0, 0.3, null, true);
346
347    /**
348     * Imitation modern French, using (too many of) the accented vowels that are present in the language. Translating it
349     * will produce gibberish if it produces anything at all. In the GDX display module, the "smooth" and "unicode"
350     * fonts support all the accented characters you need for this.
351     * <br><br>
352     * Fa veau, ja ri avé re orçe jai braï aisté.
353     */
354    public static final FakeLanguageGen FRENCH = new FakeLanguageGen(
355            new String[]{"a", "a", "a", "e", "e", "e", "i", "i", "o", "u", "a", "a", "a", "e", "e", "e", "i", "i", "o",
356                    "a", "a", "a", "e", "e", "e", "i", "i", "o", "u", "a", "a", "a", "e", "e", "e", "i", "i", "o",
357                    "a", "a", "e", "e", "i", "o", "a", "a", "a", "e", "e", "e", "i", "i", "o",
358                    "ai", "oi", "oui", "au", "œu", "ou"
359            },
360            new String[]{
361                    "ai", "aie", "aou", "eau", "oi", "oui", "oie", "eu", "eu",
362                    "à", "â", "ai", "aî", "aï", "aie", "aou", "aoû", "au", "ay", "e", "é", "ée", "è",
363                    "ê", "eau", "ei", "eî", "eu", "eû", "i", "î", "ï", "o", "ô", "oe", "oê", "oë", "œu",
364                    "oi", "oie", "oï", "ou", "oû", "oy", "u", "û", "ue",
365                    "a", "a", "a", "e", "e", "e", "i", "i", "o", "u", "a", "a", "a", "e", "e", "e", "i", "i", "o",
366                    "a", "a", "e", "e", "i", "o", "a", "a", "a", "e", "e", "e", "i", "i", "o",
367            },
368            new String[]{"tr", "ch", "m", "b", "b", "br", "j", "j", "j", "j", "g", "t", "t", "t", "c", "d", "f", "f", "h", "n", "l", "l",
369                    "s", "s", "s", "r", "r", "r", "v", "v", "p", "pl", "pr", "bl", "br", "dr", "gl", "gr"},
370            new String[]{"cqu", "gu", "qu", "rqu", "nt", "ng", "ngu", "mb", "ll", "nd", "ndr", "nct", "st",
371                    "xt", "mbr", "pl", "g", "gg", "ggr", "gl",
372                    "m", "m", "mm", "v", "v", "f", "f", "f", "ff", "b", "b", "bb", "d", "d", "dd", "s", "s", "s", "ss", "ss", "ss",
373                    "cl", "cr", "ng", "ç", "ç", "rç"},
374            new String[]{},
375            new String[]{"e", "e", "e", "e", "e", "é", "é", "er", "er", "er", "er", "er", "es", "es", "es", "es", "es", "es",
376                    "e", "e", "e", "e", "e", "é", "é", "er", "er", "er", "er", "er", "er", "es", "es", "es", "es", "es",
377                    "e", "e", "e", "e", "e", "é", "é", "é", "er", "er", "er", "er", "er", "es", "es", "es", "es", "es",
378                    "ent", "em", "en", "en", "aim", "ain", "an", "oin", "ien", "iere", "ors", "anse",
379                    "ombs", "ommes", "ancs", "ends", "œufs", "erfs", "ongs", "aps", "ats", "ives", "ui", "illes",
380                    "aen", "aon", "am", "an", "eun", "ein", "age", "age", "uile", "uin", "um", "un", "un", "un",
381                    "aille", "ouille", "eille", "ille", "eur", "it", "ot", "oi", "oi", "oi", "aire", "om", "on", "on",
382                    "im", "in", "in", "ien", "ien", "ion", "il", "eil", "oin", "oint", "iguïté", "ience", "incte",
383                    "ang", "ong", "acré", "eau", "ouche", "oux", "oux", "ect", "ecri", "agne", "uer", "aix", "eth", "ut", "ant",
384                    "anc", "anc", "anche", "ioche", "eaux", "ive", "eur", "ancois", "ecois"},
385            new String[]{}, new int[]{1, 2, 3}, new double[]{18, 7, 2}, 0.35, 1.0, 0.0, 0.55, null, true);
386
387    /**
388     * Imitation modern Russian, romanized to use the Latin alphabet. Likely to seem pretty fake to many readers.
389     * <br>
390     * Zhydotuf ruts pitsas, gogutiar shyskuchebab - gichapofeglor giunuz ieskaziuzhin.
391     */
392    public static final FakeLanguageGen RUSSIAN_ROMANIZED = new FakeLanguageGen(
393            new String[]{"a", "e", "e", "i", "i", "o", "u", "ie", "y", "e", "iu", "ia", "y", "a", "a", "o", "u"},
394            new String[]{},
395            new String[]{"b", "v", "g", "d", "k", "l", "p", "r", "s", "t", "f", "kh", "ts",
396                    "b", "v", "g", "d", "k", "l", "p", "r", "s", "t", "f", "kh", "ts",
397                    "b", "v", "g", "d", "k", "l", "p", "r", "s", "t", "f",
398                    "zh", "m", "n", "z", "ch", "sh", "shch",
399                    "br", "sk", "tr", "bl", "gl", "kr", "gr"},
400            new String[]{"bl", "br", "pl", "dzh", "tr", "gl", "gr", "kr"},
401            new String[]{"b", "v", "g", "d", "zh", "z", "k", "l", "m", "n", "p", "r", "s", "t", "f", "kh", "ts", "ch", "sh",
402                    "v", "f", "sk", "sk", "sk", "s", "b", "d", "d", "n", "r", "r"},
403            new String[]{"odka", "odna", "usk", "ask", "usky", "ad", "ar", "ovich", "ev", "ov", "of", "agda", "etsky", "ich", "on", "akh", "iev", "ian"},
404            new String[]{}, new int[]{1, 2, 3, 4, 5, 6}, new double[]{4, 5, 6, 5, 3, 1}, 0.1, 0.2, 0.0, 0.12, englishSanityChecks, true);
405
406
407    /**
408     * Imitation modern Russian, using the authentic Cyrillic alphabet used in Russia and other countries.
409     * Make sure the font you use to render this supports the Cyrillic alphabet!
410     * In the GDX display module, the "smooth" fonts support all the Cyrillic alphabet you need for this.
411     * <br>
412     * Жыдотуф руц пйцас, гогутяр шыскучэбаб - гйчапофёглор гюнуз ъсказюжин.
413     */
414    public static final FakeLanguageGen RUSSIAN_AUTHENTIC = new FakeLanguageGen(
415            new String[]{"а", "е", "ё", "и", "й", "о", "у", "ъ", "ы", "э", "ю", "я", "ы", "а", "а", "о", "у"},
416            new String[]{},
417            new String[]{"б", "в", "г", "д", "к", "л", "п", "р", "с", "т", "ф", "х", "ц",
418                    "б", "в", "г", "д", "к", "л", "п", "р", "с", "т", "ф", "х", "ц",
419                    "б", "в", "г", "д", "к", "л", "п", "р", "с", "т", "ф",
420                    "ж", "м", "н", "з", "ч", "ш", "щ",
421                    "бр", "ск", "тр", "бл", "гл", "кр", "гр"},
422            new String[]{"бл", "бр", "пл", "дж", "тр", "гл", "гр", "кр"},
423            new String[]{"б", "в", "г", "д", "ж", "з", "к", "л", "м", "н", "п", "р", "с", "т", "ф", "х", "ц", "ч", "ш",
424                    "в", "ф", "ск", "ск", "ск", "с", "б", "д", "д", "н", "р", "р"},
425            new String[]{"одка", "одна", "уск", "аск", "ускы", "ад", "ар", "овйч", "ев", "ов", "оф", "агда", "ёцкы", "йч", "он", "ах", "ъв", "ян"},
426            new String[]{}, new int[]{1, 2, 3, 4, 5, 6}, new double[]{4, 5, 6, 5, 3, 1}, 0.1, 0.2, 0.0, 0.12, null, true);
427
428    /**
429     * Imitation Japanese, romanized to use the Latin alphabet. Likely to seem pretty fake to many readers.
430     * <br>
431     * Narurehyounan nikase keho...
432     */
433    public static final FakeLanguageGen JAPANESE_ROMANIZED = new FakeLanguageGen(
434            new String[]{"a", "a", "a", "a", "e", "e", "i", "i", "i", "i", "o", "o", "o", "u", "ou", "u", "ai", "ai"},
435            new String[]{},
436            new String[]{"k", "ky", "s", "sh", "t", "ts", "ch", "n", "ny", "h", "f", "hy", "m", "my", "y", "r", "ry", "g",
437                    "gy", "z", "j", "d", "b", "by", "p", "py",
438                    "k", "t", "n", "s", "k", "t", "d", "s", "sh", "sh", "g", "r", "b",
439                    "k", "t", "n", "s", "k", "t", "b", "s", "sh", "sh", "g", "r", "b",
440                    "k", "t", "n", "s", "k", "t", "z", "s", "sh", "sh", "ch", "ry", "ts"
441            },
442            new String[]{"k", "ky", "s", "sh", "t", "ts", "ch", "n", "ny", "h", "f", "hy", "m", "my", "y", "r", "ry", "g",
443                    "gy", "z", "j", "d", "b", "by", "p", "py",
444                    "k", "t", "d", "s", "k", "t", "d", "s", "sh", "sh", "y", "j", "p", "r", "d",
445                    "k", "t", "b", "s", "k", "t", "b", "s", "sh", "sh", "y", "j", "p", "r", "d",
446                    "k", "t", "z", "s", "f", "g", "z", "b", "d", "ts",
447                    "nn", "nn", "nn", "nd", "nz", "mm", "kk", "kk", "tt", "ss", "ssh", "tch"},
448            new String[]{"n"},
449            new String[]{},
450            new String[]{}, new int[]{1, 2, 3, 4, 5}, new double[]{5, 4, 5, 4, 3}, 0.3, 0.9, 0.0, 0.0, japaneseSanityChecks, true);
451
452    /**
453     * Swahili is one of the more commonly-spoken languages in sub-Saharan Africa, and serves mainly as a shared language
454     * that is often learned after becoming fluent in one of many other (vaguely-similar) languages of the area. An
455     * example sentence in Swahili, that this might try to imitate aesthetically, is "Mtoto mdogo amekisoma," meaning
456     * "The small child reads it" (where it is a book). A notable language feature used here is the redoubling of words,
457     * which is used in Swahili to emphasize or alter the meaning of the doubled word; here, it always repeats exactly
458     * and can't make minor changes like a real language might. This generates things like "gata-gata", "hapi-hapi", and
459     * "mimamzu-mimamzu", always separating with a hyphen here.
460     * <br>
461     * As an aside, please try to avoid the ugly stereotypes that fantasy media often assigns to speakers of African-like
462     * languages when using this or any of the generators. Many fantasy tropes come from older literature written with
463     * major cultural biases, and real-world cultural elements can be much more interesting to players than yet another
464     * depiction of a "jungle savage" with stereotypical traits. Consider drawing from existing lists of real-world
465     * technological discoveries, like https://en.wikipedia.org/wiki/History_of_science_and_technology_in_Africa , for
466     * inspiration when world-building; though some groups may not have developed agriculture by early medieval times,
467     * their neighbors may be working iron and studying astronomy just a short distance away.
468     * <br>
469     * Kondueyu; ma mpiyamdabota mise-mise nizakwaja alamsa amja, homa nkajupomba.
470     */
471    public static final FakeLanguageGen SWAHILI = new FakeLanguageGen(
472            new String[]{"a", "i", "o", "e", "u",
473                    "a", "a", "i", "o", "o", "e", "u",
474                    "a", "a", "i", "o", "o", "u",
475                    "a", "a", "i", "i", "o",
476                    "a","a","a","a","a",
477                    "a", "i", "o", "e", "u",
478                    "a", "a", "i", "o", "o", "e", "u",
479                    "a", "a", "i", "o", "o", "u",
480                    "a", "a", "i", "i", "o",
481                    "a","a","a","a","a",
482                    "aa", "aa", "ue", "uo", "ii", "ea"},
483            new String[]{},
484            new String[]{
485                    "b", "h", "j", "l", "s", "y", "m", "n",
486                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
487                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
488                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
489                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
490
491                    "b", "h", "j", "l", "s", "y", "m", "n",
492                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
493                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
494                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
495                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
496
497                    "b", "h", "j", "l", "s", "y", "m", "n",
498                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
499                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
500                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
501                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
502
503                    "b", "h", "j", "l", "s", "y", "m", "n",
504                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
505                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
506                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
507                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
508
509                    "nb", "nj", "ns", "nz",
510                    "nb", "nch", "nj", "ns", "ny", "nz",
511                    "nb", "nch", "nf", "ng", "nj", "nk", "np", "ns", "nz",
512                    "nb", "nch", "nd", "nf", "ng", "nj", "nk", "np", "ns", "nt", "nz",
513                    "nb", "nch", "nd", "nf", "ng", "nj", "nk", "np", "ns", "nt", "nv", "nw", "nz",
514
515                    "mb", "ms", "my", "mz",
516                    "mb", "mch","ms", "my", "mz",
517                    "mb", "mch", "mk", "mp", "ms", "my", "mz",
518                    "mb", "mch", "md", "mk", "mp", "ms", "mt", "my", "mz",
519                    "mb", "mch", "md", "mf", "mg", "mj", "mk", "mp", "ms", "mt", "mv", "mw", "my", "mz",
520                    "sh", "sh", "sh", "ny", "kw",
521                    "dh", "th", "sh", "ny",
522                    "dh", "th", "sh", "gh", "r", "ny",
523                    "dh", "th", "sh", "gh", "r", "ny",
524            },
525            new String[]{
526                    "b", "h", "j", "l", "s", "y", "m", "n",
527                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
528                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
529                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
530                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
531
532                    "b", "h", "j", "l", "s", "y", "m", "n",
533                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
534                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
535                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
536                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
537
538                    "b", "h", "j", "l", "s", "y", "m", "n",
539                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
540                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
541                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
542                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
543
544                    "b", "h", "j", "l", "s", "y", "m", "n",
545                    "b", "ch", "h", "j", "l", "s", "y", "z", "m", "n",
546                    "b", "ch", "f", "g", "h", "j", "k", "l", "p", "s", "y", "z", "m", "n",
547                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "y", "z", "m", "n", "kw",
548                    "b", "ch", "d", "f", "g", "h", "j", "k", "l", "p", "s", "t", "v", "w", "y", "z", "m", "n", "kw",
549
550                    "nb", "nj", "ns", "nz",
551                    "nb", "nch", "nj", "ns", "ny", "nz",
552                    "nb", "nch", "nf", "ng", "nj", "nk", "np", "ns", "nz",
553                    "nb", "nch", "nd", "nf", "ng", "nj", "nk", "np", "ns", "nt", "nz",
554                    "nb", "nch", "nd", "nf", "ng", "nj", "nk", "np", "ns", "nt", "nw", "nz",
555
556                    "mb", "ms", "my", "mz",
557                    "mb", "mch","ms", "my", "mz",
558                    "mb", "mch", "mk", "mp", "ms", "my", "mz",
559                    "mb", "mch", "md", "mk", "mp", "ms", "mt", "my", "mz",
560                    "mb", "mch", "md", "mf", "mg", "mj", "mk", "mp", "ms", "mt", "mw", "my", "mz",
561                    "sh", "sh", "sh", "ny", "kw",
562                    "dh", "th", "sh", "ny",
563                    "dh", "th", "sh", "gh", "r", "ny",
564                    "dh", "th", "sh", "gh", "r", "ny",
565                    "ng", "ng", "ng", "ng", "ng"
566            },
567            new String[]{""},
568            new String[]{"a-@2a", "a-@2a", "a-@3a","a-@2a", "a-@2a", "a-@3a","i-@2i", "i-@2i", "i-@3i",
569                    "e-@2e", "e-@2e", "e-@3e", "u-@2u", "u-@2u", "u-@3u",
570            },
571            new String[]{}, new int[]{1, 2, 3, 4, 5}, new double[]{1, 7, 6, 4, 2}, 0.2, 1.0, 0.0, 0.25, null, true);
572
573    /**
574     * Imitation Somali, using the Latin alphabet. Due to uncommon word structure, unusual allowed combinations of
575     * letters, and no common word roots with most familiar languages, this may seem like an unidentifiable or "alien"
576     * language to most readers. However, it's based on the Latin writing system for the Somali language (probably
577     * closest to the northern dialect), which due to the previously mentioned properties, makes it especially good for
578     * mixing with other languages to make letter combinations that seem strange to appear. It is unlikely that this
579     * particular generated language style will be familiar to readers, so it probably won't have existing stereotypes
580     * associated with the text. One early comment this received was, "it looks like a bunch of letters semi-randomly
581     * thrown together", which is probably a typical response (the comment was made by someone fluent in German and
582     * English, and most Western European languages are about as far as you can get from Somali).
583     * <br>
584     * Libor cat naqoxekh dhuugad gisiqir?
585     */
586    public static final FakeLanguageGen SOMALI = new FakeLanguageGen(
587            new String[]{"a", "a", "a", "a", "a", "a", "a", "aa", "aa", "aa",
588                    "e", "e", "ee",
589                    "i", "i", "i", "i", "ii",
590                    "o", "o", "o", "oo",
591                    "u", "u", "u", "uu", "uu",
592                    },
593            new String[]{},
594            new String[]{"b", "t", "j", "x", "kh", "d", "r", "s", "sh", "dh", "c", "g", "f", "q", "k", "l", "m",
595                    "n", "w", "h", "y",
596                    "x", "g", "b", "d", "s", "m", "dh", "n", "r",
597                    "g", "b", "s", "dh",
598            },
599            new String[]{
600                    "bb", "gg", "dd", "bb", "dd", "rr", "ddh", "cc", "gg", "ff", "ll", "mm", "nn",
601                    "bb", "gg", "dd", "bb", "dd", "gg",
602                    "bb", "gg", "dd", "bb", "dd", "gg",
603                    "cy", "fk", "ft", "nt", "rt", "lt", "qm", "rdh", "rsh", "lq",
604                    "my", "gy", "by", "lkh", "rx", "md", "bd", "dg", "fd", "mf",
605                    "dh", "dh", "dh", "dh",
606            },
607            new String[]{
608                    "b", "t", "j", "x", "kh", "d", "r", "s", "sh", "c", "g", "f", "q", "k", "l", "m", "n", "h",
609                    "x", "g", "b", "d", "s", "m", "q", "n", "r",
610                    "b", "t", "j", "x", "kh", "d", "r", "s", "sh", "c", "g", "f", "q", "k", "l", "m", "n", "h",
611                    "x", "g", "b", "d", "s", "m", "q", "n", "r",
612                    "b", "t", "j", "x", "kh", "d", "r", "s", "sh", "c", "g", "f", "q", "k", "l", "m", "n",
613                    "g", "b", "d", "s", "q", "n", "r",
614                    "b", "t", "x", "kh", "d", "r", "s", "sh", "g", "f", "q", "k", "l", "m", "n",
615                    "g", "b", "d", "s", "r", "n",
616                    "b", "t", "kh", "d", "r", "s", "sh", "g", "f", "q", "k", "l", "m", "n",
617                    "g", "b", "d", "s", "r", "n",
618                    "b", "t", "d", "r", "s", "sh", "g", "f", "q", "k", "l", "m", "n",
619                    "g", "b", "d", "s", "r", "n",
620            },
621            new String[]{"aw", "ow", "ay", "ey", "oy", "ay", "ay"},
622            new String[]{}, new int[]{1, 2, 3, 4, 5}, new double[]{5, 4, 5, 4, 1}, 0.25, 0.3, 0.0, 0.08, null, true);
623    /**
624     * Imitation Hindi, romanized to use the Latin alphabet using accented glyphs similar to the IAST standard.
625     * Most fonts do not support the glyphs that IAST-standard romanization of Hindi needs, so this uses alternate
626     * glyphs from at most Latin Extended-A. Relative to HINDI_IAST, also defined here, the IAST standard glyphs
627     * {@code "ŗŕļĺđţńņşĕĭ"} become {@code "ŗŕļĺđţńņşĕĭ"}, with the nth glyph in the first string being substituted
628     * with the nth glyph in the second string. This version of imitation Hindi is preferred over the IAST kind because
629     * font support is much better for the glyphs this version uses.
630     * <br>
631     * Darvāga yar; ghađhinopŕauka āĕrdur, conśaigaijo śabhodhaĕđū jiviđaudu.
632     */
633    public static final FakeLanguageGen HINDI_ROMANIZED = new FakeLanguageGen(
634            new String[]{
635                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
636                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
637                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
638                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
639                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
640                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
641                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
642                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
643                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
644                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
645                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
646                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
647                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
648                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
649                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
650                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
651                    "aĕ", "aĕ", "aĕ", "aĕ", "aĕ", "āĕ", "āĕ", "iĕ", "iĕ", "iĕ", "īĕ", "īĕ",
652                    "uĕ", "uĕ", "ūĕ", "aiĕ", "aiĕ", "oĕ", "oĕ", "oĕ", "auĕ",
653                    //"aĭ", "aĭ", "aĭ", "aĭ", "aĭ", "āĭ", "āĭ", "iĭ", "iĭ", "iĭ", "īĭ", "īĭ",
654                    //"uĭ", "uĭ", "ūĭ", "aiĭ", "aiĭ", "oĭ", "oĭ", "oĭ", "auĭ",
655            },
656            new String[]{"á","í","ú", "ó", "á","í","ú", "ó",
657            },
658            new String[]{
659                    "k", "k", "k", "k", "k", "k", "k", "k", "kŗ", "kŕ", "kļ",
660                    "c", "c", "c", "c", "c", "c", "cŗ", "cŕ", "cļ",
661                    "ţ", "t", "t", "t", "t", "t", "t", "t", "t", "t", "tŗ", "tŕ", "tŗ", "tŕ",
662                    "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "pŗ", "pŕ", "pļ", "pĺ", "pŗ", "pŕ", "p", "p",
663                    "kh", "kh", "kh", "kh", "kh", "kh", "kh", "kh", "kh", "kh", "khŗ", "khŕ", "khļ", "khĺ",
664                    "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "chŗ", "chŕ", "chļ", "chĺ",
665                    "ţh", "th", "th", "th", "th", "th", "th", "th", "th", "th", "thŗ", "thŕ", "thļ", "thĺ",
666                    "ph", "ph", "ph", "ph", "ph", "ph", "ph", "phŗ", "phŕ", "phļ", "phĺ",
667                    "g", "j", "đ", "d", "b", "gh", "jh", "đh", "dh", "bh",
668                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
669                    "g", "j", "đ", "d", "b", "gh", "jh", "đh", "dh", "bh",
670                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
671                    "g", "j", "đ", "d", "b", "gh", "jh", "đh", "dh", "bh",
672                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
673                    "g", "j", "đ", "d", "b", "gh", "jh", "đh", "dh", "bh",
674                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
675                    "g", "j", "đ", "d", "b", "gh", "jh", "đh", "dh", "bh",
676                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
677                    "g", "j", "đ", "d", "b", "gh", "jh", "đh", "dh", "bh",
678                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
679                    "g", "j", "đ", "d", "b", "gh", "jh", "đh", "dh", "bh",
680                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
681                    "g", "j", "đ", "d", "b", "gh", "đh", "dh", "bh",
682                    "ń", "ñ", "ņ", "n", "m", "h", "y", "r", "l", "v", "ś", "ş", "s",
683                    "g", "j", "đ", "d", "b", "gh", "đh", "dh", "bh",
684                    "ń", "ņ", "n", "m", "h", "y", "r", "l", "v", "ş", "s",
685                    "g", "j", "đ", "d", "b", "gh", "đh", "dh", "bh",
686                    "ń", "ņ", "n", "m", "h", "y", "r", "l", "v", "ş", "s",
687                    "g", "đ", "d", "b", "gh", "đh", "dh", "bh", "n", "m", "v", "s",
688                    "g", "đ", "d", "b", "g", "d", "b", "dh", "bh", "n", "m", "v",
689                    "g", "đ", "d", "b", "g", "d", "b", "dh", "bh", "n", "m", "v",
690            },
691            new String[]{
692                    "k", "k", "k", "k", "k", "nk", "rk",
693                    "k", "k", "k", "k", "k", "nk", "rk",
694                    "k", "k", "k", "k", "k", "nk", "rk",
695                    "k", "k", "k", "k", "k", "nk", "rk",
696                    "k", "k", "k", "k", "k", "nk", "rk",
697                    "k", "k", "k", "k", "k", "nk", "rk",
698                    "k", "k", "k", "k", "k", "nk", "rk",
699                    "k", "k", "k", "k", "k", "nk", "rk",
700                    "kŗ", "kŗ", "kŗ", "kŗ", "kŗ", "nkŗ", "rkŗ",
701                    "kŕ", "kŕ", "kŕ", "kŕ", "kŕ", "nkŕ", "rkŕ",
702                    "kļ", "kļ", "kļ", "kļ", "kļ", "nkļ", "rkļ",
703
704                    "c", "c", "c", "c", "c", "c", "cŗ", "cŕ", "cļ",
705                    "ţ", "t", "t", "t", "t", "t", "nt", "rt",
706                    "ţ", "t", "t", "t", "t", "nt", "rt",
707                    "ţ", "t", "t", "t", "t", "nt", "rt",
708                    "ţ", "t", "t", "t", "t", "nt", "rt",
709                    "ţ", "t", "t", "t", "t", "nt", "rt",
710                    "ţ", "t", "t", "t", "t", "nt", "rt",
711                    "ţ", "t", "t", "t", "t", "nt", "rt",
712                    "ţ", "t", "t", "t", "t", "nt", "rt",
713                    "ţ", "t", "t", "t", "t", "nt", "rt",
714                    "tŗ", "tŗ", "tŗ", "tŗ", "tŗ", "ntŗ", "rtŗ",
715                    "tŕ", "tŕ", "tŕ", "tŕ", "tŕ", "ntŕ", "rtŕ",
716                    "tŗ", "tŗ", "tŗ", "tŗ", "tŗ", "ntŗ", "rtŗ",
717                    "tŕ", "tŕ", "tŕ", "tŕ", "tŕ", "ntŕ", "rtŕ",
718
719                    "p", "p", "p", "p", "p", "np", "rp",
720                    "p", "p", "p", "p", "p", "np", "rp",
721                    "p", "p", "p", "p", "p", "np", "rp",
722                    "p", "p", "p", "p", "p", "np", "rp",
723                    "p", "p", "p", "p", "p", "np", "rp",
724                    "p", "p", "p", "p", "p", "np", "rp",
725                    "p", "p", "p", "p", "p", "np", "rp",
726                    "p", "p", "p", "p", "p", "np", "rp",
727                    "p", "p", "p", "p", "p", "np", "rp",
728                    "p", "p", "p", "p", "p", "np", "rp",
729                    "pŗ", "pŗ", "pŗ", "pŗ", "pŗ", "npŗ", "rpŗ",
730                    "pŕ", "pŕ", "pŕ", "pŕ", "pŕ", "npŕ", "rpŕ",
731                    "pļ", "pļ", "pļ", "pļ", "pļ", "npļ", "rpļ",
732                    "pĺ", "pĺ", "pĺ", "pĺ", "pĺ", "npĺ", "rpĺ",
733                    "pŗ", "pŗ", "pŗ", "pŗ", "pŗ", "npŗ", "rpŗ",
734                    "pŕ", "pŕ", "pŕ", "pŕ", "pŕ", "npŕ", "rpŕ",
735                    "p", "p", "p", "p", "p", "np", "rp",
736                    "p", "p", "p", "p", "p", "np", "rp",
737
738                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
739                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
740                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
741                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
742                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
743                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
744                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
745                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
746                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
747                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
748                    "khŗ", "khŗ", "khŗ", "khŗ", "khŗ", "nkhŗ", "rkhŗ",
749                    "khŕ", "khŕ", "khŕ", "khŕ", "khŕ", "nkhŕ", "rkhŕ",
750                    "khļ", "khļ", "khļ", "khļ", "khļ", "nkhļ", "rkhļ",
751                    "khĺ", "khĺ", "khĺ", "khĺ", "khĺ", "nkhĺ", "rkhĺ",
752
753                    "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "chŗ", "chŕ", "chļ", "chĺ",
754                    "ţh", "th", "th", "th", "th", "th", "nth", "rth",
755                    "th", "th", "th", "th", "th", "nth", "rth",
756                    "th", "th", "th", "th", "th", "nth", "rth",
757                    "th", "th", "th", "th", "th", "nth", "rth",
758                    "th", "th", "th", "th", "th", "nth", "rth",
759                    "th", "th", "th", "th", "th", "nth", "rth",
760                    "th", "th", "th", "th", "th", "nth", "rth",
761                    "th", "th", "th", "th", "th", "nth", "rth",
762                    "th", "th", "th", "th", "th", "nth", "rth",
763                    "thŗ", "thŗ", "thŗ", "thŗ", "thŗ", "nthŗ", "rthŗ",
764                    "thŕ", "thŕ", "thŕ", "thŕ", "thŕ", "nthŕ", "rthŕ",
765                    "thļ", "thļ", "thļ", "thļ", "thļ", "nthļ", "rthļ",
766                    "thĺ", "thĺ", "thĺ", "thĺ", "thĺ", "nthĺ", "rthĺ",
767
768                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
769                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
770                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
771                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
772                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
773                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
774                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
775                    "phŗ", "phŗ", "phŗ", "phŗ", "phŗ", "nphŗ", "rphŗ",
776                    "phŕ", "phŕ", "phŕ", "phŕ", "phŕ", "nphŕ", "rphŕ",
777                    "phļ", "phļ", "phļ", "phļ", "phļ", "nphļ", "rphļ",
778                    "phĺ", "phĺ", "phĺ", "phĺ", "phĺ", "nphĺ", "rphĺ",
779
780                    "g", "g", "g", "g", "g", "ng", "rg",
781                    "j", "j", "j", "j", "j", "nj", "rj",
782                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
783                    "d", "d", "d", "d", "d", "nd", "rd",
784                    "b", "b", "b", "b", "b", "nb", "rb",
785                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
786                    "jh", "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
787                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
788                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
789
790                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
791                    "h", "y", "y", "y", "y", "y", "ny", "ry",
792                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
793                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
794                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
795                    "s", "s", "s", "s", "s", "ns", "rs",
796
797                    "g", "g", "g", "g", "g", "ng", "rg",
798                    "j", "j", "j", "j", "j", "nj", "rj",
799                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
800                    "d", "d", "d", "d", "d", "nd", "rd",
801                    "b", "b", "b", "b", "b", "nb", "rb",
802                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
803                    "jh", "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
804                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
805                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
806
807                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
808                    "h", "y", "y", "y", "y", "y", "ny", "ry",
809                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
810                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
811                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
812                    "s", "s", "s", "s", "s", "ns", "rs",
813
814                    "g", "g", "g", "g", "g", "ng", "rg",
815                    "j", "j", "j", "j", "j", "nj", "rj",
816                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
817                    "d", "d", "d", "d", "d", "nd", "rd",
818                    "b", "b", "b", "b", "b", "nb", "rb",
819                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
820                    "jh", "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
821                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
822                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
823
824                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
825                    "h", "y", "y", "y", "y", "y", "ny", "ry",
826                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
827                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
828                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
829                    "s", "s", "s", "s", "s", "ns", "rs",
830
831                    "g", "g", "g", "g", "g", "ng", "rg",
832                    "j", "j", "j", "j", "j", "nj", "rj",
833                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
834                    "d", "d", "d", "d", "d", "nd", "rd",
835                    "b", "b", "b", "b", "b", "nb", "rb",
836                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
837                    "jh", "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
838                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
839                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
840
841                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
842                    "h", "y", "y", "y", "y", "y", "ny", "ry",
843                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
844                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
845                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
846                    "s", "s", "s", "s", "s", "ns", "rs",
847
848                    "g", "g", "g", "g", "g", "ng", "rg",
849                    "j", "j", "j", "j", "j", "nj", "rj",
850                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
851                    "d", "d", "d", "d", "d", "nd", "rd",
852                    "b", "b", "b", "b", "b", "nb", "rb",
853                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
854                    "jh", "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
855                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
856                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
857
858                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
859                    "h", "y", "y", "y", "y", "y", "ny", "ry",
860                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
861                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
862                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
863                    "s", "s", "s", "s", "s", "ns", "rs",
864
865                    "g", "g", "g", "g", "g", "ng", "rg",
866                    "j", "j", "j", "j", "j", "nj", "rj",
867                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
868                    "d", "d", "d", "d", "d", "nd", "rd",
869                    "b", "b", "b", "b", "b", "nb", "rb",
870                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
871                    "jh", "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
872                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
873                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
874
875                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
876                    "h", "y", "y", "y", "y", "y", "ny", "ry",
877                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
878                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
879                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
880                    "s", "s", "s", "s", "s", "ns", "rs",
881
882                    "g", "g", "g", "g", "g", "ng", "rg",
883                    "j", "j", "j", "j", "j", "nj", "rj",
884                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
885                    "d", "d", "d", "d", "d", "nd", "rd",
886                    "b", "b", "b", "b", "b", "nb", "rb",
887                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
888                    "jh", "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
889                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
890                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
891
892                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
893                    "h", "y", "y", "y", "y", "y", "ny", "ry",
894                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
895                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
896                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
897                    "s", "s", "s", "s", "s", "ns", "rs",
898
899                    "g", "g", "g", "g", "g", "ng", "rg",
900                    "j", "j", "j", "j", "j", "nj", "rj",
901                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
902                    "d", "d", "d", "d", "d", "nd", "rd",
903                    "b", "b", "b", "b", "b", "nb", "rb",
904                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
905                    "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
906                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
907                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
908
909                    "ń", "ñ", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
910                    "h", "y", "y", "y", "y", "y", "ny", "ry",
911                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
912                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
913                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
914                    "s", "s", "s", "s", "s", "ns", "rs",
915
916                    "g", "g", "g", "g", "g", "ng", "rg",
917                    "j", "j", "j", "j", "j", "nj", "rj",
918                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
919                    "d", "d", "d", "d", "d", "nd", "rd",
920                    "b", "b", "b", "b", "b", "nb", "rb",
921                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
922                    "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
923                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
924                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
925
926                    "ń", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
927                    "h", "y", "y", "y", "y", "y", "ny", "ry",
928                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
929                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
930                    "s", "s", "s", "s", "s", "ns", "rs",
931
932                    "g", "g", "g", "g", "g", "ng", "rg",
933                    "j", "j", "j", "j", "j", "nj", "rj",
934                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
935                    "d", "d", "d", "d", "d", "nd", "rd",
936                    "b", "b", "b", "b", "b", "nb", "rb",
937                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
938                    "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
939                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
940                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
941
942                    "ń", "ņ", "n", "m", "m", "m", "m", "m", "nm", "rm",
943                    "h", "y", "y", "y", "y", "y", "ny", "ry",
944                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
945                    "ş", "ş", "ş", "ş", "ş", "nş", "rş",
946                    "s", "s", "s", "s", "s", "ns", "rs",
947
948                    "g", "g", "g", "g", "g", "ng", "rg",
949                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
950                    "d", "d", "d", "d", "d", "nd", "rd",
951                    "b", "b", "b", "b", "b", "nb", "rb",
952                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
953                    "đh", "đh", "đh", "đh", "đh", "nđh", "rđh",
954                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
955                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
956                    "n", "m", "m", "m", "m", "m", "nm", "rm",
957                    "v", "v", "v", "v", "v", "nv", "rv",
958                    "s", "s", "s", "s", "s", "ns", "rs",
959
960                    "g", "g", "g", "g", "g", "ng", "rg",
961                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
962                    "d", "d", "d", "d", "d", "nd", "rd",
963                    "b", "b", "b", "b", "b", "nb", "rb",
964                    "g", "g", "g", "g", "g", "ng", "rg",
965                    "d", "d", "d", "d", "d", "nd", "rd",
966                    "b", "b", "b", "b", "b", "nb", "rb",
967                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
968                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
969                    "n", "m", "m", "m", "m", "m", "nm", "rm",
970                    "v", "v", "v", "v", "v", "nv", "rv",
971
972                    "g", "g", "g", "g", "g", "ng", "rg",
973                    "đ", "đ", "đ", "đ", "đ", "nđ", "rđ",
974                    "d", "d", "d", "d", "d", "nd", "rd",
975                    "b", "b", "b", "b", "b", "nb", "rb",
976                    "g", "g", "g", "g", "g", "ng", "rg",
977                    "d", "d", "d", "d", "d", "nd", "rd",
978                    "b", "b", "b", "b", "b", "nb", "rb",
979                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
980                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
981                    "n", "m", "m", "m", "m", "m", "nm", "rm",
982                    "v", "v", "v", "v", "v", "nv", "rv",
983            },
984            new String[]{"t", "d", "m", "r", "dh", "b", "t", "d", "m", "r", "dh", "bh", "nt", "nt", "nk", "ş"},
985            new String[]{"it", "it", "ati", "adva", "aş", "arma", "ardha", "abi", "ab", "aya"},
986            new String[]{}, new int[]{1, 2, 3, 4, 5}, new double[]{1, 2, 3, 3, 1}, 0.15, 0.75, 0.0, 0.12, null, true);
987
988
989    /**
990     * Imitation Hindi, romanized to use the Latin alphabet using accented glyphs from the IAST standard, which are not
991     * typically printable with many fonts but are more likely to seem like samples of Hindi from, say, Wikipedia.
992     * There is also HINDI_ROMANIZED, which changes the IAST standard glyphs {@code "ṛṝḷḹḍṭṅṇṣṃḥ"} to
993     * {@code "ŗŕļĺđţńņşĕĭ"}, with the nth glyph in the first string being substituted with the nth glyph in the second
994     * string. Using HINDI_ROMANIZED is recommended if you use the fonts known by SquidLib's display module.
995     * <br>
996     * Datṝo thīndoṇa, oḍītad; ḍhivīvidh beśībo ru'markiḍaibhit.
997     */
998    public static final FakeLanguageGen HINDI_IAST = new FakeLanguageGen(
999            new String[]{
1000                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
1001                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1002                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
1003                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1004                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
1005                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1006                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "ī",
1007                    "u", "u", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1008                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
1009                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1010                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
1011                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1012                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
1013                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1014                    "a", "a", "a", "a", "a", "a", "ā", "ā", "i", "i", "i", "i", "ī", "i", "i", "ī", "ī",
1015                    "u", "u", "u", "ū", "u", "ū", "u", "ū", "e", "ai", "ai", "o", "o", "o", "au",
1016                    "aṃ", "aṃ", "aṃ", "aṃ", "aṃ", "āṃ", "āṃ", "iṃ", "iṃ", "iṃ", "īṃ", "īṃ",
1017                    "uṃ", "uṃ", "ūṃ", "aiṃ", "aiṃ", "oṃ", "oṃ", "oṃ", "auṃ",
1018                    //"aḥ", "aḥ", "aḥ", "aḥ", "aḥ", "āḥ", "āḥ", "iḥ", "iḥ", "iḥ", "īḥ", "īḥ",
1019                    //"uḥ", "uḥ", "ūḥ", "aiḥ", "aiḥ", "oḥ", "oḥ", "oḥ", "auḥ",
1020            },
1021            new String[]{"a'","i'","u'", "o'", "a'","i'","u'", "o'",
1022            },
1023            new String[]{
1024                    "k", "k", "k", "k", "k", "k", "k", "k", "kṛ", "kṝ", "kḷ",
1025                    "c", "c", "c", "c", "c", "c", "cṛ", "cṝ", "cḷ",
1026                    "ṭ", "t", "t", "t", "t", "t", "t", "t", "t", "t", "tṛ", "tṝ", "tṛ", "tṝ",
1027                    "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "pṛ", "pṝ", "pḷ", "pḹ", "pṛ", "pṝ", "p", "p",
1028                    "kh", "kh", "kh", "kh", "kh", "kh", "kh", "kh", "kh", "kh", "khṛ", "khṝ", "khḷ", "khḹ",
1029                    "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "chṛ", "chṝ", "chḷ", "chḹ",
1030                    "ṭh", "th", "th", "th", "th", "th", "th", "th", "th", "th", "thṛ", "thṝ", "thḷ", "thḹ",
1031                    "ph", "ph", "ph", "ph", "ph", "ph", "ph", "phṛ", "phṝ", "phḷ", "phḹ",
1032                    "g", "j", "ḍ", "d", "b", "gh", "jh", "ḍh", "dh", "bh",
1033                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1034                    "g", "j", "ḍ", "d", "b", "gh", "jh", "ḍh", "dh", "bh",
1035                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1036                    "g", "j", "ḍ", "d", "b", "gh", "jh", "ḍh", "dh", "bh",
1037                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1038                    "g", "j", "ḍ", "d", "b", "gh", "jh", "ḍh", "dh", "bh",
1039                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1040                    "g", "j", "ḍ", "d", "b", "gh", "jh", "ḍh", "dh", "bh",
1041                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1042                    "g", "j", "ḍ", "d", "b", "gh", "jh", "ḍh", "dh", "bh",
1043                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1044                    "g", "j", "ḍ", "d", "b", "gh", "jh", "ḍh", "dh", "bh",
1045                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1046                    "g", "j", "ḍ", "d", "b", "gh", "ḍh", "dh", "bh",
1047                    "ṅ", "ñ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ś", "ṣ", "s",
1048                    "g", "j", "ḍ", "d", "b", "gh", "ḍh", "dh", "bh",
1049                    "ṅ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ṣ", "s",
1050                    "g", "j", "ḍ", "d", "b", "gh", "ḍh", "dh", "bh",
1051                    "ṅ", "ṇ", "n", "m", "h", "y", "r", "l", "v", "ṣ", "s",
1052                    "g", "ḍ", "d", "b", "gh", "ḍh", "dh", "bh", "n", "m", "v", "s",
1053                    "g", "ḍ", "d", "b", "g", "d", "b", "dh", "bh", "n", "m", "v",
1054                    "g", "ḍ", "d", "b", "g", "d", "b", "dh", "bh", "n", "m", "v",
1055            },
1056            new String[]{
1057                    "k", "k", "k", "k", "k", "nk", "rk",
1058                    "k", "k", "k", "k", "k", "nk", "rk",
1059                    "k", "k", "k", "k", "k", "nk", "rk",
1060                    "k", "k", "k", "k", "k", "nk", "rk",
1061                    "k", "k", "k", "k", "k", "nk", "rk",
1062                    "k", "k", "k", "k", "k", "nk", "rk",
1063                    "k", "k", "k", "k", "k", "nk", "rk",
1064                    "k", "k", "k", "k", "k", "nk", "rk",
1065                    "kṛ", "kṛ", "kṛ", "kṛ", "kṛ", "nkṛ", "rkṛ",
1066                    "kṝ", "kṝ", "kṝ", "kṝ", "kṝ", "nkṝ", "rkṝ",
1067                    "kḷ", "kḷ", "kḷ", "kḷ", "kḷ", "nkḷ", "rkḷ",
1068
1069                    "c", "c", "c", "c", "c", "c", "cṛ", "cṝ", "cḷ",
1070                    "ṭ", "t", "t", "t", "t", "t", "nt", "rt",
1071                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1072                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1073                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1074                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1075                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1076                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1077                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1078                    "ṭ", "t", "t", "t", "t", "nt", "rt",
1079                    "tṛ", "tṛ", "tṛ", "tṛ", "tṛ", "ntṛ", "rtṛ",
1080                    "tṝ", "tṝ", "tṝ", "tṝ", "tṝ", "ntṝ", "rtṝ",
1081                    "tṛ", "tṛ", "tṛ", "tṛ", "tṛ", "ntṛ", "rtṛ",
1082                    "tṝ", "tṝ", "tṝ", "tṝ", "tṝ", "ntṝ", "rtṝ",
1083
1084                    "p", "p", "p", "p", "p", "np", "rp",
1085                    "p", "p", "p", "p", "p", "np", "rp",
1086                    "p", "p", "p", "p", "p", "np", "rp",
1087                    "p", "p", "p", "p", "p", "np", "rp",
1088                    "p", "p", "p", "p", "p", "np", "rp",
1089                    "p", "p", "p", "p", "p", "np", "rp",
1090                    "p", "p", "p", "p", "p", "np", "rp",
1091                    "p", "p", "p", "p", "p", "np", "rp",
1092                    "p", "p", "p", "p", "p", "np", "rp",
1093                    "p", "p", "p", "p", "p", "np", "rp",
1094                    "pṛ", "pṛ", "pṛ", "pṛ", "pṛ", "npṛ", "rpṛ",
1095                    "pṝ", "pṝ", "pṝ", "pṝ", "pṝ", "npṝ", "rpṝ",
1096                    "pḷ", "pḷ", "pḷ", "pḷ", "pḷ", "npḷ", "rpḷ",
1097                    "pḹ", "pḹ", "pḹ", "pḹ", "pḹ", "npḹ", "rpḹ",
1098                    "pṛ", "pṛ", "pṛ", "pṛ", "pṛ", "npṛ", "rpṛ",
1099                    "pṝ", "pṝ", "pṝ", "pṝ", "pṝ", "npṝ", "rpṝ",
1100                    "p", "p", "p", "p", "p", "np", "rp",
1101                    "p", "p", "p", "p", "p", "np", "rp",
1102
1103                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1104                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1105                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1106                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1107                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1108                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1109                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1110                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1111                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1112                    "kh", "kh", "kh", "kh", "kh", "nkh", "rkh",
1113                    "khṛ", "khṛ", "khṛ", "khṛ", "khṛ", "nkhṛ", "rkhṛ",
1114                    "khṝ", "khṝ", "khṝ", "khṝ", "khṝ", "nkhṝ", "rkhṝ",
1115                    "khḷ", "khḷ", "khḷ", "khḷ", "khḷ", "nkhḷ", "rkhḷ",
1116                    "khḹ", "khḹ", "khḹ", "khḹ", "khḹ", "nkhḹ", "rkhḹ",
1117
1118                    "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "ch", "chṛ", "chṝ", "chḷ", "chḹ",
1119                    "ṭh", "th", "th", "th", "th", "th", "nth", "rth",
1120                    "th", "th", "th", "th", "th", "nth", "rth",
1121                    "th", "th", "th", "th", "th", "nth", "rth",
1122                    "th", "th", "th", "th", "th", "nth", "rth",
1123                    "th", "th", "th", "th", "th", "nth", "rth",
1124                    "th", "th", "th", "th", "th", "nth", "rth",
1125                    "th", "th", "th", "th", "th", "nth", "rth",
1126                    "th", "th", "th", "th", "th", "nth", "rth",
1127                    "th", "th", "th", "th", "th", "nth", "rth",
1128                    "thṛ", "thṛ", "thṛ", "thṛ", "thṛ", "nthṛ", "rthṛ",
1129                    "thṝ", "thṝ", "thṝ", "thṝ", "thṝ", "nthṝ", "rthṝ",
1130                    "thḷ", "thḷ", "thḷ", "thḷ", "thḷ", "nthḷ", "rthḷ",
1131                    "thḹ", "thḹ", "thḹ", "thḹ", "thḹ", "nthḹ", "rthḹ",
1132
1133                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
1134                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
1135                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
1136                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
1137                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
1138                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
1139                    "ph", "ph", "ph", "ph", "ph", "nph", "rph",
1140                    "phṛ", "phṛ", "phṛ", "phṛ", "phṛ", "nphṛ", "rphṛ",
1141                    "phṝ", "phṝ", "phṝ", "phṝ", "phṝ", "nphṝ", "rphṝ",
1142                    "phḷ", "phḷ", "phḷ", "phḷ", "phḷ", "nphḷ", "rphḷ",
1143                    "phḹ", "phḹ", "phḹ", "phḹ", "phḹ", "nphḹ", "rphḹ",
1144
1145                    "g", "g", "g", "g", "g", "ng", "rg",
1146                    "j", "j", "j", "j", "j", "nj", "rj",
1147                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1148                    "d", "d", "d", "d", "d", "nd", "rd",
1149                    "b", "b", "b", "b", "b", "nb", "rb",
1150                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1151                    "jh", "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1152                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1153                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1154
1155                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1156                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1157                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1158                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1159                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1160                    "s", "s", "s", "s", "s", "ns", "rs",
1161
1162                    "g", "g", "g", "g", "g", "ng", "rg",
1163                    "j", "j", "j", "j", "j", "nj", "rj",
1164                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1165                    "d", "d", "d", "d", "d", "nd", "rd",
1166                    "b", "b", "b", "b", "b", "nb", "rb",
1167                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1168                    "jh", "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1169                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1170                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1171
1172                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1173                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1174                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1175                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1176                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1177                    "s", "s", "s", "s", "s", "ns", "rs",
1178
1179                    "g", "g", "g", "g", "g", "ng", "rg",
1180                    "j", "j", "j", "j", "j", "nj", "rj",
1181                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1182                    "d", "d", "d", "d", "d", "nd", "rd",
1183                    "b", "b", "b", "b", "b", "nb", "rb",
1184                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1185                    "jh", "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1186                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1187                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1188
1189                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1190                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1191                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1192                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1193                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1194                    "s", "s", "s", "s", "s", "ns", "rs",
1195
1196                    "g", "g", "g", "g", "g", "ng", "rg",
1197                    "j", "j", "j", "j", "j", "nj", "rj",
1198                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1199                    "d", "d", "d", "d", "d", "nd", "rd",
1200                    "b", "b", "b", "b", "b", "nb", "rb",
1201                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1202                    "jh", "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1203                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1204                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1205
1206                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1207                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1208                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1209                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1210                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1211                    "s", "s", "s", "s", "s", "ns", "rs",
1212
1213                    "g", "g", "g", "g", "g", "ng", "rg",
1214                    "j", "j", "j", "j", "j", "nj", "rj",
1215                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1216                    "d", "d", "d", "d", "d", "nd", "rd",
1217                    "b", "b", "b", "b", "b", "nb", "rb",
1218                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1219                    "jh", "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1220                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1221                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1222
1223                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1224                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1225                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1226                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1227                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1228                    "s", "s", "s", "s", "s", "ns", "rs",
1229
1230                    "g", "g", "g", "g", "g", "ng", "rg",
1231                    "j", "j", "j", "j", "j", "nj", "rj",
1232                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1233                    "d", "d", "d", "d", "d", "nd", "rd",
1234                    "b", "b", "b", "b", "b", "nb", "rb",
1235                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1236                    "jh", "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1237                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1238                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1239
1240                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1241                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1242                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1243                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1244                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1245                    "s", "s", "s", "s", "s", "ns", "rs",
1246
1247                    "g", "g", "g", "g", "g", "ng", "rg",
1248                    "j", "j", "j", "j", "j", "nj", "rj",
1249                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1250                    "d", "d", "d", "d", "d", "nd", "rd",
1251                    "b", "b", "b", "b", "b", "nb", "rb",
1252                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1253                    "jh", "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1254                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1255                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1256
1257                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1258                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1259                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1260                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1261                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1262                    "s", "s", "s", "s", "s", "ns", "rs",
1263
1264                    "g", "g", "g", "g", "g", "ng", "rg",
1265                    "j", "j", "j", "j", "j", "nj", "rj",
1266                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1267                    "d", "d", "d", "d", "d", "nd", "rd",
1268                    "b", "b", "b", "b", "b", "nb", "rb",
1269                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1270                    "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1271                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1272                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1273
1274                    "ṅ", "ñ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1275                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1276                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1277                    "ś", "ś", "ś", "ś", "ś", "nś", "rś",
1278                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1279                    "s", "s", "s", "s", "s", "ns", "rs",
1280
1281                    "g", "g", "g", "g", "g", "ng", "rg",
1282                    "j", "j", "j", "j", "j", "nj", "rj",
1283                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1284                    "d", "d", "d", "d", "d", "nd", "rd",
1285                    "b", "b", "b", "b", "b", "nb", "rb",
1286                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1287                    "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1288                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1289                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1290
1291                    "ṅ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1292                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1293                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1294                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1295                    "s", "s", "s", "s", "s", "ns", "rs",
1296
1297                    "g", "g", "g", "g", "g", "ng", "rg",
1298                    "j", "j", "j", "j", "j", "nj", "rj",
1299                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1300                    "d", "d", "d", "d", "d", "nd", "rd",
1301                    "b", "b", "b", "b", "b", "nb", "rb",
1302                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1303                    "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1304                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1305                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1306
1307                    "ṅ", "ṇ", "n", "m", "m", "m", "m", "m", "nm", "rm",
1308                    "h", "y", "y", "y", "y", "y", "ny", "ry",
1309                    "r", "l", "v", "v", "v", "v", "v", "nv", "rv",
1310                    "ṣ", "ṣ", "ṣ", "ṣ", "ṣ", "nṣ", "rṣ",
1311                    "s", "s", "s", "s", "s", "ns", "rs",
1312
1313                    "g", "g", "g", "g", "g", "ng", "rg",
1314                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1315                    "d", "d", "d", "d", "d", "nd", "rd",
1316                    "b", "b", "b", "b", "b", "nb", "rb",
1317                    "gh", "gh", "gh", "gh", "gh", "ngh", "rgh",
1318                    "ḍh", "ḍh", "ḍh", "ḍh", "ḍh", "nḍh", "rḍh",
1319                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1320                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1321                    "n", "m", "m", "m", "m", "m", "nm", "rm",
1322                    "v", "v", "v", "v", "v", "nv", "rv",
1323                    "s", "s", "s", "s", "s", "ns", "rs",
1324
1325                    "g", "g", "g", "g", "g", "ng", "rg",
1326                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1327                    "d", "d", "d", "d", "d", "nd", "rd",
1328                    "b", "b", "b", "b", "b", "nb", "rb",
1329                    "g", "g", "g", "g", "g", "ng", "rg",
1330                    "d", "d", "d", "d", "d", "nd", "rd",
1331                    "b", "b", "b", "b", "b", "nb", "rb",
1332                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1333                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1334                    "n", "m", "m", "m", "m", "m", "nm", "rm",
1335                    "v", "v", "v", "v", "v", "nv", "rv",
1336
1337                    "g", "g", "g", "g", "g", "ng", "rg",
1338                    "ḍ", "ḍ", "ḍ", "ḍ", "ḍ", "nḍ", "rḍ",
1339                    "d", "d", "d", "d", "d", "nd", "rd",
1340                    "b", "b", "b", "b", "b", "nb", "rb",
1341                    "g", "g", "g", "g", "g", "ng", "rg",
1342                    "d", "d", "d", "d", "d", "nd", "rd",
1343                    "b", "b", "b", "b", "b", "nb", "rb",
1344                    "dh", "dh", "dh", "dh", "dh", "ndh", "rdh",
1345                    "bh", "bh", "bh", "bh", "bh", "nbh", "rbh",
1346                    "n", "m", "m", "m", "m", "m", "nm", "rm",
1347                    "v", "v", "v", "v", "v", "nv", "rv",
1348            },
1349            new String[]{"t", "d", "m", "r", "dh", "b", "t", "d", "m", "r", "dh", "bh", "nt", "nt", "nk", "ṣ"},
1350            new String[]{"it", "it", "ati", "adva", "aṣ", "arma", "ardha", "abi", "ab", "aya"},
1351            new String[]{}, new int[]{1, 2, 3, 4, 5}, new double[]{1, 2, 3, 3, 1}, 0.15, 0.75, 0.0, 0.12, null, true);
1352
1353    /**
1354     * Imitation Arabic, using mostly the Latin alphabet but with some Greek letters for tough transliteration topics.
1355     * It's hard to think of a more different (widely-spoken) language to romanize than Arabic. Written Arabic does not
1356     * ordinarily use vowels (the writing system is called an abjad, in contrast to an alphabet), and it has more than a
1357     * few sounds that are very different from those in English. This version, because of limited support in fonts and
1358     * the need for separate words to be distinguishable with regular expressions, uses Greek letters in place of hamzah
1359     * and 'ayin (the second of the two isn't entered correctly here since it wouldn't be printed with most fonts; you
1360     * can see https://en.wikipedia.org/wiki/Ayin for more details). Hamzah is represented with Greek delta, 'δ', and
1361     * 'ayin is represented with Greek xi, 'ξ', both picked because of similarity to some forms of the glyphs in the
1362     * Arabic script. Many other letters are mapped to alternate representations because the common romanizations use
1363     * rare glyphs that SquidLib's fonts in the display module can't support. Using the conventions this FakeLanguageGen
1364     * does for writing the Arabic glyph names, these are: ţāδ becomes ţ, ĥāδ becomes ĥ, ħāδ becomes ħ, đāl becomes
1365     * đ, šīn becomes š, şād becomes ş, ďād becomes ď, ťāδ becomes ť, żāδ becomes ż, gain becomes g, wāw becomes ū, and
1366     * yāδ becomes ī.
1367     * <br>
1368     * Please try to be culturally-sensitive about how you use this generator. Classical Arabic (the variant that
1369     * normally marks vowels explicitly and is used to write the Qur'an) has deep religious significance in Islam, and
1370     * if you machine-generate text that (probably) isn't valid Arabic, but claim that it is real, or that it has
1371     * meaning when it actually doesn't, that would be an improper usage of what this generator is meant to do. In a
1372     * fantasy setting, you can easily confirm that the language is fictional and any overlap is coincidental; an
1373     * example of imitation Arabic in use is the Dungeons and Dragons setting, Al-Qadim, which according to one account
1374     * sounds similar to a word in real Arabic (that does not mean anything like what the designer was aiming for). In a
1375     * historical setting, FakeLanguageGen is probably "too fake" to make a viable imitation for any language, and may
1376     * just sound insulting if portrayed as realistic. You may want to mix ARABIC_ROMANIZED with a very different kind
1377     * of language, like GREEK_ROMANIZED or RUSSIAN_AUTHENTIC, to emphasize that this is not a real-world language.
1378     * <br>
1379     * Iramzā qāşi, qīqa banji, rūşiďīq ifateh!
1380     */
1381    public static final FakeLanguageGen ARABIC_ROMANIZED = new FakeLanguageGen(
1382            new String[]{"a", "a", "a", "a", "a", "a", "ā", "ā", "ā", "ai", "au",
1383                    "a", "i", "u", "a", "i", "u",
1384                    "i", "i", "i", "i", "i", "ī", "ī", "ī",
1385                    "u", "u", "u", "ū", "ū",
1386            },
1387            new String[]{},
1388            new String[]{"δ", "b", "t", "ţ", "j", "ĥ", "ħ", "d", "đ", "r", "z", "s", "š", "ş", "ď", "ť",
1389                    "ż", "ξ", "g", "f", "q", "k", "l", "m", "n", "h", "w",
1390                    "q", "k", "q", "k", "b", "d", "f", "l", "z", "ż", "h", "h", "ĥ", "j", "s", "š", "ş", "r",
1391                    "q", "k", "q", "k", "f", "l", "z", "h", "h", "j", "s", "r",
1392                    "q", "k", "f", "l", "z", "h", "h", "j", "s", "r",
1393                    "al-", "al-", "ibn-",
1394            },
1395            new String[]{
1396                    "kk", "kk", "kk", "kk", "kk", "dd", "dd", "dd", "dd",
1397                    "nj", "mj", "bj", "mj", "bj", "mj", "bj", "dj", "ďj", "đj",
1398                    "nz", "nż", "mz", "mż", "rz", "rż", "bz", "dz", "tz",
1399                    "s-h", "š-h", "ş-h", "tw", "bn", "fq", "hz", "hl", "ĥm",
1400                    "lb", "lz", "lj", "lf", "ll", "lk", "lq", "lg", "ln"
1401            },
1402            new String[]{
1403                    "δ", "b", "t", "ţ", "j", "ĥ", "ħ", "d", "đ", "r", "z", "s", "š", "ş", "ď", "ť",
1404                    "ż", "ξ", "g", "f", "q", "k", "l", "m", "n", "h", "w",
1405                    "k", "q", "k", "b", "d", "f", "l", "z", "ż", "h", "h", "ĥ", "j", "s", "š", "ş", "r",
1406                    "k", "q", "k", "f", "l", "z", "h", "h", "j", "s", "r",
1407                    "k", "f", "l", "z", "h", "h", "j", "s", "r",
1408                    "b", "t", "ţ", "j", "ĥ", "ħ", "d", "đ", "r", "z", "s", "š", "ş", "ď", "ť",
1409                    "ż", "g", "f", "q", "k", "l", "m", "n", "h", "w",
1410                    "k", "q", "k", "b", "d", "f", "l", "z", "ż", "h", "h", "ĥ", "j", "s", "š", "ş", "r",
1411                    "k", "q", "k", "f", "l", "z", "h", "h", "j", "s", "r",
1412                    "k", "f", "l", "z", "h", "h", "j", "s", "r",
1413            },
1414            new String[]{"āδ", "āδ", "ari", "ari", "aīd", "ūq", "arīd", "adih", "ateh", "adeš", "amīt", "it",
1415                    "īt", "aĥmen","aĥmed", "ani", "abīb", "īb", "ūni", "īz", "aqarī", "adīq",
1416            },
1417            new String[]{}, new int[]{1, 2, 3, 4}, new double[]{6, 5, 5, 1}, 0.55, 0.65, 0.0, 0.15, arabicSanityChecks, true);
1418
1419    /**
1420     * A mix of four different languages, using only ASCII characters, that is meant for generating single words for
1421     * creature or place names in fantasy settings.
1422     * <br>
1423     * Adeni, Sainane, Caneros, Sune, Alade, Tidifi, Muni, Gito, Lixoi, Bovi...
1424     */
1425    public static final FakeLanguageGen FANTASY_NAME = GREEK_ROMANIZED.mix(
1426            RUSSIAN_ROMANIZED.mix(
1427                    FRENCH.removeAccents().mix(
1428                            JAPANESE_ROMANIZED, 0.5), 0.85), 0.925);
1429    /**
1430     * A mix of four different languages with some accented characters added onto an ASCII base, that can be good for
1431     * generating single words for creature or place names in fantasy settings that should have a "fancy" feeling from
1432     * having unnecessary accents added primarily for visual reasons.
1433     * <br>
1434     * Askieno, Blarcīnũn, Mēmida, Zizhounkô, Blęrinaf, Zemĭ, Mónazôr, Renerstă, Uskus, Toufounôr...
1435     */
1436    public static final FakeLanguageGen FANCY_FANTASY_NAME = FANTASY_NAME.addAccents(0.47, 0.07);
1437
1438            /**
1439             * Zero-arg constructor for a FakeLanguageGen; produces a FakeLanguageGen equivalent to FakeLanguageGen.ENGLISH .
1440             */
1441    public FakeLanguageGen() {
1442        this(
1443                new String[]{
1444                        "a", "a", "a", "a", "o", "o", "o", "e", "e", "e", "e", "e", "i", "i", "i", "i", "u",
1445                        "a", "a", "a", "a", "o", "o", "o", "e", "e", "e", "e", "e", "i", "i", "i", "i", "u",
1446                        "a", "a", "a", "o", "o", "e", "e", "e", "i", "i", "i", "u",
1447                        "a", "a", "a", "o", "o", "e", "e", "e", "i", "i", "i", "u",
1448                        "au", "ai", "ai", "ou", "ea", "ie", "io", "ei",
1449                },
1450                new String[]{"u", "u", "oa", "oo", "oo", "oo", "ee", "ee", "ee", "ee",},
1451                new String[]{
1452                        "b", "bl", "br", "c", "cl", "cr", "ch", "d", "dr", "f", "fl", "fr", "g", "gl", "gr", "h", "j", "k", "l", "m", "n",
1453                        "p", "pl", "pr", "qu", "r", "s", "sh", "sk", "st", "sp", "sl", "sm", "sn", "t", "tr", "th", "thr", "v", "w", "y", "z",
1454                        "b", "bl", "br", "c", "cl", "cr", "ch", "d", "dr", "f", "fl", "fr", "g", "gr", "h", "j", "k", "l", "m", "n",
1455                        "p", "pl", "pr", "r", "s", "sh", "st", "sp", "sl", "t", "tr", "th", "w", "y",
1456                        "b", "br", "c", "ch", "d", "dr", "f", "g", "h", "j", "l", "m", "n",
1457                        "p", "r", "s", "sh", "st", "sl", "t", "tr", "th",
1458                        "b", "d", "f", "g", "h", "l", "m", "n",
1459                        "p", "r", "s", "sh", "t", "th",
1460                        "b", "d", "f", "g", "h", "l", "m", "n",
1461                        "p", "r", "s", "sh", "t", "th",
1462                        "r", "s", "t", "l", "n",
1463                        "str", "spr", "spl", "wr", "kn", "kn", "gn",
1464                },
1465                new String[]{"x", "cst", "bs", "ff", "lg", "g", "gs",
1466                        "ll", "ltr", "mb", "mn", "mm", "ng", "ng", "ngl", "nt", "ns", "nn", "ps", "mbl", "mpr",
1467                        "pp", "ppl", "ppr", "rr", "rr", "rr", "rl", "rtn", "ngr", "ss", "sc", "rst", "tt", "tt", "ts", "ltr", "zz"
1468                },
1469                new String[]{"b", "rb", "bb", "c", "rc", "ld", "d", "ds", "dd", "f", "ff", "lf", "rf", "rg", "gs", "ch", "lch", "rch", "tch",
1470                        "ck", "ck", "lk", "rk", "l", "ll", "lm", "m", "rm", "mp", "n", "nk", "nch", "nd", "ng", "ng", "nt", "ns", "lp", "rp",
1471                        "p", "r", "rn", "rts", "s", "s", "s", "s", "ss", "ss", "st", "ls", "t", "t", "ts", "w", "wn", "x", "ly", "lly", "z",
1472                        "b", "c", "d", "f", "g", "k", "l", "m", "n", "p", "r", "s", "t", "w",
1473                },
1474                new String[]{"ate", "ite", "ism", "ist", "er", "er", "er", "ed", "ed", "ed", "es", "es", "ied", "y", "y", "y", "y",
1475                        "ate", "ite", "ism", "ist", "er", "er", "er", "ed", "ed", "ed", "es", "es", "ied", "y", "y", "y", "y",
1476                        "ate", "ite", "ism", "ist", "er", "er", "er", "ed", "ed", "ed", "es", "es", "ied", "y", "y", "y", "y",
1477                        "ay", "ay", "ey", "oy", "ay", "ay", "ey", "oy",
1478                        "ough", "aught", "ant", "ont", "oe", "ance", "ell", "eal", "oa", "urt", "ut", "iom", "ion", "ion", "ision", "ation", "ation", "ition",
1479                        "ough", "aught", "ant", "ont", "oe", "ance", "ell", "eal", "oa", "urt", "ut", "iom", "ion", "ion", "ision", "ation", "ation", "ition",
1480                        "ily", "ily", "ily", "adly", "owly", "oorly", "ardly", "iedly",
1481                },
1482                new String[]{}, new int[]{1, 2, 3, 4}, new double[]{7, 8, 4, 1}, 0.22, 0.1, 0.0, 0.25, englishSanityChecks, true);
1483    }
1484
1485    /**
1486     * This is a very complicated constructor! Maybe look at the calls to this to initialize static members of this
1487     * class, LOVECRAFT and GREEK_ROMANIZED.
1488     *
1489     * @param openingVowels        String array where each element is a vowel or group of vowels that may appear at the start
1490     *                             of a word or in the middle; elements may be repeated to make them more common
1491     * @param midVowels            String array where each element is a vowel or group of vowels that may appear in the
1492     *                             middle of the word; all openingVowels are automatically copied into this internally.
1493     *                             Elements may be repeated to make them more common
1494     * @param openingConsonants    String array where each element is a consonant or consonant cluster that can appear
1495     *                             at the start of a word; elements may be repeated to make them more common
1496     * @param midConsonants        String array where each element is a consonant or consonant cluster than can appear
1497     *                             between vowels; all closingConsonants are automatically copied into this internally.
1498     *                             Elements may be repeated to make them more common
1499     * @param closingConsonants    String array where each element is a consonant or consonant cluster than can appear
1500     *                             at the end of a word; elements may be repeated to make them more common
1501     * @param closingSyllables     String array where each element is a syllable starting with a vowel and ending in
1502     *                             whatever the word should end in; elements may be repeated to make them more common
1503     * @param vowelSplitters       String array where each element is a mark that goes between vowels, so if "-" is in this,
1504     *                             then "a-a" may be possible; elements may be repeated to make them more common
1505     * @param syllableLengths      int array where each element is a possible number of syllables a word can use; closely
1506     *                             tied to syllableFrequencies
1507     * @param syllableFrequencies  double array where each element corresponds to an element in syllableLengths and
1508     *                             represents how often each syllable count should appear relative to other counts; there
1509     *                             is no need to restrict the numbers to add up to any other number
1510     * @param vowelStartFrequency  a double between 0.0 and 1.0 that determines how often words start with vowels;
1511     *                             higher numbers yield more words starting with vowels
1512     * @param vowelEndFrequency    a double between 0.0 and 1.0 that determines how often words end with vowels; higher
1513     *                             numbers yield more words ending in vowels
1514     * @param vowelSplitFrequency  a double between 0.0 and 1.0 that, if vowelSplitters is not empty, determines how
1515     *                             often a vowel will be split into two vowels separated by one of those splitters
1516     * @param syllableEndFrequency a double between 0.0 and 1.0 that determines how often an element of
1517     *                             closingSyllables is used instead of ending normally
1518     */
1519    public FakeLanguageGen(String[] openingVowels, String[] midVowels, String[] openingConsonants,
1520                    String[] midConsonants, String[] closingConsonants, String[] closingSyllables, String[] vowelSplitters,
1521                    int[] syllableLengths, double[] syllableFrequencies, double vowelStartFrequency,
1522                    double vowelEndFrequency, double vowelSplitFrequency, double syllableEndFrequency) {
1523        this(openingVowels, midVowels, openingConsonants, midConsonants, closingConsonants, closingSyllables,
1524                vowelSplitters, syllableLengths, syllableFrequencies, vowelStartFrequency, vowelEndFrequency,
1525                vowelSplitFrequency, syllableEndFrequency, englishSanityChecks, true);
1526    }
1527
1528    /**
1529     * This is a very complicated constructor! Maybe look at the calls to this to initialize static members of this
1530     * class, LOVECRAFT and GREEK_ROMANIZED.
1531     *
1532     * @param openingVowels        String array where each element is a vowel or group of vowels that may appear at the start
1533     *                             of a word or in the middle; elements may be repeated to make them more common
1534     * @param midVowels            String array where each element is a vowel or group of vowels that may appear in the
1535     *                             middle of the word; all openingVowels are automatically copied into this internally.
1536     *                             Elements may be repeated to make them more common
1537     * @param openingConsonants    String array where each element is a consonant or consonant cluster that can appear
1538     *                             at the start of a word; elements may be repeated to make them more common
1539     * @param midConsonants        String array where each element is a consonant or consonant cluster than can appear
1540     *                             between vowels; all closingConsonants are automatically copied into this internally.
1541     *                             Elements may be repeated to make them more common
1542     * @param closingConsonants    String array where each element is a consonant or consonant cluster than can appear
1543     *                             at the end of a word; elements may be repeated to make them more common
1544     * @param closingSyllables     String array where each element is a syllable starting with a vowel and ending in
1545     *                             whatever the word should end in; elements may be repeated to make them more common
1546     * @param vowelSplitters       String array where each element is a mark that goes between vowels, so if "-" is in this,
1547     *                             then "a-a" may be possible; elements may be repeated to make them more common
1548     * @param syllableLengths      int array where each element is a possible number of syllables a word can use; closely
1549     *                             tied to syllableFrequencies
1550     * @param syllableFrequencies  double array where each element corresponds to an element in syllableLengths and
1551     *                             represents how often each syllable count should appear relative to other counts; there
1552     *                             is no need to restrict the numbers to add up to any other number
1553     * @param vowelStartFrequency  a double between 0.0 and 1.0 that determines how often words start with vowels;
1554     *                             higher numbers yield more words starting with vowels
1555     * @param vowelEndFrequency    a double between 0.0 and 1.0 that determines how often words end with vowels; higher
1556     *                             numbers yield more words ending in vowels
1557     * @param vowelSplitFrequency  a double between 0.0 and 1.0 that, if vowelSplitters is not empty, determines how
1558     *                             often a vowel will be split into two vowels separated by one of those splitters
1559     * @param syllableEndFrequency a double between 0.0 and 1.0 that determines how often an element of
1560     *                             closingSyllables is used instead of ending normally
1561     * @param sane true to perform sanity checks for pronounce-able sounds to most English speakers, replacing many
1562     *             words that are impossible to say; slows down generation slightly, irrelevant for non-Latin alphabets
1563     * @param clean true to perform vulgarity/obscenity checks on the word, replacing it if it is too close to a
1564     *              common English vulgarity, obscenity, or slur/epithet; slows down generation slightly
1565     */
1566    public FakeLanguageGen(String[] openingVowels, String[] midVowels, String[] openingConsonants,
1567                    String[] midConsonants, String[] closingConsonants, String[] closingSyllables, String[] vowelSplitters,
1568                    int[] syllableLengths, double[] syllableFrequencies, double vowelStartFrequency,
1569                    double vowelEndFrequency, double vowelSplitFrequency, double syllableEndFrequency,
1570                    Pattern[] sane, boolean clean) {
1571        this.openingVowels = openingVowels;
1572        this.midVowels = new String[openingVowels.length + midVowels.length];
1573        System.arraycopy(midVowels, 0, this.midVowels, 0, midVowels.length);
1574        System.arraycopy(openingVowels, 0, this.midVowels, midVowels.length, openingVowels.length);
1575        this.openingConsonants = openingConsonants;
1576        this.midConsonants = new String[midConsonants.length + closingConsonants.length];
1577        System.arraycopy(midConsonants, 0, this.midConsonants, 0, midConsonants.length);
1578        System.arraycopy(closingConsonants, 0, this.midConsonants, midConsonants.length, closingConsonants.length);
1579        this.closingConsonants = closingConsonants;
1580        this.vowelSplitters = vowelSplitters;
1581        this.closingSyllables = closingSyllables;
1582
1583        this.syllableFrequencies = new LinkedHashMap<>(syllableLengths.length);
1584        for (int i = 0; i < syllableLengths.length && i < syllableFrequencies.length; i++) {
1585            this.syllableFrequencies.put(syllableLengths[i], syllableFrequencies[i]);
1586        }
1587        for (Double freq : this.syllableFrequencies.values()) {
1588            totalSyllableFrequency += freq;
1589        }
1590        if (vowelStartFrequency > 1.0)
1591            this.vowelStartFrequency = 1.0 / vowelStartFrequency;
1592        else
1593            this.vowelStartFrequency = vowelStartFrequency;
1594        if (vowelEndFrequency > 1.0)
1595            this.vowelEndFrequency = 1.0 / vowelEndFrequency;
1596        else
1597            this.vowelEndFrequency = vowelEndFrequency;
1598        if (vowelSplitters.length == 0)
1599            this.vowelSplitFrequency = 0.0;
1600        else if (vowelSplitFrequency > 1.0)
1601            this.vowelSplitFrequency = 1.0 / vowelSplitFrequency;
1602        else
1603            this.vowelSplitFrequency = vowelSplitFrequency;
1604        if (closingSyllables.length == 0)
1605            this.syllableEndFrequency = 0.0;
1606        else if (syllableEndFrequency > 1.0)
1607            this.syllableEndFrequency = 1.0 / syllableEndFrequency;
1608        else
1609            this.syllableEndFrequency = syllableEndFrequency;
1610        this.clean = clean;
1611        sanityChecks = sane;
1612        modifiers = new ArrayList<>(16);
1613    }
1614
1615    private FakeLanguageGen(String[] openingVowels, String[] midVowels, String[] openingConsonants,
1616                            String[] midConsonants, String[] closingConsonants, String[] closingSyllables,
1617                            String[] vowelSplitters, LinkedHashMap<Integer, Double> syllableFrequencies,
1618                            double vowelStartFrequency, double vowelEndFrequency, double vowelSplitFrequency,
1619                            double syllableEndFrequency, Pattern[] sanityChecks, boolean clean,
1620                            List<Modifier> modifiers) {
1621        this.openingVowels = copyStrings(openingVowels);
1622        this.midVowels = copyStrings(midVowels);
1623        this.openingConsonants = copyStrings(openingConsonants);
1624        this.midConsonants = copyStrings(midConsonants);
1625        this.closingConsonants = copyStrings(closingConsonants);
1626        this.closingSyllables = copyStrings(closingSyllables);
1627        this.vowelSplitters = copyStrings(vowelSplitters);
1628        this.syllableFrequencies = new LinkedHashMap<>(syllableFrequencies);
1629        this.vowelStartFrequency = vowelStartFrequency;
1630        this.vowelEndFrequency = vowelEndFrequency;
1631        this.vowelSplitFrequency = vowelSplitFrequency;
1632        this.syllableEndFrequency = syllableEndFrequency;
1633        for (Double freq : this.syllableFrequencies.values()) {
1634            totalSyllableFrequency += freq;
1635        }
1636        if (sanityChecks == null)
1637            this.sanityChecks = null;
1638        else {
1639            this.sanityChecks = new Pattern[sanityChecks.length];
1640            System.arraycopy(sanityChecks, 0, this.sanityChecks, 0, sanityChecks.length);
1641        }
1642        this.clean = clean;
1643        this.modifiers = new ArrayList<>(modifiers);
1644    }
1645
1646    protected boolean checkAll(CharSequence testing, Pattern[] checks)
1647    {
1648        CharSequence fixed = removeAccents(testing);
1649        for (int i = 0; i < checks.length; i++) {
1650            if(checks[i].matcher(fixed).find())
1651                return false;
1652        }
1653        return true;
1654    }
1655    /**
1656     * Generate a word from this FakeLanguageGen, using and changing the current seed.
1657     * @param capitalize true if the word should start with a capital letter, false otherwise
1658     * @return a word in the fake language as a String
1659     */
1660    public String word(boolean capitalize)
1661    {
1662        return word(srng, capitalize);
1663    }
1664    /**
1665     * Generate a word from this FakeLanguageGen using the specified RNG.
1666     *
1667     * @param rng        the RNG to use for the randomized string building
1668     * @param capitalize true if the word should start with a capital letter, false otherwise
1669     * @return a word in the fake language as a String
1670     */
1671    public String word(RNG rng, boolean capitalize) {
1672        while(true) {
1673            StringBuilder sb = new StringBuilder(20);
1674            double syllableChance = rng.nextDouble(totalSyllableFrequency);
1675            int syllables = 1, i = 0;
1676            for (Map.Entry<Integer, Double> kv : syllableFrequencies.entrySet()) {
1677                if (syllableChance < kv.getValue()) {
1678                    syllables = kv.getKey();
1679                    break;
1680                } else
1681                    syllableChance -= kv.getValue();
1682            }
1683            if (rng.nextDouble() < vowelStartFrequency) {
1684                sb.append(rng.getRandomElement(openingVowels));
1685                sb.append(rng.getRandomElement(midConsonants));
1686                i++;
1687            } else {
1688                sb.append(rng.getRandomElement(openingConsonants));
1689            }
1690
1691            for (; i < syllables - 1; i++) {
1692                sb.append(rng.getRandomElement(midVowels));
1693                if (rng.nextDouble() < vowelSplitFrequency) {
1694                    sb.append(rng.getRandomElement(vowelSplitters));
1695                    sb.append(rng.getRandomElement(midVowels));
1696                }
1697                sb.append(rng.getRandomElement(midConsonants));
1698            }
1699            if (rng.nextDouble() < syllableEndFrequency) {
1700                String close = rng.getRandomElement(closingSyllables);
1701                if((close.contains("@1") && syllables == 1) ||
1702                        (close.contains("@2") && syllables == 2) ||
1703                        (close.contains("@3") && syllables == 3) )
1704                {
1705                    sb.append(close.replaceAll("@\\d", sb.toString()));
1706                }
1707                else if(!close.contains("@"))
1708                    sb.append(close);
1709                else if (rng.nextDouble() < vowelEndFrequency) {
1710                    sb.append(rng.getRandomElement(midVowels));
1711                    if (rng.nextDouble() < vowelSplitFrequency) {
1712                        sb.append(rng.getRandomElement(vowelSplitters));
1713                        sb.append(rng.getRandomElement(midVowels));
1714                    }
1715                }
1716            } else {
1717                sb.append(rng.getRandomElement(midVowels));
1718                if (rng.nextDouble() < vowelSplitFrequency) {
1719                    sb.append(rng.getRandomElement(vowelSplitters));
1720                    sb.append(rng.getRandomElement(midVowels));
1721                }
1722                if (rng.nextDouble() >= vowelEndFrequency) {
1723                    sb.append(rng.getRandomElement(closingConsonants));
1724                    if (rng.nextDouble() < syllableEndFrequency) {
1725                        String close = rng.getRandomElement(closingSyllables);
1726                        if((close.contains("@1") && syllables == 1) ||
1727                                (close.contains("@2") && syllables == 2) ||
1728                                (close.contains("@3") && syllables == 3) )
1729                        {
1730                            sb.append(close.replaceAll("@\\d", sb.toString()));
1731                        }
1732                        else if(!close.contains("@"))
1733                            sb.append(close);
1734                    }
1735                }
1736            }
1737            if(sanityChecks != null && !checkAll(sb, sanityChecks))
1738                continue;
1739
1740            for(Modifier mod : modifiers)
1741            {
1742                sb = mod.modify(rng, sb);
1743            }
1744
1745            if (capitalize)
1746                sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
1747
1748            if(clean && !checkAll(sb, vulgarChecks))
1749                continue;
1750            return sb.toString();
1751        }
1752    }/**
1753     * Generate a word from this FakeLanguageGen using the specified RNG.
1754     *
1755     * @param rng        the RNG to use for the randomized string building
1756     * @param capitalize true if the word should start with a capital letter, false otherwise
1757     * @return a word in the fake language as a String
1758     */
1759    public String word(RNG rng, boolean capitalize, int approxSyllables) {
1760        if(approxSyllables <= 0)
1761        {
1762            String finished = rng.getRandomElement(openingVowels);
1763            if(capitalize) return finished.substring(0, 1).toUpperCase();
1764            else return finished.substring(0, 1);
1765        }
1766        while(true) {
1767            StringBuilder sb = new StringBuilder(20);
1768            int i = 0;
1769            if (rng.nextDouble() < vowelStartFrequency) {
1770                sb.append(rng.getRandomElement(openingVowels));
1771                sb.append(rng.getRandomElement(midConsonants));
1772                i++;
1773            } else {
1774                sb.append(rng.getRandomElement(openingConsonants));
1775            }
1776
1777            for (; i < approxSyllables - 1; i++) {
1778                sb.append(rng.getRandomElement(midVowels));
1779                if (rng.nextDouble() < vowelSplitFrequency) {
1780                    sb.append(rng.getRandomElement(vowelSplitters));
1781                    sb.append(rng.getRandomElement(midVowels));
1782                }
1783                sb.append(rng.getRandomElement(midConsonants));
1784            }
1785            if (rng.nextDouble() < syllableEndFrequency) {
1786                String close = rng.getRandomElement(closingSyllables);
1787                if((close.contains("@1") && approxSyllables == 1) || (close.contains("@2") && approxSyllables == 2) ||
1788                        (close.contains("@3") && approxSyllables == 3) )
1789                {
1790                    sb.append(close.replaceAll("@\\d", sb.toString()));
1791                }
1792                else if(!close.contains("@"))
1793                    sb.append(close);
1794                else if (rng.nextDouble() < vowelEndFrequency) {
1795                    sb.append(rng.getRandomElement(midVowels));
1796                    if (rng.nextDouble() < vowelSplitFrequency) {
1797                        sb.append(rng.getRandomElement(vowelSplitters));
1798                        sb.append(rng.getRandomElement(midVowels));
1799                    }
1800                }
1801            } else {
1802                sb.append(rng.getRandomElement(midVowels));
1803                if (rng.nextDouble() < vowelSplitFrequency) {
1804                    sb.append(rng.getRandomElement(vowelSplitters));
1805                    sb.append(rng.getRandomElement(midVowels));
1806                }
1807                if (rng.nextDouble() >= vowelEndFrequency) {
1808                    sb.append(rng.getRandomElement(closingConsonants));
1809                    if (rng.nextDouble() < syllableEndFrequency) {
1810                        String close = rng.getRandomElement(closingSyllables);
1811                        if((close.contains("@1") && approxSyllables == 1) ||
1812                                (close.contains("@2") && approxSyllables == 2) ||
1813                                (close.contains("@3") && approxSyllables == 3) )
1814                        {
1815                            close = close.replaceAll("@\\d", sb.toString());
1816                            sb.append(close);
1817                        }
1818                        else if(!close.contains("@"))
1819                            sb.append(close);
1820                    }
1821                }
1822            }
1823
1824            if(sanityChecks != null && !checkAll(sb, sanityChecks))
1825                continue;
1826
1827            for(Modifier mod : modifiers)
1828            {
1829                sb = mod.modify(rng, sb);
1830            }
1831
1832            if (capitalize)
1833                sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
1834
1835            if(clean && !checkAll(sb, vulgarChecks))
1836                continue;
1837            return sb.toString();
1838        }
1839    }
1840
1841    /**
1842     * Generate a sentence from this FakeLanguageGen, using and changing the current seed.
1843     * @param minWords                an int for the minimum number of words in a sentence; should be at least 1
1844     * @param maxWords                an int for the maximum number of words in a sentence; should be at least equal to minWords
1845     * @return a sentence in the gibberish language as a String
1846     */
1847    public String sentence(int minWords, int maxWords)
1848    {
1849        return sentence(srng, minWords, maxWords, new String[]{",", ",", ",", ";"},
1850                new String[]{".", ".", ".", "!", "?", "..."}, 0.2);
1851    }
1852
1853    /**
1854     * Generate a sentence from this FakeLanguageGen, using and changing the current seed.
1855     *
1856     * @param minWords                an int for the minimum number of words in a sentence; should be at least 1
1857     * @param maxWords                an int for the maximum number of words in a sentence; should be at least equal to minWords
1858     * @param midPunctuation          a String array where each element is a comma, semicolon, or the like that goes before a
1859     *                                space in the middle of a sentence
1860     * @param endPunctuation          a String array where each element is a period, question mark, or the like that goes at
1861     *                                the very end of a sentence
1862     * @param midPunctuationFrequency a double between 0.0 and 1.0 that determines how often Strings from
1863     *                                midPunctuation should be inserted before spaces
1864     * @return a sentence in the gibberish language as a String
1865     */
1866    public String sentence(int minWords, int maxWords, String[] midPunctuation, String[] endPunctuation,
1867                           double midPunctuationFrequency)
1868    {
1869        return sentence(srng, minWords, maxWords, midPunctuation, endPunctuation, midPunctuationFrequency);
1870    }
1871    /**
1872     * Generate a sentence from this FakeLanguageGen using the specific RNG.
1873     *
1874     * @param rng                     the RNG to use for the randomized string building
1875     * @param minWords                an int for the minimum number of words in a sentence; should be at least 1
1876     * @param maxWords                an int for the maximum number of words in a sentence; should be at least equal to minWords
1877     * @param midPunctuation          a String array where each element is a comma, semicolon, or the like that goes before a
1878     *                                space in the middle of a sentence
1879     * @param endPunctuation          a String array where each element is a period, question mark, or the like that goes at
1880     *                                the very end of a sentence
1881     * @param midPunctuationFrequency a double between 0.0 and 1.0 that determines how often Strings from
1882     *                                midPunctuation should be inserted before spaces
1883     * @return a sentence in the gibberish language as a String
1884     */
1885    public String sentence(RNG rng, int minWords, int maxWords, String[] midPunctuation, String[] endPunctuation,
1886                           double midPunctuationFrequency) {
1887        if (minWords < 1)
1888            minWords = 1;
1889        if (minWords > maxWords)
1890            maxWords = minWords;
1891        if (midPunctuationFrequency > 1.0) {
1892            midPunctuationFrequency = 1.0 / midPunctuationFrequency;
1893        }
1894        StringBuilder sb = new StringBuilder(12 * maxWords);
1895        sb.append(word(rng, true));
1896        for (int i = 1; i < minWords; i++) {
1897            if (rng.nextDouble() < midPunctuationFrequency) {
1898                sb.append(rng.getRandomElement(midPunctuation));
1899            }
1900            sb.append(' ');
1901            sb.append(word(rng, false));
1902        }
1903        for (int i = minWords; i < maxWords && rng.nextInt(2 * maxWords) > i; i++) {
1904            if (rng.nextDouble() < midPunctuationFrequency) {
1905                sb.append(rng.getRandomElement(midPunctuation));
1906            }
1907            sb.append(' ');
1908            sb.append(word(rng, false));
1909        }
1910        sb.append(rng.getRandomElement(endPunctuation));
1911        return sb.toString();
1912    }
1913    /**
1914     * Generate a sentence from this FakeLanguageGen that fits in the given length limit..
1915     *
1916     * @param minWords                an int for the minimum number of words in a sentence; should be at least 1
1917     * @param maxWords                an int for the maximum number of words in a sentence; should be at least equal to minWords
1918     * @param midPunctuation          a String array where each element is a comma, semicolon, or the like that goes before a
1919     *                                space in the middle of a sentence
1920     * @param endPunctuation          a String array where each element is a period, question mark, or the like that goes at
1921     *                                the very end of a sentence
1922     * @param midPunctuationFrequency a double between 0.0 and 1.0 that determines how often Strings from
1923     *                                midPunctuation should be inserted before spaces
1924     * @param maxChars the longest string length this can produce; should be at least {@code 6 * minWords}
1925     * @return a sentence in the gibberish language as a String
1926     */
1927    public String sentence(int minWords, int maxWords, String[] midPunctuation, String[] endPunctuation,
1928                           double midPunctuationFrequency, int maxChars) {
1929        return sentence(srng, minWords, maxWords, midPunctuation, endPunctuation, midPunctuationFrequency, maxChars);
1930    }
1931    /**
1932     * Generate a sentence from this FakeLanguageGen using the specific RNG that fits in the given length limit.
1933     *
1934     * @param rng                     the RNG to use for the randomized string building
1935     * @param minWords                an int for the minimum number of words in a sentence; should be at least 1
1936     * @param maxWords                an int for the maximum number of words in a sentence; should be at least equal to minWords
1937     * @param midPunctuation          a String array where each element is a comma, semicolon, or the like that goes before a
1938     *                                space in the middle of a sentence
1939     * @param endPunctuation          a String array where each element is a period, question mark, or the like that goes at
1940     *                                the very end of a sentence
1941     * @param midPunctuationFrequency a double between 0.0 and 1.0 that determines how often Strings from
1942     *                                midPunctuation should be inserted before spaces
1943     * @param maxChars the longest string length this can produce; should be at least {@code 6 * minWords}
1944     * @return a sentence in the gibberish language as a String
1945     */
1946    public String sentence(RNG rng, int minWords, int maxWords, String[] midPunctuation, String[] endPunctuation,
1947                           double midPunctuationFrequency, int maxChars) {
1948        if (minWords < 1)
1949            minWords = 1;
1950        if (minWords > maxWords)
1951            maxWords = minWords;
1952        if (midPunctuationFrequency > 1.0) {
1953            midPunctuationFrequency = 1.0 / midPunctuationFrequency;
1954        }
1955        if(maxChars < 4)
1956            return "!";
1957        if(maxChars <= 5 * minWords) {
1958            minWords = 1;
1959            maxWords = 1;
1960        }
1961        int frustration = 0;
1962        StringBuilder sb = new StringBuilder(maxChars);
1963        String next = word(rng, true);
1964        while (next.length() >= maxChars - 1 && frustration < 50) {
1965            next = word(rng, true);
1966            frustration++;
1967        }
1968        if(frustration >= 50) return "!";
1969        sb.append(next);
1970        for (int i = 1; i < minWords && frustration < 50 && sb.length() < maxChars - 7; i++) {
1971            if (rng.nextDouble() < midPunctuationFrequency && sb.length() < maxChars - 3) {
1972                sb.append(rng.getRandomElement(midPunctuation));
1973            }
1974            next = word(rng, false);
1975            while (sb.length() + next.length() >= maxChars - 2 && frustration < 50) {
1976                next = word(rng, false);
1977                frustration++;
1978            }
1979            if(frustration >= 50) break;
1980            sb.append(' ');
1981            sb.append(next);
1982        }
1983        for (int i = minWords; i < maxWords && sb.length() < maxChars - 7 && rng.nextInt(2 * maxWords) > i && frustration < 50; i++) {
1984            if (rng.nextDouble() < midPunctuationFrequency && sb.length() < maxChars - 3) {
1985                sb.append(rng.getRandomElement(midPunctuation));
1986            }
1987            next = word(rng, false);
1988            while (sb.length() + next.length() >= maxChars - 2 && frustration < 50) {
1989                next = word(rng, false);
1990                frustration++;
1991            }
1992            if(frustration >= 50) break;
1993            sb.append(' ');
1994            sb.append(next);
1995        }
1996        next = rng.getRandomElement(endPunctuation);
1997        if(sb.length() + next.length() >= maxChars)
1998            next = ".";
1999        sb.append(next);
2000        if(sb.length() > maxChars)
2001            return "!";
2002        return sb.toString();
2003    }
2004
2005    protected String[] merge1000(RNG rng, String[] me, String[] other, double otherInfluence) {
2006        if(other.length <= 0 && me.length <= 0)
2007            return new String[]{};
2008        String[] ret = new String[1000];
2009        int otherCount = (int) (1000 * otherInfluence);
2010        int idx = 0;
2011        if (other.length > 0) {
2012            String[] tmp = new String[other.length];
2013            rng.shuffle(other, tmp);
2014            for (idx = 0; idx < otherCount; idx++) {
2015                ret[idx] = tmp[idx % tmp.length];
2016            }
2017        }
2018        if (me.length > 0) {
2019            String[] tmp = new String[me.length];
2020            rng.shuffle(me, tmp);
2021            for (; idx < 1000; idx++) {
2022                ret[idx] = tmp[idx % tmp.length];
2023            }
2024        }
2025        else
2026        {
2027            for (; idx < 1000; idx++) {
2028                ret[idx] = other[idx % other.length];
2029            }
2030        }
2031        return ret;
2032    }
2033
2034
2035    protected String[] accentVowels(RNG rng, String[] me, double influence) {
2036        String[] ret = new String[1000];
2037        int otherCount = (int) (1000 * influence);
2038        int idx = 0;
2039        Matcher matcher;
2040        if (me.length > 0) {
2041            String[] tmp = new String[me.length];
2042            rng.shuffle(me, tmp);
2043            for (idx = 0; idx < otherCount; idx++) {
2044                ret[idx] = tmp[idx % tmp.length]
2045                        .replace('a', accentedVowels[0][rng.nextInt(accentedVowels[0].length)])
2046                        .replace('e', accentedVowels[1][rng.nextInt(accentedVowels[1].length)])
2047                        .replace('i', accentedVowels[2][rng.nextInt(accentedVowels[2].length)])
2048                        .replace('o', accentedVowels[3][rng.nextInt(accentedVowels[3].length)])
2049                        .replace('u', accentedVowels[4][rng.nextInt(accentedVowels[4].length)]);
2050                matcher = repeats.matcher(ret[idx]);
2051                if (matcher.find()) {
2052                    ret[idx] = matcher.replaceAll(rng.getRandomElement(me));
2053                }
2054            }
2055            for (; idx < 1000; idx++) {
2056                ret[idx] = tmp[idx % tmp.length];
2057            }
2058        } else
2059            return new String[]{};
2060        return ret;
2061    }
2062
2063    protected String[] accentConsonants(RNG rng, String[] me, double influence) {
2064        String[] ret = new String[1000];
2065        int otherCount = (int) (1000 * influence);
2066        int idx = 0;
2067        Matcher matcher;
2068        if (me.length > 0) {
2069            String[] tmp = new String[me.length];
2070            rng.shuffle(me, tmp);
2071            for (idx = 0; idx < otherCount; idx++) {
2072                ret[idx] = tmp[idx % tmp.length]
2073                        //0
2074                        .replace('c', accentedConsonants[1][rng.nextInt(accentedConsonants[1].length)])
2075                        .replace('d', accentedConsonants[2][rng.nextInt(accentedConsonants[2].length)])
2076                        .replace('f', accentedConsonants[3][rng.nextInt(accentedConsonants[3].length)])
2077                        .replace('g', accentedConsonants[4][rng.nextInt(accentedConsonants[4].length)])
2078                        .replace('h', accentedConsonants[5][rng.nextInt(accentedConsonants[5].length)])
2079                        .replace('j', accentedConsonants[6][rng.nextInt(accentedConsonants[6].length)])
2080                        .replace('k', accentedConsonants[7][rng.nextInt(accentedConsonants[7].length)])
2081                        .replace('l', accentedConsonants[8][rng.nextInt(accentedConsonants[8].length)])
2082                        //9
2083                        .replace('n', accentedConsonants[10][rng.nextInt(accentedConsonants[10].length)])
2084                        //11
2085                        //12
2086                        .replace('r', accentedConsonants[13][rng.nextInt(accentedConsonants[13].length)])
2087                        .replace('s', accentedConsonants[14][rng.nextInt(accentedConsonants[14].length)])
2088                        .replace('t', accentedConsonants[15][rng.nextInt(accentedConsonants[15].length)])
2089                        //16
2090                        .replace('w', accentedConsonants[17][rng.nextInt(accentedConsonants[17].length)])
2091                        //18
2092                        .replace('y', accentedConsonants[19][rng.nextInt(accentedConsonants[19].length)])
2093                        .replace('z', accentedConsonants[20][rng.nextInt(accentedConsonants[20].length)]);
2094
2095                matcher = repeats.matcher(ret[idx]);
2096                if (matcher.find()) {
2097                    ret[idx] = matcher.replaceAll(rng.getRandomElement(me));
2098                }
2099            }
2100            for (; idx < 1000; idx++) {
2101                ret[idx] = tmp[idx % tmp.length];
2102            }
2103        } else
2104            return new String[]{};
2105        return ret;
2106    }
2107
2108    protected String[] accentBoth(RNG rng, String[] me, double vowelInfluence, double consonantInfluence) {
2109        String[] ret = new String[1000];
2110        int idx = 0;
2111        Matcher matcher;
2112        if (me.length > 0) {
2113            String[] tmp = new String[me.length];
2114            rng.shuffle(me, tmp);
2115            for (idx = 0; idx < 1000; idx++) {
2116                boolean subVowel = rng.nextDouble() < vowelInfluence, subCon = rng.nextDouble() < consonantInfluence;
2117                if (subVowel && subCon) {
2118                    ret[idx] = tmp[idx % tmp.length]
2119                            .replace('a', accentedVowels[0][rng.nextInt(accentedVowels[0].length)])
2120                            .replace('e', accentedVowels[1][rng.nextInt(accentedVowels[1].length)])
2121                            .replace('i', accentedVowels[2][rng.nextInt(accentedVowels[2].length)])
2122                            .replace('o', accentedVowels[3][rng.nextInt(accentedVowels[3].length)])
2123                            .replace('u', accentedVowels[4][rng.nextInt(accentedVowels[4].length)])
2124
2125                            //0
2126                            .replace('c', accentedConsonants[1][rng.nextInt(accentedConsonants[1].length)])
2127                            .replace('d', accentedConsonants[2][rng.nextInt(accentedConsonants[2].length)])
2128                            .replace('f', accentedConsonants[3][rng.nextInt(accentedConsonants[3].length)])
2129                            .replace('g', accentedConsonants[4][rng.nextInt(accentedConsonants[4].length)])
2130                            .replace('h', accentedConsonants[5][rng.nextInt(accentedConsonants[5].length)])
2131                            .replace('j', accentedConsonants[6][rng.nextInt(accentedConsonants[6].length)])
2132                            .replace('k', accentedConsonants[7][rng.nextInt(accentedConsonants[7].length)])
2133                            .replace('l', accentedConsonants[8][rng.nextInt(accentedConsonants[8].length)])
2134                            //9
2135                            .replace('n', accentedConsonants[10][rng.nextInt(accentedConsonants[10].length)])
2136                            //11
2137                            //12
2138                            .replace('r', accentedConsonants[13][rng.nextInt(accentedConsonants[13].length)])
2139                            .replace('s', accentedConsonants[14][rng.nextInt(accentedConsonants[14].length)])
2140                            .replace('t', accentedConsonants[15][rng.nextInt(accentedConsonants[15].length)])
2141                            //16
2142                            .replace('w', accentedConsonants[17][rng.nextInt(accentedConsonants[17].length)])
2143                            //18
2144                            .replace('y', accentedConsonants[19][rng.nextInt(accentedConsonants[19].length)])
2145                            .replace('z', accentedConsonants[20][rng.nextInt(accentedConsonants[20].length)]);
2146
2147                    matcher = repeats.matcher(ret[idx]);
2148                    if (matcher.find()) {
2149                        ret[idx] = matcher.replaceAll(rng.getRandomElement(me));
2150                    }
2151                } else if (subVowel) {
2152                    ret[idx] = tmp[idx % tmp.length]
2153                            .replace('a', accentedVowels[0][rng.nextInt(accentedVowels[0].length)])
2154                            .replace('e', accentedVowels[1][rng.nextInt(accentedVowels[1].length)])
2155                            .replace('i', accentedVowels[2][rng.nextInt(accentedVowels[2].length)])
2156                            .replace('o', accentedVowels[3][rng.nextInt(accentedVowels[3].length)])
2157                            .replace('u', accentedVowels[4][rng.nextInt(accentedVowels[4].length)]);
2158
2159                    matcher = repeats.matcher(ret[idx]);
2160                    if (matcher.find()) {
2161                        ret[idx] = matcher.replaceAll(rng.getRandomElement(me));
2162                    }
2163                } else if (subCon) {
2164                    ret[idx] = tmp[idx % tmp.length]
2165                            //0
2166                            .replace('c', accentedConsonants[1][rng.nextInt(accentedConsonants[1].length)])
2167                            .replace('d', accentedConsonants[2][rng.nextInt(accentedConsonants[2].length)])
2168                            .replace('f', accentedConsonants[3][rng.nextInt(accentedConsonants[3].length)])
2169                            .replace('g', accentedConsonants[4][rng.nextInt(accentedConsonants[4].length)])
2170                            .replace('h', accentedConsonants[5][rng.nextInt(accentedConsonants[5].length)])
2171                            .replace('j', accentedConsonants[6][rng.nextInt(accentedConsonants[6].length)])
2172                            .replace('k', accentedConsonants[7][rng.nextInt(accentedConsonants[7].length)])
2173                            .replace('l', accentedConsonants[8][rng.nextInt(accentedConsonants[8].length)])
2174                            //9
2175                            .replace('n', accentedConsonants[10][rng.nextInt(accentedConsonants[10].length)])
2176                            //11
2177                            //12
2178                            .replace('r', accentedConsonants[13][rng.nextInt(accentedConsonants[13].length)])
2179                            .replace('s', accentedConsonants[14][rng.nextInt(accentedConsonants[14].length)])
2180                            .replace('t', accentedConsonants[15][rng.nextInt(accentedConsonants[15].length)])
2181                            //16
2182                            .replace('w', accentedConsonants[17][rng.nextInt(accentedConsonants[17].length)])
2183                            //18
2184                            .replace('y', accentedConsonants[19][rng.nextInt(accentedConsonants[19].length)])
2185                            .replace('z', accentedConsonants[20][rng.nextInt(accentedConsonants[20].length)]);
2186
2187                    matcher = repeats.matcher(ret[idx]);
2188                    if (matcher.find()) {
2189                        ret[idx] = matcher.replaceAll(rng.getRandomElement(me));
2190                    }
2191                } else ret[idx] = tmp[idx % tmp.length];
2192
2193            }
2194        } else
2195            return new String[]{};
2196        return ret;
2197    }
2198
2199    public FakeLanguageGen mix(FakeLanguageGen other, double otherInfluence) {
2200        otherInfluence = Math.max(0.0, Math.min(otherInfluence, 1.0));
2201        double myInfluence = 1.0 - otherInfluence;
2202
2203        RNG rng = new RNG((hashCode() & 0xffffffffL) | ((other.hashCode() & 0xffffffffL) << 32)
2204                ^ Double.doubleToLongBits(otherInfluence));
2205
2206        String[] ov = merge1000(rng, openingVowels, other.openingVowels, otherInfluence),
2207                mv = merge1000(rng, midVowels, other.midVowels, otherInfluence),
2208                oc = merge1000(rng, openingConsonants, other.openingConsonants, otherInfluence *
2209                        Math.max(0.0, Math.min(1.0, (1.0 - other.vowelStartFrequency + vowelStartFrequency)))),
2210                mc = merge1000(rng, midConsonants, other.midConsonants, otherInfluence),
2211                cc = merge1000(rng, closingConsonants, other.closingConsonants, otherInfluence *
2212                        Math.max(0.0, Math.min(1.0, (1.0 - other.vowelEndFrequency + vowelEndFrequency)))),
2213                cs = merge1000(rng, closingSyllables, other.closingSyllables, otherInfluence *
2214                        Math.max(0.0, Math.min(1.0, (other.syllableEndFrequency - syllableEndFrequency)))),
2215                splitters = merge1000(rng, vowelSplitters, other.vowelSplitters, otherInfluence);
2216
2217        LinkedHashMap<Integer, Double> freqs = new LinkedHashMap<>(syllableFrequencies);
2218        for (Map.Entry<Integer, Double> kv : other.syllableFrequencies.entrySet()) {
2219            if (freqs.containsKey(kv.getKey()))
2220                freqs.put(kv.getKey(), kv.getValue() + freqs.get(kv.getKey()));
2221            else
2222                freqs.put(kv.getKey(), kv.getValue());
2223        }
2224        List<Modifier> mods = new ArrayList<>((int)(Math.ceil(modifiers.size() * myInfluence) +
2225                Math.ceil(other.modifiers.size() * otherInfluence)));
2226        mods.addAll(rng.randomPortion(modifiers, (int)Math.ceil(modifiers.size() * myInfluence)));
2227        mods.addAll(rng.randomPortion(other.modifiers, (int)Math.ceil(other.modifiers.size() * otherInfluence)));
2228        FakeLanguageGen finished = new FakeLanguageGen(ov, mv, oc, mc, cc, cs, splitters, freqs,
2229                vowelStartFrequency * myInfluence + other.vowelStartFrequency * otherInfluence,
2230                vowelEndFrequency * myInfluence + other.vowelEndFrequency * otherInfluence,
2231                vowelSplitFrequency * myInfluence + other.vowelSplitFrequency * otherInfluence,
2232                syllableEndFrequency * myInfluence + other.syllableEndFrequency * otherInfluence,
2233                (sanityChecks == null) ? other.sanityChecks : sanityChecks, true, mods);
2234        return finished;
2235    }
2236
2237    public FakeLanguageGen addAccents(double vowelInfluence, double consonantInfluence) {
2238        vowelInfluence = Math.max(0.0, Math.min(vowelInfluence, 1.0));
2239        consonantInfluence = Math.max(0.0, Math.min(consonantInfluence, 1.0));
2240
2241        RNG rng = new RNG((hashCode() & 0xffffffffL) ^
2242                ((Double.doubleToLongBits(vowelInfluence) & 0xffffffffL) | (Double.doubleToLongBits(consonantInfluence) << 32)));
2243        String[] ov = accentVowels(rng, openingVowels, vowelInfluence),
2244                mv = accentVowels(rng, midVowels, vowelInfluence),
2245                oc = accentConsonants(rng, openingConsonants, consonantInfluence),
2246                mc = accentConsonants(rng, midConsonants, consonantInfluence),
2247                cc = accentConsonants(rng, closingConsonants, consonantInfluence),
2248                cs = accentBoth(rng, closingSyllables, vowelInfluence, consonantInfluence);
2249
2250
2251        FakeLanguageGen finished = new FakeLanguageGen(ov, mv, oc, mc, cc, cs, vowelSplitters, syllableFrequencies,
2252                vowelStartFrequency,
2253                vowelEndFrequency,
2254                vowelSplitFrequency,
2255                syllableEndFrequency, sanityChecks, clean, modifiers);
2256        return finished;
2257    }
2258    static String[] copyStrings(String[] start)
2259    {
2260        String[] next = new String[start.length];
2261        System.arraycopy(start, 0, next, 0, start.length);
2262        return next;
2263    }
2264    public FakeLanguageGen removeAccents() {
2265
2266        String[] ov = copyStrings(openingVowels),
2267                mv = copyStrings(midVowels),
2268                oc = copyStrings(openingConsonants),
2269                mc = copyStrings(midConsonants),
2270                cc = copyStrings(closingConsonants),
2271                cs = copyStrings(closingSyllables);
2272        for (int i = 0; i < ov.length; i++) {
2273            ov[i] = removeAccents(openingVowels[i]).toString();
2274        }
2275        for (int i = 0; i < mv.length; i++) {
2276            mv[i] = removeAccents(midVowels[i]).toString();
2277        }
2278        for (int i = 0; i < oc.length; i++) {
2279            oc[i] = removeAccents(openingConsonants[i]).toString();
2280        }
2281        for (int i = 0; i < mc.length; i++) {
2282            mc[i] = removeAccents(midConsonants[i]).toString();
2283        }
2284        for (int i = 0; i < cc.length; i++) {
2285            cc[i] = removeAccents(closingConsonants[i]).toString();
2286        }
2287        for (int i = 0; i < cs.length; i++) {
2288            cs[i] = removeAccents(closingSyllables[i]).toString();
2289        }
2290
2291        return new FakeLanguageGen(ov, mv, oc, mc, cc, cs, vowelSplitters, syllableFrequencies,
2292                vowelStartFrequency,
2293                vowelEndFrequency,
2294                vowelSplitFrequency,
2295                syllableEndFrequency, sanityChecks, clean, modifiers);
2296    }
2297
2298    /**
2299     * Adds the specified Modifier objects from a Collection to a copy of this FakeLanguageGen and returns it.
2300     * You can obtain a Modifier with the static constants in the FakeLanguageGen.Modifier nested class, the
2301     * FakeLanguageGen.modifier() method, or Modifier's constructor.
2302     * @param mods an array or vararg of Modifier objects
2303     * @return a copy of this with the Modifiers added
2304     */
2305    public FakeLanguageGen addModifiers(Collection<Modifier> mods)
2306    {
2307        FakeLanguageGen next = copy();
2308        next.modifiers.addAll(mods);
2309        return next;
2310    }
2311
2312    /**
2313     * Adds the specified Modifier objects to a copy of this FakeLanguageGen and returns it.
2314     * You can obtain a Modifier with the static constants in the FakeLanguageGen.Modifier nested class, the
2315     * FakeLanguageGen.modifier() method, or Modifier's constructor.
2316     * @param mods an array or vararg of Modifier objects
2317     * @return a copy of this with the Modifiers added
2318     */
2319    public FakeLanguageGen addModifiers(Modifier... mods)
2320    {
2321        FakeLanguageGen next = copy();
2322        Collections.addAll(next.modifiers, mods);
2323        return next;
2324    }
2325
2326    /**
2327     * Creates a copy of this FakeLanguageGen with no modifiers.
2328     * @return a copy of this FakeLanguageGen with modifiers removed.
2329     */
2330    public FakeLanguageGen removeModifiers()
2331    {
2332        FakeLanguageGen next = copy();
2333        next.modifiers.clear();
2334        return next;
2335    }
2336
2337    public static Modifier modifier(String pattern, String replacement)
2338    {
2339        return new Modifier(pattern, replacement);
2340    }
2341    public static Modifier modifier(String pattern, String replacement, double chance)
2342    {
2343        return new Modifier(pattern, replacement, chance);
2344    }
2345
2346    @Override
2347    public boolean equals(Object o) {
2348        if (this == o) return true;
2349        if (o == null || getClass() != o.getClass()) return false;
2350
2351        FakeLanguageGen that = (FakeLanguageGen) o;
2352
2353        if (clean != that.clean) return false;
2354        if (Double.compare(that.totalSyllableFrequency, totalSyllableFrequency) != 0) return false;
2355        if (Double.compare(that.vowelStartFrequency, vowelStartFrequency) != 0) return false;
2356        if (Double.compare(that.vowelEndFrequency, vowelEndFrequency) != 0) return false;
2357        if (Double.compare(that.vowelSplitFrequency, vowelSplitFrequency) != 0) return false;
2358        if (Double.compare(that.syllableEndFrequency, syllableEndFrequency) != 0) return false;
2359        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2360        if (!Arrays.equals(openingVowels, that.openingVowels)) return false;
2361        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2362        if (!Arrays.equals(midVowels, that.midVowels)) return false;
2363        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2364        if (!Arrays.equals(openingConsonants, that.openingConsonants)) return false;
2365        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2366        if (!Arrays.equals(midConsonants, that.midConsonants)) return false;
2367        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2368        if (!Arrays.equals(closingConsonants, that.closingConsonants)) return false;
2369        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2370        if (!Arrays.equals(vowelSplitters, that.vowelSplitters)) return false;
2371        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2372        if (!Arrays.equals(closingSyllables, that.closingSyllables)) return false;
2373        if (!syllableFrequencies.equals(that.syllableFrequencies)) return false;
2374        // Probably incorrect - comparing Object[] arrays with Arrays.equals
2375        if (!Arrays.equals(sanityChecks, that.sanityChecks)) return false;
2376        return modifiers != null ? modifiers.equals(that.modifiers) : that.modifiers == null;
2377    }
2378
2379    @Override
2380    public int hashCode() {
2381        int result;
2382        long temp;
2383        result = CrossHash.hash(openingVowels);
2384        result = 31 * result + CrossHash.hash(midVowels);
2385        result = 31 * result + CrossHash.hash(openingConsonants);
2386        result = 31 * result + CrossHash.hash(midConsonants);
2387        result = 31 * result + CrossHash.hash(closingConsonants);
2388        result = 31 * result + CrossHash.hash(vowelSplitters);
2389        result = 31 * result + CrossHash.hash(closingSyllables);
2390        result = 31 * result + (clean ? 1 : 0);
2391        result = 31 * result + syllableFrequencies.hashCode();
2392        temp = Double.doubleToLongBits(totalSyllableFrequency);
2393        result = 31 * result + (int) (temp ^ (temp >>> 32));
2394        temp = Double.doubleToLongBits(vowelStartFrequency);
2395        result = 31 * result + (int) (temp ^ (temp >>> 32));
2396        temp = Double.doubleToLongBits(vowelEndFrequency);
2397        result = 31 * result + (int) (temp ^ (temp >>> 32));
2398        temp = Double.doubleToLongBits(vowelSplitFrequency);
2399        result = 31 * result + (int) (temp ^ (temp >>> 32));
2400        temp = Double.doubleToLongBits(syllableEndFrequency);
2401        result = 31 * result + (int) (temp ^ (temp >>> 32));
2402        result = 31 * result + (sanityChecks != null ? sanityChecks.length + 1 : 0);
2403        result = 31 * result + (modifiers != null ? modifiers.hashCode() : 0);
2404        return result;
2405    }
2406
2407    @Override
2408    public String toString() {
2409        return "FakeLanguageGen{" +
2410                "openingVowels=" + Arrays.toString(openingVowels) +
2411                ", midVowels=" + Arrays.toString(midVowels) +
2412                ", openingConsonants=" + Arrays.toString(openingConsonants) +
2413                ", midConsonants=" + Arrays.toString(midConsonants) +
2414                ", closingConsonants=" + Arrays.toString(closingConsonants) +
2415                ", vowelSplitters=" + Arrays.toString(vowelSplitters) +
2416                ", closingSyllables=" + Arrays.toString(closingSyllables) +
2417                ", clean=" + clean +
2418                ", syllableFrequencies=" + syllableFrequencies +
2419                ", totalSyllableFrequency=" + totalSyllableFrequency +
2420                ", vowelStartFrequency=" + vowelStartFrequency +
2421                ", vowelEndFrequency=" + vowelEndFrequency +
2422                ", vowelSplitFrequency=" + vowelSplitFrequency +
2423                ", syllableEndFrequency=" + syllableEndFrequency +
2424                ", sanityChecks=" + Arrays.toString(sanityChecks) +
2425                ", modifiers=" + modifiers +
2426                '}';
2427    }
2428
2429    public FakeLanguageGen copy()
2430    {
2431        return new FakeLanguageGen(openingVowels, midVowels, openingConsonants, midConsonants,
2432                closingConsonants, closingSyllables, vowelSplitters, syllableFrequencies, vowelStartFrequency,
2433                vowelEndFrequency, vowelSplitFrequency, syllableEndFrequency, sanityChecks, clean, modifiers);
2434    }
2435
2436    public static class Modifier implements Serializable
2437    {
2438        private static final long serialVersionUID = 1734863678490422371L;
2439        public final Alteration[] alterations;
2440        public Modifier()
2441        {
2442            this("[tţťțṭ]?[sśŝşšș]+h?", "th");
2443        }
2444        public Modifier(String pattern, String replacement)
2445        {
2446            alterations = new Alteration[]{new Alteration(pattern, replacement)};
2447        }
2448
2449        public Modifier(String pattern, String replacement, double chance)
2450        {
2451            alterations = new Alteration[]{new Alteration(pattern, replacement, chance)};
2452        }
2453
2454        public Modifier(Alteration... alts)
2455        {
2456            alterations = (alts == null) ? new Alteration[0] : alts;
2457        }
2458        public StringBuilder modify(RNG rng, StringBuilder sb)
2459        {
2460            Matcher m;
2461            Replacer.StringBuilderBuffer tb, working = Replacer.wrap(sb);
2462            String tmp;
2463            boolean found;
2464            for(Alteration alt : alterations) {
2465                tmp = working.toString();
2466                tb = Replacer.wrap(new StringBuilder(tmp.length()));
2467                m = alt.replacer.getPattern().matcher(tmp);
2468
2469                found = false;
2470                while (true) {
2471                    if (rng.nextDouble() < alt.chance) {
2472                        if(!Replacer.replaceStep(m, alt.replacer.getSubstitution(), tb))
2473                            break;
2474                        found = true;
2475                    } else {
2476                        if(!m.find())
2477                            break;
2478                        found = true;
2479                        m.getGroup(MatchResult.PREFIX, tb);
2480                        m.getGroup(MatchResult.MATCH, tb);
2481                        m.setTarget(m, MatchResult.SUFFIX);
2482                    }
2483                }
2484                if (found) {
2485                    m.getGroup(MatchResult.TARGET, tb);
2486                    working = tb;
2487                }
2488            }
2489            return working.toStringBuilder();
2490        }
2491        /**
2492         * For a character who always pronounces 's', 'ss', and 'sh' as 'th'.
2493         */
2494        public static final Modifier LISP = new Modifier("[tţťțṭ]?[sśŝşšș]+h?", "th");
2495
2496        /**
2497         * For a character who always lengthens 's' and 'z' sounds not starting a word.
2498         */
2499        public static final Modifier HISS = new Modifier("(.)([sśŝşšșzźżž])+", "$1$2$2$2");
2500
2501        /**
2502         * For a character who has a 20% chance to repeat a starting consonant or vowel.
2503         */
2504        public static final Modifier STUTTER = new Modifier(
2505                new Alteration("^([^aàáâãäåæāăąǻǽeèéêëēĕėęěiìíîïĩīĭįıoòóôõöøōŏőœǿuùúûüũūŭůűųyýÿŷỳαοειυаеёийъыэюяоу]+)", "$1-$1", 0.2),
2506                new Alteration("^([aàáâãäåæāăąǻǽeèéêëēĕėęěiìíîïĩīĭįıoòóôõöøōŏőœǿuùúûüũūŭůűųαοειυаеёийъыэюяоу]+)", "$1-$1", 0.2));
2507
2508        /**
2509         * For a language that has a 40% chance to repeat a single Latin vowel (a, e, o, or a variant on one of them
2510         * like å or ö, but not merged letters like æ and œ).
2511         */
2512        public static final Modifier DOUBLE_VOWELS = new Modifier(
2513                "([^aàáâãäåæāăąǻǽeèéêëēĕėęěiìíîïĩīĭįıoòóôõöøōŏőœǿuùúûüũūŭůűųyýÿŷỳ]|^)"
2514                + "([aàáâãäåāăąǻeèéêëēĕėęěòóôõöøōŏőǿ])"
2515                + "([^aàáâãäåæāăąǻǽeèéêëēĕėęěiìíîïĩīĭįıoòóôõöøōŏőœǿuùúûüũūŭůűųyýÿŷỳ]|$)", "$1$2$2$3", 0.4);
2516
2517
2518        /**
2519         * For a language that has a 50% chance to repeat a single consonant.
2520         */
2521        public static final Modifier DOUBLE_CONSONANTS = new Modifier("([aàáâãäåæāăąǻǽeèéêëēĕėęěiìíîïĩīĭįıoòóôõöøōŏőœǿuùúûüũūŭůűųyýÿŷỳαοειυаеёийъыэюяоу])" +
2522                "([^aàáâãäåæāăąǻǽeèéêëēĕėęěiìíîïĩīĭįıoòóôõöøōŏőœǿuùúûüũūŭůűųyýÿŷỳαοειυаеёийъыэюяоуqwhjx])" +
2523                "([aàáâãäåæāăąǻǽeèéêëēĕėęěiìíîïĩīĭįıoòóôõöøōŏőœǿuùúûüũūŭůűųyýÿŷỳαοειυаеёийъыэюяоу]|$)", "$1$2$2$3", 0.5);
2524
2525        /**
2526         * For a language that never repeats the same letter twice in a row.
2527         */
2528        public static final Modifier NO_DOUBLES = new Modifier("(.)\\1", "$1");
2529
2530        /**
2531         * Creates a Modifier that will replace the nth char in initial with the nth char in change. Expects initial and
2532         * change to be the same length, but will use the lesser length if they are not equal-length. Because of the
2533         * state of the text at the time modifiers are run, only lower-case letters need to be searched for.
2534         * @param initial a String containing lower-case letters or other symbols to be swapped out of a text
2535         * @param change a String containing characters that will replace occurrences of characters in initial
2536         * @return a Modifier that can be added to a FakeLanguageGen with its addModifiers() method
2537         */
2538        public static Modifier replacementTable(String initial, String change)
2539        {
2540            Alteration[] alts = new Alteration[Math.min(initial.length(), change.length())];
2541            for (int i = 0; i < alts.length; i++) {
2542                //literal string syntax; avoids sensitive escaping issues and also doesn't need a character class,
2543                // which is slightly slower and has some odd escaping cases.
2544                alts[i] = new Alteration("\\Q" + initial.charAt(i), change.substring(i, i+1));
2545            }
2546            return new Modifier(alts);
2547        }
2548
2549        public static final Modifier SIMPLIFY_ARABIC = new Modifier(
2550                new Alteration("ţ", "th"),
2551                new Alteration("ĥ", "kh"),
2552                new Alteration("ħ", "khr"),
2553                new Alteration("đ", "dh"),
2554                new Alteration("ď", "dt"),
2555                new Alteration("š", "sh"),
2556                new Alteration("ş", "shw"),
2557                new Alteration("ť", "ch"),
2558                new Alteration("ż", "zh"),
2559                new Alteration("ξ", "ql"),
2560                new Alteration("δ", "qh"),
2561                new Alteration("ġ", "gh"),
2562                new Alteration("ā", "aa"),
2563                new Alteration("ū", "uu"),
2564                new Alteration("ī", "ii"));
2565
2566        @Override
2567        public boolean equals(Object o) {
2568            if (this == o) return true;
2569            if (o == null || getClass() != o.getClass()) return false;
2570
2571            Modifier modifier = (Modifier) o;
2572
2573            // Probably incorrect - comparing Object[] arrays with Arrays.equals
2574            return Arrays.equals(alterations, modifier.alterations);
2575        }
2576
2577        @Override
2578        public int hashCode() {
2579            return Arrays.hashCode(alterations);
2580        }
2581
2582        @Override
2583        public String toString() {
2584            return "Modifier{" +
2585                    "alterations=" + Arrays.toString(alterations) +
2586                    '}';
2587        }
2588    }
2589
2590    public static class Alteration implements Serializable
2591    {
2592        private static final long serialVersionUID = -2138854697837563188L;
2593        public Replacer replacer;
2594        public double chance;
2595        public Alteration()
2596        {
2597            this("[tţťțṭ]?[sśŝşšș]+h?", "th");
2598        }
2599        public Alteration(String pattern, String replacement)
2600        {
2601            replacer = Pattern.compile(pattern).replacer(replacement);
2602            chance = 1.0;
2603        }
2604        public Alteration(String pattern, String replacement, double chance)
2605        {
2606            replacer = Pattern.compile(pattern).replacer(replacement);
2607            this.chance = chance;
2608        }
2609
2610        @Override
2611        public boolean equals(Object o) {
2612            if (this == o) return true;
2613            if (o == null || getClass() != o.getClass()) return false;
2614
2615            Alteration that = (Alteration) o;
2616
2617            if (Double.compare(that.chance, chance) != 0) return false;
2618            return replacer.equals(that.replacer);
2619
2620        }
2621
2622        @Override
2623        public int hashCode() {
2624            int result;
2625            long temp;
2626            result = replacer.hashCode();
2627            temp = Double.doubleToLongBits(chance);
2628            result = 31 * result + (int) (temp ^ (temp >>> 32));
2629            return result;
2630        }
2631
2632        @Override
2633        public String toString() {
2634            return "Alteration{" +
2635                    "replacer=" + replacer +
2636                    ", chance=" + chance +
2637                    '}';
2638        }
2639    }
2640
2641}