Случайность в php7
Содержание:
- Final Thoughts
- Истинная случайность
- Генерация случайных универсально уникальных ID
- ChaosKey
- Лава-лампы в Cloudflare
- Использование случайных чисел
- Usage
- Генерация случайных буквенно-цифровых строк в PHP
- Квантовые флуктуации вакуума
- Создание случайных чисел в PHP
- Выбор случайного элемента из списка choice() модуль random
- Usage
- Пример 2. Вывод нескольких случайных элементов массива
Final Thoughts
In this tutorial, we looked at the generation of random numbers and alphanumeric strings in PHP. Generating random numbers can be useful in a variety of situations, like in games where you have to spawn enemy players or randomly give users some clues about letters so they can form a whole word.
Just like random numbers, the generation of random alphanumeric strings can also be pretty helpful in many circumstances. With the help of , you can choose which set of characters appear in your random strings. With and , you can easily generate random hexadecimal sequences, and with you can generate cryptographically secure strings. This will allow you to generate meaningful yet randomized filenames and usernames that are hard to guess.
I hope you enjoyed this tutorial. If you have any questions, feel free to ask them in the comments.
Истинная случайность
Имея так много источников данных, логично задаться вопросом: а как в действительности проверить случайность бит?
Национальный институт стандартов и технологий США предложил «Пакет статистических тестов для случайных и псевдослучайных генераторов чисел для криптографических приложений». В его состав входят 15 статистических тестов, целью которых является определение меры случайности бит, порожденных либо аппаратными, либо программными генераторами.
Одни из самых строгих статистических тестов предложил профессор Джордж Марсалья из Университета штата Флорида. «Тесты diehard» включают 17 различных проверок, некоторые из них требуют очень длинных последовательностей: минимум 268 мегабайт.
Случайность можно проверить с помощью библиотеки TestU01, представленной Пьером Л`Экуйе и Ричардом Симардом из Монреальского университета, включающей классические тесты и некоторые оригинальные, а также посредством общедоступной библиотеки SPRNG.
Еще один полезный сервис для количественного измерения случайности — http://www.fourmilab.ch/random.
И не будем забывать про самый главный критерий — каждый следующий бит в последовательности случайных данных должен быть предсказан с вероятностью, не превышающей вероятность встретить динозавра на улице 😉
Генерация случайных универсально уникальных ID
Модуль Python UUID предоставляет неизменяемые UUID объекты. UUID является универсально уникальным идентификатором.
У модуля есть функции для генерации всех версий UUID. Используя функцию , можно получить случайно сгенерированное уникальное ID длиной в 128 битов, которое к тому же является криптографически надежным.
Полученные уникальные ID используются для идентификации документов, пользователей, ресурсов и любой другой информации на компьютерных системах.
Пример использования в Python:
Python
import uuid
# получить уникальный UUID
safeId = uuid.uuid4()
print(«безопасный уникальный id «, safeId)
1 |
importuuid safeId=uuid.uuid4() print(«безопасный уникальный id «,safeId) |
Вывод:
Shell
безопасный уникальный id fb62463a-cd93-4f54-91ab-72a2e2697aff
1 | безопасныйуникальныйidfb62463a-cd93-4f54-91ab-72a2e2697aff |
ChaosKey
Кейт Паккард, активный разработчик Debian, лидер проекта X.Org, создатель XRender, XComposite и XRandR, совместно с известным сторонником открытых данных, главой направления Linux-проектов Hewlett-Packard Бдейлом Гарби разработали недорогой, но надежный генератор случайных чисел ChaosKey.
Устройство с открытым исходным кодом ChaosKey 1.0 было представлено на конференции DeConf16 Debian. Цель разработки — добавить реальную энтропию генератору случайных чисел, обеспечить высокий уровень безопасности и снизить цену до минимума.
К ChaosKey невозможно получить удаленный доступ и нельзя перезаписать прошивку, даже когда устройство подключено по USB к компьютеру. Для этого вам необходимо получить физический доступ, вскрыть гаджет и соединить два контакта внутри.
Сам полнофункциональный RNG похож на маленькую флешку и занимает один USB-разъем. Конструкция платы минималистична — на ней фактически имеется только процессор и транзисторы для генерации шума. Скорость передачи случайных значений — около мегабайта в секунду.
И по поводу цены. Устройство можно заказать, но все схемы и прошивка выложены в открытый доступ — разработчики сами рекомендуют каждому создавать и совершенствовать ChaosKey.
Лава-лампы в Cloudflare
Компания CloudFlare, которая сама заявляет, что через ее сеть проходит «около 10 % мирового трафика», защищает интернет-проекты от DDoS-атак, но и ей самой нужна защита. Трафик, который проходит через сети CloudFlare, шифруется — и в этом помогает сотня разноцветных лава-ламп на «Стене Энтропии».
Лавовая лампа представляет собой стеклянную емкость, заполненную прозрачным маслом и полупрозрачным парафином. Расположенная в нижней части емкости лампочка накаливания нагревает и подсвечивает содержимое цилиндра, при этом происходит «лавообразное» перемещение парафина в масле. Парафин немного тяжелее масла, но при небольшом нагреве становится легче и всплывает.
За движением жидкостей наблюдают несколько камер, делающих моментальные фотоснимки. Снимки преобразовываются в числа, из которых потом генерируются ключи шифрования. За один кадр получается 16 384 бит энтропии.
В двух других офисах CloudFlare используются иные способы получать случайные значения. В Лондоне камера снимает движения трех хаотических маятников, а в Сингапуре поставили счетчик Гейгера, замеряющий показатели радиоактивного распада небольшого кусочка урана. В последнем случае уран используется в качестве «источника данных», поскольку для радиоактивного излучения характерна случайность каждого отдельного акта распада.
Все эти способы работы с данными привлекают внимание к деятельности компании, чья работа часто остается невидимой для обычных клиентов
Использование случайных чисел
В PHP случайные числа играют огромную роль, т.к. очень часто используются для различных целей. В основном, они связаны с безопасностью. На их основе генерируются токены CSRF, ключи API, значения для аутентификации, значения для сброса паролей и многое другое. Всё это делается, для того чтобы получаемые значение было невозможно предугадать.
Самые важные примеры применения случайных значений:
- Генерация соли для криптографии — случайное число соли используется, как правило, для шифрования “в одну сторону”, а также для хэширования паролей. Это случайное значение используется как вектор инициализации в криптографии.
- Генерация случайных значений, таких как ID сессии — PHP используется для создания огромного количества приложений, где безопасность стоит на первом месте. Многий функционал базируется на работе с сессиями и сгенерированными ID сессий.
- Генерация токенов для аутентификации, которые практически невозможно предугадать — многие PHP приложения базируются на работе с другими системами через специальные API интерфейсы. Обычно перед использованием API нужно пройти процесс аутентификации. Получать трудно-подбираемые значения для токенов очень сложно. Именно поэтому в данных задачах используются случайные числа.
Usage
Generating integers and natural numbers
Random::intBetween(300, 999); // => e.g. int(510) // or Random::intBelow(5); // => e.g. int(1) // or Random::intValue(); // => e.g. int(935477113)
Generating floating-point numbers (floats, doubles or reals)
Random::floatBetween(1, 9); // => e.g. float(7.6388446512546) // or Random::floatBelow(499); // => e.g. float(281.51015805504) // or Random::floatValue(); // => e.g. float(0.05532844200834)
Generating hexadecimal strings
Random::hexLowercaseString(2); // => e.g. string(2) "5f" // or Random::hexUppercaseString(7); // => e.g. string(7) "B0F4B0C"
Generating strings consisting of letters
Random::alphaString(32); // => e.g. string(32) "GvogcpNdwaxsmOAzKGYwDwqfMOUkZvAn" // or Random::alphaHumanString(7); // => e.g. string(7) "KykrbXb" // or Random::alphaLowercaseString(2); // => e.g. string(2) "ce" // or Random::alphaLowercaseHumanString(32); // => e.g. string(32) "kqbgcmkttvmxjthwpgbhkgdfqhxqmmxh" // or Random::alphaUppercaseString(2); // => e.g. string(2) "IJ" // or Random::alphaUppercaseHumanString(7); // => e.g. string(7) "LJWLLHJ"
Generating strings consisting of letters and numbers
Random::alphanumericString(2); // => e.g. string(2) "h9" // or Random::alphanumericHumanString(7); // => e.g. string(7) "NF34gd4" // or Random::alphanumericLowercaseString(2); // => e.g. string(2) "4g" // or Random::alphanumericLowercaseHumanString(32); // => e.g. string(32) "wbdt3fdtg9c33dnxxtphp3qd9tgdvhbn" // or Random::alphanumericUppercaseString(2); // => e.g. string(2) "O7" // or Random::alphanumericUppercaseHumanString(32); // => e.g. string(32) "TWFHNNWLNKMPJMYKXHX3TXRCRFTFMYYW" // or Random::base32String(7); // => e.g. string(7) "AZUELC4" // or Random::base58String(32); // => e.g. string(32) "jut9s2LdWHT1EGJeBug3F58oYsaoU85s"
Generating strings consisting of letters, numbers and punctuation
Random::alphanumericAndPunctuationHumanString(32); // => e.g. string(32) "x%jJ7pWTFwc94ctX3phyy^KT~??H4+JY" // or Random::asciiPrintableString(32); // => e.g. string(32) "N~5_ kP5muxf,HeDY2(W_Bwy^qOkgOXa" // or Random::asciiPrintableHumanString(7); // => e.g. string(7) "_r=kb?v" // or Random::base64String(19); // => e.g. string(19) "I7A/H8D2R54uAo6jCQ3" // or Random::base64UrlString(4); // => e.g. string(4) "_6GS" // or Random::base85String(7); // => e.g. string(7) "j8o6sp:"
Using hyphens as dividers
\Delight\Random\Util::hyphenate('aaaaaaaaaa'); // => string(12) "aaaa-aaaa-aa" // or \Delight\Random\Util::hyphenate('bbbbbbbbbb', 3); // => string(13) "bbb-bbb-bbb-b"
Using spaces as dividers
\Delight\Random\Util::spaceOut('cccccccccc'); // => string(12) "cccc cccc cc" // or \Delight\Random\Util::spaceOut('dddddddddd', 3); // => string(13) "ddd ddd ddd d"
Using arbitrary dividers
\Delight\Random\Util::segmentize('eeeeeeeeee', '/'); // => string(12) "eeee/eeee/ee" // or \Delight\Random\Util::segmentize('ffffffffff', '/', 3); // => string(13) "fff/fff/fff/f"
Генерация случайных буквенно-цифровых строк в PHP
Существует множество способов генерации случайных буквенно-цифровых строк, и то, что вы используете, будет зависеть от ваших потребностей.
Создание перетасованных строк
Если вы хотите генерировать случайные буквенно-цифровые строки из фиксированного набора символов, вы можете использовать функцию . Эта функция предоставит вам случайную перетасованную строку. Начиная с PHP 7.1, алгоритм, который определяет случайный порядок символов в выходной строке, был изменен на Mersenne Twister.
Помните, что случайная строка, сгенерированная таким образом, не является криптографически безопасной. Тем не менее, строка по-прежнему будет довольно непредсказуемой для общего использования, например, для генерации случайных имен файлов или URL-адресов. Вот несколько примеров:
В обоих случаях ваш результат, скорее всего, будет разным. В первом случае мы просто перетасовали допустимую строку символов, а затем взяли первые 10 символов. Во втором случае мы добавили «video» в начале сгенерированной строки и «.mp4» в конце.
Этот способ генерации случайных буквенно-цифровых строк очень прост, но у него есть несколько проблем. Например, вы никогда не получите одинаковые символы в своей случайной строке дважды. Кроме того, длина строки случайного вывода может достигать только длины входной строки.
Создание случайных строк
Если проблемы, перечисленные выше, являются неприемлемыми, вы можете посмотреть на некоторые другие реализации. Следующий код поможет решить эти проблемы.
Вы можете изменить его, чтобы добавить определенные суффиксы и префиксы к сгенерированной случайной строке. Люди, которые используют PHP 7, могут улучшить генерацию строк дальше, используя криптографически защищенную функцию вместо .
Создание случайных шестнадцатеричных строк
Если вы хотите генерировать случайные шестнадцатеричные строки в PHP, вы также можете использовать либо , либо функцию . Обе они будут генерировать хэши входной строки.
Вы будете получать уникальные хэши, пока вход уникален. Этого можно добиться, используя результат функции, такой как в качестве входного параметра. По умолчанию вернет шестнадцатеричную строку с 32 символами, а вернет 40-значную шестнадцатеричную строку. Их можно обрезать до определенной длины, используя функцию .
Ниже приведен пример вывода, возвращаемого этими функциями:
Как вы можете видеть, генерация случайных и уникальных шестнадцатеричных строк длиной до 40 символов очень проста в PHP.
Генерация криптографически безопасных случайных строк
Три функции генерации случайных буквенно-цифровых строк, которые мы обсуждали до сих пор, не являются криптографически безопасными. К счастью, PHP также имеет функцию для генерации криптографически защищенных псевдослучайных байтов. Параметр определяет длительность строки вывода.
Как только вы получите выход в виде случайных байтов, вы можете использовать функцию , чтобы преобразовать их в шестнадцатеричные значения. Это удвоит длину строки.
Другая функция, которую вы можете использовать для генерации криптографически безопасных случайных байтов, — . Значение второго параметра можно использовать для определения того, будет ли генерироваться строка вывода с использованием криптографически безопасного алгоритма или нет.
Квантовые флуктуации вакуума
Схематическое представление пространственно-временных отклонений от уровня невозмущенных вакуумных флуктуаций электрического поляИсточник
Вакуум, вопреки названию («vacuus» — пустой), истинно пустым считать нельзя, поскольку в нем в силу принципа неопределенности Гейзенберга беспрестанно рождаются и умирают виртуальные частицы — происходят так называемые квантовые флуктуации, колебания уровня энергии в единице объема пространства-времени.
Канадские физики сконструировали быстрый и конструктивно простой генератор случайных чисел на базе вакуумных флуктуаций. Генератор состоит из импульсного лазера с высокой частотой излучения, среды с высоким коэффициентом преломления (алмаз) и детектора. Проходя через алмаз, каждый импульс на детекторе показывает разные характеристики, в зависимости от вакуумных флуктуаций поля, которые встретились на пути фотонов. В спектре рассеянного излучения на выходе появляются спектральные линии, которых нет в спектре первичного света, а из-за непредсказуемости вакуумных флуктуаций эти линии каждый раз отличаются непредсказуемым образом.
Физики говорят, что не существует даже концепции того, как теоретически можно было бы разработать устройство для предсказаний колебаний энергии появления и аннигиляции виртуальных частиц — это одно из самых случайных событий, которое мы наблюдаем во Вселенной.
Осталось сделать этот генератор достаточно компактным и дешевым, чтобы начать его массовое использование.
Создание случайных чисел в PHP
Существуют три различные функции для генерации случайных чисел в PHP. Все они будут принимать минимальное и максимально возможное значение для случайных чисел и выдавать случайное число для вас. Это , и .
С минимальные и максимальные значения целых чисел, которые вы можете генерировать, лежат между и значением, возвращаемым функцией . До PHP 7.1.0 эта функция была примерно в четыре раза медленнее, чем . Однако, начиная с PHP 7.1.0, она была создана как псевдоним . В отличие от , вы можете установить значение меньше, чем , не вызывая ошибки.
С минимальные и максимальные значения целых чисел, которые вы можете генерировать, лежат между и значением, возвращаемым . Функция использует реализацию Mersenne Twister для генерации случайных чисел. Остерегайтесь, до PHP 7.1.0, эта функция реализовывала неверную версию алгоритма для генерации чисел. Однако он был исправлен в более новых версиях.
Функция стала еще лучше в PHP 7.2.0, избавившись от ошибки модульного смещения. Это означает, что для некоторых конкретных значений ваша последовательность случайных чисел теперь будет немного лучше по сравнению со старыми версиями. Однако какой-то специализированный код может действительно полагаться на эту предвзятость. Если это так, вы можете использовать более старый алгоритм, вызвав функцию для задания начального числа для генератора случайных чисел и передачи в качестве значения второго параметра.
Функция имеет период 219937-1, что в основном означает, что в наилучших сценариях вы получаете целых 219937-1 случайных чисел до того, как последовательность начнет повторяться. Следует отметить, что повторение последовательности не совпадает с повторением определенного числа. Другими словами, вы можете получить одно и то же случайное число дважды, но это не значит, что сама последовательность начала повторяться. Примером может служить следующая последовательность:
В приведенной выше последовательности у нас было 1267 два раза на выходе, но это не означает, что после этого вся последовательность начала повторяться. Маловероятно, чтобы такое же число повторялось так быстро в случайной последовательности, но это возможно!
Криптографически безопасные случайные целые числа
Если вы хотите криптографически безопасные псевдослучайные числа, функция в PHP — ваш лучший выбор. Он будет генерировать случайные числа между предоставленными значениями и , которые по умолчанию соответствуют и . К сожалению, эта функция доступна только с PHP 7.0. Для версий до этого вы можете использовать этот полифилл на GitHub.
Случайные числа с плавающей точкой
Вместо генерации случайных целых чисел вы также можете генерировать числа с плавающей точкой. Это можно сделать легко, просто разделив случайное число на значение, возвращаемое . В следующем примере будет проиллюстрировано, как создать случайное значение с плавающей точкой от 0 до 1 или между любыми другими минимальными и максимальными пределами.
При генерации случайного числа с плавающей точкой между заданными пределами мы убеждаемся, что случайные целые числа не превышают . Таким образом, мы можем быть уверены, что добавление float-части не займет числа выше максимального предела.
Изменение начального числа генераторов случайных чисел
Одна концепция, которая нуждается в небольшом объяснении, — это начальные числа. Проще говоря, это просто цифры, которые можно использовать для инициализации функций и перед генерированием случайных чисел. Функция, которая изменяет начальное число генератора псевдослучайных чисел называется , и функция, которая изменяет начальное число генератора псевдослучайных чисел называется .
Важно помнить, что предоставление начального значения случайного числа за один раз до вызова и не обязательно приведет к получению более качественных случайных чисел. Фактически, использование одного и того же начального случайного числа каждый раз даст вам одно и то же случайное число!. Изменение случайного числа полезно в ситуациях, когда вы хотите создать случайную, но воспроизводимую последовательность
Следующий фрагмент кода генерирует одну и ту же последовательность случайных чисел при повторном запуске
Изменение случайного числа полезно в ситуациях, когда вы хотите создать случайную, но воспроизводимую последовательность. Следующий фрагмент кода генерирует одну и ту же последовательность случайных чисел при повторном запуске.
Генерирование воспроизводимых случайных последовательностей таким образом может помочь отлаживать программы, которые тестировались с использованием случайных данных — если вы отслеживаете начальное случайное число, вы можете воспроизвести тот же ввод, чтобы выяснить, что пошло не так.
Выбор случайного элемента из списка choice() модуль random
Метод используется для выбора случайного элемента из списка. Набор может быть представлен в виде списка или python строки. Метод возвращает один случайный элемент последовательности.
Пример использования в Python:
Python
import random
list =
print(«random.choice используется для выбора случайного элемента из списка — «, random.choice(list))
1 |
importrandom list=55,66,77,88,99 print(«random.choice используется для выбора случайного элемента из списка — «,random.choice(list)) |
Вывод:
Shell
random.choice используется для выбора случайного элемента из списка — 55
1 | random.choiceиспользуетсядлявыбораслучайногоэлементаизсписка-55 |
Usage
Generate a string of random bytes
try { $string = random_bytes(32); } catch (TypeError $e) { // Well, it's an integer, so this IS unexpected. die("An unexpected error has occurred"); } catch (Error $e) { // This is also unexpected because 32 is a reasonable integer. die("An unexpected error has occurred"); } catch (Exception $e) { // If you get this message, the CSPRNG failed hard. die("Could not generate a random string. Is our OS secure?"); } var_dump(bin2hex($string)); // string(64) "5787c41ae124b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2eeac6f"
Generate a random integer between two given integers (inclusive)
try { $int = random_int(, 255); } catch (TypeError $e) { // Well, it's an integer, so this IS unexpected. die("An unexpected error has occurred"); } catch (Error $e) { // This is also unexpected because 0 and 255 are both reasonable integers. die("An unexpected error has occurred"); } catch (Exception $e) { // If you get this message, the CSPRNG failed hard. die("Could not generate a random int. Is our OS secure?"); } var_dump($int); // int(47)
Exception handling
When handling exceptions and errors you must account for differences between
PHP 5 and PHP7.
The differences:
- Catching works, so long as it is caught before .
- Catching has different behavior, without previously catching .
- There is no portable way to catch all errors/exceptions.
Example
try { return random_int(1, $userInput); } catch (TypeError $e) { // This is okay, so long as `Error` is caught before `Exception`. throw new Exception('Please enter a number!'); } catch (Error $e) { // This is required, if you do not need to do anything just rethrow. throw $e; } catch (Exception $e) { // This is optional and maybe omitted if you do not want to handle errors // during generation. throw new InternalServerErrorException( 'Oops, our server is bust and cannot generate any random data.', 500, $e ); }
Troubleshooting
Exception: «Could not gather sufficient random data»
If an Exception is thrown, then your operating system is not secure.
- If you’re on Windows, make sure you enable mcrypt.
- If you’re on any other OS, make sure is readable.
- FreeBSD jails need to expose from the host OS
- If you use , make sure is allowed
This library does not (and will not accept any patches to) fall back to
an insecure random number generator.
Version Conflict with
If you’re using a project that has a line like this in its composer.json
…and then you try to add random_compat 2 (or another library that explicitly
requires random_compat 2, such as this secure PHP encryption library),
you will get a version conflict.
The solution is to get the project to update its requirement string to allow
version 2 and above to be used instead of hard-locking users to version 1.
"require" { ... - "paragonie/random_compat": "~1.1", + "paragonie/random_compat": ">=1", ... }
Version 9.99.99
Note: There is a special version called which makes this
library do nothing, but is only installable on PHP 7.
If you’re writing software (e.g. a library) that supports PHP 5, but may
be used by software that doesn’t, you’ll want to allow to be
installed. The above diff is what you want.
Conversely, if you’re writing software that (in and of itself) supports
PHP 5, you do not want 9.99.99 to be installed, so you’ll want to make
this change instead:
"require" { ... - "paragonie/random_compat": "~1.1", + "paragonie/random_compat": ">=1 <9.99", ... }
To avoid installing «empty» version you can add section
in your root :
Manifest Read Length Error
If you’re using the PHP Archive (Phar) approach rather than Composer, and
you are getting an error message to the effect of «manifest read length
was should be «, the Phar extension may not be enabled.
See
for specific guidance on how to fix this issue.
Пример 2. Вывод нескольких случайных элементов массива
Теперь давайте потренируемся с выводом нескольких случайных элементов массива.
В случае с одним элементом возвращается его ключ, а в случае с несколькими случайными элементами массива возвращается массив ключей. От этого мы и будем отталкиваться при выводе на экран.
Сначала создадим массив, в который занесем 7 разных имен.
Далее создаем переменную, в которую будет занесена работа функции array_rand(). Только теперь в скобках у этой функции в качестве второго аргумента укажем цифру «2». Это будет обозначать, что нам нужны 2 случайных элемента.
Результатом работы функции в данной ситуации будет массив, содержащий два случайных ключа элементов из нашего основного массива.
Поэтому при выводе на экран это нужно учесть и в квадратных скобках указать не просто имя переменной, а имя переменной, затем квадратные скобки и индекс массива. Так как элемента у нас 2, то в первом случае индекс будет , а во втором . (Вы помните, что индексация в массивах начинается с «0».)
Вот и все. Посмотрите на код, чтобы Вам стало все более понятно:
$names = array('Маша','Саша','Надя','Мила','Андрей','Сергей','Антон'); $rand_names = array_rand($names,2); echo '<h2 style="color:#00C; margin-left:300px">'.$names].' и '.$names].'</h2>';
Как результат, на экран будут выводиться два случайных имени. Каждый раз при обновлении страницы имена будут меняться.
Это всего лишь два простых примера, но думаю, они помогут Вам понять работу данной функции. Если Вы проделаете все это один раз самостоятельно, то окончательно во всем разберетесь.
Конечно же, вывод случайного элемента (или элементов) массива может быть применен для решения разнообразных (и конечно же, более сложных) задач, но принцип останется прежним.
Надеюсь, Вам было интересно.
Исходный файл можно скачать в «Исходниках» к уроку.
В следующий раз разберем еще какой-нибудь практический пример, касающийся массивов, потому что теория – это хорошо, но ее нужно подкреплять и практикой.
Придумайте свои задачи для использования функции array_rand(). Это будет очень полезно.
Делитесь уроком при помощи соц. кнопок, расположенных ниже. Подписывайтесь на обновление блога и оставляйте свои комментарии.
С Вами была Анна Котельникова.
Успехов Вам и до скорых встреч!