\documentclass[a4paper, final]{article}
%\usepackage{literat} % Нормальные шрифты
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
\usepackage{tabularx}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{amsmath}
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
\usepackage{ragged2e} %для растягивания по ширине
\usepackage{setspace} %для межстрочно   го интервала
\usepackage{moreverb} %для работы с листингами
\usepackage{indentfirst} % для абзацного отступа
\usepackage{moreverb} %для печати в листинге исходного кода программ
\usepackage{pdfpages} %для вставки других pdf файлов
\usepackage{tikz}
\usepackage{graphicx}
\usepackage{afterpage}
\usepackage{longtable}
\usepackage{float}



% \usepackage[paper=A4,DIV=12]{typearea}
\usepackage{pdflscape}
% \usepackage{lscape}

\usepackage{array}
\usepackage{multirow}

\renewcommand\verbatimtabsize{4\relax}
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
\usepackage{listings} %листинги
\usepackage{xcolor} % цвета
\usepackage{hyperref}% для гиперссылок
\usepackage{enumitem} %для перечислений

\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}


\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях

\hypersetup{colorlinks,
    allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)

% подгружаемые языки — подробнее в документации listings (это всё для листингов)
\lstloadlanguages{ SQL}
% включаем кириллицу и добавляем кое−какие опции
\lstset{tabsize=2,
    breaklines,
    basicstyle=\footnotesize,
    columns=fullflexible,
    flexiblecolumns,
    numbers=left,
    numberstyle={\footnotesize},
    keywordstyle=\color{blue},
    inputencoding=cp1251,
    extendedchars=true
}
\lstdefinelanguage{MyC}{
    language=SQL,
%    ndkeywordstyle=\color{darkgray}\bfseries,
%    identifierstyle=\color{black},
%    morecomment=[n]{/**}{*/},
%    commentstyle=\color{blue}\ttfamily,
%    stringstyle=\color{red}\ttfamily,
%    morestring=[b]",
%    showstringspaces=false,
%    morecomment=[l][\color{gray}]{//},
    keepspaces=true,
    escapechar=\%,
    texcl=true
}

\textheight=24cm % высота текста
\textwidth=16cm % ширина текста
\oddsidemargin=0pt % отступ от левого края
\topmargin=-1.5cm % отступ от верхнего края
\parindent=24pt % абзацный отступ
\parskip=5pt % интервал между абзацами
\tolerance=2000 % терпимость к "жидким" строкам
\flushbottom % выравнивание высоты страниц


% Настройка листингов
\lstset{
    language=C++,
    extendedchars=\true,
    inputencoding=utf8,
    keepspaces=true,
    % captionpos=b, % подписи листингов снизу
}

\begin{document} % начало документа
    


    % НАЧАЛО ТИТУЛЬНОГО ЛИСТА
    \begin{center}
        \hfill \break
        \hfill \break
        \normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
            федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
        \normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt] 
        \normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt] 
        \normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
        
        \hfill \break
        \hfill \break
        \hfill \break
        \hfill \break
        \large{Лабораторная работа №1}\\
        \large{по дисциплине}\\
        \large{<<Методы тестирования программного обеспечения>>}\\
        \hfill \break
        
        % \hfill \break
        \hfill \break
    \end{center}
    
    \small{ 
        \begin{tabular}{lrrl}
            \!\!\!Студент, & \hspace{2cm} & & \\
            \!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
            \!\!\!Преподаватель & \hspace{2cm} &  \underline{\hspace{3cm}} &  Курочкин М. А. \\\\
            &&\hspace{4cm}
        \end{tabular}
        \begin{flushright}
            <<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
        \end{flushright}
    }
    
    \hfill \break
    % \hfill \break
    \begin{center} \small{Санкт-Петербург, 2025} \end{center}
    \thispagestyle{empty} % выключаем отображение номера для этой страницы
    
    % КОНЕЦ ТИТУЛЬНОГО ЛИСТА
    \newpage
    
    \tableofcontents


    \newpage
    
    \section*{Введение}
    
    \addcontentsline{toc}{section}{Введение}

    В разработке ПО, помимо основной задачи — реализовать заявленную в спецификации функциональность, — существует не менее важная задача — обеспечить качество разработанного решения.

    Тестирование программного обеспечения — проверка соответствия между реальным и ожидаемым поведением программы, осуществляемая на конечном наборе тестов.

    На практике ни один метод тестирования не может выявить все ошибки в программе. Это связано с тем, что ресурсы проекта (деньги, время, персонал), в том числе и на тестирование, ограничены. Но все-таки, правильно проведенное тестирование позволяет обнаружить большинство ошибок, что позволит их оперативно исправить, и тем самым повысить качество программного обеспечения.

    В данной лабораторной работе используется ручное тестирование — процесс проверки ПО, выполняемый специалистами без использования каких-либо специальных автоматизированных средств.

    Ручное тестирование применяется не вместо компьютерного тестирования, а вместе с ним, что позволяет выявить ошибки в программе на более ранних стадиях.


    \newpage
    
    \section {Постановка задачи}
    Требуется: провести инспекцию кода, выступая в роли разработчика программы.

    Для выполнения данной задачи необходимо:
    \begin{itemize}
        \item изучить методы ручного тестирования;
        \item провести инспекцию кода, проанализировать ее результаты;
        \item исправить программу в соответствии с рекомендациями специалиста по тестированию.
    \end{itemize}


    \newpage
    
    \section {Описание методов инспекции кода}
    Существуют три основных метода ручного тестирования:
    \begin{itemize}
        \item инспекция кода;
        \item сквозной просмотр;
        \item тестирование удобства использования.
    \end{itemize}

    Эти методы могут применяться на любой стадии разработки ПО, причем как к отдельным готовым модулям или блокам, так и к приложению в целом.

    Инспекция и сквозной просмотр включают в себя чтение или визуальную проверку исходного кода программы группой лиц. Оба метода предполагают выполнение определенной подготовительной работы. Завершающим этапом является обмен мнениями между участниками проверки на специальном заседании. Цель такого заседания — нахождение ошибок, но не их устранение (т.е. тестирование, а не отладка).~\cite{mayers}

    \subsection{Инспекция кода}

    Инспекция кода — это набор процедур и методик обнаружения ошибок путем анализа (чтения) кода группой специалистов.~\cite{mayers}

    \subsubsection{Группа инспектирования кода}

    Обычно в состав группы входят четыре человека, один из которых играет роль координатора. Координатор должен быть квалифицированным программистом, но не автором тестируемой программы, детальное знание которой от него не требуется. Вторым участником группы является программист, а остальными — проектировщик программы (если это не сам программист) и специалист по тестированию.~\cite{mayers}

    В рамках выполнения лабораторной работы в инспекции кода участвовало всего три человека: программист, специалист по тестированию и координатор.


    \subsubsection{Человеческий фактор}

    Если программист воспринимает инспектирование своей программы как деятельность, направленную против него лично, и занимает оборонительную позицию, то процесс инспектирования не будет эффективным. Программист должен оставить самолюбие в стороне и рассматривать инспекцию только в позитивном и конструктивном ключе, не забывая о том, что целью инспекции является нахождение ошибок и, следовательно, улучшение качества программы.~\cite{mayers}

    \subsection{Сквозной просмотр}
    Тестирование программы методом сквозного просмотра также как и в инспекции кода включает в себя проверку программного кода группой лиц.

    При сквозном просмотре код проверяется группой разработчиков (оптимально — 3-4 человека), лишь один из которых является автором программы. Таким образом, большую часть программы тестирует не ее создатель, а другие члены команды разработчиков, что согласуется со вторым принципом, согласно которому тестирование программистом собственной программы редко бывает эффективным.

    Преимуществом сквозных просмотров, снижающим стоимость отладки (исправления ошибок), является возможность точной локализации ошибки. Кроме того, в процессе сквозного просмотра обычно удается выявить целую группу ошибок, которые впоследствии можно устранить все вместе. При тестировании программы на компьютере обычно проявляются лишь признаки ошибок (например, программа не может корректно завершиться или выводит бессмысленные результаты), а сами они обнаруживаются и устраняются по отдельности.~\cite{mayers}

    \subsection{Проверка за столом}

    Метод ручного тестирования «проверка за столом» может рассматриваться как инспекция или сквозной просмотр кода, выполняемые одним человеком, который вычитывает код программы, проверяет его, руководствуясь контрольным списком ошибок, и (или) прогоняет через логику программы тестовые данные.

    Для большинства людей проверка за столом является относительно непродуктивной. Это объясняется прежде всего тем, что такая проверка представляет собой полностью неупорядоченный процесс. Вторая, более важная причина заключается в том, что проверка за
    столом вступает в противоречие со вторым принципом тестирования, согласно которому
    тестирование программистом собственных программ обычно оказывается неэффективным.
    Следовательно, оптимальный вариант состоит в том, чтобы такую проверку выполнял человек, не являющийся автором программы (например, два программиста могут обмениваться
    программами для взаимной проверки, а не проверять собственные программы), но даже в
    этом случае такая проверка менее эффективна, чем сквозные просмотры или инспекции. В
    основном именно по этой причине лучше, чтобы сквозные просмотры или инспекции осуществлялись в группе.~\cite{mayers}

    \subsection{Рецензирование}
    Рецензирование — это процедура анонимной оценки общих характеристик качества, обслуживаемости, расширяемости, удобства использования и ясности программного обеспечения. Цель данного метода — предоставить программисту возможность получить стороннюю
    оценку результатов своего труда.
    Общее руководство процессом осуществляет администратор, выбираемый из числа программистов. В свою очередь, администратор отбирает в группу рецензентов от 6 до 20 участников (6 — это необходимый минимум, обеспечивающий анонимность оценок). Предполагается, что все участники специализируются в одной области. Каждый из участников предоставляет для рецензирования две своих программы, одну из которых он считает наилучшей,
    а вторую — наихудшей по качеству.
    Когда будут собраны все программы, их распределяют случайным образом между участниками. Каждому участнику дают для рецензирования четыре программы. Две из них относятся
    к категории «наилучших», а две — к категории «наихудших» программ, но рецензенту не
    сообщают, какой именно является каждая из них. Любой участник тратит на просмотр одной программы 30 минут и заполняет ее оценочную анкету. После просмотра всех четырех
    программ рецензент оценивает их относительное качество.
    Рецензента также просят предоставить свои замечания к программе и дать рекомендации по
    ее улучшению.
    После просмотра всех программ каждому участнику передают анкеты с оценками двух его
    программ. Кроме того, участники получают статистическую сводку, отражающую общие и
    детализированные данные о рейтинге их собственных программ среди всего набора, а также
    анализ того, насколько оценки, данные участником чужим программам, близки к оценкам
    тех же программ со стороны других рецензентов~\cite{mayers}
    

    \section{Технология инспекции кода}

    \subsection{Состав группы}

    Заседание происходило в следущем составе:

    \begin{itemize}
        \item секретарь Астафьев Игорь, исполняющий роль координатора;
        \item специалист по тестированию Кондраев Дмитрий;
        \item программист Тищенко Артём, он же проектировщик программы.
    \end{itemize}

    \subsection{Исходный код программы}

    Код исходного файла программы представлен в листинге~\ref{lst:source}.

