• Объем флешки 8 гб компьютер видит 7.20 гб?

    @Mercury13
    Программист на «си с крестами» и не только
    Я просто вынесу ответ из обсуждений.

    Ёмкость механических дисков и SSD — неформатированная доступная пользователю в десятичных единицах. Для них надо вычесть:
    • коэффициент перевода из десятичных гигабайт в более привычные двоичные гигабайты (или гибибайты, как их недавно обозвали);
    • главный загрузочный сектор;
    • таблицу разделов, загрузочные сектора разделов, скрытые разделы, неразбитое место;
    • технические поля файловой системы.
    Технические поля винта/SSD скрыты и вычитать из номинальной ёмкости их не надо.

    Ёмкость флэшек — полная ёмкость чипов памяти в двоичных единицах. Для флэшек надо вычесть:
    • технические поля самóй флэшки (в них пишется как минимум таблица сбойных секторов, жёстко заданная на заводе);
    • главный загрузочный сектор;
    • таблицу разделов, загрузочные сектора разделов, скрытые разделы, неразбитое место (если есть; чаще флэшки форматируют в единый раздел);
    • технические поля файловой системы.
    Из-за особенностей адресации не имеет смысла делать чипы полупроводниковой памяти не двоичного размера. Просто во флэшке один-два чипа, в SSD — целая батарея, и чтобы износ, неизменный спутник системного раздела, не сделал в SSD дыру, там есть сложные механизмы резервирования секторов и балансировки износа. Потому в SSD принцип винчестерный, а не флэшечный.

    «Десятично-круглая» цифра — неплохая оценка, сколько поместится на флэшку. Цифра не точная, т.к. зависит от модели флэшки и файловой системы на ней, но очень близкая к реальности. А на винчестер столько гарантированно не поместится.

    www.ixbt.com/storage/flashdrives/svodka/size.shtml
    Вот по этой ссылке видим, что в Windows может быть доступно как немного больше 4 млрд, так и немного меньше. А если взять неформатированную ёмкость первой попавшейся флэшки — у меня получилось 4039114752 байт, круглое плюс 1%. Для сравнения: неформатированная ёмкость моего «хитати» будет 2000395698176 байт — круглое плюс 0,02%. Вот вам разница между флэшкой и жёстким диском.

    P.S. У меня экономии ради диски только механические, завтра на работе проверю ёмкость SSD.
    UPD. Посмотрел. «СамСунь» неформатированного объёма в 256.052.822.016 байт. Отформатирован в один раздел объёмом 255.466.663.936. Так что тут явно по-винчестерному, с заделом в 0,02%.

    P.P.S. Возьмём реальную флэшку Verbatim на 8Г = 8589934592.
    Если вычесть технические поля, получится неформатированный размер, который равняется 7640M = 8011120640. Съедено 579 млн байт.
    Загрузочный сектор и таблица разделов — мизер.
    Куда больше занимают технические поля файловой системы. Раз на диске доступно 8010067968 байт, то съедено всего 1,05 млн. Но это NTFS, вероятно, там нет таблицы размещения файлов, которая всегда занимает определённые секторы.
    Действительно, форматирование в FAT дало размер 7993294848 байт, т.е. технические поля FAT заняли 17,8 млн.
    Ответ написан
  • Как работать с спец символами Delphi?

    @Mercury13
    Программист на «си с крестами» и не только
    Думаю, ваша задача более высокого уровня такова. Пользователи разрисовывают свои имена всякими там картинками, задача — убрать их.

    Немного непонятно, с какой версией Delphi вы работаете, но, предположим, с 2010+.
    Там string эквивалентно UnicodeString, 16-битной строке неограниченной длины, управляемой подсчётом ссылок средствами Delphi.
    Юникод очень велик и будет расширяться, в идеале там может быть 1,15 млн. символов. Из них 120 тыс. уже занято.
    Поэтому лучшим решением будет делать не чёрный список символов, а белый.

    Качаем базу символов Unicode (есть где-то на unicode.org).
    Делаем из неё фильтр по каким-нибудь признакам: например, буква/цифра/знак/комбинирующий/пробел и направление письма — либо слева направо, либо адаптирующееся.
    Также для простоты исключаем символы с кодом 65536+ (с дополнительных плоскостей, они кодируются двумя WideChar).
    Вот этим белым списком и пользуемся. Проходимся по строке и убираем все лишние.
    Ответ написан
    1 комментарий
  • Как хранить записи со стены ВК?

    @Mercury13
    Программист на «си с крестами» и не только
    Не громоздко и не ресурсоёмко (особенно в C++11 — кстати, скоро Qt вообще откажется от 03).
    Однако я бы в таких нагромождениях делал struct’ы из одного элемента. Что-то типа
    struct Comment {
      QMap<QString, QString> objects;
    };
    
    struct Post {
       QList<Comment> comments;
    };

    Так код проще будет разобрать.

    Только одно непонятно. Что собой представляют эти objects и на что тебе эта «строкотипизированность»?
    Ответ написан
    Комментировать
  • Как конвертировать list в string[]?

    @Mercury13
    Программист на «си с крестами» и не только
    Итератор — это объект с семантикой указателя, который может указывать на N+1 точку в объекте.
    5f40aef227644b9ca57224ffeb571a97.png
    Раз он с семантикой указателя, у него есть операции «унарная звезда» и −> (разыменование и разыменование+взятие поля). Также у итератора есть операция ++ (сдвинуться на следующую позицию). Если это т.н. «однонаправленный итератор» — всё, больше ничего.

    Также бывают т.н. двунаправленные итераторы (есть операция −−), и итераторы произвольного доступа (их можно свободно складывать с числами — ну совсем как указатели). В частности, у std::list итераторы двунаправленные.

    У итераторов неопределённое поведение…
    • при попытке выйти за начало или конец;
    • при попытке разыменовать, если он смотрит на последнюю позицию (отмеченную как «конец»).

    Конкретно о задаче.
    1. std::vector предпочтительнее std::list.
    2. Не нужно возвращать string*, хватает какого-нибудь контейнера (std::vector<std::string> или std::list<std::string>).
    3. Если функциональности и скорости istringstream хватает, флаг в руки! Я бы написал по хардкору, с нуля. Вот мой код, выдранный из моего проекта, наверно, будет несложно переделать его в учебный.

    void parseCommaList(
            const char *aStart,   // указатель на начало
            const char *aEnd,    // указатель на символ за концом
            char aComma,        // символ-разделитель
            bool aSkipEmpty,   // true, если пустые подстроки пропускать
            ProcParseCommaList aCallback,   // функция-нагрузка
            void *aData)   // этот параметр нужен, чтобы передавать какие хочешь данные в функцию-нагрузку, удаляй его смело!
    {
        str::trim(aStart, aEnd);    // моя функция; пододвигает aStart вперёд и aEnd назад, убирая пробелы.
                                    // Если удаление пробелов не нужно — удаляй! Если нужно — пиши сам.
        if (aStart == aEnd) return;
        const char *sstart = aStart;
        for (const char *p = aStart; p != aEnd; ++p)
        {
            if (*p != aComma) continue;
            const char *send = p;
            str::trim(sstart, send);   // то же самое, можно убрать
            if (p != sstart || !aSkipEmpty)
                aCallback(sstart, send, aData);    // замени на боевую нагрузку
            sstart = p + 1;
        }
        str::trim(sstart, aEnd);   // то же самое, можно убрать
        if (sstart != aEnd || !aSkipEmpty)
            aCallback(sstart, aEnd, aData);    // замени на боевую нагрузку
    }


    И, соответственно, версия для std::string.

    inline void parseCommaList(
            const std::string &aIn,
            char aComma,
            bool aSkipEmpty,
            ProcParseCommaList aCallback,
            void *aData)
    {
        parseCommaList(aIn.data(), aIn.data() + aIn.length(), aComma, aSkipEmpty,
                aCallback, aData);
    }
    Ответ написан
    Комментировать
  • Реальный размер iphone 6, 375 × 667px или 750 × 1334px(экран)?

    @Mercury13
    Программист на «си с крестами» и не только
    Размер экрана iPhone 6 — 750×1334 px.
    Для рекламных тизеров его действительно стоит уменьшить, например, вдвое.
    А когда показываем заказчику/программисту/верстальщику скриншоты, макеты и прочее — только хардкор, только пиксель в пиксель.
    Ответ написан
    Комментировать
  • Держит ли QSqlQuery умный указатель на БД?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Окончательно разобрался.
    1. Да.
    2. Да.
    3. Да.
    Ответ написан
    Комментировать
  • Как добавить все элементы из вектора в контейнер multimap под определенный ключ?

    @Mercury13
    Программист на «си с крестами» и не только
    Если вам нужен именно multimap, другого способа я не вижу. Но если элементов много, стоит задействовать хинт (параметр position, где примерно элемент будет стоять). Что-то типа (в компиляторе не проверял, код почти гарантированно неверный).
    if (!names.empty()) {
        Users::const_iterator hint = users.upper_bound(1);
        for (auto &i :  names) {
          users.insert(hint, {1, i});
        }
    }

    Учтите, что при переходе с 03 в 11 поменялся смысл хинта, тут версия для 11.
    P.S. Извините, код для хинта можно сделать и проще. После вставки и ++ хинт останется тем же.
    Ответ написан
    Комментировать
  • В какой момент пора использовать ООП?

    @Mercury13
    Программист на «си с крестами» и не только
    ООП, как известно, упрощает разработку программ, состоящих из взаимодействующих компонентов с меняющимся состоянием. В вебе этого мало, и потому можно быть успешным вебистом и не знать ООП. ООП даёт двоякий выигрыш.

    1) Инкапсуляция — мы прячем внутреннее состояние, давая его менять специальными выведенными наружу «рычажками».
    • Тесная работа с коммуникационными протоколами (например, почтой).
    • Поддержка какой-то вещи с меняющимся состоянием (в вебе этого мало — может, какая-нибудь автоматическая вёрстка?)

    2) Абстракция и полиморфизм — в общем, поддержка разных вещей под общим фасадом.
    • Неопределённость в технологиях — может, MySQL, а может, SqLite. Тогда создаём абстрактный класс «БД» и от него наследуем MySQL и SqLite.
    • Какие-нибудь штуки из предметной отрасли. Пишем игру — персонажей игры удобно так держать. Хотя можно ли написать многопользовательскую игру целиком на PHP — в этом я не уверен.
    • Ну, не знаю, где ещё. Настольная/мобильная версия, что ли?
    Ответ написан
    Комментировать
  • Как происходит "обновление" кодировок?

    @Mercury13
    Программист на «си с крестами» и не только
    UTF-8 — это кодировка, позволяющая (теоретически) 2 млрд символов.
    Ограничением Юникода сейчас является кодировка UTF-16, она кодирует только 1 млн символов.

    Чтобы устройства по всему миру показывали новые символы, надо…
    1. Консорциуму Юникода где-то среди этого миллиона нарисовать новый символ. Сейчас занято около 120 тыс., ещё 130 тыс. — небольшой диапазон в базовой плоскости и две полных плоскости по 216 шт. — объявлены пользовательскими и их может занимать кто угодно в пределах своей ОС или программы.
    2. Консорциум Юникода публикует изображение символа и обновляет таблицы свойств символа.
    • Тип: буква/цифра/пробел/знак препинания/технический знак/управляющий/комбинирующий/…
    • Положение в двунаправленном письме: слева направо/справа налево/приспосабливается/управляет. Если приспосабливается — в письме справа налево можно поменять местами, например, скобки, для этого тоже есть поле.
    • Письменность: латиница/кириллица/ноты/смайлики/символы валют…
    • Как переводят в заглавные буквы и в нормальную форму. Настройка заглавных букв может переписываться локалью, но есть и «общая юникодная» таблица.
    3. Разработчик ОС переводит таблицу во внутренний формат ОС и обновляет шрифты. С очередным обновлением ОС шрифты и таблицы придут на компьютер, и будут символы.
    4. Чаще всего «левые» символы неверно отображаются в браузере. Для этого разработчики браузеров по хитрым алгоритмам ищут на компьютере пользователя шрифт, где этот символ есть. И, допустим, на вики «рогалика» Brogue (brogue.wikia.com) на моём компьютере ранее отображались все символы монстров, сейчас — пара тотемов не показываются. Видимо, с кучей софта ранее установился подходящий шрифт, а сейчас — фигвам. А на рабочей «десятке» всё в порядке.
    Ответ написан
    1 комментарий
  • Кто такой сливапер?

    @Mercury13
    Программист на «си с крестами» и не только
    Посетитель сайта типа «wikileaks для коммерческих и не только тайн»
    slivup [тощка] biz
    Ответ написан
    Комментировать
  • Как работает аппаратное распараллеливание?

    @Mercury13
    Программист на «си с крестами» и не только
    Попробую рассказать.

    Шаг 1. Конвейерная архитектура (Pentium 1).
    Чем-то напоминает многоствольный пулемёт. Один патрон заряжается, другой выстреливается, третий выбрасывается. Стоп… Одна команда выбирается, вторая декодируется, третья и четвёртая исполняются.

    Шаг 2. Суперскалярность (Pentium то ли Pro, то ли MMX).
    У нас есть несколько исполнительных блоков (в данном случае целочисленных). Если команды не противоречат друг другу, их можно пустить на обоих блоках параллельно.

    Шаг 3. Микрокоманды и VLIW (если не ошибаюсь, из x86 первым реализован в Transmeta Crusoe).
    Состоит из таких этапов.
    1. Разбиваем операции x86 на микрокоманды — например, «перебросить из eax в сумматор», «сдвинуть содержимое сумматора на 1 вправо»…
    2. Собираем это самое «очень длинное слово операции» из микрокоманд, заботясь о том, чтобы не было зависимостей по данным. Один сумматор получает слово из eax, а параллельно второй сдвигает на 1. Каждый из разрядов «длинного слова» управляет своим блоком процессора: сумматором, памятью, вводом-выводом…
    3. Ну и исполняем это самое слово.

    Всем этим архитектурам важно т.н. предсказание ветвлений. Чтобы всё это дело работало, нужно, чтобы несколько операций были декодированы наперёд. Проблема — ветвление: если мы не угадаем, случилось ли ветвление, вся предварительная работа насмарку. В микроконтроллерах с коротким конвейером и предсказуемым до такта временем работы мы на это чхаем: например, в инструкции к AVR говорится, что там двухместный конвейер: один такт декодирование и один (два, три) — исполнение. Обычно такт на декодирование не важен (и не указан в инструкции), но мы теряем его, если случился переход.

    А в x86 алгоритмы предсказания ветвлений довольно сложны.

    ЗЫ. В суперскалярных процессорах несколько десятков регистров, на которые динамически навешиваются ярлыки: сейчас EAX=r5, а через две команды — уже r13. Так называемое «переименование регистров».
    Ответ написан
    3 комментария
  • Как корректно обрезать строку тип string в utf8 на с++?

    @Mercury13
    Программист на «си с крестами» и не только
    Символов Юникода или байтов UTF-8?

    В любом случае байты UTF-8 делятся на три категории…
    • Начальные: 0x00…0x79, и 0xC0…0xF4
    • Дополнительные (не бывают в начале): 0x80…0xBF
    • Запрещённые: 0xF5…0xFF. В наших целях тоже можно отнести к начальным.

    Если задача — получить 10 символов, то находим 11-й начальный символ и обрезаем перед ним.

    Если задача — получить 10 байт и 11-й (s[10], если таковой есть, разумеется) — не начальный, начинаем урезать строку, пока не отрежем начальный символ.
    Ответ написан
  • Как подключить к KVM материнки со спаренным гнездом PS/2 для мыши/клавы?

    @Mercury13
    Программист на «си с крестами» и не только
    Нет, вам нужны разветвители типа 06013 или 06015 — которые «для старых ноутбуков» и на одном конце нарисована клавиатура, на другом мышь.

    USB-адаптеры с али позволяют подключать сколько угодно каких угодно клав и мышей (мультиплексирование на USB, в отличие от PS/2, есть), но, по опыту, многие из знакомых мне адаптеров подглючивают (клавиатура на время пропадает).
    Ответ написан
    3 комментария
  • План подготовки для поступления в Яндекс ШАД?

    @Mercury13
    Программист на «си с крестами» и не только
    Алгоритмы. Немного олимпиадного программирования ОЧЕНЬ не помешает. Алгоритмы там предлагают несложные, но очень нетривиальные, надо чувствовать, как решить задачу. Элементы сложности алгоритмов. Две задачи из восьми гарантированно будут.

    Алгебра и дискретная математика. Первый курс, всё скопом, без доказательств. Линейные уравнения, квадратичные формы, матрицы, собственные векторы, жорданова форма, перестановки, графы, теория множеств, комбинаторика, алгебра логики…

    Интегралы (не слишком «злые», но приёмы «подстановка», «по частям» и «тригонометрический интеграл» всё же освоить стоит). Интеграл средней сложности — постоянный гость в ШАДý. Может быть и ещё одна задача из мутьанализа — но это как повезёт и задача будет гарантированно нетривиальная, но решающаяся на «том, что помнишь с института» — дифференцирование, ряды Тейлора, основы топологии, простейшие пределы, правило Лопиталя. Вспомни, как берутся простейшие двойные интегралы, может попасться, например, на теории вероятностей.

    ФКП. Самое начало. Аналитических функций и рядов Лорана точно не будет. А вот то, что в комплексном поле многочлен n-й степени имеет n корней, знать надо.

    Теория вероятностей. Непрерывные и дискретные вероятности. Нечто несложное, почти что на уровне кубиков и карт, но одна-две из восьми будет. Хотя статистика — важная часть ШАДа, на экзамене не требуют. И пекла типа белых шумов и интегралов Ито не будет. Хотя что-то типа дискретной марковской цепи — а вдруг, хотя знакомые мне три экзамена не было.

    Школьные олимпиадные задачи. Возможна одна.

    Итого.
    Две — алгоритмы.
    Одна-две — вероятность.
    Одна — интеграл.
    Две-три — что угодно из школьной математики, дискретной математики, матанализа, алгебры, ФКП…

    P.S. Очень хороший приём, который мне помог. Конечно, вам придётся держать скан какого-нибудь справочника или распечатку Википедии (это не возбраняется, но электроника запрещена — впрочем, калькулятора задачи не требуют). Печатайте на одной стороне, вторую — на черновик!
    Ответ написан
    4 комментария
  • Qt. Как передавать данные между формами?

    @Mercury13
    Программист на «си с крестами» и не только
    Вариантов много, в зависимости от того, форма модальная или нет, и нужно ли каким-то моделям передавать сообщение «обновись».

    Вариант 1.
    class AddItem : public QWidget
    {
    public:
      AddItem(QWidget* aParent, QList<Institution>& aInstitutions)
          : QWidget(aParent), institutions(aInstitutions) {}
    private:
      QList<Institution>& institutions;
    };


    Во втором варианте у нас модальная форма, но редактировать можно только копию (например, институции первой формы задействованы в какой-то модели, или нужны каскадные удаления, или что-то ещё).
    class AddItem : public QDialog
    {
    public:
      int exec(QList<Institution>& aInstitutions);
    private:
      QList<Institution> institutions;
    };
    
    int AddItem::exec(QList<Institution>& aInstitutions)
    {
      institutions = aInstitutions;
      int r = QDialog::exec();
      if (r) {
         aInstitutions = std::move(institutions);
      }
      return r;
    }


    И много-много других вариантов.
    Ответ написан
    Комментировать
  • Непонятка с сортировкой - почему random более плохой случай, чем decremental array?

    @Mercury13
    Программист на «си с крестами» и не только
    Для пузырька убывающий массив действительно худший возможный случай, и единственное, что могу предположить,— JIT и предсказание ветвлений на уровне процессора. Ради интереса перейди на устаревшую виртуальную машину — думаю, этот случай сразу же станет худшим.

    Для быстрой убывающий массив — один из лучших случаев, будет сделано ровно [n/2] обменов. Легко показать, что элементы в начале и конце массива, стоящие на своих местах, быстрая сортировка не сместит никогда. Так что первое, что сделает сортировка,— разобьёт на меньший (отсортированный) и больший (частично отсортированный как надо, частично — в обратном порядке). Ту часть, которая как надо, сортировка не тронет. А ту, которая наоборот — задействуем трансфинитную индукцию, и получаем, что ровно [n/2].
    Ответ написан
    Комментировать
  • Когда ооп быстрее процедурного?

    @Mercury13
    Программист на «си с крестами» и не только
    ООП рассчитано не на скорость исполнения, а на скорость разработки. Как, впрочем, и многие другие современные технологии разработки. Всё, что ООП делает, можно реализовать и без ООП, и даже эффективнее. Стоит ли — другой вопрос.

    Какую задачу конкретно решает ООП? Обуздать сложность разработки программ, собранных из взаимодействующих компонентов. Вот от этого и пляшем: если программа не модульная (например, какой-нибудь сложный научный расчёт), ООП мало поможет. Также ООП не поможет, если стандартная реализация ООП недостаточно эффективна по процессору или по памяти — например, в мою бытность JavaMe’шником ООП не жаловали, поскольку памяти много ел, типичный мобильник имел от 215 до 800 килобайт доступной памяти. Также плохо будет работать там, где нет взаимодействия (на типичном PHP, который выдал страничку и исчез).

    Что на PHP можно реализовать объектно?
    • Поддержку каких-то протоколов (БД, почта, какая-нибудь внешняя веб-служба наподобие VK API или Mandrill).
    • Что-нибудь из предметной отрасли, что меняет своё состояние — например, генерация картинок, звуков, архивов, PDF…
    • Может, сделаешь какой-нибудь генератор страниц, который сначала собирает каркас страницы, а затем, в зависимости от настроек и целевого устройства, обращивает его HTML-кодом.
    Ответ написан
    Комментировать
  • Qt как создать много окон внутри одного?

    @Mercury13
    Программист на «си с крестами» и не только
    Сам не работал с этим под Qt, но называется это «многодокументный интерфейс» и простейший пример тут.
    doc.qt.io/qt-5/qtwidgets-mainwindows-mdi-example.html

    Также можно погуглить «Qt MDI».
    Ответ написан
    1 комментарий
  • Векторная алгебра может ли полностью заменить тригонометрию?

    @Mercury13
    Программист на «си с крестами» и не только
    Скажу по-другому: в тех местах, где не нужны углы в явном виде, тригонометрия, скорее всего, излишня, и действительно вопрос очень часто можно решить средствами векторной алгебры. В большинстве задач геймдева и трёхмерной графики — так точно.

    Ну буду настолько категоричным, чтобы сказать «полностью». Была как-то задача: есть шарнирный четырёхугольник, по диагонали натянута пружина. Может ли эта конструкция (в первом приближении) сымитировать педаль сцепления автомобиля? А то даже у Logitech G27 педаль сцепления не слишком убедительная. Решал численно и тригонометрией, хотя можно аналитически и векторной алгеброй попробовать.

    ЗЫ. Основные аргументы против — простота и накопление погрешностей. Но и накопление иногда можно обойти, не прибегая к тригонометрии — например, использовать кватернионы вместо матриц поворота в 3D или нормировать вектор в 2D.
    Ответ написан
    Комментировать
  • Как на основе расстояния Левенштейна вывести промежуточные слова?

    @Mercury13
    Программист на «си с крестами» и не только
    Разумеется, будем считать, что слова не должны быть осмысленными (иначе это другая задача, где этот алгоритм, с одной стороны, тяжеловат, с другой — совсем не нужен). Если между словами «коза» и «волк» расстояние 3, то промежуточные слова — «коза → воза → вола → волк». Или, если не против, «коза → козк →колк → волк».

    Итак, перед нами типичный алгоритм динамического программирования. В алгоритме динамического программирования есть два этапа: обратный ход и прямой. Этот алгоритм страдает двумя недостатками, мешающими взять и написать прямой ход.
    1. Матрица сжата. Вместо матрицы (M+1)×(N+1) мы имеем два массива длины N+1, хранящие одну строчку и потихоньку перезаписываемые.
    2. Нет второй матрицы, которая указывает направление, куда идти. В нашем случае направления — это три действия над строкой: замена, вставка и удаление.
    ну и 3) В сокращённом варианте хватит двух массивов, обмениваемых местами. К делу отношения не имеет, просто вопросы умелого программирования. Кажется, перед нами Java и утечек памяти тут нет — тем лучше.

    С задачами 1 и 2 надо сделать три вещи.
    1. Массивы D1 и D2 превратить в единую матрицу (M+1)×(N+1).
    2. Добавить второй такой же. Элементы его — enum: удалить/вставить/заменить.
    3. Написать прямой ход. Мы находимся в точке (M, N); смотрим, куда идти. Заменить — идём туда-то, вставить — идём туда-то, удалить — идём туда-то. Ну и, разумеется, выполняем нашу операцию на строках и выводим, что получилось. Продолжаем путь, пока не попадём в (0, 0).
    Ответ написан