% -*- mode: LaTeX; coding: koi8-r -*-
% $Id: rm_libchecker.tex 68 2006-03-04 16:10:06Z cher $
% Copyright (C) 2003-2006 Alexander Chernov <cher@unicorn.cmc.msu.ru>

\chapter{Разработка проверяющих программ}

В данной главе описывается интерфейс, которому должна
удовлетворять проверяющая программа, работающая в системе
\S{ejudge}. Далее описывается библиотека \V{libchecker} для
проверяющих программ на языке Си, которая призвана упростить
написание проверяющих программ. Для проверяющих программ,
написанных на Delphi или FreePascal, можно использовать популярный
модуль testlib, но описание этого модуля не входит в данную
документацию.

\section{Проверяющие программы}

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

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

\subsection{Параметры командной строки}

В зависимости от значения конфигурационных переменных
\hyperlink{F:serve.cfg,S:problem,T:use_corr}{\V{use\_corr}},
\hyperlink{F:serve.cfg,S:problem,T:use_info}{\V{use\_info}} и
\hyperlink{F:serve.cfg,S:problem,T:use_tgz}{\V{use\_tgz}}
(доступных как на глобальном уровне, так и на уровне описания
задачи) проверяющая программа может получать от 2 до 6
параметров командной строки.

Первый параметр (\V{argv[1]}) командной строки --- это всегда
путь к файлу с тестовыми данными. Несмотря на то, что каждый раз
файл с тестовыми данными копируется из каталога тестов в рабочий
каталог тестирования, проверяющей программе всегда передаётся
путь к файлу с тестовыми данными, находящемуся в каталоге
тестов, а не в рабочем каталоге тестирования, так как
тестируемая программа может случайно или намеренно испортить
свой входной файл.

Второй параметр (\V{argv[2]}) командной строки проверяющей
программы --- это всегда путь к файлу с результатом работы
тестируемой программы. Этот файл находится в рабочем каталоге
тестирования.

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

Если значение конфигурационной переменной
\hyperlink{F:serve.cfg,S:problem,T:use_corr}{\V{use\_corr}}
установлено в \I{true}, то следующим параметром командной строки
(\V{argv[3]}) передаётся путь к файлу с правильным ответом на
данный тест. Этот файл находится в каталоге правильных ответов.
На самом деле, этот файл не обязан содержать в точности
правильный ответ, но может содержать произвольную информацию,
которая поможет проверяющей программе проверить правильность
ответа тестируемой программы.

Если значение конфигурационной переменной
\hyperlink{F:serve.cfg,S:problem,T:use_info}{\V{use\_info}}
установлено в \I{true}, то следующим параметром командной строки
(\V{argv[3]} или \V{argv[4]} в зависимости от значения
переменной \V{use\_corr}) передаётся путь к файлу с
дополнительной информацией о тесте (см. раздел
\ref{sectionTestInf}).

Наконец, если значение конфигурационной переменной
\hyperlink{F:serve.cfg,S:problem,T:use_tgz}{\V{use\_tgz}}
установлено в \I{true}, проверяющей программе передаются ещё два
параметра командной строки: путь к незаархивированному исходному
содержимому рабочего каталога тестируемой программы и путь к
рабочему каталогу тестируемой программы. Проверяющая программа
может сравнивать первый каталог со вторым, чтобы определить, как
изменился рабочий каталог в результате работы программы.

\subsection{Результат проверки}

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

Проверяющая программа сообщает системе \S{ejudge} свой вердикт с
помощью кода завершения программы.

\begin{center}
\begin{tabular}{|p{2cm}|p{12cm}|}
\hline
\B{Код завершения} & \B{Вердикт} \\
\hline
0 & OK --- тестируемая программа на данном тесте дала правильный
    ответ.\\
4 & Presentation error --- результат, выведенный тестируемой
    программой, не может быть проверен на правильность,
    поскольку нарушен специфицированный в условии задачи формат
    вывода результата.\\
5 & Wrong answer --- тестируемая программа выработала
    неправильный ответ.\\
6 & Check failed --- внутренняя ошибка проверяющей программы.\\
\hline
\end{tabular}
\end{center}

Если проверяющая программа завершается аварийно из-за получения
фатального сигнала, или код её завершения не равен одному из
кодов, перечисленных выше, система \S{ejudge} считает, что
произошла внутренняя ошибка проверяющей программы. Такие посылки
отмечаются в базе данных посылок со статусом
``Check failed''.

\section{Библиотека \V{libchecker}}

Библиотека предназначена для разработки проверяющих программ для
системы \S{ejudge}. Задача проверяющей программы --- по
результату работы тестируемой программы определить, правильный
ли ответ был получен тестируемой программой.

Библиотека представляет собой каркас (framework), в который
вставляются необходимые действия по проверке результата.
В библиотеке определяется функция \V{main}, то есть при старте
проверяющей программы управление получает модуль инициализации
библиотеки \V{libchecker}. После инициализации библиотеки
вызывается функция \V{checker\_main}, которая играет роль
функции \V{main} для проверяющей программы.

\subsection{Вид проверяющей программы}

\subsubsection{Простейший случай}

Если проверяющая программа не использует расширенных возможностей
системы \S{ejudge}: передачи дополнительного файла информации
о тестах \hyperlink{F:test.inf}{\V{test.inf}} или подготовленного
рабочего каталога, простейшая проверяющая программа выглядит
следующим образом:

{\small
\begin{lstlisting}[numbers=left,print]
#define NEED_CORR 0
#include "checker.h"

int checker_main(int argc, char **argv)
{
  // checker code
  checker_OK();
}
\end{lstlisting}
}

В строке 1 устанавливается режим работы проверяющей программы.
Перед подключением заголовочного файла \V{"checker.h"}
обязательно должны быть определён макрос \V{NEED\_CORR}

Макрос \V{NEED\_CORR} необходимо установить в ненулевое
значение, если для проверки решения проверяющей программе
требуется файл с правильным ответом. Тогда файл с правильным
ответом будет открыт автоматически при старте проверяющей
программы, и он будет доступен через глобальные переменные
\V{f\_corr} и \V{f\_arr[2]}. Путь к файлу с правильным ответом
передаётся третьим параметром командной строки и может быть
использован и в функции \V{checker\_main}. Обратите внимание,
что чтобы программа \S{run} передавала проверяющей программе
путь к файлу с правильным ответом, в разделе описания задачи
должна быть установлена конфигурационная переменная
\hyperlink{F:serve.cfg,S:problem,T:use_corr}{\V{use\_corr}}.

Если макрос \V{NEED\_CORR} установлен в \V{0}, считается, что
файл с правильным ответом для проверяющей программы не нужен, и
он не открывается.

{\raggedright
Если макрос \V{NEED\_CORR} не установлен до включения
заголовочного файла \V{"checker.h"}, то будет выдана ошибка
компиляции.

}

%В строках 1, 2 и 3 устанавливается режим работы проверяющей
%программы. Перед подключением заголовочного файла
%\V{"checker.h"} обязательно должны быть определены три макроса
%\V{NEED\_CORR}, \V{NEED\_INFO}, \V{NEED\_TGZ}.

\subsubsection{Использование файлов информации о тестах}

Если проверяющая программа использует файлы информации о тесте
\hyperlink{F:test.inf}{\V{test.inf}}, то до подключения
заголовочного файла \V{"checker.h"} необходимо установить
макрос \V{NEED\_INFO}
в ненулевое значение. Тогда начало проверяющей программы
может иметь следующий вид:

{\small
\begin{lstlisting}[numbers=left,print]
#define NEED_CORR 0
#define NEED_INFO 1
#include "checker.h"
\end{lstlisting}
}

Если макрос \V{NEED\_INFO} установлен в ненулевое
значение, для проверки решения проверяющей программе
необходим файл с дополнительной информацией о тесте
\hyperlink{F:test.inf}{\V{test.inf}}. Тогда файл с информацией о
тесте будет считан автоматически при старте проверяющей
программы, и информация из него будет доступна через глобальную
переменную \V{test\_info}. Путь к файлу информации о тесте
передаётся в параметрах командной строки и доступен в функции
\V{checker\_main}. Чтобы программа \S{run} передавала
проверяющей программе путь к файлу с правильным ответом, в
разделе описания задачи должна быть установлена конфигурационная
переменная \hyperlink{F:serve.cfg,S:problem,T:use_info}{\V{use\_info}}.

Если макрос \V{NEED\_INFO} установлен в \V{0}, считается, что
файл с информацией о тесте для проверяющей программы не нужен, и
он не открывается. Если макрос \V{NEED\_INFO} не установлен,
его значение полагается равным 0.