\begin{lstlisting}[caption={Исходный код программы \texttt{bubble\_sort.cpp}.}, label={lst:source}]
#include <iostream>
#include <string>
#include <locale>
#include <vector>
#include <windows.h>

using namespace std;

struct BSTNode {
    string word;
    BSTNode* left;
    BSTNode* right;
    BSTNode(const string& w) : word(w), left(nullptr), right(nullptr) {}
};

class BST {
    BSTNode* root;

    bool insertNode(BSTNode*& node, const string& w) {
        if (!node) {
            node = new BSTNode(w);
            return true;
        }
        if (w == node->word) return false;
        if (w < node->word) return insertNode(node->left, w);
        return insertNode(node->right, w);
    }

    bool removeNode(BSTNode*& node, const string& w) {
        if (!node) return false;
        if (w < node->word) return removeNode(node->left, w);
        if (w > node->word) return removeNode(node->right, w);
        if (!node->left) {
            BSTNode* temp = node->right;
            delete node;
            node = temp;
        } else if (!node->right) {
            BSTNode* temp = node->left;
            delete node;
            node = temp;
        } else {
            BSTNode* minNode = findMin(node->right);
            node->word = minNode->word;
            removeNode(node->right, minNode->word);
        }
        return true;
    }

    BSTNode* findMin(BSTNode* node) {
        if (!node) return nullptr;
        while (node->left) node = node->left;
        return node;
    }

