private string Shuffle(string password, KeccakNumberGenerator rng)
        {
            var builder = new StringBuilder(password);

            for (int i = builder.Length; i > 1; --i)
            {
                int swapIndex = rng.NextInt(i);
                var temp      = builder[i - 1];
                builder[i - 1]     = builder[swapIndex];
                builder[swapIndex] = temp;
            }
            return(builder.ToString());
        }
        private string PickUnits(KeccakNumberGenerator rng)
        {
            var builder         = new StringBuilder();
            var remainingCounts = UnitSets.ToDictionary(u => u, u => u.Bounds.Max);

            // Fill in the minimum amount of each unit set.
            foreach (var unitSet in UnitSets)
            {
                for (int i = 0; i < unitSet.Bounds.Min; ++i)
                {
                    builder.Append(unitSet[rng.NextInt(unitSet.Count)]);
                }
            }

            // Fill in the rest with random characters from the entire superset,
            // removing sets if they reach their maximum
            var availableUnitSets = UnitSets.Where(u => u.Bounds.Max > u.Bounds.Min).ToList();
            int supersetCount     = availableUnitSets.Sum(u => u.Count);

            while (builder.Length < Length)
            {
                int supersetIndex = rng.NextInt(supersetCount);

                for (int i = 0; i < availableUnitSets.Count; ++i)
                {
                    if (supersetIndex < availableUnitSets[i].Count)
                    {
                        builder.Append(availableUnitSets[i][supersetIndex]);

                        --remainingCounts[availableUnitSets[i]];
                        if (remainingCounts[availableUnitSets[i]] == 0)
                        {
                            availableUnitSets.Remove(availableUnitSets[i]);
                            supersetCount = availableUnitSets.Sum(u => u.Count);
                        }

                        break;
                    }

                    supersetIndex -= availableUnitSets[i].Count;
                }
            }
            return(builder.ToString());
        }
        public string Generate()
        {
            var seedBuilder = new StringBuilder();

            AddSeedPart(seedBuilder, MasterPassword);
            AddSeedPart(seedBuilder, Keyword);
            AddSeedPart(seedBuilder, Modifier);
            AddSeedPart(seedBuilder, Length);
            foreach (var unitSet in UnitSets)
            {
                AddSeedPart(seedBuilder, unitSet);
            }

            var rng = new KeccakNumberGenerator(seedBuilder.ToString());

            string unshuffled = PickUnits(rng);
            string password   = Shuffle(unshuffled, rng);

            return(password);
        }