\subsubsection{Использование подготовленного рабочего каталога}

Если проверяющая программа использует подготовленный рабочий
каталог, то до подключения заголовочного файла \V{"checker.h"}
необходимо установить макрос \V{NEED\_TGZ}
в ненулевое значение. Тогда начало проверяющей программы
может иметь следующий вид:

{\small
\begin{lstlisting}[numbers=left,print]
#define NEED_CORR 0
#define NEED_TGZ  1
#include "checker.h"
\end{lstlisting}
}

Если макрос \V{NEED\_TGZ} установлен в ненулевое значение,
тестируемая программа запускается в специально создаваемом
каталоге, начальное содержимое которого копируется из каталога
тестов (см. описание конфигурационной переменной
\hyperlink{F:serve.cfg,S:problem,T:use_tgz}{\V{use\_tgz}}). В
таком случае при старте проверяющей программы будет открыт
рабочий каталог тестируемой программы и открыт эталонный каталог
из тестового каталога. Каталоги открываются с помощью
стандартной функции \V{opendir}. Дескрипторы открытых каталогов
помещаются в глобальные переменные \V{dir\_in} (эталонный
каталог), \V{dir\_out} (рабочий каталог после завершения
тестируемой программы). Дополнительно в глобальные переменные
\V{dir\_in\_path}, \V{dir\_out\_path} помещаются пути к этим
каталогам. Пути к этим каталогам передаются тестирующей
программе в аргументах командной строки, но только если
конфигурационная переменная \V{use\_tgz} для данной задачи
установлена в \I{true}.

Если макрос \V{NEED\_TGZ} установлен в нулевое значение или вообще
не установлен, считается, что подготовленный рабочий каталог не
используется, и никакие каталоги не открываются.

\subsubsection{Начало работы проверяющей программы}

При запуске проверяющей программы будет выполнена начальная
инициализация в соответствии с установками, заданными макросами
\V{USE\_CORR}, \V{USE\_INFO} и \V{USE\_TGZ}. Затем управление
будет передано на функцию \V{checker\_main}, которая должна
выполнить проверку и вернуть код возврата в соответствии с
соглашениями об интерфейсе проверяющих программ. Функции
\V{checker\_main} передаются все аргументы командной строки
проверяющей программы.

\subsection{Компиляция проверяющих программ}

При компиляции проверяющих программ, использующих библиотеку
\V{libchecker} необходимо указывать опции компилятора, задающие
пути к заголовочным файлам, пути к исполняемым файлам и т. п.
Для этой цели рекомендуется использовать программу
\hyperlink{P:ejudge-config}{\S{ejudge-config}}, передавая ей
опции \verb|--cflags|, \verb|--ldflags|, \verb|--libs|.

Опция \verb|--cflags| вызывает печать на стандартный поток вывода
опций компилятора, указывающих путь к заголовочным файлам библиотеки
\V{libchecker}. Опция \verb|--ldflags| вызывает печать на стандартный
поток вывода опций компилятора, указывающих пути к библиотечным файлам
\V{libchecker}. Наконец, опция \verb|--libs| вызывает печать на
стандартный поток вывода имен необходимых библиотек.

Таким образом, строка компиляции проверяющей программы может выглядеть
следующим образом:
{\small
\begin{verbatim}
gcc `${prefix}/bin/ejudge-config --cflags --ldflags` check_A.c \
  -o check_A `${prefix}/bin/ejudge-config --libs`
\end{verbatim}

}

{\raggedright
Здесь \verb|${prefix}| должен быть заменён на корень инсталляции
системы \S{ejudge}. Если каталог \verb|${prefix}/bin| добавлен
в переменную \V{PATH}, вместо \verb|${prefix}/bin/ejudge-config|
можно просто писать \verb|ejudge-config|.

}

Аналогичным образом может быть написан \V{Makefile}:
{\small
\begin{verbatim}
EJUDGE_CONFIG = ${prefix}/bin/ejudge-config
CC = gcc
LD = gcc
CFLAGS = $(shell $(EJUDGE_CONFIG) --cflags)
LDFLAGS = $(shell $(EJUDGE_CONFIG) --ldflags)
LDLIBS = $(shell $(EJUDGE_CONFIG) --libs)

all : check_A
check_A : check_A.o
check_A.o : check_A.c
\end{verbatim}

}

Здесь \verb|${prefix}| также должен быть заменён на корень инсталляции
системы \S{ejudge}.

Если для создания и настройки турниров используется веб-редактор
настроек турниров в программе \hyperlink{P:serve-control}{\S{serve-control}},
то все проверяющие программы могут быть скомпилированы автоматически
при выполнении команды ``Check contest settings''.
При этом корректные опции компилятора будут указаны автоматически.

\subsection{Константы}

{\small
\begin{lstlisting}[print]
enum
{
  RUN_OK               = 0,
  RUN_PRESENTATION_ERR = 4,
  RUN_WRONG_ANSWER_ERR = 5,
  RUN_CHECK_FAILED     = 6
};
\end{lstlisting}
}

Данные константы перечисляют коды завершения, доступные для
проверяющей программы. Если проверяющая программа вернёт
какой-либо код возврата, кроме указанных выше, программа \S{run}
преобразует этот код возврата в код возврата
\V{RUN\_CHECK\_FAILED}.

\subsection{Глобальные переменные}

В данном разделе перечислены глобальные переменные, определяемые
в библиотеке \V{libchecker} и доступные для использования
проверяющими программами.

\subsubsection{Переменная \V{f\_in}}

{\small
\begin{lstlisting}[print]
extern FILE *f_in;
\end{lstlisting}
}

С помощью данного дескриптора потока можно работать с входным
файлом для тестируемой программы, то есть файлом, содержащим
тест. Даже если согласно условию задачи тестируемая программа
должна считывать данные со стандартного потока ввода, данный
дескриптор потока будет связан с файлом входных данных на
диске. Входной файл всегда открывается в каталоге тестов, а
не в рабочем каталоге программы. Если при открытии входного
файла при запуске проверяющей программы возникает ошибка, то
выполнение проверяющей программы немедленно завершается с кодом
возврата \V{RUN\_CHECK\_FAILED}. Функция \V{checker\_main} в
этом случае даже не получает управление.

\subsubsection{Переменная \V{f\_team}}

{\small
\begin{lstlisting}[print]
extern FILE *f_team;
\end{lstlisting}
}

Данная переменная содержит дескриптор потока, связанный с
выходным файлом тестируемой программы. Если согласно условию
задачи тестируемая программа должна выводить результат на
стандартный поток вывода, этот дескриптор потока связан с
временным файлом на диске, где находится вся выдача на
\V{stdout} (вывод в \V{stderr} перенаправляется в другой файл и
проверяющей программе не доступен). Если согласно условию задачи
результаты должны быть записаны в файл, то этот дескриптор
потока связан с этим файлом. Если при открытии выходного
файла при запуске проверяющей программы возникает ошибка, то
выполнение проверяющей программы немедленно завершается с кодом
возврата \V{RUN\_PRESENTATION\_ERR}. Функция \V{checker\_main} в
этом случае даже не получает управление.

\subsubsection{Переменная \V{f\_corr}}

{\small
\begin{lstlisting}[print]
extern FILE *f_corr;
\end{lstlisting}
}

Данная переменная содержит дескриптор потока, связанный с
эталонным файлом (файлом с правильным ответом). Этот файл
открывается, если макрос \V{USE\_CORR} установлен в ненулевое
значение. Если при этом в проверяющую программу передано
неверное количество параметров командной строки, или при
открытии файла возникла ошибка, выполнение проверяющей программы
немедленно завершается с кодом возврата \V{RUN\_CHECK\_FAILED}.
Функция \V{checker\_main} в этом случае даже не получает
управление.

\subsubsection{Переменная \V{f\_arr}}

{\small
\begin{lstlisting}[print]
extern FILE *f_arr[3];
\end{lstlisting}
}

Данный массив содержит те же самые дескрипторы потоков, что и
описанные выше глобальные переменные и введён для удобства
программиста. \V{f\_arr[0]} соответствует \V{f\_in},
\V{f\_arr[1]} соответствует \V{f\_team}, а \V{f\_arr[2]} ---
\V{f\_corr} (если последний открыт).

Некоторые функции требуют в качестве параметра, задающего
входной поток, целое число. Это целое число используется как
индекс в массиве \V{f\_arr}, то есть \V{0} --- входной файл,
\V{1} --- выходной файл тестируемой программы, \V{2} ---
эталонный файл.