    void clearTree(BSTNode*& node) {
        if (!node) return;
        clearTree(node->left);
        clearTree(node->right);
        delete node;
        node = nullptr;
    }

    void inorder(BSTNode* node, vector<string>& result) const {
        if (!node) return;
        inorder(node->left, result);
        result.push_back(node->word);
        inorder(node->right, result);
    }

public:
    BST() : root(nullptr) {}
    bool insert(const string& w) { return insertNode(root, w); }
    bool remove(const string& w) { return removeNode(root, w); }
    void clear() { clearTree(root); }
    vector<string> getAllWords() const {
        vector<string> result;
        inorder(root, result);
        return result;
    }
};

int main() {
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);
    setlocale(LC_ALL, "Russian");
    BST dictionary;

    cout << "Команды:\n"
         << "0 - очистить словарь;\n"
         << "1,<строка> - добавить строку;\n"
         << "2,<строка> - удалить строку;\n"
         << "3 - вывести все слова;\n"
         << "4 - завершить работу.\n\n";

    while (true) {
        string input;
        if (!getline(cin, input)) break;
        if (input.empty()) {
            cout << "Некорректный ввод!\n";
            continue;
        }
        int commaPos = input.find(',');
        int command;
        string cmdStr, word;
        if (commaPos == (int)string::npos) {
            cmdStr = input;
        } else {
            cmdStr = input.substr(0, commaPos);
            if (commaPos + 1 < (int)input.size()) {
                word = input.substr(commaPos + 1);
            }
        }
        try { command = stoi(cmdStr); }
        catch (...) {
            cout << "Некорректный ввод!\n";
            continue;
        }

        switch (command) {
            case 0:
                dictionary.clear();
                cout << "Словарь очищен\n";
                break;
            case 1:
                if (word.empty()) {
                    cout << "Некорректный ввод!\n";
                    break;
                }
                if (dictionary.insert(word))
                    cout << "Строка \"" << word << "\" добавлена в словарь\n";
                else
                    cout << "Строка \"" << word << "\" уже есть в словаре\n";
                break;
            case 2:
                if (word.empty()) {
                    cout << "Некорректный ввод!\n";
                    break;
                }
                if (dictionary.remove(word))
                    cout << "Строка \"" << word << "\" удалена из словаря\n";
                else
                    cout << "Строка \"" << word << "\" не найдена в словаре\n";
                break;
            case 3: {
                vector<string> allWords = dictionary.getAllWords();
                if (allWords.empty()) {
                    cout << "Словарь пуст\n";
                    break;
                }
                for (int i = 0; i < (int)allWords.size(); i++) {
                    cout << allWords[i];
                    if (i + 1 < (int)allWords.size()) cout << ", ";
                }
                cout << "\n";
                break;
            }
            case 4:
                return 0;
            default:
                cout << "Некорректный ввод!\n";
                break;
        }
    }
    return 0;
}
\end{lstlisting}
    
    \subsection{Список вопросов и ответов на них}

    Ниже представлен список вопросов, которые специалист по тестированию Кондраев задавал программисту Тищенко. После каждого вопроса приведен ответ. После некоторых ответов следуют замечания специалиста по тестированию.

    \begin{enumerate}
        \item Программа реализует бинарное дерево поиска?  
        
        \textit{Ответ:} Да.  
        
        \item На каком языке написана программа?  
        
        \textit{Ответ:} C++.  
        
        \item Какие типы переменных используются?  
        
        \textit{Ответ:} int, string, BSTNode*, BST, vector<string>, bool.  
        
        \item Все ли константные переменные объявлены при их инициализации?  
        
        \textit{Ответ:} Да.  
        
        \item Где-то используется индексация массивов?  
        
        \textit{Ответ:} Да, в выводе слов (например, \texttt{allWords[i]}) и обработке ввода.  
        
        \item Проверяется ли аргумент индексации что он целочисленный?  
        
        \textit{Ответ:} Да, через приведение типа и проверку условий (например, \texttt{(int)allWords.size()}).  
        
        \item Корректно ли названы все методы и переменные?  
        
        \textit{Ответ:} Да, кроме параметра \texttt{w} в методах BST. 

        \textbf{Замечание:} Параметры вроде \texttt{w} стоит переименовать в \texttt{word} для ясности.  
        
        \item Возможно ли деление на 0?  
        
        \textit{Ответ:} Нет.  
        
        \item Как выполняются проверки сравнения?  
        
        \textit{Ответ:} Сравниваются строки (например, \texttt{w == node->word}) и целые числа.  
        
        \item Есть ли операторы сравнения с целыми константами?  
        
        \textit{Ответ:} Да (например, проверка \texttt{command == 0}).  
        
        \item Используются ли переменные double, float?  
        
        \textit{Ответ:} Нет.  
        
        \item Может ли какая-нибудь переменная типа int переполниться?  
        
        \textit{Ответ:} Теоретически возможно, но маловероятно (например, при очень большом количестве слов).  
        
        \item Соблюдены ли правила наследования объектов?  
        
        \textit{Ответ:} Да, наследование не используется.  
        
        \item Все ли переменные объявлены?  
        
        \textit{Ответ:} Да.  
        
        \item Правильно ли инициализированы массивы и строки?  
        
        \textit{Ответ:} Да (например, \texttt{vector<string> result;} инициализируется пустым).  
        
        \item Удаляются ли неиспользуемые переменные из памяти?  
        
        \textit{Ответ:} Частично.  

        \textbf{Замечание:} Не хватает деструктора для класса \texttt{BST} для полной очистки памяти.  
        
        \item Присутствуют ли в программе потенциально бесконечные циклы?  
        
        \textit{Ответ:} Нет.  
        
        \item Может ли значение индекса выйти за размер массива?  
        
        \textit{Ответ:} Нет (используется \texttt{(int)allWords.size()}, что предотвращает выход за границы).  
        
        \item Проверяются ли входные данные на принадлежность к домену?  
        
        \textit{Ответ:} Частично.  
        
        \textbf{Замечание:} Не проверяется, содержит ли строка только допустимые символы (например, кириллицу).  
        
        \item Используются ли логические выражения?  
        
        \textit{Ответ:} Да (например, \texttt{if (!node)}).  
        
        \item Используются ли файлы для ввода-вывода?  
        
        \textit{Ответ:} Нет.  
        
        \item У всех ли классов есть конструктор?  
        
        \textit{Ответ:} Да (например, \texttt{BSTNode(const string\& w)}).  
        
        \item В цикле for корректно ли написана операция сравнения в цикле?  
        
        \textit{Ответ:} Да (например, \texttt{for (int i = 0; i < (int)allWords.size(); i++)}).  
        
        \item Проверяется ли наличие элементов в дереве перед удалением?  
        
        \textit{Ответ:} Нет.  
        
        \textbf{Замечание:} Метод \texttt{remove} возвращает \texttt{false} при отсутствии элемента, но нет явной проверки на пустое дерево.  
        
        \item Совпадает ли число аргументов, передаваемых вызываемым модулям и число ожидаемых параметров?  
        
        \textit{Ответ:} Да.  
        
        \item Делаются ли в программе попытки поправить входные аргументы?  
        
        \textit{Ответ:} Да (например, обработка \texttt{stoi(cmdStr)} с исключениями).  
        
        \item Нет ли пропущенных функций?  
        
        \textit{Ответ:} Нет, все функции реализованы (вставка, удаление, очистка, вывод).  
        
        \item Выдаются ли предупреждения при компиляции?  
        
        \textit{Ответ:} Нет (при условии корректных настроек компилятора).  
        
        \item Выдаются ли ошибки при компиляции?  
        
        \textit{Ответ:} Нет.  
        
        \item Выполняются ли вычисления с присваиванием несовпадающих типов?  
        
        \textit{Ответ:} Нет.  
        
        \item Есть ли комментарии в программе?  
        
        \textit{Ответ:} Нет.  
        
        \textbf{Замечание:} Стоит добавить пояснения к методам класса \texttt{BST}.  
        
        \item Оформлен ли код в соответствии с некоторым регламентом?  
        
        \textit{Ответ:} Да (отступы, именование классов и методов в camelCase).  
    \end{enumerate}


