/// <summary>
        /// Функция генерации случайных чисел: Получает случайные числа из начальных значений, используя сетку КА. Запускает настолько
        /// много итераций, насколько требуется чтобы получить данные. Большая сетка и большее количество генераций на блок, дадут
        /// более "случайные" данные, потребуют меньшие блоки, но потребуют больше вычислений.
        /// </summary>
        /// <param name="outputStream">Поток для записи</param>
        /// <param name="lengthBytes">Количество данных для генерации</param>
        /// <param name="seed">Начальные значения</param>
        /// <param name="gridWidth">Ширина сетки</param>
        /// <param name="gridHeight">Высота сетки</param>
        /// <param name="generationsPerBlock">Количество итераций правила Фредкина на блок</param>
        public static void GenerateRandomFile(Stream outputStream, int lengthBytes, int seed, int gridWidth, int gridHeight, int generationsPerBlock)
        {
            // Создаем сетку
            CAGrid grid = new CAGrid();

            // Создаем 8Кб данных ячейки
            grid.BuildFromPseudoRandomNumbers(seed, gridWidth, gridHeight);

            // Вычисляем длину
            int written = 0;
            int remaining = lengthBytes;

            // Продолжаем пока есть что записывать
            while (written < lengthBytes)
            {
                // Запускаем правило Фредкина N раз
                for (int i = 0; i < generationsPerBlock; i++)
                    grid.RunFredkinRule();

                // Получаем содержимое сетки
                byte[] buffer = grid.GetBytes();

                // Вычисляем текущее количество записываемой информации
                int toWrite = buffer.Length;

                // Если оставшееся количество байтов меньше чем длина буфера
                if (remaining < buffer.Length)
                    toWrite = remaining;

                // Пишем это в поток вывода
                outputStream.Write(buffer, 0, toWrite);

                // Увеличиваем счетчик записанного
                written += toWrite;

                // Вычисляем количество оставшихся байтов
                remaining = lengthBytes - written;

                // Запоминаем количество записаных данных
                Debug.Print("Written:" + written + " bytes, remaining: " + remaining);
            }
        }
        /// <summary>
        /// Инициализация сетки КА и запуск определенного числа генераций
        /// </summary>
        public void GenerateCellData()
        {
            // Запуск сетки КА
            _caGrid = new CAGrid();
            _caGrid.BuildFromPassCode(_passCode, _passCodeSize);

            var s = Stopwatch.StartNew();
            // Запуск определенного числа генераций
            while (_caGrid.Generation < _generationCount)
                _caGrid.RunFredkinRule();
            s.Stop();
            Debug.Print(s.ElapsedMilliseconds.ToString());
        }
        /// <summary>
        /// Функция генерации случайных чисел: Получает случайные числа из начальных значений, используя сетку КА. Запускает настолько
        /// много итераций, насколько требуется чтобы получить данные. Большая сетка и большее количество генераций на блок, дадут
        /// более "случайные" данные, потребуют меньшие блоки, но потребуют больше вычислений.
        /// </summary>
        /// <param name="outputStream">the stream to write to</param>
        /// <param name="lengthBytes">Количество данных для генерации</param>
        /// <param name="seed">Начапльные значения</param>
        /// <param name="gridWidth">Ширина сетки</param>
        /// <param name="gridHeight">Высота сетки</param>
        /// <param name="generationsPerBlock">Количество итераций правила Фредкина на блок</param>
        public static IEnumerable<byte> GenerateKeyData(long lengthBytes, int seed, int gridWidth, int gridHeight, int generationsPerBlock)
        {
            // Создаем сетку
            CAGrid grid = new CAGrid();

            // Инициализируем сетку
            grid.BuildFromPseudoRandomNumbers(seed, gridWidth, gridHeight);

            // Считаем длину
            long written = 0;
            long remaining = lengthBytes;

            // Продолжаем пока есть что записывать
            while (written < lengthBytes)
            {
                // Запускаем правило Фредкина N раз
                for (int i = 0; i < generationsPerBlock; i++)
                    grid.RunFredkinRule();

                // Получаем содержимое сетки
                byte[] buffer = grid.GetBytes();

                // Вычисляем текущее количество записываемой информации
                long toWrite = buffer.Length;

                // Если оставшееся количество байтов меньше чем длина буфера
                if (remaining < buffer.Length)
                    toWrite = remaining;

                // Побайтово считываем данные в буфер
                for (int i = 0; i < toWrite; i++)
                    yield return buffer[i];

                // Увеличиваем счетчик записанного
                written += toWrite;

                // Вычисляем количество оставшихся байтов
                remaining = lengthBytes - written;

                // Запоминаем количество записаных данных
                Debug.Print("Written:" + written + " bytes, remaining: " + remaining);

                // Вызываем событие роста прогресса
                OnProgress(lengthBytes, written, remaining);
            }
        }