\subsubsection{Переменная \V{f\_arr\_names}}

{\small
\begin{lstlisting}[print]
extern const char * const f_arr_names[3];
\end{lstlisting}
}

Этот массив содержит <<имена>> потоков для массива \V{f\_arr}. В
соответствующем Си-файле массив определяется следующим образом:

{\small
\begin{lstlisting}[print]
const char * const f_arr_names[3] =
{
  "input",
  "team output",
  "correct output"
};
\end{lstlisting}
}

\subsubsection{Переменная \V{dir\_in}}

{\small
\begin{lstlisting}[print]
extern DIR *dir_in;
\end{lstlisting}
}

Данная переменная содержит дескриптор открытого эталонного
рабочего каталога тестируемой программы. Переменная доступна,
только если макрос \V{USE\_TGZ} установлен в ненулевое значение.
Эталонный рабочий каталог находится в том же каталоге, где и
тесты. Эталонный рабочий каталог можно считать специальной
формой входных данных программы. Если в проверяющую программу
передано неверное количество параметров командной строки, или
при открытии каталога возникла ошибка, выполнение проверяющей
программы немедленно завершается с кодом возврата
\V{RUN\_CHECK\_FAILED}. Функция \V{checker\_main} в этом случае
даже не получает управление.

\subsubsection{Переменная \V{dir\_out}}

{\small
\begin{lstlisting}[print]
extern DIR *dir_out;
\end{lstlisting}
}

Данная переменная содержит дескриптор открытого рабочего
каталога тестируемой программы, оставшегося после её завершения.
Переменная доступна, только если макрос \V{USE\_TGZ} установлен
в ненулевое значение. Если в проверяющую программу передано
неверное количество параметров командной строки, или при
открытии каталога возникла ошибка, выполнение проверяющей
программы немедленно завершается с кодом возврата
\V{RUN\_CHECK\_FAILED}. Функция \V{checker\_main} в этом случае
даже не получает управление.

\subsubsection{Переменная \V{dir\_in\_path}}

{\small
\begin{lstlisting}[print]
extern char *dir_in_path;
\end{lstlisting}
}

Данная переменная содержит путь к эталонному рабочему каталогу
тестируемой программы. Переменная доступна, только если макрос
\V{USE\_TGZ} установлен в ненулевое значение.

\subsubsection{Переменная \V{dir\_out\_path}}

{\small
\begin{lstlisting}[print]
extern char *dir_out_path;
\end{lstlisting}
}

Данная переменная содержит путь к рабочему каталогу тестируемой
программы. Переменная доступна, только если макрос \V{USE\_TGZ}
установлен в ненулевое значение.

\subsubsection{Переменная \V{test\_info}}

{\small
\begin{lstlisting}[print]
struct testinfo_struct
{
  int cmd_argc;
  unsigned char **cmd_argv;
  unsigned char *comment;
  unsigned char *team_comment;
};
typedef struct testinfo_struct testinfo_t;

extern testinfo_t test_info;
\end{lstlisting}
}

Переменная \V{test\_info} содержит дополнительную информацию о
тесте, заданную в \V{.inf}-файле. Переменная \V{test\_info}
доступна, если макрос \V{NEED\_INFO} установлен в ненулевое
значение. Если в проверяющую программу передано неверное
количество параметров командной строки, или при открытии
файла с дополнительной информацией о тесте возникла ошибка,
выполнение проверяющей  программы немедленно завершается с кодом
возврата \V{RUN\_CHECK\_FAILED}. Функция \V{checker\_main} в
этом случае даже не получает управление.

Поля структуры \V{testinfo\_t} имеют следующее назначение.

\begin{itemize}
\item
\K{cmd\_argc}. Содержит количество аргументов командной строки,
которые были переданы в тестируемую программу при её запуске.
\item
\K{cmd\_argv}. Аргументы командной строки, которые были переданы
в тестируемую программу при её запуске.
\item
\K{comment}. Дополнительный комментарий к тесту. Текст
комментария добавляется в судейский протокол тестирования.
\item
\K{team\_comment}. Дополнительный комментарий к тесту. Текст
комментария добавляется в пользовательский протокол
тестирования.
\end{itemize}

\subsection{Макросы распределения памяти}

{\small
\begin{lstlisting}[print]
#define XCALLOC(p,s)
#define XREALLOC(p,s)
#define XALLOCA(p,s)
#define XALLOCAZ(p,s)
#define XMEMMOVE(d,s,c)
#define XMEMZERO(d,c)
\end{lstlisting}
}

Данные макросы предоставляют удобный интерфейс к функциям
выделения динамической памяти.

\subsubsection{Макрос \V{XCALLOC}}

