private static IEnumerable <WordWithValue> CreateWords(IEnumerable <IList <Arrangement> > arrangements)
        {
            var arrangementsList    = arrangements.ToList();
            var alphabetSize        = arrangementsList.Count;
            var language            = CreateBaseLanguage(alphabetSize);
            var tilesInArrangements = arrangementsList.Select(a => a.Max(b => b.TotalValue)).ToList();

            foreach (var word in language)
            {
                var sumOfTiles = word.Sum(c => tilesInArrangements[c]);
                if (sumOfTiles <= 14 && sumOfTiles >= 5)
                {
                    var analyzer = new ArrangementAnalyzer();
                    foreach (var character in word)
                    {
                        analyzer.AddSetOfArrangements(arrangementsList[character]);
                    }
                    var shanten = analyzer.CalculateShanten();
                    if (shanten < 9)
                    {
                        yield return(new WordWithValue(word, shanten + 1));
                    }
                }
            }
        }
        private Dictionary <string, string> CreateRedundantArrangements(string workingDirectory)
        {
            var fileName = Path.Combine(workingDirectory, "replacements.txt");

            if (File.Exists(fileName))
            {
                var redundanciesLines = File.ReadAllLines(fileName);
                return(redundanciesLines.ToDictionary(
                           line => line.Substring(0, line.IndexOf('>')),
                           line => line.Substring(line.IndexOf('>') + 1)));
            }

            var arrangements        = GetAllArrangements().ToList();
            var alphabetSize        = arrangements.Count;
            var tilesInArrangements = arrangements.Select(a => a.Max(b => b.TotalValue)).ToList();
            var replacements        = new Dictionary <string, string>();

            var foundRedundancy = true;

            while (foundRedundancy)
            {
                foundRedundancy = false;

                for (var i = 0; i < arrangements.Count; ++i)
                {
                    // If there is only a single arrangement, it can't have any redundancies.
                    // Still need to keep those in the list for their interactions with others.
                    var arrangement = arrangements[i];
                    if (arrangement.Count < 2)
                    {
                        continue;
                    }

                    // Check for each arrangement in the current group if it is redundant.
                    for (var j = 0; j < arrangement.Count; ++j)
                    {
                        var isRedundant = true;
                        // Create words for all possible combinations of arrangement groups.
                        var language = CreateBaseLanguage(alphabetSize);
                        foreach (var word in language)
                        {
                            // If the current group of arrangements is not part of the word, skip the word.
                            if (word.All(c => c != i))
                            {
                                continue;
                            }

                            // Pick the arrangements that correspond to the word and sum their tile counts.
                            var sumOfTiles = word.Sum(c => tilesInArrangements[c]);
                            if (sumOfTiles > 14)
                            {
                                continue;
                            }

                            // It's impossible to have less than 5 usable tiles in a hand.
                            if (sumOfTiles < 5)
                            {
                                continue;
                            }

                            // Calculate the shanten for the word.
                            var analyzer = new ArrangementAnalyzer();
                            foreach (var character in word)
                            {
                                analyzer.AddSetOfArrangements(arrangements[character]);
                            }
                            var shanten = analyzer.CalculateShanten();
                            if (shanten >= 9)
                            {
                                continue;
                            }

                            // Calculate the shanten with one arrangement from the current arrangement group removed.
                            var replacement = arrangement.Where((t, index) => index != j).ToList();
                            var analyzer2   = new ArrangementAnalyzer();
                            foreach (var character in word)
                            {
                                analyzer2.AddSetOfArrangements(character == i ? replacement : arrangements[character]);
                            }
                            var shanten2 = analyzer2.CalculateShanten();

                            // If for any word that contains the current arrangement group there is difference in shanten,
                            // the arrangement that was removed above is not redundant.
                            if (shanten != shanten2)
                            {
                                isRedundant = false;
                                break;
                            }
                        }

                        if (isRedundant)
                        {
                            var current = string.Join("", arrangements[i]);
                            arrangements[i].RemoveAt(j);
                            var compacted = string.Join("", arrangements[i]);

                            if (!replacements.ContainsKey(current) || replacements[current] != compacted)
                            {
                                replacements.Add(current, compacted);
                            }

                            foundRedundancy = true;
                            break;
                        }
                    }
                    if (foundRedundancy)
                    {
                        break;
                    }
                }
            }

            var lines = replacements.Select(r => r.Key + ">" + r.Value);

            File.WriteAllLines(fileName, lines);
            return(replacements);
        }