\section{Код программы с внесенными изменениями}
Результатом метода инспекции кода является список правок, которые необходимо внести в программу:
\begin{enumerate}
    \item Переименовать параметр \texttt{w} в \texttt{word} для улучшения читаемости
    \item Добавить деструктор для класса \texttt{BST}
    \item Добавить проверку входных строк на наличие только буквенных символов
    \item Заменить проверки вида \texttt{if (!node)} на \texttt{if (node == nullptr)}
    \item Добавить комментарии к методам класса \texttt{BST}
\end{enumerate}

Измененный код программы представлен в листинге~\ref{lst:new-code}.

\begin{lstlisting}[caption={Модифицированная реализация BST}, label={lst:new-code}, language=C++]
#include <iostream>
#include <string>
#include <locale>
#include <vector>
#include <windows.h>
#include <cctype> // Добавлен новый заголовочный файл

using namespace std;

struct BSTNode {
    string word;
    BSTNode* left;
    BSTNode* right;
    BSTNode(const string& w) : word(w), left(nullptr), right(nullptr) {}
};

class BST {
    BSTNode* root;

    /* Рекурсивная вставка слова в дерево */
    bool insertNode(BSTNode*& node, const string& word) { // Переименован параметр
        if (node == nullptr) { // Изменена проверка на nullptr
            node = new BSTNode(word);
            return true;
        }
        if (word == node->word) return false;
        if (word < node->word) return insertNode(node->left, word);
        return insertNode(node->right, word);
    }

    bool removeNode(BSTNode*& node, const string& word) {
        if (!node) return false;
        if (word < node->word) return removeNode(node->left, word);
        if (word > node->word) return removeNode(node->right, word);
        if (!node->left) {
            BSTNode* temp = node->right;
            delete node;
            node = temp;
        } else if (!node->right) {
            BSTNode* temp = node->left;
            delete node;
            node = temp;
        } else {
            BSTNode* minNode = findMin(node->right);
            node->word = minNode->word;
            removeNode(node->right, minNode->word);
        }
        return true;
    }

    BSTNode* findMin(BSTNode* node) {
        if (!node) return nullptr;
        while (node->left) node = node->left;
        return node;
    }

    void clearTree(BSTNode*& node) { // Рекурсивная очистка всех узлов дерева
        if (!node) return;
        clearTree(node->left);
        clearTree(node->right);
        delete node;
        node = nullptr;
    }

    void inorder(BSTNode* node, vector<string>& result) const { // Рекурсивный обход дерева в порядке возврастания элементов
        if (!node) return;
        inorder(node->left, result);
        result.push_back(node->word);
        inorder(node->right, result);
    }

    // Добавлен деструктор
    ~BST() {
        clear();
    }

public:
    BST() : root(nullptr) {}

    // Добавлена проверка входных данных
    bool validateWord(const string& word) {
        for (char c : word) {
            if (!isalpha(c) && static_cast<unsigned char>(c) < 128) {
                return false;
            }
        }
        return true;
    }

    bool insert(const string& word) {
        if (!validateWord(word)) return false;
        return insertNode(root, word);
    }

    bool remove(const string& word) {
        return removeNode(root, word);
    }

    void clear() {
        clearTree(root);
    }

    vector<string> getAllWords() const {
        vector<string> result;
        inorder(root, result);
        return result;
    }
};

int main() {
    // Добавлена проверка кириллических символов
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);
    setlocale(LC_ALL, "Russian");

    BST dictionary;
    cout << "Команды:\n"
         << "0 - очистить словарь;\n"
         << "1,<строка> - добавить строку;\n"
         << "2,<строка> - удалить строку;\n"
         << "3 - вывести все слова;\n"
         << "4 - завершить работу.\n\n";

    /* Основной цикл обработки ввода */
    while (true) {
        string input;
        if (!getline(cin, input)) break;
        if (input.empty()) {
            cout << "Некорректный ввод!\n";
            continue;
        }
        int commaPos = input.find(',');
        int command;
        string cmdStr, word;
        if (commaPos == (int)string::npos) {
            cmdStr = input;
        } else {
            cmdStr = input.substr(0, commaPos);
            if (commaPos + 1 < (int)input.size()) {
                word = input.substr(commaPos + 1);
            }
        }
        try {
            command = stoi(cmdStr);
        } catch (...) {
            cout << "Некорректный ввод!\n";
            continue;
        }

        switch (command) {
            case 0:
                dictionary.clear();
                cout << "Словарь очищен\n";
                break;
            case 1:
                if (word.empty() || !dictionary.validateWord(word)) { // Добавлена проверка
                    cout << "Недопустимые символы в строке!\n";
                    break;
                }
                if (dictionary.insert(word))
                    cout << "Строка \"" << word << "\" добавлена в словарь\n";
                else
                    cout << "Строка \"" << word << "\" уже есть в словаре\n";
                break;
            case 2:
                if (word.empty()) {
                    cout << "Некорректный ввод!\n";
                    break;
                }
                if (dictionary.remove(word))
                    cout << "Строка \"" << word << "\" удалена из словаря\n";
                else
                    cout << "Строка \"" << word << "\" не найдена в словаре\n";
                break;
            case 3: {
                vector<string> allWords = dictionary.getAllWords();
                if (allWords.empty()) {
                    cout << "Словарь пуст\n";
                    break;
                }
                for (int i = 0; i < (int)allWords.size(); i++) {
                    cout << allWords[i];
                    if (i + 1 < (int)allWords.size()) cout << ", ";
                }
                cout << "\n";
                break;
            }
            case 4:
                return 0;
            default:
                cout << "Некорректный ввод!\n";
                break;
        }
    }
    return 0;
}
\end{lstlisting}

Список изменений:
\begin{itemize}
    \item В строке 21: Переименован параметр \texttt{w} в \texttt{word}.
    \item В строке 22: Добавлена явная проверка на nullptr вместо неявного приведения к bool.
    \item В строке 73: Добавлен явный деструктор для очистки памяти.
    \item В строках 81-88: Реализована проверка на допустимые символы (латиница и кириллица).
    \item В строках 20, 57, 65: Добавлены поясняющие комментарии к методам.
\end{itemize}

    \section{Обобщение работы группы}  
    В ходе работы группы в программе были выявлены следующие недостатки: отсутствовала проверка корректности входных данных (например, на допустимые символы в строках); некоторые параметры методов имели неудачные названия (например, параметр \texttt{w} вместо \texttt{word}); сложные для понимания фрагменты кода, такие как рекурсивные методы работы с деревом, не были снабжены комментариями.  

    Сделан вывод, что код в целом соответствует стандартам оформления, что делает его удобным для чтения и анализа. Однако для улучшения читаемости и надёжности были предложены и внедрены изменения.

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

    \newpage
    \section*{Заключение}
    \addcontentsline{toc}{section}{Заключение}

    В ходе выполнения данной лабораторной работы был изучен метод ручного тестирования
    — инспекция кода. Было проведено инспекционное собрание, на котором участвовали секретарь, программист и специалист по тестированию.
    По итогу собрания был составлен протокол заседания, далее программистом был проведен
    анализ составленного протокола и внесены необходимые изменения в код программы.
    В результате выполненной работы, можно сделать вывод, что методы ручного тестирования, в частности инспекция кода, являются эффективными для выявления ошибок в логике
    программы, а также для выявления несоответствий требованиям.

    В результате работы я пришёл к выводу, что методы ручного тестирования, особенно инспекция кода, эффективны: удалось улучшить читаемость кода, добавить поясняющие комментарии и исправить некоторые недочёты. В частности, было добавлено считывание словаря из файла, выход из бесконечного цикла и обработка пустых строк при вводе для повышения удобства и безопасности.

\newpage
\section*{Список литературы}
\addcontentsline{toc}{section}{Список литературы}

\vspace{-1.5cm}
\begin{thebibliography}{0}
    \bibitem{mayers}
    Майерс, Г. Искусство тестирования программ. -- Санкт-Петербург: Диалектика, 2012 г.
\end{thebibliography}

\end{document}