Макрос \V{XCALLOC(p,s)} выделяет динамическую память и обнуляет её.
Если попытка выделения динамической памяти завершилась ошибкой,
процесс немедленно завершается с кодом возврата
\V{RUN\_CHECK\_FAILED}. Таким образом можно считать, что
выделение памяти с помощью данного макроса всегда завершается
успешно. \V{p} должно быть переменной, элементом массива или
полем структуры (то есть \V{p} --- это адресуемое выражение,
которое может находиться в левой части оператора присваивания
--- \I{lvalue}). \V{p} должно иметь указательный тип. Выражение
для \V{p} не должно иметь побочных эффектов (то есть не должно
использовать операцию присваивания, \V{++} или \V{-{}-}.
 \V{s} --- выражение целочисленного типа, которое не обязано
быть lvalue или не иметь побочных эффектов. Если \V{p} имеет тип
\V{TYPE *}, то вызов макроса \V{XCALLOC(p, n)} выделяет
динамическую память под массив из \V{n} элементов типа \V{TYPE}.
Например,
{\small
\begin{lstlisting}[print]
  int *p;

  XCALLOC(p, 10);
\end{lstlisting}
}
выделит память под массив из 10 элементов типа \V{int} и
присвоит адрес начала массива переменной \V{p}. Выделенная
память может быть освобождена явно с помощью вызова функции
\V{free}.

\subsubsection{Макрос \V{XREALLOC}}

Макрос \V{XREALLOC(p,s)} изменяет размер выделенного блока памяти.
Если попытка изменения размера динамической памяти завершилась
ошибкой, процесс немедленно завершается с кодом возврата
\V{RUN\_CHECK\_FAILED}. \V{p} должно быть lvalue указательного
типа без побочных эффектов. \V{s} --- это выражение целого типа
--- новое количество элементов в массиве. Если размер области
памяти увеличивается, добавленная память не инициализируется.
Выделенная память может быть освобождена явно с помощью вызова
функции \V{free}.

\subsubsection{Макрос \V{XALLOCA}}

Макрос \V{XALLOCA(p,s)} выделяет память в стековой области.
Выделенная память не инициализируется. Если запрашиваемый размер
памяти превышает возможности системы, процесс немедленно
завершается либо по сигналу \V{SIGSEGV} (Segmentation fault),
либо завершится с кодом возврата \V{RUN\_CHECK\_FAILED}. В обоих
случаях будет диагностирована внутренняя ошибка тестировщика.
\V{p} должно быть lvalue указательного типа без побочных
эффектов. \V{s} --- это выражение целого типа --- количество
элементов в массиве. Поскольку память выделяется на стеке, она
будет освобождена автоматически при завершении работы функции, в
которой используется \V{XALLOCA}. Функция \V{free}
использоваться не должна.

\subsubsection{Макрос \V{XALLOCAZ}}

Макрос \V{XALLOCAZ(p,s)} выделяет память в стековой области и
инициализирует её нулями. В остальном данный макрос полностью
аналогичен \V{XALLOCA}.

\subsubsection{Макрос \V{XMEMMOVE}}

Макрос \V{XMEMMOVE(d,s,c)} копирует массив из \V{c} элементов,
начинающийся с адреса \V{s}, по адресу \V{d}. Области памяти
могут накладываться произвольным образом. \V{d} должно быть
выражением указательного типа без побочных эффектов. \V{s} ---
это выражение указательного типа.

\subsubsection{Макрос \V{XMEMZERO}}

Макрос \V{XMEMZERO(d,c)} очищает массив из \V{c} элементов типа
\V{TYPE}, начинающийся с адреса \V{s}. \V{s} должно быть
выражением указательного типа \V{TYPE *}.

\subsection{Функции выдачи диагностики}

\subsubsection{Функция \V{checker\_OK}}

{\small
\begin{lstlisting}[print]
void checker_OK(void);
\end{lstlisting}
}

Данная функция печатает на стандартный поток вывода сообщение
\V{"OK"} и завершает проверяющую программу с кодом возврата 0,
сигнализируя программе \S{run} о том, что тестируемая программа
дала правильный ответ на тесте. Данная функция никогда не
возвращает управление в проверяющую программу.

\subsubsection{Функция \V{fatal}}

{\small
\begin{lstlisting}[print]
void fatal(int code, char const *format, ...);
\end{lstlisting}
}

Данная функция печатает на стандартный поток вывода сообщение в
соответствии со спецификацией формата \V{format} для функции
\V{printf} и дополнительными аргументами, затем печатается
символ перехода на новую строку, затем выполнение проверяющей
программы завершается с кодом возврата \V{code}. Данная функция
никогда не возвращает управление в проверяющую программу.

\subsubsection{Функция \V{fatal\_CF}}

{\small
\begin{lstlisting}[print]
void fatal_CF(char const *format, ...);
\end{lstlisting}
}

Данная функция печатает на стандартный поток вывода сообщение в
соответствии со спецификацией формата \V{format} для функции
\V{printf} и дополнительными аргументами, затем печатается
символ перехода на новую строку, затем выполнение проверяющей
программы завершается с кодом возврата \V{RUN\_CHECK\_FAILED}.
Данная функция никогда не возвращает управление в проверяющую
программу.

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

{\small
\begin{lstlisting}[print]
  if (N >= 1000) fatal_CF("Value %d of N is out of range", N); 
\end{lstlisting}
}

\subsubsection{Функция \V{fatal\_PE}}

{\small
\begin{lstlisting}[print]
void fatal_PE(char const *format, ...);
\end{lstlisting}
}

Данная функция печатает на стандартный поток вывода сообщение в
соответствии со спецификацией формата \V{format} для функции
\V{printf} и дополнительными аргументами, затем печатается
символ перехода на новую строку, затем выполнение проверяющей
программы завершается с кодом возврата
\V{RUN\_PRESENTATION\_ERR}. Данная функция никогда не возвращает
управление в проверяющую программу.

Данная функция может использоваться, тогда, когда тестируемая
программа не соблюдает формат выходных данных (Presentation
error).

{\small
\begin{lstlisting}[print]
  if (fscanf(f_team, "%d", &N) != 1)
    fatal_PE("Cannot read N");
\end{lstlisting}
}

\subsubsection{Функция \V{fatal\_WA}}

{\small
\begin{lstlisting}[print]
void fatal_WA(char const *format, ...);
\end{lstlisting}
}

Данная функция печатает на стандартный поток вывода сообщение в
соответствии со спецификацией формата \V{format} для функции
\V{printf} и дополнительными аргументами, затем печатается
символ перехода на новую строку, затем выполнение проверяющей
программы завершается с кодом возврата \V{5} (Wrong answer).
Данная функция никогда не возвращает управление в проверяющую
программу.

Данная функция может использоваться, тогда, когда тестируемая
программа выдаёт неправильный ответ на тест (Wrong answer).

{\small
\begin{lstlisting}[print]
  if (team_ans != corr_ans)
    fatal_WA("Wrong answer: team: %d, corr: %d", team_ans, corr_ans);
\end{lstlisting}
}

\subsubsection{Функция \V{fatal\_read}}

{\small
\begin{lstlisting}[print]
void fatal_read(int streamno, char const *format, ...);
\end{lstlisting}
}

Данная функция печатает на стандартный поток вывода сообщение в
соответствии со спецификацией формата \V{format} для функции
\V{printf} и дополнительными аргументами, затем печатается
символ перехода на новую строку, затем выполнение проверяющей
программы завершается. Код завершения программы зависит от значения
аргумента \V{streamno} --- номера потока. Если значение аргумента равно
\V{0} (файл исходных данных) или \V{2} (файл с правильным ответом),
то код завершения программы равен \V{6} (Check failed). Если значение
аргумента равно \V{1} (файл результата работы программы), то код
завершения программы равен \V{4} (Presentation error).

Данная функция может использоваться в функциях проверяющей программы,
которые должны считывать какие-либо сложные структуры данных из файла.
В этом случае, если считываение не удалось из входного файла или
файла с правильным ответом, то есть из файлов, структура которых
предполагается корректной, генерируется ошибка ``Check failed''.
Но если считывание не удалось из файла результата работы программы
(то есть тестируемая программа работает некорректно), то
генерируется ошибка ``Presentation error''.

\subsection{Функции распределения памяти}

\subsubsection{Функция \V{xmalloc}}

{\small
\begin{lstlisting}[print]
void *xmalloc(size_t size);
\end{lstlisting}
}

Данная функция --- оболочка над стандартной библиотечной
функцией \V{malloc}. В случае, если \V{malloc} возвращает
\V{NULL}, проверяющая программа завершается немедленно с кодом
возврата \V{RUN\_CHECK\_FAILED}. Таким образом, функция
\V{xmalloc} никогда не возвращает \V{NULL}. Память, выделенная
\V{xmalloc} может освобождаться с помощью \V{free}, либо
перераспределяться с помощью \V{xrealloc}, \V{realloc}.

\subsubsection{Функция \V{xcalloc}}

{\small
\begin{lstlisting}[print]
void *xcalloc(size_t nmemb, size_t size);
\end{lstlisting}
}

Данная функция --- оболочка над стандартной библиотечной
функцией \V{calloc}. В случае, если \V{calloc} возвращает
\V{NULL}, проверяющая программа завершается немедленно с кодом
возврата \V{RUN\_CHECK\_FAILED}. Таким образом, функция
\V{xcalloc} никогда не возвращает \V{NULL}. Память, выделенная
\V{xcalloc} может освобождаться с помощью \V{free}, либо
перераспределяться с помощью \V{xrealloc}, \V{realloc}.

\subsubsection{Функция \V{xrealloc}}

{\small
\begin{lstlisting}[print]
void *xrealloc(void *ptr, size_t size);
\end{lstlisting}
}

Данная функция --- оболочка над стандартной библиотечной
функцией \V{realloc}. В случае, если \V{realloc} возвращает
\V{NULL}, проверяющая программа завершается немедленно с кодом
возврата \V{RUN\_CHECK\_FAILED}. Таким образом, функция
\V{xrealloc} никогда не возвращает \V{NULL}. Память, выделенная
\V{xrealloc} может освобождаться с помощью \V{free}, либо
перераспределяться с помощью \V{xrealloc}, \V{realloc}.

\subsubsection{Функция \V{xstrdup}}

{\small
\begin{lstlisting}[print]
char *xstrdup(const char *str);
\end{lstlisting}
}

Данная функция --- оболочка над стандартной библиотечной
функцией \V{strdup}. В случае, если \V{strdup} возвращает
\V{NULL}, проверяющая программа завершается немедленно с кодом
возврата \V{RUN\_CHECK\_FAILED}. Таким образом, функция
\V{xstrdup} никогда не возвращает \V{NULL}. Память, выделенная
\V{xstrdup} может освобождаться с помощью \V{free}, либо
перераспределяться с помощью \V{xrealloc}, \V{realloc}.

\subsection{Проверка конца файла}

{\small
\begin{lstlisting}[print]
void checker_in_eof(void);
void checker_team_eof(void);
void checker_corr_eof(void);
\end{lstlisting}
}

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

Функция \V{checker\_in\_eof} проверяет условие конца файла во
входном тестовом файле, если конец файла не достигнут,
проверяющая программа завершается с кодом
\V{RUN\_CHECK\_FAILED}.

Функция \V{checker\_team\_eof} проверяет условие конца файла в
файле результата тестируемой программы. Если конец файла не
достигнут, проверяющая программа завершается с кодом
\V{RUN\_PRESENTATION\_ERR}.

Функция \V{checker\_corr\_eof} проверяет условие конца файла в
эталонном файле результата. Если конец файла не достигнут,
проверяющая программа завершается с кодом
\V{RUN\_CHECK\_FAILED}.

\subsection{Закрытие файла}

{\small
\begin{lstlisting}[print]
void checker_in_close(void);
void checker_team_close(void);
void checker_corr_close(void);
\end{lstlisting}
}

Эти функции закрывают соответствующий входной файл.
\V{checker\_in\_close} закрывает тестовый файл,
\V{checker\_team\_close} закрывает файл результата тестируемой
программы, \V{checker\_corr\_close} закрывает эталонный файл
результата.

\subsection{Чтение целых и вещественных данных}

{\small
\begin{lstlisting}[print]
int  checker_read_in_int(const char *, int, int *);
int  checker_read_in_unsigned_int(const char *, int,
                                  unsigned int *);
int  checker_read_in_long_long(const char *, int, long long *);
int  checker_read_in_unsigned_long_long(const char *, int,
                                        unsigned long long *);
int  checker_read_in_double(const char *, int, double *);
int  checker_read_in_long_double(const char *, int, long double *);
int  checker_read_team_int(const char *, int, int *);
int  checker_read_team_unsigned_int(const char *, int,
                                    unsigned int *);
int  checker_read_team_long_long(const char *, int, long long *);
int  checker_read_team_unsigned_long_long(const char *, int,
                                          unsigned long long *);
int  checker_read_team_double(const char *, int, double *);
int  checker_read_team_long_double(const char *, int, long double *);
int  checker_read_corr_int(const char *, int, int *);
int  checker_read_corr_unsigned_int(const char *, int,
                                    unsigned int *);
int  checker_read_corr_long_long(const char *, int, long long *);
int  checker_read_corr_unsigned_long_long(const char *, int,
                                          unsigned long long *);
int  checker_read_corr_double(const char *, int, double *);
int  checker_read_corr_long_double(const char *, int, long double *);
\end{lstlisting}
}

Эта группа функций считывает одно целое или вещественное число
из одного из входных файлов. Функции имеют следующий общий вид:

{\small
\begin{lstlisting}[print]
int checker_read_FILE_TYPE(const char *name,
                           int eof_error_flag,
                           TYPE *p_var);
\end{lstlisting}
}

В имени функции \V{FILE} обозначает файл, из которого
считывается число. \V{in} --- входной тестовый файл, \V{team}
--- результат работы тестируемой программы, \V{corr} ---
эталонный файл результата. \V{TYPE} --- тип считываемого
значения.

Параметр \V{name} содержит имя считываемой переменной.
Оно печатается, если чтение значения переменной завершилось
ошибкой. Параметр \V{eof\_error\_flag} определяет реакцию на
достижение конца файла. Если параметр имеет ненулевое значение,
и при попытке чтения из входного файла число считано не было, но
был достигнут конец файла, выполнение проверяющей программы
завершается с ошибкой. Код ошибки зависит от типа входного
файла. Если параметр \V{eof\_error\_flag} равен 0, при
достижении конца файла функция возвращает -1. Параметр
\V{p\_var} --- это адрес, по которому нужно поместить считанное
значение.

Функция возвращает 1, если число было успешно считано, либо -1,
если был достигнут конец файла, а параметр \V{eof\_error\_flag}
равен нулю. Во всех остальных случаях выполнение проверяющей
программы завершается с кодом возврата, зависящем от
считываемого файла. Если считывается входной тестовый файл
\V{in} или эталонный файл результата V{corr}, код возврата равен
\V{RUN\_CHECK\_FAILED}. Если считывается файл результата работы
тестируемой программы, код возврата равен
\V{RUN\_PRESENTATION\_ERROR}.

{\small
\begin{lstlisting}[print]
int  checker_read_int(int, const char *, int, int *);
int  checker_read_unsigned_int(int, const char *, int,
                               unsigned int *);
int  checker_read_long_long(int, const char *, int, long long *);
int  checker_read_unsigned_long_long(int, const char *, int,
                                     unsigned long long *);
int  checker_read_double(int, const char *, int, double *);
int  checker_read_long_double(int, const char *, int, long double *);
\end{lstlisting}
}

Эта группа функций считывает одно целое или вещественное число
из входного файла, задаваемого параметром функции. Функции имеют
следующий общий вид:

{\small
\begin{lstlisting}[print]
int checker_read_TYPE(int ind,
                      const char *name,
                      int eof_error_flag,
                      TYPE *p_var);
\end{lstlisting}
}

Здесь параметр \V{ind} определяет файл, из которого будет
считываться значение. Если \V{ind} равен 0, чтение ведётся из
входного тестового файла (\V{in}). Если \V{ind} равен 1, чтение
ведётся из файла результата тестируемой программы. Если \V{ind}
равен 2, чтение ведётся из эталонного файла результата. В
остальном поведение этих функций аналогично поведению функций
группы \V{checker\_read\_FILE\_TYPE}.

\subsection{Чтение текста}

\subsubsection{Функция \V{checker\_skip\_eoln}}

{\small
\begin{lstlisting}[print]
int  checker_skip_eoln(int ind, int eof_error_flag);
\end{lstlisting}
}

Данная функция пропускает все символы в входном файле до символа
перехода на новую строку \verb|'\n'| включительно.

Здесь параметр \V{ind} определяет файл, из которого будет
считываться значение. Если \V{ind} равен 0, чтение ведётся из
входного тестового файла (\V{in}). Если \V{ind} равен 1, чтение
ведётся из файла результата тестируемой программы. Если \V{ind}
равен 2, чтение ведётся из эталонного файла результата.

Параметр \V{eof\_error\_flag} определяет реакцию, когда конец
файла был достигнут ранее, чем символ \verb|'\n'|. Если параметр
имеет ненулевое значение, то в такой ситуации выполнение
проверяющей программы завершается. Если параметр равен 0,
функция возвращает -1.

Функция возвращает 0, если выполнение функции завершилось без
ошибок, и -1, если был достигнут конец файла, и параметр
\V{eof\_error\_flag} равен 0. Во всех остальных случаях
выполнение проверяющей программы завершается с кодом возврата,
зависящем от считываемого файла. Если считывается входной
тестовый файл \V{in} или эталонный файл результата V{corr}, код
возврата равен \V{RUN\_CHECK\_FAILED}. Если считывается файл
результата работы тестируемой программы, код возврата равен
\V{RUN\_PRESENTATION\_ERROR}.

\subsubsection{Функция \V{checker\_read\_file}}

{\small
\begin{lstlisting}[print]
void checker_read_file(int ind, char **out, size_t *out_size);
\end{lstlisting}
}

Функция считывает файл, задаваемый параметром \V{ind} целиком в
память. Память под считываемый файл выделяется в куче с помощью
функции \V{xrealloc}. Функция не возвращает результат, а при
возможной ошибке ввода вывода выполнение проверяющей программы
завершается с кодом возврата \V{RUN\_CHECK\_FAILED}. Функция
корректно считывает произвольные файлы, в том числе файлы,
содержащие нулевые байты.

Параметр \V{ind} определяет файл, из которого будет считываться
значение. Если \V{ind} равен 0, чтение ведётся из входного
тестового файла (\V{in}). Если \V{ind} равен 1, чтение ведётся
из файла результата тестируемой программы. Если \V{ind} равен 2,
чтение ведётся из эталонного файла результата. Параметр \V{out}
--- это адрес переменной указательного типа, в которую будет
записан адрес начала буфера считанного файла. Параметр
\V{out\_size} --- адрес переменной, в которую будет записан
размер считанного файла.

Под буфер файла выделяется на 1 байт больше, чем размер файла, и
в этот дополнительный байт записывается 0. Таким образом, если
считываемый файл не содержит в середине нулевых байтов, с
буфером считанного файла можно будет работать как с символьной
строкой.

\subsubsection{Функция \V{checker\_read\_buf}}

{\small
\begin{lstlisting}[print]
int  checker_read_buf(int ind,
                      const char *name,
                      int eof_error_flag,
                      char *buf,
                      size_t buf_size);
\end{lstlisting}
}

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

Параметр \V{ind} определяет файл, из которого будет считываться
значение. Если \V{ind} равен 0, чтение ведётся из входного
тестового файла (\V{in}). Если \V{ind} равен 1, чтение ведётся
из файла результата тестируемой программы. Если \V{ind} равен 2,
чтение ведётся из эталонного файла результата.

Параметр \V{name} позволяет задать имя считываемой переменной.
Оно печатается, если выполнение функции завершается из-за ошибки.

Параметр \V{eof\_error\_flag} определяет реакцию, когда конец
файла был достигнут ранее, чем считан хотя бы один значащий
символ строки. Если параметр имеет ненулевое значение, то в
такой ситуации выполнение проверяющей программы завершается.
Если параметр равен 0, функция возвращает -1.

В параметре \V{buf} передаётся адрес начала буфера для
считывания строки. Если \V{buf} --- нулевой указатель, функция
завершает выполнение проверяющей программы с кодом возврата
\V{RUN\_CHECK\_FAILED}.

В параметре \V{buf\_size} передаётся размер буфера. Если размер
буфера равен 0 или превышает 100000, функция завершает
выполнение проверяющей программы с кодом возврата
\V{RUN\_CHECK\_FAILED}. Значение размера буфера на 1 превышает
максимальную длину строки, которая может быть записана в этот
буфер из-за нулевого байта-терминатора строки. Например, если
размер буфера --- 80 байтов, то максимальная длина строки,
которая может быть в него записана --- 79 байтов. Если длина
считанной из файла строки превышает максимальную длину,
выполнение проверяющей программы завершается с соответствующим
кодом возврата.

Функция возвращает длину считанной строки и -1, если был
достигнут конец файла, и параметр \V{eof\_error\_flag} равен 0.
Во всех остальных случаях выполнение проверяющей программы
завершается с кодом возврата, зависящем от считываемого
файла. Если считывается входной тестовый файл \V{in} или
эталонный файл результата V{corr}, код возврата равен
\V{RUN\_CHECK\_FAILED}. Если считывается файл результата работы
тестируемой программы, код возврата равен
\V{RUN\_PRESENTATION\_ERROR}.

\subsubsection{Функция \V{checker\_read\_line}}

{\small
\begin{lstlisting}[print]
int checker_read_line(int ind,
                      const char *name,
                      int eof_error_flag,
                      char **out_str);
\end{lstlisting}
}

Данная функция считывает одну текстовую строку из входного
файла. Текстовая строка --- это последовательность произвольных
байтов, завершающихся байтом \verb|'\n'|, который считается
входящим в строку. Память под считываемую строку текста
выделяется с помощью функции \V{xrealloc}. В буфер строки после
символа \verb|'\n'| добавляется нулевой байт. Если строка не
содержит нулевых байтов в середине, то с ней можно работать как
с символьной строкой Си.

Параметр \V{ind} определяет файл, из которого будет считываться
значение. Если \V{ind} равен 0, чтение ведётся из входного
тестового файла (\V{in}). Если \V{ind} равен 1, чтение ведётся
из файла результата тестируемой программы. Если \V{ind} равен 2,
чтение ведётся из эталонного файла результата.

Параметр \V{name} позволяет задать имя считываемой переменной.
Оно печатается, если выполнение функции завершается из-за ошибки.

Параметр \V{eof\_error\_flag} определяет реакцию, когда конец
файла был достигнут ранее, чем считан хотя бы один символ
строки текста. Если параметр имеет ненулевое значение, то в 
такой ситуации выполнение проверяющей программы завершается.
Если параметр равен 0, функция возвращает -1.

Параметр \V{out\_str} --- это адрес указательной переменной, в
которую будет записан адрес начала буфера строки.

Функция возвращает длину считанной строки и -1, если был
достигнут конец файла, и параметр \V{eof\_error\_flag} равен 0.
Во всех остальных случаях выполнение проверяющей программы
завершается с кодом возврата, зависящем от считываемого
файла. Если считывается входной тестовый файл \V{in} или
эталонный файл результата V{corr}, код возврата равен
\V{RUN\_CHECK\_FAILED}. Если считывается файл результата работы
тестируемой программы, код возврата равен
\V{RUN\_PRESENTATION\_ERROR}.

Длина считанной строки, возвращаемая функцией, корректна, даже
если в середине считанной строки присутствуют нулевые байты. В
этом случае нельзя полагать, что длина считанной строки равна
\V{strlen(*out\_str)}.

\subsubsection{Функция \V{checker\_normalize\_line}}

{\small
\begin{lstlisting}[print]
void checker_normalize_line(char *str);
\end{lstlisting}
}

Данная функция <<нормализует>> поданную на вход символьную
строку \V{str}. Нормализация заключается в отбрасывании
пробельных символов (в том числе \verb|'\n'|) от конца
символьной строки. Поскольку длина строки явно не передаётся,
символьная строка \V{str} не должна содержать нулевых байтов в
середине строки.

\subsubsection{Функция \V{checker\_read\_file\_by\_line}}

{\small
\begin{lstlisting}[print]
void checker_read_file_by_line(int ind,
                               char ***out_lines,
                               size_t *out_lines_num);
\end{lstlisting}
}

Данная функция считывает файл целиком в память. При чтении файл
разбивается на строки текста, используя \verb|'\n'| как
разделитель строк. Считываемый файл не должен содержать нулевые
байты.

Параметр \V{ind} определяет файл, из которого будет считываться
значение. Если \V{ind} равен 0, чтение ведётся из входного
тестового файла (\V{in}). Если \V{ind} равен 1, чтение ведётся
из файла результата тестируемой программы. Если \V{ind} равен 2,
чтение ведётся из эталонного файла результата.

Параметр \V{out\_lines} --- это адрес переменной, в которую
будет записан адрес начала массива строк. Каждый элемент массива
строк --- это указатель на символьную строку, считанную из
файла.

Параметр \V{out\_lines\_num} --- это адрес переменной, в которую
будет записано количество считанных строк, то есть количество
элементов в массиве строк.

Функция не возвращает результата. При возникновении ошибки
чтения из файла выполнение проверяющей программы завершается с
кодом возврата \V{RUN\_CHECK\_FAILED}. Если при чтение файла был
считан нулевой байт, выполнение проверяющей программы
завершается с соответствующим кодом возврата. Если считывается
входной тестовый файл \V{in} или эталонный файл результата
V{corr}, код возврата равен \V{RUN\_CHECK\_FAILED}. Если
считывается файл результата работы тестируемой программы
\V{team}, код возврата равен \V{RUN\_PRESENTATION\_ERROR}.

\subsubsection{Функция \V{checker\_read\_file\_by\_line\_f}}

{\small
\begin{lstlisting}[print]
void checker_read_file_by_line(FILE *f, const char *name,
                               char ***out_lines,
                               size_t *out_lines_num);
\end{lstlisting}
}

Данная функция считывает файл целиком в память. При чтении файл
разбивается на строки текста, используя \verb|'\n'| как
разделитель строк. Считываемый файл не должен содержать нулевые
байты.

Параметр \V{f} --- это дескриптор потока считываемого файла,
а параметр \V{name} --- имя считываемого файла. Имя используется только
в диагностических сообщениях.

Параметр \V{out\_lines} --- это адрес переменной, в которую
будет записан адрес начала массива строк. Каждый элемент массива
строк --- это указатель на символьную строку, считанную из
файла.

Параметр \V{out\_lines\_num} --- это адрес переменной, в которую
будет записано количество считанных строк, то есть количество
элементов в массиве строк.

Функция не возвращает результата. При возникновении ошибки
чтения из файла выполнение проверяющей программы завершается с
кодом возврата \V{RUN\_CHECK\_FAILED}.

\subsubsection{Функция \V{checker\_normalize\_file}}

{\small
\begin{lstlisting}[print]
void checker_normalize_file(char **out_lines,
                            size_t *out_lines_num);
\end{lstlisting}
}

{\raggedright
Данная функция <<нормализует>> файл, считанный в память с
помощью функции \V{checker\_read\_file\_by\_line}. При этом
нормализуется каждая строка файла отбрасыванием пробельных
символов, находящихся в конце строки. После этого отбрасываются
все строки нулевой длины, находящиеся в конце считанного файла.
При этом количество строк в считанном файле уменьшается.

}

{\raggedright
Параметр \V{out\_lines} --- это адрес начала массива строк,
полученный от функции \V{checker\_read\_file\_by\_line}.
Параметр \V{out\_lines\_num} --- это адрес переменной, хранящей
количество строк в считанном файле, тоже полученное от функции
\V{checker\_read\_file\_by\_line}. В результате работы функции
\V{checker\_normalize\_file} значение по адресу, указанному в
данном параметре, может уменьшится. Для пустого файла это
значение равно 0.

}

\subsubsection{Функция \V{checker\_normalize\_spaces\_in\_file}}

{\small
\begin{lstlisting}[print]
void checker_normalize_spaces_in_file(char **out_lines,
                                      size_t *out_lines_num);
\end{lstlisting}
}

{\raggedright
Данная функция <<нормализует>> файл, считанный в память с
помощью функции \V{checker\_read\_file\_by\_line}. Данная
функция выполняет все действия, которые выполняет функция
\V{checker\_normalize\_file}, то есть отбрасывает пробельные
символы с концов строк и удаляет последние пустые строки в
файле. В дополнение к этому функция удаляет все пустые строки из
начала и середины файла, удаляет пробельные символы в начале
каждой строки и заменяет все последовательности пробельных
символов в середине строки на один символ пробела.

}

{\raggedright
Параметр \V{out\_lines} --- это адрес начала массива строк,
полученный от функции \V{checker\_read\_file\_by\_line}.
Параметр \V{out\_lines\_num} --- это адрес переменной, хранящей
количество строк в считанном файле, тоже полученное от функции
\V{checker\_read\_file\_by\_line}. В результате работы функции
\V{checker\_normalize\_file} значение по адресу, указанному в
данном параметре, может уменьшится. Для пустого файла это
значение равно 0.

}

\subsection{Сравнение вещественных чисел}

Как известно, вещественные числа нельзя сравнивать с помощью
операций \verb|<|, \verb|==| и т. д. Кроме того, для вещественных
чисел допустимы значения бесконечности (со знаком) и значения
<<не число>>. Это требует определённых усилий при написании проверяющих
программ, сравнивающих вещественные числа.

Традиционно, для сравнения вещественных чисел задаётся
достаточно малая константа \verb|eps| (например, \verb|1e-8|),
и сравнение ведётся следующим образом:
{\small
\begin{verbatim}
  if (fabs(a - b) <= 2*eps) { // значения равны
\end{verbatim}
}
Однако, если сравнивается два больших числа, такой подход уже
не применим, так как при вычитании двух больших чисел может произойти
катастрофическая потеря точности.

{\raggedright
Поэтому функции сравнения вещественных чисел, реализованные в библиотеке
\S{libchecker}, работают следующим образом:\par}
\begin{itemize}
\item Отдельно проверяются специальные значения \V{NaN}, \V{Inf}, \V{-Inf}.
\item Если оба числа по модулю меньше 1, то сравнение выполняется
сравнением модуля разности двух чисел и числа \verb|2*eps|.
\item В противном случае если числа имеют разные знаки, то числа не равны.
\item В противном случае если порядки двух чисел отличаются больше чем на 1,
то числа не равны.
\item В противном случае из порядка каждого числа вычитается минимальный
порядок двух чисел, далее два получившихся числа сравниваются
сравнением модуля разности двух чисел и числа \verb|2*eps|.
\end{itemize}
Данная процедура работает корректно для всех возможных пар вещественных
чисел.

\subsubsection{Функция \V{checker\_eq\_double}}

{\small
\begin{lstlisting}[print]
int checker_eq_double(double v1, double v2, double eps);
\end{lstlisting}
}

Сравнение двух чисел \V{v1} и \V{v2} типа \K{double}
на равенство с точностью \V{eps}.
Если два числа равны, возвращается \V{1}, в противном случае
возвращается \V{0}. Для сравнения используется алгоритм,
описанный выше.

\subsubsection{Функция \V{checker\_eq\_long\_double}}

{\small
\begin{lstlisting}[print]
int checker_eq_long_double(long double v1, long double v2,
                           long double eps);
\end{lstlisting}
}

Сравнение двух чисел \V{v1} и \V{v2} типа \K{long double}
на равенство с точностью \V{eps}.
Если два числа равны, возвращается \V{1}, в противном случае
возвращается \V{0}. Для сравнения используется алгоритм,
описанный выше.

\subsubsection{Функция \V{checker\_eq\_float}}

{\small
\begin{lstlisting}[print]
int checker_eq_double(float v1, float v2, float eps);
\end{lstlisting}
}

Сравнение двух чисел \V{v1} и \V{v2} типа \K{float}
на равенство с точностью \V{eps}.
Если два числа равны, возвращается \V{1}, в противном случае
возвращается \V{0}. Для сравнения используется алгоритм,
описанный выше.

\subsection{Примеры}

\subsubsection{Сравнение двух целых чисел}

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

{\small
\begin{lstlisting}[print,numbers=left]
#define NEED_CORR 1
#define NEED_INFO 0
#define NEED_TGZ  0
#include "checker.h"

int checker_main(int argc, char **argv)
{
  int team_ans, corr_ans;

  // presentation error, если файл пуст или нельзя считать число
  checker_read_team_int("team_ans", 1, &team_ans);

  // checker failed, если файл пуст или нельзя считать число
  checker_read_corr_int("corr_ans", 1, &corr_ans);

  // presentation error, если после числа в файле идёт "мусор"
  checker_team_eof();

  // checker failed, если после числа в файле идёт "мусор"
  checker_corr_eof();

  if (team_ans != corr_ans)
    fatal_WA("Answers do not match: team = %d, corr = %d",
             team_ans, corr_ans);
  checker_OK();
}
\end{lstlisting}
}

\subsubsection{Сравнение двух текстовых файлов}

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

{\small
\begin{lstlisting}[print,numbers=left]
#define NEED_CORR 1
#define NEED_INFO 0
#define NEED_TGZ  0
#include "checker.h"

int checker_main(int argc, char **argv)
{
  char **team_lines, **corr_lines;
  size_t team_lines_num, corr_lines_num, i;

  // считываем файл результата работы программы
  checker_read_file_by_line(1, &team_lines, &team_lines_num);
  // считываем эталонный файл
  checker_read_file_by_line(2, &corr_lines, &corr_lines_num);
  // отбрасываем пробелы в результате работы программы
  checker_normalize_file(team_lines, &team_lines_num);
  // отбрасываем пробелы в эталонном файле
  checker_normalize_file(corr_lines, &corr_lines_num);

  if (team_lines_num != corr_lines_num)
    fatal_WA("Different number of lines: team = %zu, corr = %zu",
             team_lines_num, corr_lines_num);
  for (i = 0; i < team_lines_num; i++)
    if (strcmp(team_lines[i], corr_lines[i]) != 0)
      fatal_WA("Line %zu differs: team:\n>%s<\ncorr:\n>%s<",
               i + 1, team_lines[i], corr_lines[i]);
  
  checker_OK();
}
\end{lstlisting}
}

\section{Стандартные проверяющие программы}

{\raggedright
Система \S{ejudge} предоставляет набор проверяющих программ для
выполнения простейших операций: сравнения целого значения с
эталонным, сравнения файла с эталонным. Проверяющие программы
по умолчанию инсталлируются в каталог \verb|${prefix}/libexec/ejudge|,
где \verb|${prefix}| --- корень инсталляции системы \S{ejudge},
задаваемый с помощью опции \verb|--prefix| скрипта
\hyperlink{P:configure}{\S{configure}}. Скрипт настраивает все
необходимые переменные в программах, поэтому в конфигурационных
файлах не требуется явное задание пути к каталогу стандартных
проверяющих программ.

}

Для использования стандартных проверяющих программ необходимо
установить переменную
\hyperlink{F:serve.cfg,S:problem,T:standard_checker}{\V{standard\_checker}}
секции описания задачи в имя требуемой стандартной проверяющей
программы. Кроме того, для некоторых стандартных проверяющих
программ требуется установка переменных окружения, которую
можно выполнить с помощью переменной
\hyperlink{F:serve.cfg,S:problem,T:checker_env}{\V{checker\_env}}.

Описание стандартных проверяющих программ дано ниже.

\hypertarget{P:cmp_int}{}
\B{Программа \V{cmp\_int}} сравнивает целое число типа \V{int},
находящееся в файле результата работы проверяемой программы, с
эталонным значением, находящимся в файле правильного ответа.
Кроме этого целого числа файлы не должны содержать никаких
других непробельных символов. Если это условие нарушается,
генерируется соответствующий код возврата \V{RUN\_CHECK\_FAILED}
или \V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в
котором нарушено условие.

\hypertarget{P:cmp_int_seq}{}
\B{Программа \V{cmp\_int\_seq}} сравнивает две последовательности
целых чисел типа \K{int}: выведенную тестируемой программой и эталонную.
Каждая последовательность --- это текстовый файл, в котором числа записаны
в десятичном виде.
Кроме целых чисел файлы не должны содержать никаких непробельных 
символов. Если это условие нарушается, или числа не могут быть
прочитаны (например, нарушен стандартный формат представления числа,
или при чтении произошло переполнение),
генерируется соответствующий код возврата \V{RUN\_CHECK\_FAILED}
или \V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в
котором нарушено условие.
Если сравниваемые файлы содержат разное количество чисел, или сами
числа отличаются, генерируется код возврата \V{RUN\_WRONG\_ANSWER}.

\hypertarget{P:cmp_unsigned_int}{}
\B{Программа \V{cmp\_unsigned\_int}} сравнивает целое число типа
\V{unsigned int}, находящееся в файле результата работы
проверяемой программы, с эталонным значением, находящимся в
файле правильного ответа. Кроме этого целого числа файлы не
должны содержать никаких других непробельных символов. Если это
условие нарушается, генерируется соответствующий код возврата
\V{RUN\_CHECK\_FAILED} или \V{RUN\_PRESENTATION\_ERROR} в
зависимости от файла, в котором нарушено условие.

\hypertarget{P:cmp_unsigned_int_seq}{}
\B{Программа \V{cmp\_unsigned\_int\_seq}} сравнивает две последовательности
целых чисел типа \K{unsigned int}:
выведенную тестируемой программой и эталонную.
Каждая последовательность --- это текстовый файл, в котором числа записаны
в десятичном виде.
Кроме целых чисел файлы не должны содержать никаких непробельных 
символов. Если это условие нарушается, или числа не могут быть
прочитаны (например, нарушен стандартный формат представления числа,
или при чтении произошло переполнение),
генерируется соответствующий код возврата \V{RUN\_CHECK\_FAILED}
или \V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в
котором нарушено условие.
Если сравниваемые файлы содержат разное количество чисел, или сами
числа отличаются, генерируется код возврата \V{RUN\_WRONG\_ANSWER}.

\hypertarget{P:cmp_long_long}{}
\B{Программа \V{cmp\_long\_long}} сравнивает целое число типа
\V{long long}, находящееся в файле результата работы проверяемой
программы, с эталонным значением, находящимся в файле
правильного ответа. Кроме этого целого числа файлы не должны
содержать никаких других непробельных символов. Если это условие
нарушается, генерируется соответствующий код возврата
\V{RUN\_CHECK\_FAILED} или \V{RUN\_PRESENTATION\_ERROR} в
зависимости от файла, в котором нарушено условие.

\hypertarget{P:cmp_long_long_seq}{}
\B{Программа \V{cmp\_long\_long\_seq}} сравнивает две последовательности
целых чисел типа \K{long long}:
выведенную тестируемой программой и эталонную.
Каждая последовательность --- это текстовый файл, в котором числа записаны
в десятичном виде.
Кроме целых чисел файлы не должны содержать никаких непробельных 
символов. Если это условие нарушается, или числа не могут быть
прочитаны (например, нарушен стандартный формат представления числа,
или при чтении произошло переполнение),
генерируется соответствующий код возврата \V{RUN\_CHECK\_FAILED}
или \V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в
котором нарушено условие.
Если сравниваемые файлы содержат разное количество чисел, или сами
числа отличаются, генерируется код возврата \V{RUN\_WRONG\_ANSWER}.

\hypertarget{P:cmp_unsigned_long_long}{}
\B{Программа \V{cmp\_unsigned\_long\_long}} сравнивает целое
число типа \V{unsigned long long}, находящееся в файле
результата работы проверяемой программы, с эталонным значением,
находящимся в файле правильного ответа. Кроме этого целого числа
файлы не должны содержать никаких других непробельных символов.
Если это условие нарушается, генерируется соответствующий код
возврата  \V{RUN\_CHECK\_FAILED} или
\V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в котором
нарушено условие.

\hypertarget{P:cmp_unsigned_long_long_seq}{}
\B{Программа \V{cmp\_unsigned\_long\_long\_seq}}
сравнивает две последовательности
целых чисел типа \K{unsigned long long}:
выведенную тестируемой программой и эталонную.
Каждая последовательность --- это текстовый файл, в котором числа записаны
в десятичном виде.
Кроме целых чисел файлы не должны содержать никаких непробельных 
символов. Если это условие нарушается, или числа не могут быть
прочитаны (например, нарушен стандартный формат представления числа,
или при чтении произошло переполнение),
генерируется соответствующий код возврата \V{RUN\_CHECK\_FAILED}
или \V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в
котором нарушено условие.
Если сравниваемые файлы содержат разное количество чисел, или сами
числа отличаются, генерируется код возврата \V{RUN\_WRONG\_ANSWER}.

\hypertarget{P:cmp_double}{}
\B{Программа \V{cmp\_double}} сравнивает вещественное
число типа \V{double}, находящееся в файле
результата работы проверяемой программы, с эталонным значением,
находящимся в файле правильного ответа. Кроме этого вещественного числа
файлы не должны содержать никаких других непробельных символов.
Если это условие нарушается, генерируется соответствующий код
возврата  \V{RUN\_CHECK\_FAILED} или
\V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в котором
нарушено условие. Вещественное число должно записываться в формате,
распознаваемом функцией \V{scanf}, то есть кроме стандартного
десятичного представления числа допускаются специальные
значения \V{Inf}, \V{NaN}, а также запись вещественных чисел
в шестнадцатиричном виде. Точность сравнения должна задаваться
с помощью переменной окружения \V{EPS}. Если эта переменная не установлена,
программа генерирует код завершения \V{RUN\_CHECK\_FAILED}.

\hypertarget{P:cmp_double_seq}{}
\B{Программа \V{cmp\_double\_seq}} сравнивает две последовательности
вещественных чисел типа \K{double}:
выведенную тестируемой программой и эталонную.
Каждая последовательность --- это текстовый файл, в котором числа записаны
в десятичном виде, распознаваемом функцией \V{scanf} (см. выше).
Кроме целых чисел файлы не должны содержать никаких непробельных 
символов. Если это условие нарушается, или числа не могут быть
прочитаны (например, нарушен стандартный формат представления числа,
или при чтении произошло переполнение),
генерируется соответствующий код возврата \V{RUN\_CHECK\_FAILED}
или \V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в
котором нарушено условие.
Если сравниваемые файлы содержат разное количество чисел, или сами
числа отличаются, генерируется код возврата \V{RUN\_WRONG\_ANSWER}.
Точность сравнения должна задаваться
с помощью переменной окружения \V{EPS}. Если эта переменная не установлена,
программа генерирует код завершения \V{RUN\_CHECK\_FAILED}.

\hypertarget{P:cmp_long_double}{}
\B{Программа \V{cmp\_long\_double}} сравнивает вещественное
число типа \V{long double}, находящееся в файле
результата работы проверяемой программы, с эталонным значением,
находящимся в файле правильного ответа. Кроме этого вещественного числа
файлы не должны содержать никаких других непробельных символов.
Если это условие нарушается, генерируется соответствующий код
возврата  \V{RUN\_CHECK\_FAILED} или
\V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в котором
нарушено условие. Вещественное число должно записываться в формате,
распознаваемом функцией \V{scanf}, то есть кроме стандартного
десятичного представления числа допускаются специальные
значения \V{Inf}, \V{NaN}, а также запись вещественных чисел
в шестнадцатиричном виде. Точность сравнения должна задаваться
с помощью переменной окружения \V{EPS}. Если эта переменная не установлена,
программа генерирует код завершения \V{RUN\_CHECK\_FAILED}.

\hypertarget{P:cmp_long_double_seq}{}
\B{Программа \V{cmp\_long\_double\_seq}} сравнивает две последовательности
вещественных чисел типа \K{long double}:
выведенную тестируемой программой и эталонную.
Каждая последовательность --- это текстовый файл, в котором числа записаны
в десятичном виде, распознаваемом функцией \V{scanf} (см. выше).
Кроме целых чисел файлы не должны содержать никаких непробельных 
символов. Если это условие нарушается, или числа не могут быть
прочитаны (например, нарушен стандартный формат представления числа,
или при чтении произошло переполнение),
генерируется соответствующий код возврата \V{RUN\_CHECK\_FAILED}
или \V{RUN\_PRESENTATION\_ERROR} в зависимости от файла, в
котором нарушено условие.
Если сравниваемые файлы содержат разное количество чисел, или сами
числа отличаются, генерируется код возврата \V{RUN\_WRONG\_ANSWER}.
Точность сравнения должна задаваться
с помощью переменной окружения \V{EPS}. Если эта переменная не установлена,
программа генерирует код завершения \V{RUN\_CHECK\_FAILED}.

\hypertarget{P:cmp_file}{}
\B{Программа \V{cmp\_file}} сравнивает файл результата работы
проверяемой программы с эталонным файлом. При этом игнорируются
пробельные символы на концах строк и пустые строки в конце файла
(то есть используется функция \V{checker\_normalize\_file}).
При несовпадении генерируется код возврата
\V{RUN\_WRONG\_ANSWER}.

\hypertarget{P:cmp_file_nospace}{}
\B{Программа \V{cmp\_file\_nospace}} сравнивает файл результата
работы проверяемой программы с эталонным файлом. При этом
игнорируются пробельные символы в начале и на конце строк,
пустые строки в файле, а последовательности пробельных символов
в середине строки заменяются на один символ пробела (то есть
используется функция \V{checker\_normalize\_spaces\_in\_file}). 
При несовпадении генерируется код возврата
\V{RUN\_WRONG\_ANSWER}.

\hypertarget{P:cmp_bytes}{}
\B{Программа \V{cmp\_bytes}} сравнивает файл результата работы
тестируемой программы и эталонный файл. Сравнение ведётся
байт в байт. При любом расхождении генерируется код возврата
\V{RUN\_WRONG\_ANSWER}.

% Local variables:
%  compile-command: "make"
% End:
