From b9a96ddc83bfcbfe369f4c33fd25692bdd2087c7 Mon Sep 17 00:00:00 2001 From: arthur Date: Mon, 23 Feb 2026 22:51:03 -0300 Subject: [PATCH] feat: new Divide and Conquer problem partialy formated. --- README.md | 6 + dominancia-de-pontos/Makefile | 97 + dominancia-de-pontos/dominancia-de-pontos.pdf | Bin 0 -> 92316 bytes dominancia-de-pontos/dominancia-de-pontos.tex | 39 + dominancia-de-pontos/maratona.cls | 188 + dominancia-de-pontos/problem.json | 64 + dominancia-de-pontos/src/ac.cpp | 10 + dominancia-de-pontos/src/checker.cpp | 18 + dominancia-de-pontos/src/generator.cpp | 93 + dominancia-de-pontos/src/script.sh | 1 + dominancia-de-pontos/src/testlib.h | 5963 +++++++++++++++++ dominancia-de-pontos/src/validator.cpp | 23 + .../statement/description.tex | 9 + dominancia-de-pontos/statement/input.tex | 5 + dominancia-de-pontos/statement/notes.tex | 0 dominancia-de-pontos/statement/output.tex | 1 + dominancia-de-pontos/statement/preamble.tex | 0 dominancia-de-pontos/statement/tutorial.tex | 0 18 files changed, 6517 insertions(+) create mode 100644 dominancia-de-pontos/Makefile create mode 100644 dominancia-de-pontos/dominancia-de-pontos.pdf create mode 100644 dominancia-de-pontos/dominancia-de-pontos.tex create mode 100644 dominancia-de-pontos/maratona.cls create mode 100644 dominancia-de-pontos/problem.json create mode 100644 dominancia-de-pontos/src/ac.cpp create mode 100644 dominancia-de-pontos/src/checker.cpp create mode 100644 dominancia-de-pontos/src/generator.cpp create mode 100644 dominancia-de-pontos/src/script.sh create mode 100644 dominancia-de-pontos/src/testlib.h create mode 100644 dominancia-de-pontos/src/validator.cpp create mode 100644 dominancia-de-pontos/statement/description.tex create mode 100644 dominancia-de-pontos/statement/input.tex create mode 100644 dominancia-de-pontos/statement/notes.tex create mode 100644 dominancia-de-pontos/statement/output.tex create mode 100644 dominancia-de-pontos/statement/preamble.tex create mode 100644 dominancia-de-pontos/statement/tutorial.tex diff --git a/README.md b/README.md index 4a29e75..a699e09 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ Repositório com a formatação de problemas que exploram variadas técnicas de --- +### Divide and Conquer + +- [Dominância de Pontos](./dominancia-de-pontos/) + +--- + ## Greedy - [Caching Offline](./caching-offline) diff --git a/dominancia-de-pontos/Makefile b/dominancia-de-pontos/Makefile new file mode 100644 index 0000000..4227f1a --- /dev/null +++ b/dominancia-de-pontos/Makefile @@ -0,0 +1,97 @@ +# Normal directories +SRC_DIR := src +BIN_DIR := bin +DBG_DIR := bin/debug + +# Grader directories +GRADER := $(wildcard $(SRC_DIR)/grader.cpp) +GRADER_DIR := $(SRC_DIR)/grader +HANDLER_DIR := $(SRC_DIR)/handler + +GRADER_SRC := $(wildcard $(GRADER_DIR)/*.cpp) +GRADER_BIN := $(patsubst $(GRADER_DIR)/%.cpp, $(BIN_DIR)/%, $(GRADER_SRC)) +GRADER_DBG := $(patsubst $(GRADER_DIR)/%.cpp, $(DBG_DIR)/%, $(GRADER_SRC)) + +# Change CPP source directories if grader is defined +ifdef GRADER + SRC := $(wildcard $(HANDLER_DIR)/*.cpp) + BIN := $(patsubst $(HANDLER_DIR)/%.cpp, $(BIN_DIR)/%, $(SRC)) + DBG := $(patsubst $(HANDLER_DIR)/%.cpp, $(DBG_DIR)/%, $(SRC)) +else + SRC := $(wildcard $(SRC_DIR)/*.cpp) + BIN := $(patsubst $(SRC_DIR)/%.cpp, $(BIN_DIR)/%, $(SRC)) + DBG := $(patsubst $(SRC_DIR)/%.cpp, $(DBG_DIR)/%, $(SRC)) +endif + +SRC_C := $(wildcard $(SRC_DIR)/*.c) +BIN_C := $(patsubst $(SRC_DIR)/%.c, $(BIN_DIR)/%, $(SRC_C)) +DBG_C := $(patsubst $(SRC_DIR)/%.c, $(DBG_DIR)/%, $(SRC_C)) + +SRC_JAVA := $(wildcard $(SRC_DIR)/*.java) +BIN_JAVA := $(patsubst $(SRC_DIR)/%.java, $(BIN_DIR)/%.class, $(SRC_JAVA)) +DBG_JAVA := $(patsubst $(SRC_DIR)/%.java, $(DBG_DIR)/%.class, $(SRC_JAVA)) + +CHECKER := $(wildcard $(SRC_DIR)/checker.cpp) + +C := gcc +CPP := g++ +CXX_FLAGS := -Wall -O2 +DEBUG_FLAGS := -Wall -g +BOCA_FLAGS := -static -DBOCA_SUPPORT + +JV = javac +JV_DEBUG = -g +JV_DIR = -d bin +JV_DBG_DIR = -d bin/debug + +.PHONY: all debug release checker clean + +all: debug release checker + +debug: $(DBG) $(DBG_C) $(DBG_JAVA) $(GRADER_DBG) + +release: $(BIN) $(BIN_C) $(BIN_JAVA) $(GRADER_BIN) + +ifdef CHECKER +checker: $(DBG_DIR)/checker-boca $(BIN_DIR)/checker-boca +endif + +$(BIN): $(BIN_DIR)/% : $(SRC_DIR)/%.cpp | $(BIN_DIR) + $(CPP) $(CXX_FLAGS) $^ -o $@ + +$(DBG): $(DBG_DIR)/% : $(SRC_DIR)/%.cpp | $(DBG_DIR) + $(CPP) $(DEBUG_FLAGS) $^ -o $@ + +$(BIN_C): $(BIN_DIR)/% : $(SRC_DIR)/%.c | $(BIN_DIR) + $(C) $(CXX_FLAGS) $^ -o $@ + +$(DBG_C): $(DBG_DIR)/% : $(SRC_DIR)/%.c | $(DBG_DIR) + $(C) $(DEBUG_FLAGS) $^ -o $@ + +$(BIN_JAVA): $(BIN_DIR)/%.class : $(SRC_DIR)/%.java | $(BIN_DIR) + $(JV) $(JV_DIR) $^ + +$(DBG_JAVA): $(DBG_DIR)/%.class : $(SRC_DIR)/%.java | $(DBG_DIR) + $(JV) $(JV_DEBUG) $(JV_DBG_DIR) $^ + +$(GRADER_BIN): $(BIN_DIR)/% : $(GRADER_DIR)/%.cpp $(GRADER) $(GRADER_DIR)/*.h + $(CPP) $(CXX_FLAGS) $^ -o $@ + +$(GRADER_DBG): $(DBG_DIR)/% : $(GRADER_DIR)/%.cpp $(GRADER) $(GRADER_DIR)/*.h + $(CPP) $(DEBUG_FLAGS) $^ -o $@ + +$(BIN_DIR): + mkdir -p $@ + +$(DBG_DIR): + mkdir -p $@ + +$(BIN_DIR)/checker-boca: $(SRC_DIR)/checker.cpp + $(CPP) $(CXX_FLAGS) $(BOCA_FLAGS) $^ -o $@ + +$(DBG_DIR)/checker-boca: $(SRC_DIR)/checker.cpp + $(CPP) $(DEBUG_FLAGS) $(BOCA_FLAGS) $^ -o $@ + +clean: + @echo Cleaning problem files + rm -rf bin diff --git a/dominancia-de-pontos/dominancia-de-pontos.pdf b/dominancia-de-pontos/dominancia-de-pontos.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1da8fc0de7859755826ec311e97883c635875919 GIT binary patch literal 92316 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 d!3)wDz>9pLp^}-td3$N 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 literal 0 HcmV?d00001 diff --git a/dominancia-de-pontos/dominancia-de-pontos.tex b/dominancia-de-pontos/dominancia-de-pontos.tex new file mode 100644 index 0000000..31e9d57 --- /dev/null +++ b/dominancia-de-pontos/dominancia-de-pontos.tex @@ -0,0 +1,39 @@ +\documentclass{maratona} + +\begin{document} +\begin{ProblemaAutor}{}{Dominância de Pontos}{1}{256}{} + +Considere um conjunto de \( n \) pontos \( P = \{(x_0, y_0), (x_1, y_1), \ldots, (x_{n-1}, y_{n-1}) \} \) no plano cartesiano \( \mathbb{R}^2 \). + +Dizemos que um ponto \( A = (x_a, y_a) \) \textbf{domina} outro ponto \( B = (x_b, y_b) \) se, e somente se, as seguintes condições forem satisfeitas simultaneamente: +\begin{itemize} + \item \( x_a < x_b \) + \item \( y_a < y_b \) +\end{itemize} + +O objetivo é contar o número total de relações de dominância existentes no conjunto \( P \). Uma relação de dominância é definida por um par ordenado de pontos \( (P_i, P_j) \) tal que \( P_i \) domina \( P_j \). + +\Entrada + +A entrada consiste em um único caso de teste. + +A primeira linha contém um inteiro \( n \) (\( 1 \le n \le 10^5 \)), representando o número de pontos no conjunto. + +As próximas \( n \) linhas contêm, cada uma, dois inteiros \( x_i \) e \( y_i \) (\( -10^9 \le x_i, y_i \le 10^9 \)), representando as coordenadas de cada ponto. + +\Saida + +A saída deve conter uma única linha com um número inteiro representando o total de relações de dominância encontradas no conjunto de pontos dado. + +\ExemploEntrada +\begin{Exemplo} +\texttt{3} & \texttt{0}\\ +\texttt{1~1} & \\ +\texttt{2~2} & \\ +\texttt{3~3} & \\ +\end{Exemplo} + + + +\end{ProblemaAutor} +\end{document} diff --git a/dominancia-de-pontos/maratona.cls b/dominancia-de-pontos/maratona.cls new file mode 100644 index 0000000..e11d53d --- /dev/null +++ b/dominancia-de-pontos/maratona.cls @@ -0,0 +1,188 @@ +\ProvidesPackage{maratona} +\LoadClass[11pt]{article} + +% remove page numbers +\pagenumbering{gobble} + +\RequirePackage{fancyhdr} + +\RequirePackage{tabularx,colortbl} + +%\RequirePackage{arial} +\RequirePackage{ifpdf} +\RequirePackage[T1]{fontenc} +\RequirePackage[utf8]{inputenc} +\RequirePackage[portuguese]{babel} +\RequirePackage{graphics} +\RequirePackage{graphicx} +\RequirePackage{amssymb,amsmath,wrapfig} +\RequirePackage{xcolor,colortbl} +\RequirePackage{xcolor} +\RequirePackage{ifthen} +\oddsidemargin 0cm +\evensidemargin -2cm +\topmargin -1cm +\textwidth 16cm +\textheight 23cm + +\ifpdf +\RequirePackage[pdftex]{hyperref} +\else +\RequirePackage[hypertex]{hyperref} +\fi + + +\newcommand{\var}[1]{\ensuremath{{#1}}} + + +\hypersetup{ + letterpaper, + colorlinks=true, + linkcolor=blue, + urlcolor=blue, + pdfpagemode=none, + pdftitle={IV Maratona de Programação do IFB \today}, + pdfauthor={}, + pdfsubject={Caderno de problemas da IV Maratona de Programação do IFB }, + pdfkeywords={maratona, programação, IFB} +} + + + +\DeclareGraphicsExtensions{png} + +\lhead{DS Contest Tools} +\pagestyle{fancy} + +% Capa +\newenvironment{Maratona}[3] +{ + \begin{titlepage} + \begin{center} + + \vspace{1cm} + \Large{\textbf{#1}} \\ + \vspace{1cm} + {\textbf{Caderno de Problemas}} \\ + \vspace{1cm} + \begin{small} + \textsl{#2} + \end{small} \\ + \begin{figure}[htp] + \begin{center} + \includegraphics[scale=1]{logos/logo-maratona.png} + \end{center} + \end{figure} + {(Este caderno contém {#3} problemas)} \\ + \vspace{1cm} + } + { + \vfill + \begin{small} + {QNM 40, Área Especial nº 01, + Taguatinga/DF, 72146-000 , + Brasil } \\ + {Telefone (61) 2103-2200 \\http://www.ifb.edu.br/taguatinga} \\ + \end{small} + \end{center} + \end{titlepage} +} + +\newcommand{\Organizacao}[2]{ + {\small \vfill + \begin{center} + + \textbf{Comissão Organizadora:} \\ + {#1} \\ + \bigskip + \textbf{Apoio:}\\ + {#2} + \end{center} + } + \vfill +} + + +% Problema +\newcounter{problem} +\newenvironment{Problema}[4]{ + \stepcounter{problem} + \newpage + \begin{center} + \Large{\ifthenelse{\equal{#1}{}}{\textbf{{#2}}}{\textbf{Problema {#1} -- {#2} }}}{\\\footnotesize \textbf{Limite de tempo: {#3}s}}{\\[-0.1cm]\footnotesize \textbf{Limite de memória: {#4}MB}} + \end{center} +} + +\newcounter{problemAutor} +\newenvironment{ProblemaAutor}[5]{ + \stepcounter{problemAutor} + \newpage + \begin{center} + \Large{\ifthenelse{\equal{#1}{}}{\textbf{{#2}}}{\textbf{Problema {#1} -- {#2} }}}{\\\footnotesize \textbf{Limite de tempo: {#3}s}}{\\[-0.1cm]\footnotesize \textbf{Limite de memória: {#4}MB\\}}{ + \footnotesize Autor: {#5} + } + \end{center} +} + + +% Código-fonte +\newcommand{\codigofonte}[1]{Nome do arquivo fonte: {#1}} + +% Entrada +\newcommand{\Entrada}{ + \bigskip + \begin{large} + \textbf{Entrada} \\ + \end{large} +} + +% Saida +\newcommand{\Saida}{ + \bigskip + \begin{large} + \textbf{Saída} \\ + \end{large} +} + +\newcommand{\Interacao}{ + \bigskip + \begin{large} + \textbf{Interação} \\ + \end{large} +} + +\newcommand{\Notas}{ + \bigskip + \begin{large} + \textbf{Notas} \\ + \end{large} +} + +% Exemplo +\newenvironment{Exemplo} +{ + + \tabularx{\textwidth}{XX} + % {@{\extracolsep{\fill}}|l|l|} + % {|l|l@{\extracolsep{\fill}|}} + \hline + Entrada & Saída \\\hline +} +{ + \hline + \endtabularx +} + +% Exemplo de Entrada +\newenvironment{ExemploEntrada} +{ + \bigskip + \begin{large} + \textbf{Exemplo} \\ + \end{large} +} +{ +} + +% Sample Output + diff --git a/dominancia-de-pontos/problem.json b/dominancia-de-pontos/problem.json new file mode 100644 index 0000000..318e1ab --- /dev/null +++ b/dominancia-de-pontos/problem.json @@ -0,0 +1,64 @@ +{ + "version": "1.0", + "problem": { + "title": "Dominância de Pontos", + "event": "", + "time_limit": 1.0, + "memory_limit_mb": 256, + "input_file": "stdin", + "output_file": "stdout", + "interactive": false, + "grader": false, + "subject": { + "en_us": [ + "divide-and-conquer" + ], + "pt_br": [ + "divisão-e-conquista" + ], + "es": [ + "" + ] + } + }, + "author": { + "name": "", + "affiliation": "", + "country": "", + "email": "" + }, + "build": { + "run_generator": true, + "run_validator": true, + "produce_outputs": true, + "run_checker": true, + "run_all_solutions": true, + "run_specific_solution": "", + "generate_io_only": false, + "generate_pdf_only": false, + "cpu_count": 1, + "build_pdf": true, + "pdf_format": "ds", + "io_samples": 1 + }, + "solutions": { + "main-ac": "ac.cpp", + "alternative-ac": [], + "wrong-answer": [], + "time-limit": [], + "time-limit-or-ac": [], + "time-limit-or-memory-limit": [], + "memory-limit": [], + "presentation-error": [], + "runtime-error": [] + }, + "polygon_config": { + "id": "" + }, + "boca_config": { + "time_limit": 1, + "number_of_repetitions": 1, + "maximum_memory_mb": 512, + "maximum_output_size_kb": 24096 + } +} \ No newline at end of file diff --git a/dominancia-de-pontos/src/ac.cpp b/dominancia-de-pontos/src/ac.cpp new file mode 100644 index 0000000..c5624d3 --- /dev/null +++ b/dominancia-de-pontos/src/ac.cpp @@ -0,0 +1,10 @@ +#include + +using namespace std; + + +int main(){ + // TODO + cout << 0 << endl; + return 0; +} \ No newline at end of file diff --git a/dominancia-de-pontos/src/checker.cpp b/dominancia-de-pontos/src/checker.cpp new file mode 100644 index 0000000..8438508 --- /dev/null +++ b/dominancia-de-pontos/src/checker.cpp @@ -0,0 +1,18 @@ +#include "testlib.h" +#include + +using namespace std; + +int main(int argc, char* argv[]) { + setName("compare two numbers"); + registerTestlibCmd(argc, argv); + long long ja = ans.readLong(); + long long pa = ouf.readLong(); + + if (ja != pa) + quitf(_wa, "expected %lld, found %lld", ja, pa); + + quitf(_ok, "answer is %lld", ja); + + return 0; +} \ No newline at end of file diff --git a/dominancia-de-pontos/src/generator.cpp b/dominancia-de-pontos/src/generator.cpp new file mode 100644 index 0000000..f122aff --- /dev/null +++ b/dominancia-de-pontos/src/generator.cpp @@ -0,0 +1,93 @@ +#include "testlib.h" +#include + +using namespace std; + +const int MIN_N = 1; +const int MAX_N = 1e5; +const int MIN_COORD = -1e9; +const int MAX_COORD = 1e9; + +const int rnd_test_n = 30; + +template void append(vector &dest, const vector &orig) { + dest.insert(dest.end(), orig.begin(), orig.end()); +} + +string output_tc(const vector> &coords) { + ostringstream oss; + oss << coords.size() << endl; + for (const auto [x, y] : coords) { + oss << x << " " << y << endl; + } + return oss.str(); +} + +vector generate_sample_tests() { + vector tests; + tests.push_back(output_tc({{1, 1}, {2, 2}, {3, 3}})); + return tests; +} + +vector generate_manual_tests() { + vector tests; + tests.push_back(output_tc({{1, 1}})); + return tests; +} + +vector> generate_random_coords_array(int size) { + vector> coords(size); + for (int i = 0; i < size; i++) { + coords[i].first = rnd.next(MIN_COORD, MAX_COORD); + coords[i].second = rnd.next(MIN_COORD, MAX_COORD); + } + return coords; +} + +string rnd_test(int i){ + int min_n = MIN_N; + int max_n = MAX_N; + + if(i generate_random_tests() { + vector tests; + for (int i = 0; i < rnd_test_n; i++){ + tests.push_back(rnd_test(i)); + } + return tests; +} + +string extreme_test_1(){ + return(output_tc(generate_random_coords_array(MAX_N))); +} + +vector generate_extreme_tests(){ + vector tests; + tests.push_back(extreme_test_1()); + return tests; +} + +int main(int argc, char *argv[]) { + registerGen(argc, argv, 1); + vector tests; + size_t test = 0; + append(tests, generate_sample_tests()); + append(tests, generate_manual_tests()); + append(tests, generate_random_tests()); + append(tests, generate_extreme_tests()); + for (const auto &t : tests) { + startTest(++test); + cout << t; + } + return 0; +} \ No newline at end of file diff --git a/dominancia-de-pontos/src/script.sh b/dominancia-de-pontos/src/script.sh new file mode 100644 index 0000000..a8a6bda --- /dev/null +++ b/dominancia-de-pontos/src/script.sh @@ -0,0 +1 @@ +generator \ No newline at end of file diff --git a/dominancia-de-pontos/src/testlib.h b/dominancia-de-pontos/src/testlib.h new file mode 100644 index 0000000..fac02ad --- /dev/null +++ b/dominancia-de-pontos/src/testlib.h @@ -0,0 +1,5963 @@ +/* + * It is strictly recommended to include "testlib.h" before any other include + * in your code. In this case testlib overrides compiler specific "random()". + * + * If you can't compile your code and compiler outputs something about + * ambiguous call of "random_shuffle", "rand" or "srand" it means that + * you shouldn't use them. Use "shuffle", and "rnd.next()" instead of them + * because these calls produce stable result for any C++ compiler. Read + * sample generator sources for clarification. + * + * Please read the documentation for class "random_t" and use "rnd" instance in + * generators. Probably, these sample calls will be usefull for you: + * rnd.next(); rnd.next(100); rnd.next(1, 2); + * rnd.next(3.14); rnd.next("[a-z]{1,100}"). + * + * Also read about wnext() to generate off-center random distribution. + * + * See https://github.com/MikeMirzayanov/testlib/ to get latest version or bug tracker. + */ + +#ifndef _TESTLIB_H_ +#define _TESTLIB_H_ + +/* + * Copyright (c) 2005-2022 + */ + +#define VERSION "0.9.40-SNAPSHOT" + +/* + * Mike Mirzayanov + * + * This material is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * Permission to use or copy this software for any purpose is hereby granted + * without fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* NOTE: This file contains testlib library for C++. + * + * Check, using testlib running format: + * check.exe [ [-appes]], + * If result file is specified it will contain results. + * + * Validator, using testlib running format: + * validator.exe < input.txt, + * It will return non-zero exit code and writes message to standard output. + * + * Generator, using testlib running format: + * gen.exe [parameter-1] [parameter-2] [... paramerter-n] + * You can write generated test(s) into standard output or into the file(s). + * + * Interactor, using testlib running format: + * interactor.exe [ [ [-appes]]], + * Reads test from inf (mapped to args[1]), writes result to tout (mapped to argv[2], + * can be judged by checker later), reads program output from ouf (mapped to stdin), + * writes output to program via stdout (use cout, printf, etc). + */ + +const char *latestFeatures[] = { + "Supported '--testMarkupFileName fn' and '--testCase tc/--testCaseFileName fn' for validators", + "Added opt defaults via opt(key/index, default_val); check unused opts when using has_opt or default opt (turn off this check with suppressEnsureNoUnusedOpt()).", + "For checker added --group and --testset command line params (like for validator), use checker.group() or checker.testset() to get values", + "Added quitpi(points_info, message) function to return with _points exit code 7 and given points_info", + "rnd.partition(size, sum[, min_part=1]) returns random (unsorted) partition which is a representation of the given `sum` as a sum of `size` positive integers (or >=min_part if specified)", + "rnd.distinct(size, n) and rnd.distinct(size, from, to)", + "opt(\"some_missing_key\") returns false now", + "has_opt(key)", + "Abort validator on validator.testset()/validator.group() if registered without using command line", + "Print integer range violations in a human readable way like `violates the range [1, 10^9]`", + "Opts supported: use them like n = opt(\"n\"), in a command line you can use an exponential notation", + "Reformatted", + "Use setTestCase(i) or unsetTestCase() to support test cases (you can use it in any type of program: generator, interactor, validator or checker)", + "Fixed issue #87: readStrictDouble accepts \"-0.00\"", + "Fixed issue #83: added InStream::quitif(condition, ...)", + "Fixed issue #79: fixed missed guard against repeated header include", + "Fixed issue #80: fixed UB in case of huge quitf message", + "Fixed issue #84: added readXs(size, indexBase = 1)", + "Fixed stringstream repeated usage issue", + "Fixed compilation in g++ (for std=c++03)", + "Batch of println functions (support collections, iterator ranges)", + "Introduced rnd.perm(size, first = 0) to generate a `first`-indexed permutation", + "Allow any whitespace in readInts-like functions for non-validators", + "Ignore 4+ command line arguments ifdef EJUDGE", + "Speed up of vtos", + "Show line number in validators in case of incorrect format", + "Truncate huge checker/validator/interactor message", + "Fixed issue with readTokenTo of very long tokens, now aborts with _pe/_fail depending of a stream type", + "Introduced InStream::ensure/ensuref checking a condition, returns wa/fail depending of a stream type", + "Fixed compilation in VS 2015+", + "Introduced space-separated read functions: readWords/readTokens, multilines read functions: readStrings/readLines", + "Introduced space-separated read functions: readInts/readIntegers/readLongs/readUnsignedLongs/readDoubles/readReals/readStrictDoubles/readStrictReals", + "Introduced split/tokenize functions to separate string by given char", + "Introduced InStream::readUnsignedLong and InStream::readLong with unsigned long long paramerters", + "Supported --testOverviewLogFileName for validator: bounds hits + features", + "Fixed UB (sequence points) in random_t", + "POINTS_EXIT_CODE returned back to 7 (instead of 0)", + "Removed disable buffers for interactive problems, because it works unexpectedly in wine", + "InStream over string: constructor of InStream from base InStream to inherit policies and std::string", + "Added expectedButFound quit function, examples: expectedButFound(_wa, 10, 20), expectedButFound(_fail, ja, pa, \"[n=%d,m=%d]\", n, m)", + "Fixed incorrect interval parsing in patterns", + "Use registerGen(argc, argv, 1) to develop new generator, use registerGen(argc, argv, 0) to compile old generators (originally created for testlib under 0.8.7)", + "Introduced disableFinalizeGuard() to switch off finalization checkings", + "Use join() functions to format a range of items as a single string (separated by spaces or other separators)", + "Use -DENABLE_UNEXPECTED_EOF to enable special exit code (by default, 8) in case of unexpected eof. It is good idea to use it in interactors", + "Use -DUSE_RND_AS_BEFORE_087 to compile in compatibility mode with random behavior of versions before 0.8.7", + "Fixed bug with nan in stringToDouble", + "Fixed issue around overloads for size_t on x64", + "Added attribute 'points' to the XML output in case of result=_points", + "Exit codes can be customized via macros, e.g. -DPE_EXIT_CODE=14", + "Introduced InStream function readWordTo/readTokenTo/readStringTo/readLineTo for faster reading", + "Introduced global functions: format(), englishEnding(), upperCase(), lowerCase(), compress()", + "Manual buffer in InStreams, some IO speed improvements", + "Introduced quitif(bool, const char* pattern, ...) which delegates to quitf() in case of first argument is true", + "Introduced guard against missed quitf() in checker or readEof() in validators", + "Supported readStrictReal/readStrictDouble - to use in validators to check strictly float numbers", + "Supported registerInteraction(argc, argv)", + "Print checker message to the stderr instead of stdout", + "Supported TResult _points to output calculated score, use quitp(...) functions", + "Fixed to be compilable on Mac", + "PC_BASE_EXIT_CODE=50 in case of defined TESTSYS", + "Fixed issues 19-21, added __attribute__ format printf", + "Some bug fixes", + "ouf.readInt(1, 100) and similar calls return WA", + "Modified random_t to avoid integer overflow", + "Truncated checker output [patch by Stepan Gatilov]", + "Renamed class random -> class random_t", + "Supported name parameter for read-and-validation methods, like readInt(1, 2, \"n\")", + "Fixed bug in readDouble()", + "Improved ensuref(), fixed nextLine to work in case of EOF, added startTest()", + "Supported \"partially correct\", example: quitf(_pc(13), \"result=%d\", result)", + "Added shuffle(begin, end), use it instead of random_shuffle(begin, end)", + "Added readLine(const string& ptrn), fixed the logic of readLine() in the validation mode", + "Package extended with samples of generators and validators", + "Written the documentation for classes and public methods in testlib.h", + "Implemented random routine to support generators, use registerGen() to switch it on", + "Implemented strict mode to validate tests, use registerValidation() to switch it on", + "Now ncmp.cpp and wcmp.cpp are return WA if answer is suffix or prefix of the output", + "Added InStream::readLong() and removed InStream::readLongint()", + "Now no footer added to each report by default (use directive FOOTER to switch on)", + "Now every checker has a name, use setName(const char* format, ...) to set it", + "Now it is compatible with TTS (by Kittens Computing)", + "Added \'ensure(condition, message = \"\")\' feature, it works like assert()", + "Fixed compatibility with MS C++ 7.1", + "Added footer with exit code information", + "Added compatibility with EJUDGE (compile with EJUDGE directive)", + "Added compatibility with Contester (compile with CONTESTER directive)" +}; + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NO_VA_START_VALIDATION +#endif + +/* Overrides random() for Borland C++. */ +#define random __random_deprecated +#include +#include +#include +#include +#undef random + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef TESTLIB_THROW_EXIT_EXCEPTION_INSTEAD_OF_EXIT +# include +#endif + +#if (_WIN32 || __WIN32__ || __WIN32 || _WIN64 || __WIN64__ || __WIN64 || WINNT || __WINNT || __WINNT__ || __CYGWIN__) +# if !defined(_MSC_VER) || _MSC_VER > 1400 +# define NOMINMAX 1 +# include +# else +# define WORD unsigned short +# include +# endif +# include +# define ON_WINDOWS +# if defined(_MSC_VER) && _MSC_VER > 1400 +# pragma warning( disable : 4127 ) +# pragma warning( disable : 4146 ) +# pragma warning( disable : 4458 ) +# endif +#else +# define WORD unsigned short +# include +#endif + +#if defined(FOR_WINDOWS) && defined(FOR_LINUX) +#error Only one target system is allowed +#endif + +#ifndef LLONG_MIN +#define LLONG_MIN (-9223372036854775807LL - 1) +#endif + +#ifndef ULLONG_MAX +#define ULLONG_MAX (18446744073709551615) +#endif + +#define LF ((char)10) +#define CR ((char)13) +#define TAB ((char)9) +#define SPACE ((char)' ') +#define EOFC (255) + +#ifndef OK_EXIT_CODE +# ifdef CONTESTER +# define OK_EXIT_CODE 0xAC +# elif defined BOCA_SUPPORT +# define OK_EXIT_CODE 4 +# else +# define OK_EXIT_CODE 0 +# endif +#endif + +#ifndef WA_EXIT_CODE +# ifdef EJUDGE +# define WA_EXIT_CODE 5 +# elif defined(CONTESTER) +# define WA_EXIT_CODE 0xAB +# elif defined BOCA_SUPPORT +# define WA_EXIT_CODE 6 +# else +# define WA_EXIT_CODE 1 +# endif +#endif + +#ifndef PE_EXIT_CODE +# ifdef EJUDGE +# define PE_EXIT_CODE 4 +# elif defined(CONTESTER) +# define PE_EXIT_CODE 0xAA +# elif defined BOCA_SUPPORT +# define PE_EXIT_CODE 6 +# else +# define PE_EXIT_CODE 2 +# endif +#endif + +#ifndef FAIL_EXIT_CODE +# ifdef EJUDGE +# define FAIL_EXIT_CODE 6 +# elif defined(CONTESTER) +# define FAIL_EXIT_CODE 0xA3 +# elif defined BOCA_SUPPORT +# define FAIL_EXIT_CODE 7 +# else +# define FAIL_EXIT_CODE 3 +# endif +#endif + +#ifndef DIRT_EXIT_CODE +# ifdef EJUDGE +# define DIRT_EXIT_CODE 6 +# else +# define DIRT_EXIT_CODE 4 +# endif +#endif + +#ifndef POINTS_EXIT_CODE +# ifndef BOCA_SUPPORT +# define POINTS_EXIT_CODE 7 +# else +# define POINTS_EXIT_CODE 5 +# endif +#endif + +#ifndef UNEXPECTED_EOF_EXIT_CODE +# define UNEXPECTED_EOF_EXIT_CODE 8 +#endif + +#ifndef PC_BASE_EXIT_CODE +# ifdef TESTSYS +# define PC_BASE_EXIT_CODE 50 +# else +# define PC_BASE_EXIT_CODE 0 +# endif +#endif + +#ifdef __GNUC__ +# define __TESTLIB_STATIC_ASSERT(condition) typedef void* __testlib_static_assert_type[(condition) ? 1 : -1] __attribute__((unused)) +#else +# define __TESTLIB_STATIC_ASSERT(condition) typedef void* __testlib_static_assert_type[(condition) ? 1 : -1] +#endif + +#ifdef ON_WINDOWS +#define I64 "%I64d" +#define U64 "%I64u" +#else +#define I64 "%lld" +#define U64 "%llu" +#endif + +#ifdef _MSC_VER +# define NORETURN __declspec(noreturn) +#elif defined __GNUC__ +# define NORETURN __attribute__ ((noreturn)) +#else +# define NORETURN +#endif + +static char __testlib_format_buffer[16777216]; +static int __testlib_format_buffer_usage_count = 0; + +#define FMT_TO_RESULT(fmt, cstr, result) std::string result; \ + if (__testlib_format_buffer_usage_count != 0) \ + __testlib_fail("FMT_TO_RESULT::__testlib_format_buffer_usage_count != 0"); \ + __testlib_format_buffer_usage_count++; \ + va_list ap; \ + va_start(ap, fmt); \ + vsnprintf(__testlib_format_buffer, sizeof(__testlib_format_buffer), cstr, ap); \ + va_end(ap); \ + __testlib_format_buffer[sizeof(__testlib_format_buffer) - 1] = 0; \ + result = std::string(__testlib_format_buffer); \ + __testlib_format_buffer_usage_count--; \ + +const long long __TESTLIB_LONGLONG_MAX = 9223372036854775807LL; +const int __TESTLIB_MAX_TEST_CASE = 1073741823; + +int __testlib_exitCode; + +bool __testlib_hasTestCase; +int __testlib_testCase = -1; + +void setTestCase(int testCase); + +void unsetTestCase() { + __testlib_hasTestCase = false; + __testlib_testCase = -1; +} + +NORETURN static void __testlib_fail(const std::string &message); + +template +static inline T __testlib_abs(const T &x) { + return x > 0 ? x : -x; +} + +template +static inline T __testlib_min(const T &a, const T &b) { + return a < b ? a : b; +} + +template +static inline T __testlib_max(const T &a, const T &b) { + return a > b ? a : b; +} + +template +static inline T __testlib_crop(T value, T a, T b) { + return __testlib_min(__testlib_max(value, a), --b); +} + +static inline double __testlib_crop(double value, double a, double b) { + value = __testlib_min(__testlib_max(value, a), b); + if (value >= b) + value = std::nexttoward(b, a); + return value; +} + +static bool __testlib_prelimIsNaN(double r) { + volatile double ra = r; +#ifndef __BORLANDC__ + return ((ra != ra) == true) && ((ra == ra) == false) && ((1.0 > ra) == false) && ((1.0 < ra) == false); +#else + return std::_isnan(ra); +#endif +} + +static std::string removeDoubleTrailingZeroes(std::string value) { + while (!value.empty() && value[value.length() - 1] == '0' && value.find('.') != std::string::npos) + value = value.substr(0, value.length() - 1); + if (!value.empty() && value[value.length() - 1] == '.') + return value + '0'; + else + return value; +} + +#ifdef __GNUC__ +__attribute__ ((format (printf, 1, 2))) +#endif +std::string format(const char *fmt, ...) { + FMT_TO_RESULT(fmt, fmt, result); + return result; +} + +std::string format(const std::string fmt, ...) { + FMT_TO_RESULT(fmt, fmt.c_str(), result); + return result; +} + +static std::string __testlib_part(const std::string &s); + +static bool __testlib_isNaN(double r) { + __TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long)); + volatile double ra = r; + long long llr1, llr2; + std::memcpy((void *) &llr1, (void *) &ra, sizeof(double)); + ra = -ra; + std::memcpy((void *) &llr2, (void *) &ra, sizeof(double)); + long long llnan = 0xFFF8000000000000LL; + return __testlib_prelimIsNaN(r) || llnan == llr1 || llnan == llr2; +} + +static double __testlib_nan() { + __TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long)); +#ifndef NAN + long long llnan = 0xFFF8000000000000LL; + double nan; + std::memcpy(&nan, &llnan, sizeof(double)); + return nan; +#else + return NAN; +#endif +} + +static bool __testlib_isInfinite(double r) { + volatile double ra = r; + return (ra > 1E300 || ra < -1E300); +} + +#ifdef __GNUC__ +__attribute__((const)) +#endif +inline bool doubleCompare(double expected, double result, double MAX_DOUBLE_ERROR) { + MAX_DOUBLE_ERROR += 1E-15; + if (__testlib_isNaN(expected)) { + return __testlib_isNaN(result); + } else if (__testlib_isInfinite(expected)) { + if (expected > 0) { + return result > 0 && __testlib_isInfinite(result); + } else { + return result < 0 && __testlib_isInfinite(result); + } + } else if (__testlib_isNaN(result) || __testlib_isInfinite(result)) { + return false; + } else if (__testlib_abs(result - expected) <= MAX_DOUBLE_ERROR) { + return true; + } else { + double minv = __testlib_min(expected * (1.0 - MAX_DOUBLE_ERROR), + expected * (1.0 + MAX_DOUBLE_ERROR)); + double maxv = __testlib_max(expected * (1.0 - MAX_DOUBLE_ERROR), + expected * (1.0 + MAX_DOUBLE_ERROR)); + return result >= minv && result <= maxv; + } +} + +#ifdef __GNUC__ +__attribute__((const)) +#endif +inline double doubleDelta(double expected, double result) { + double absolute = __testlib_abs(result - expected); + + if (__testlib_abs(expected) > 1E-9) { + double relative = __testlib_abs(absolute / expected); + return __testlib_min(absolute, relative); + } else + return absolute; +} + +/** It does nothing on non-windows and files differ from stdin/stdout/stderr. */ +static void __testlib_set_binary(std::FILE *file) { + if (NULL != file) { +#ifdef ON_WINDOWS +# ifdef _O_BINARY + if (stdin == file) +# ifdef STDIN_FILENO + return void(_setmode(STDIN_FILENO, _O_BINARY)); +# else + return void(_setmode(_fileno(stdin), _O_BINARY)); +# endif + if (stdout == file) +# ifdef STDOUT_FILENO + return void(_setmode(STDOUT_FILENO, _O_BINARY)); +# else + return void(_setmode(_fileno(stdout), _O_BINARY)); +# endif + if (stderr == file) +# ifdef STDERR_FILENO + return void(_setmode(STDERR_FILENO, _O_BINARY)); +# else + return void(_setmode(_fileno(stderr), _O_BINARY)); +# endif +# elif O_BINARY + if (stdin == file) +# ifdef STDIN_FILENO + return void(setmode(STDIN_FILENO, O_BINARY)); +# else + return void(setmode(fileno(stdin), O_BINARY)); +# endif + if (stdout == file) +# ifdef STDOUT_FILENO + return void(setmode(STDOUT_FILENO, O_BINARY)); +# else + return void(setmode(fileno(stdout), O_BINARY)); +# endif + if (stderr == file) +# ifdef STDERR_FILENO + return void(setmode(STDERR_FILENO, O_BINARY)); +# else + return void(setmode(fileno(stderr), O_BINARY)); +# endif +# endif +#endif + } +} + +#if __cplusplus > 199711L || defined(_MSC_VER) +template +static std::string vtos(const T &t, std::true_type) { + if (t == 0) + return "0"; + else { + T n(t); + bool negative = n < 0; + std::string s; + while (n != 0) { + T digit = n % 10; + if (digit < 0) + digit = -digit; + s += char('0' + digit); + n /= 10; + } + std::reverse(s.begin(), s.end()); + return negative ? "-" + s : s; + } +} + +template +static std::string vtos(const T &t, std::false_type) { + std::string s; + static std::stringstream ss; + ss.str(std::string()); + ss.clear(); + ss << t; + ss >> s; + return s; +} + +template +static std::string vtos(const T &t) { + return vtos(t, std::is_integral()); +} + +/* signed case. */ +template +static std::string toHumanReadableString(const T &n, std::false_type) { + if (n == 0) + return vtos(n); + int trailingZeroCount = 0; + T n_ = n; + while (n_ % 10 == 0) + n_ /= 10, trailingZeroCount++; + if (trailingZeroCount >= 7) { + if (n_ == 1) + return "10^" + vtos(trailingZeroCount); + else if (n_ == -1) + return "-10^" + vtos(trailingZeroCount); + else + return vtos(n_) + "*10^" + vtos(trailingZeroCount); + } else + return vtos(n); +} + +/* unsigned case. */ +template +static std::string toHumanReadableString(const T &n, std::true_type) { + if (n == 0) + return vtos(n); + int trailingZeroCount = 0; + T n_ = n; + while (n_ % 10 == 0) + n_ /= 10, trailingZeroCount++; + if (trailingZeroCount >= 7) { + if (n_ == 1) + return "10^" + vtos(trailingZeroCount); + else + return vtos(n_) + "*10^" + vtos(trailingZeroCount); + } else + return vtos(n); +} + +template +static std::string toHumanReadableString(const T &n) { + return toHumanReadableString(n, std::is_unsigned()); +} +#else +template +static std::string vtos(const T& t) +{ + std::string s; + static std::stringstream ss; + ss.str(std::string()); + ss.clear(); + ss << t; + ss >> s; + return s; +} + +template +static std::string toHumanReadableString(const T &n) { + return vtos(n); +} +#endif + +template +static std::string toString(const T &t) { + return vtos(t); +} + +#if __cplusplus > 199711L || defined(_MSC_VER) +/* opts */ +void prepareOpts(int argc, char* argv[]); +#endif + +/* + * Very simple regex-like pattern. + * It used for two purposes: validation and generation. + * + * For example, pattern("[a-z]{1,5}").next(rnd) will return + * random string from lowercase latin letters with length + * from 1 to 5. It is easier to call rnd.next("[a-z]{1,5}") + * for the same effect. + * + * Another samples: + * "mike|john" will generate (match) "mike" or "john"; + * "-?[1-9][0-9]{0,3}" will generate (match) non-zero integers from -9999 to 9999; + * "id-([ac]|b{2})" will generate (match) "id-a", "id-bb", "id-c"; + * "[^0-9]*" will match sequences (empty or non-empty) without digits, you can't + * use it for generations. + * + * You can't use pattern for generation if it contains meta-symbol '*'. Also it + * is not recommended to use it for char-sets with meta-symbol '^' like [^a-z]. + * + * For matching very simple greedy algorithm is used. For example, pattern + * "[0-9]?1" will not match "1", because of greedy nature of matching. + * Alternations (meta-symbols "|") are processed with brute-force algorithm, so + * do not use many alternations in one expression. + * + * If you want to use one expression many times it is better to compile it into + * a single pattern like "pattern p("[a-z]+")". Later you can use + * "p.matches(std::string s)" or "p.next(random_t& rd)" to check matching or generate + * new string by pattern. + * + * Simpler way to read token and check it for pattern matching is "inf.readToken("[a-z]+")". + * + * All spaces are ignored in regex, unless escaped with \. For example, ouf.readLine("NO SOLUTION") + * will expect "NOSOLUTION", the correct call should be ouf.readLine("NO\\ SOLUTION") or + * ouf.readLine(R"(NO\ SOLUTION)") if you prefer raw string literals from C++11. + */ +class random_t; + +class pattern { +public: + /* Create pattern instance by string. */ + pattern(std::string s); + + /* Generate new string by pattern and given random_t. */ + std::string next(random_t &rnd) const; + + /* Checks if given string match the pattern. */ + bool matches(const std::string &s) const; + + /* Returns source string of the pattern. */ + std::string src() const; + +private: + bool matches(const std::string &s, size_t pos) const; + + std::string s; + std::vector children; + std::vector chars; + int from; + int to; +}; + +/* + * Use random_t instances to generate random values. It is preffered + * way to use randoms instead of rand() function or self-written + * randoms. + * + * Testlib defines global variable "rnd" of random_t class. + * Use registerGen(argc, argv, 1) to setup random_t seed be command + * line (to use latest random generator version). + * + * Random generates uniformly distributed values if another strategy is + * not specified explicitly. + */ +class random_t { +private: + unsigned long long seed; + static const unsigned long long multiplier; + static const unsigned long long addend; + static const unsigned long long mask; + static const int lim; + + long long nextBits(int bits) { + if (bits <= 48) { + seed = (seed * multiplier + addend) & mask; + return (long long) (seed >> (48 - bits)); + } else { + if (bits > 63) + __testlib_fail("random_t::nextBits(int bits): n must be less than 64"); + + int lowerBitCount = (random_t::version == 0 ? 31 : 32); + + long long left = (nextBits(31) << 32); + long long right = nextBits(lowerBitCount); + + return left ^ right; + } + } + +public: + static int version; + + /* New random_t with fixed seed. */ + random_t() + : seed(3905348978240129619LL) { + } + + /* Sets seed by command line. */ + void setSeed(int argc, char *argv[]) { + random_t p; + + seed = 3905348978240129619LL; + for (int i = 1; i < argc; i++) { + std::size_t le = std::strlen(argv[i]); + for (std::size_t j = 0; j < le; j++) + seed = seed * multiplier + (unsigned int) (argv[i][j]) + addend; + seed += multiplier / addend; + } + + seed = seed & mask; + } + + /* Sets seed by given value. */ + void setSeed(long long _seed) { + _seed = (_seed ^ multiplier) & mask; + seed = _seed; + } + +#ifndef __BORLANDC__ + + /* Random string value by given pattern (see pattern documentation). */ + std::string next(const std::string &ptrn) { + pattern p(ptrn); + return p.next(*this); + } + +#else + /* Random string value by given pattern (see pattern documentation). */ + std::string next(std::string ptrn) + { + pattern p(ptrn); + return p.next(*this); + } +#endif + + /* Random value in range [0, n-1]. */ + int next(int n) { + if (n <= 0) + __testlib_fail("random_t::next(int n): n must be positive"); + + if ((n & -n) == n) // n is a power of 2 + return (int) ((n * (long long) nextBits(31)) >> 31); + + const long long limit = INT_MAX / n * n; + + long long bits; + do { + bits = nextBits(31); + } while (bits >= limit); + + return int(bits % n); + } + + /* Random value in range [0, n-1]. */ + unsigned int next(unsigned int n) { + if (n >= INT_MAX) + __testlib_fail("random_t::next(unsigned int n): n must be less INT_MAX"); + return (unsigned int) next(int(n)); + } + + /* Random value in range [0, n-1]. */ + long long next(long long n) { + if (n <= 0) + __testlib_fail("random_t::next(long long n): n must be positive"); + + const long long limit = __TESTLIB_LONGLONG_MAX / n * n; + + long long bits; + do { + bits = nextBits(63); + } while (bits >= limit); + + return bits % n; + } + + /* Random value in range [0, n-1]. */ + unsigned long long next(unsigned long long n) { + if (n >= (unsigned long long) (__TESTLIB_LONGLONG_MAX)) + __testlib_fail("random_t::next(unsigned long long n): n must be less LONGLONG_MAX"); + return (unsigned long long) next((long long) (n)); + } + + /* Random value in range [0, n-1]. */ + long next(long n) { + return (long) next((long long) (n)); + } + + /* Random value in range [0, n-1]. */ + unsigned long next(unsigned long n) { + if (n >= (unsigned long) (LONG_MAX)) + __testlib_fail("random_t::next(unsigned long n): n must be less LONG_MAX"); + return (unsigned long) next((unsigned long long) (n)); + } + + /* Returns random value in range [from,to]. */ + int next(int from, int to) { + return int(next((long long) to - from + 1) + from); + } + + /* Returns random value in range [from,to]. */ + unsigned int next(unsigned int from, unsigned int to) { + return (unsigned int) (next((long long) to - from + 1) + from); + } + + /* Returns random value in range [from,to]. */ + long long next(long long from, long long to) { + return next(to - from + 1) + from; + } + + /* Returns random value in range [from,to]. */ + unsigned long long next(unsigned long long from, unsigned long long to) { + if (from > to) + __testlib_fail("random_t::next(unsigned long long from, unsigned long long to): from can't not exceed to"); + return next(to - from + 1) + from; + } + + /* Returns random value in range [from,to]. */ + long next(long from, long to) { + return next(to - from + 1) + from; + } + + /* Returns random value in range [from,to]. */ + unsigned long next(unsigned long from, unsigned long to) { + if (from > to) + __testlib_fail("random_t::next(unsigned long from, unsigned long to): from can't not exceed to"); + return next(to - from + 1) + from; + } + + /* Random double value in range [0, 1). */ + double next() { + long long left = ((long long) (nextBits(26)) << 27); + long long right = nextBits(27); + return __testlib_crop((double) (left + right) / (double) (1LL << 53), 0.0, 1.0); + } + + /* Random double value in range [0, n). */ + double next(double n) { + if (n <= 0.0) + __testlib_fail("random_t::next(double): n should be positive"); + return __testlib_crop(n * next(), 0.0, n); + } + + /* Random double value in range [from, to). */ + double next(double from, double to) { + if (from >= to) + __testlib_fail("random_t::next(double from, double to): from should be strictly less than to"); + return next(to - from) + from; + } + + /* Returns random element from container. */ + template + typename Container::value_type any(const Container &c) { + int size = int(c.size()); + if (size <= 0) + __testlib_fail("random_t::any(const Container& c): c.size() must be positive"); + return *(c.begin() + next(size)); + } + + /* Returns random element from iterator range. */ + template + typename Iter::value_type any(const Iter &begin, const Iter &end) { + int size = int(end - begin); + if (size <= 0) + __testlib_fail("random_t::any(const Iter& begin, const Iter& end): range must have positive length"); + return *(begin + next(size)); + } + + /* Random string value by given pattern (see pattern documentation). */ +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif + std::string next(const char *format, ...) { + FMT_TO_RESULT(format, format, ptrn); + return next(ptrn); + } + + /* + * Weighted next. If type == 0 than it is usual "next()". + * + * If type = 1, than it returns "max(next(), next())" + * (the number of "max" functions equals to "type"). + * + * If type < 0, than "max" function replaces with "min". + */ + int wnext(int n, int type) { + if (n <= 0) + __testlib_fail("random_t::wnext(int n, int type): n must be positive"); + + if (abs(type) < random_t::lim) { + int result = next(n); + + for (int i = 0; i < +type; i++) + result = __testlib_max(result, next(n)); + + for (int i = 0; i < -type; i++) + result = __testlib_min(result, next(n)); + + return result; + } else { + double p; + + if (type > 0) + p = std::pow(next() + 0.0, 1.0 / (type + 1)); + else + p = 1 - std::pow(next() + 0.0, 1.0 / (-type + 1)); + + return __testlib_crop((int) (double(n) * p), 0, n); + } + } + + /* See wnext(int, int). It uses the same algorithms. */ + long long wnext(long long n, int type) { + if (n <= 0) + __testlib_fail("random_t::wnext(long long n, int type): n must be positive"); + + if (abs(type) < random_t::lim) { + long long result = next(n); + + for (int i = 0; i < +type; i++) + result = __testlib_max(result, next(n)); + + for (int i = 0; i < -type; i++) + result = __testlib_min(result, next(n)); + + return result; + } else { + double p; + + if (type > 0) + p = std::pow(next() + 0.0, 1.0 / (type + 1)); + else + p = 1 - std::pow(next() + 0.0, 1.0 / (-type + 1)); + + return __testlib_crop((long long) (double(n) * p), 0LL, n); + } + } + + /* Returns value in [0, n). See wnext(int, int). It uses the same algorithms. */ + double wnext(double n, int type) { + if (n <= 0) + __testlib_fail("random_t::wnext(double n, int type): n must be positive"); + + if (abs(type) < random_t::lim) { + double result = next(); + + for (int i = 0; i < +type; i++) + result = __testlib_max(result, next()); + + for (int i = 0; i < -type; i++) + result = __testlib_min(result, next()); + + return n * result; + } else { + double p; + + if (type > 0) + p = std::pow(next() + 0.0, 1.0 / (type + 1)); + else + p = 1 - std::pow(next() + 0.0, 1.0 / (-type + 1)); + + return __testlib_crop(n * p, 0.0, n); + } + } + + /* Returns value in [0, 1). See wnext(int, int). It uses the same algorithms. */ + double wnext(int type) { + return wnext(1.0, type); + } + + /* See wnext(int, int). It uses the same algorithms. */ + unsigned int wnext(unsigned int n, int type) { + if (n >= INT_MAX) + __testlib_fail("random_t::wnext(unsigned int n, int type): n must be less INT_MAX"); + return (unsigned int) wnext(int(n), type); + } + + /* See wnext(int, int). It uses the same algorithms. */ + unsigned long long wnext(unsigned long long n, int type) { + if (n >= (unsigned long long) (__TESTLIB_LONGLONG_MAX)) + __testlib_fail("random_t::wnext(unsigned long long n, int type): n must be less LONGLONG_MAX"); + + return (unsigned long long) wnext((long long) (n), type); + } + + /* See wnext(int, int). It uses the same algorithms. */ + long wnext(long n, int type) { + return (long) wnext((long long) (n), type); + } + + /* See wnext(int, int). It uses the same algorithms. */ + unsigned long wnext(unsigned long n, int type) { + if (n >= (unsigned long) (LONG_MAX)) + __testlib_fail("random_t::wnext(unsigned long n, int type): n must be less LONG_MAX"); + + return (unsigned long) wnext((unsigned long long) (n), type); + } + + /* Returns weighted random value in range [from, to]. */ + int wnext(int from, int to, int type) { + if (from > to) + __testlib_fail("random_t::wnext(int from, int to, int type): from can't not exceed to"); + return wnext(to - from + 1, type) + from; + } + + /* Returns weighted random value in range [from, to]. */ + int wnext(unsigned int from, unsigned int to, int type) { + if (from > to) + __testlib_fail("random_t::wnext(unsigned int from, unsigned int to, int type): from can't not exceed to"); + return int(wnext(to - from + 1, type) + from); + } + + /* Returns weighted random value in range [from, to]. */ + long long wnext(long long from, long long to, int type) { + if (from > to) + __testlib_fail("random_t::wnext(long long from, long long to, int type): from can't not exceed to"); + return wnext(to - from + 1, type) + from; + } + + /* Returns weighted random value in range [from, to]. */ + unsigned long long wnext(unsigned long long from, unsigned long long to, int type) { + if (from > to) + __testlib_fail( + "random_t::wnext(unsigned long long from, unsigned long long to, int type): from can't not exceed to"); + return wnext(to - from + 1, type) + from; + } + + /* Returns weighted random value in range [from, to]. */ + long wnext(long from, long to, int type) { + if (from > to) + __testlib_fail("random_t::wnext(long from, long to, int type): from can't not exceed to"); + return wnext(to - from + 1, type) + from; + } + + /* Returns weighted random value in range [from, to]. */ + unsigned long wnext(unsigned long from, unsigned long to, int type) { + if (from > to) + __testlib_fail("random_t::wnext(unsigned long from, unsigned long to, int type): from can't not exceed to"); + return wnext(to - from + 1, type) + from; + } + + /* Returns weighted random double value in range [from, to). */ + double wnext(double from, double to, int type) { + if (from >= to) + __testlib_fail("random_t::wnext(double from, double to, int type): from should be strictly less than to"); + return wnext(to - from, type) + from; + } + + /* Returns weighted random element from container. */ + template + typename Container::value_type wany(const Container &c, int type) { + size_t size = c.size(); + if (size <= 0) + __testlib_fail("random_t::wany(const Container& c, int type): c.size() must be positive"); + return *(c.begin() + wnext(size, type)); + } + + /* Returns weighted random element from iterator range. */ + template + typename Iter::value_type wany(const Iter &begin, const Iter &end, int type) { + int size = int(end - begin); + if (size <= 0) + __testlib_fail( + "random_t::any(const Iter& begin, const Iter& end, int type): range must have positive length"); + return *(begin + wnext(size, type)); + } + + /* Returns random permutation of the given size (values are between `first` and `first`+size-1)*/ + template + std::vector perm(T size, E first) { + if (size < 0) + __testlib_fail("random_t::perm(T size, E first = 0): size must non-negative"); + else if (size == 0) + return std::vector(); + std::vector p(size); + E current = first; + for (T i = 0; i < size; i++) + p[i] = current++; + if (size > 1) + for (T i = 1; i < size; i++) + std::swap(p[i], p[next(i + 1)]); + return p; + } + + /* Returns random permutation of the given size (values are between 0 and size-1)*/ + template + std::vector perm(T size) { + return perm(size, T(0)); + } + + /* Returns `size` unordered (unsorted) distinct numbers between `from` and `to`. */ + template + std::vector distinct(int size, T from, T to) { + std::vector result; + if (size == 0) + return result; + + if (from > to) + __testlib_fail("random_t::distinct expected from <= to"); + + if (size < 0) + __testlib_fail("random_t::distinct expected size >= 0"); + + uint64_t n = to - from + 1; + if (uint64_t(size) > n) + __testlib_fail("random_t::distinct expected size <= to - from + 1"); + + double expected = 0.0; + for (int i = 1; i <= size; i++) + expected += double(n) / double(n - i + 1); + + if (expected < double(n)) { + std::set vals; + while (int(vals.size()) < size) { + T x = T(next(from, to)); + if (vals.insert(x).second) + result.push_back(x); + } + } else { + if (n > 1000000000) + __testlib_fail("random_t::distinct here expected to - from + 1 <= 1000000000"); + std::vector p(perm(int(n), from)); + result.insert(result.end(), p.begin(), p.begin() + size); + } + + return result; + } + + /* Returns `size` unordered (unsorted) distinct numbers between `0` and `upper`-1. */ + template + std::vector distinct(int size, T upper) { + if (size < 0) + __testlib_fail("random_t::distinct expected size >= 0"); + if (size == 0) + return std::vector(); + + if (upper <= 0) + __testlib_fail("random_t::distinct expected upper > 0"); + if (size > upper) + __testlib_fail("random_t::distinct expected size <= upper"); + + return distinct(size, T(0), upper - 1); + } + + /* Returns random (unsorted) partition which is a representation of sum as a sum of integers not less than min_part. */ + template + std::vector partition(int size, T sum, T min_part) { + if (size < 0) + __testlib_fail("random_t::partition: size < 0"); + if (size == 0 && sum != 0) + __testlib_fail("random_t::partition: size == 0 && sum != 0"); + if (min_part * size > sum) + __testlib_fail("random_t::partition: min_part * size > sum"); + if (size == 0 && sum == 0) + return std::vector(); + + T sum_ = sum; + sum -= min_part * size; + + std::vector septums(size); + std::vector d = distinct(size - 1, T(1), T(sum + size - 1)); + for (int i = 0; i + 1 < size; i++) + septums[i + 1] = d[i]; + sort(septums.begin(), septums.end()); + + std::vector result(size); + for (int i = 0; i + 1 < size; i++) + result[i] = septums[i + 1] - septums[i] - 1; + result[size - 1] = sum + size - 1 - septums.back(); + + for (std::size_t i = 0; i < result.size(); i++) + result[i] += min_part; + + T result_sum = 0; + for (std::size_t i = 0; i < result.size(); i++) + result_sum += result[i]; + if (result_sum != sum_) + __testlib_fail("random_t::partition: partition sum is expected to be the given sum"); + + if (*std::min_element(result.begin(), result.end()) < min_part) + __testlib_fail("random_t::partition: partition min is expected to be no less than the given min_part"); + + if (int(result.size()) != size || result.size() != (size_t) size) + __testlib_fail("random_t::partition: partition size is expected to be equal to the given size"); + + return result; + } + + /* Returns random (unsorted) partition which is a representation of sum as a sum of positive integers. */ + template + std::vector partition(int size, T sum) { + return partition(size, sum, T(1)); + } +}; + +const int random_t::lim = 25; +const unsigned long long random_t::multiplier = 0x5DEECE66DLL; +const unsigned long long random_t::addend = 0xBLL; +const unsigned long long random_t::mask = (1LL << 48) - 1; +int random_t::version = -1; + +/* Pattern implementation */ +bool pattern::matches(const std::string &s) const { + return matches(s, 0); +} + +static bool __pattern_isSlash(const std::string &s, size_t pos) { + return s[pos] == '\\'; +} + +#ifdef __GNUC__ +__attribute__((pure)) +#endif +static bool __pattern_isCommandChar(const std::string &s, size_t pos, char value) { + if (pos >= s.length()) + return false; + + int slashes = 0; + + int before = int(pos) - 1; + while (before >= 0 && s[before] == '\\') + before--, slashes++; + + return slashes % 2 == 0 && s[pos] == value; +} + +static char __pattern_getChar(const std::string &s, size_t &pos) { + if (__pattern_isSlash(s, pos)) + pos += 2; + else + pos++; + + return s[pos - 1]; +} + +#ifdef __GNUC__ +__attribute__((pure)) +#endif +static int __pattern_greedyMatch(const std::string &s, size_t pos, const std::vector chars) { + int result = 0; + + while (pos < s.length()) { + char c = s[pos++]; + if (!std::binary_search(chars.begin(), chars.end(), c)) + break; + else + result++; + } + + return result; +} + +std::string pattern::src() const { + return s; +} + +bool pattern::matches(const std::string &s, size_t pos) const { + std::string result; + + if (to > 0) { + int size = __pattern_greedyMatch(s, pos, chars); + if (size < from) + return false; + if (size > to) + size = to; + pos += size; + } + + if (children.size() > 0) { + for (size_t child = 0; child < children.size(); child++) + if (children[child].matches(s, pos)) + return true; + return false; + } else + return pos == s.length(); +} + +std::string pattern::next(random_t &rnd) const { + std::string result; + result.reserve(20); + + if (to == INT_MAX) + __testlib_fail("pattern::next(random_t& rnd): can't process character '*' for generation"); + + if (to > 0) { + int count = rnd.next(to - from + 1) + from; + for (int i = 0; i < count; i++) + result += chars[rnd.next(int(chars.size()))]; + } + + if (children.size() > 0) { + int child = rnd.next(int(children.size())); + result += children[child].next(rnd); + } + + return result; +} + +static void __pattern_scanCounts(const std::string &s, size_t &pos, int &from, int &to) { + if (pos >= s.length()) { + from = to = 1; + return; + } + + if (__pattern_isCommandChar(s, pos, '{')) { + std::vector parts; + std::string part; + + pos++; + + while (pos < s.length() && !__pattern_isCommandChar(s, pos, '}')) { + if (__pattern_isCommandChar(s, pos, ',')) + parts.push_back(part), part = "", pos++; + else + part += __pattern_getChar(s, pos); + } + + if (part != "") + parts.push_back(part); + + if (!__pattern_isCommandChar(s, pos, '}')) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + + pos++; + + if (parts.size() < 1 || parts.size() > 2) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + + std::vector numbers; + + for (size_t i = 0; i < parts.size(); i++) { + if (parts[i].length() == 0) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + int number; + if (std::sscanf(parts[i].c_str(), "%d", &number) != 1) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + numbers.push_back(number); + } + + if (numbers.size() == 1) + from = to = numbers[0]; + else + from = numbers[0], to = numbers[1]; + + if (from > to) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + } else { + if (__pattern_isCommandChar(s, pos, '?')) { + from = 0, to = 1, pos++; + return; + } + + if (__pattern_isCommandChar(s, pos, '*')) { + from = 0, to = INT_MAX, pos++; + return; + } + + if (__pattern_isCommandChar(s, pos, '+')) { + from = 1, to = INT_MAX, pos++; + return; + } + + from = to = 1; + } +} + +static std::vector __pattern_scanCharSet(const std::string &s, size_t &pos) { + if (pos >= s.length()) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + + std::vector result; + + if (__pattern_isCommandChar(s, pos, '[')) { + pos++; + bool negative = __pattern_isCommandChar(s, pos, '^'); + if (negative) + pos++; + + char prev = 0; + + while (pos < s.length() && !__pattern_isCommandChar(s, pos, ']')) { + if (__pattern_isCommandChar(s, pos, '-') && prev != 0) { + pos++; + + if (pos + 1 == s.length() || __pattern_isCommandChar(s, pos, ']')) { + result.push_back(prev); + prev = '-'; + continue; + } + + char next = __pattern_getChar(s, pos); + if (prev > next) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + + for (char c = prev; c != next; c++) + result.push_back(c); + result.push_back(next); + + prev = 0; + } else { + if (prev != 0) + result.push_back(prev); + prev = __pattern_getChar(s, pos); + } + } + + if (prev != 0) + result.push_back(prev); + + if (!__pattern_isCommandChar(s, pos, ']')) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + + pos++; + + if (negative) { + std::sort(result.begin(), result.end()); + std::vector actuals; + for (int code = 0; code < 255; code++) { + char c = char(code); + if (!std::binary_search(result.begin(), result.end(), c)) + actuals.push_back(c); + } + result = actuals; + } + + std::sort(result.begin(), result.end()); + } else + result.push_back(__pattern_getChar(s, pos)); + + return result; +} + +pattern::pattern(std::string s) : s(s), from(0), to(0) { + std::string t; + for (size_t i = 0; i < s.length(); i++) + if (!__pattern_isCommandChar(s, i, ' ')) + t += s[i]; + s = t; + + int opened = 0; + int firstClose = -1; + std::vector seps; + + for (size_t i = 0; i < s.length(); i++) { + if (__pattern_isCommandChar(s, i, '(')) { + opened++; + continue; + } + + if (__pattern_isCommandChar(s, i, ')')) { + opened--; + if (opened == 0 && firstClose == -1) + firstClose = int(i); + continue; + } + + if (opened < 0) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + + if (__pattern_isCommandChar(s, i, '|') && opened == 0) + seps.push_back(int(i)); + } + + if (opened != 0) + __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); + + if (seps.size() == 0 && firstClose + 1 == (int) s.length() + && __pattern_isCommandChar(s, 0, '(') && __pattern_isCommandChar(s, s.length() - 1, ')')) { + children.push_back(pattern(s.substr(1, s.length() - 2))); + } else { + if (seps.size() > 0) { + seps.push_back(int(s.length())); + int last = 0; + + for (size_t i = 0; i < seps.size(); i++) { + children.push_back(pattern(s.substr(last, seps[i] - last))); + last = seps[i] + 1; + } + } else { + size_t pos = 0; + chars = __pattern_scanCharSet(s, pos); + __pattern_scanCounts(s, pos, from, to); + if (pos < s.length()) + children.push_back(pattern(s.substr(pos))); + } + } +} + +/* End of pattern implementation */ + +template +inline bool isEof(C c) { + return c == EOFC; +} + +template +inline bool isEoln(C c) { + return (c == LF || c == CR); +} + +template +inline bool isBlanks(C c) { + return (c == LF || c == CR || c == SPACE || c == TAB); +} + +inline std::string trim(const std::string &s) { + if (s.empty()) + return s; + + int left = 0; + while (left < int(s.length()) && isBlanks(s[left])) + left++; + if (left >= int(s.length())) + return ""; + + int right = int(s.length()) - 1; + while (right >= 0 && isBlanks(s[right])) + right--; + if (right < 0) + return ""; + + return s.substr(left, right - left + 1); +} + +enum TMode { + _input, _output, _answer +}; + +/* Outcomes 6-15 are reserved for future use. */ +enum TResult { + _ok = 0, + _wa = 1, + _pe = 2, + _fail = 3, + _dirt = 4, + _points = 5, + _unexpected_eof = 8, + _partially = 16 +}; + +enum TTestlibMode { + _unknown, _checker, _validator, _generator, _interactor, _scorer +}; + +#define _pc(exitCode) (TResult(_partially + (exitCode))) + +/* Outcomes 6-15 are reserved for future use. */ +const std::string outcomes[] = { + "accepted", + "wrong-answer", + "presentation-error", + "fail", + "fail", +#ifndef PCMS2 + "points", +#else + "relative-scoring", +#endif + "reserved", + "reserved", + "unexpected-eof", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "partially-correct" +}; + +class InputStreamReader { +public: + virtual void setTestCase(int testCase) = 0; + + virtual std::vector getReadChars() = 0; + + virtual int curChar() = 0; + + virtual int nextChar() = 0; + + virtual void skipChar() = 0; + + virtual void unreadChar(int c) = 0; + + virtual std::string getName() = 0; + + virtual bool eof() = 0; + + virtual void close() = 0; + + virtual int getLine() = 0; + + virtual ~InputStreamReader() = 0; +}; + +InputStreamReader::~InputStreamReader() { + // No operations. +} + +class StringInputStreamReader : public InputStreamReader { +private: + std::string s; + size_t pos; + +public: + StringInputStreamReader(const std::string &content) : s(content), pos(0) { + // No operations. + } + + void setTestCase(int) { + __testlib_fail("setTestCase not implemented in StringInputStreamReader"); + } + + std::vector getReadChars() { + __testlib_fail("getReadChars not implemented in StringInputStreamReader"); + } + + int curChar() { + if (pos >= s.length()) + return EOFC; + else + return s[pos]; + } + + int nextChar() { + if (pos >= s.length()) { + pos++; + return EOFC; + } else + return s[pos++]; + } + + void skipChar() { + pos++; + } + + void unreadChar(int c) { + if (pos == 0) + __testlib_fail("StringInputStreamReader::unreadChar(int): pos == 0."); + pos--; + if (pos < s.length()) + s[pos] = char(c); + } + + std::string getName() { + return __testlib_part(s); + } + + int getLine() { + return -1; + } + + bool eof() { + return pos >= s.length(); + } + + void close() { + // No operations. + } +}; + +class FileInputStreamReader : public InputStreamReader { +private: + std::FILE *file; + std::string name; + int line; + std::vector undoChars; + std::vector readChars; + std::vector undoReadChars; + + inline int postprocessGetc(int getcResult) { + if (getcResult != EOF) + return getcResult; + else + return EOFC; + } + + int getc(FILE *file) { + int c; + int rc; + + if (undoChars.empty()) { + c = rc = ::getc(file); + } else { + c = undoChars.back(); + undoChars.pop_back(); + rc = undoReadChars.back(); + undoReadChars.pop_back(); + } + + if (c == LF) + line++; + + readChars.push_back(rc); + return c; + } + + int ungetc(int c/*, FILE* file*/) { + if (!readChars.empty()) { + undoReadChars.push_back(readChars.back()); + readChars.pop_back(); + } + if (c == LF) + line--; + undoChars.push_back(c); + return c; + } + +public: + FileInputStreamReader(std::FILE *file, const std::string &name) : file(file), name(name), line(1) { + // No operations. + } + + void setTestCase(int testCase) { + if (testCase < 0 || testCase > __TESTLIB_MAX_TEST_CASE) + __testlib_fail(format("testCase expected fit in [1,%d], but %d doesn't", __TESTLIB_MAX_TEST_CASE, testCase)); + readChars.push_back(testCase + 256); + } + + std::vector getReadChars() { + return readChars; + } + + int curChar() { + if (feof(file)) + return EOFC; + else { + int c = getc(file); + ungetc(c/*, file*/); + return postprocessGetc(c); + } + } + + int nextChar() { + if (feof(file)) + return EOFC; + else + return postprocessGetc(getc(file)); + } + + void skipChar() { + getc(file); + } + + void unreadChar(int c) { + ungetc(c/*, file*/); + } + + std::string getName() { + return name; + } + + int getLine() { + return line; + } + + bool eof() { + if (NULL == file || feof(file)) + return true; + else { + int c = nextChar(); + if (c == EOFC || (c == EOF && feof(file))) + return true; + unreadChar(c); + return false; + } + } + + void close() { + if (NULL != file) { + fclose(file); + file = NULL; + } + } +}; + +class BufferedFileInputStreamReader : public InputStreamReader { +private: + static const size_t BUFFER_SIZE; + static const size_t MAX_UNREAD_COUNT; + + std::FILE *file; + std::string name; + int line; + + char *buffer; + bool *isEof; + int bufferPos; + size_t bufferSize; + + bool refill() { + if (NULL == file) + __testlib_fail("BufferedFileInputStreamReader: file == NULL (" + getName() + ")"); + + if (bufferPos >= int(bufferSize)) { + size_t readSize = fread( + buffer + MAX_UNREAD_COUNT, + 1, + BUFFER_SIZE - MAX_UNREAD_COUNT, + file + ); + + if (readSize < BUFFER_SIZE - MAX_UNREAD_COUNT + && ferror(file)) + __testlib_fail("BufferedFileInputStreamReader: unable to read (" + getName() + ")"); + + bufferSize = MAX_UNREAD_COUNT + readSize; + bufferPos = int(MAX_UNREAD_COUNT); + std::memset(isEof + MAX_UNREAD_COUNT, 0, sizeof(isEof[0]) * readSize); + + return readSize > 0; + } else + return true; + } + + char increment() { + char c; + if ((c = buffer[bufferPos++]) == LF) + line++; + return c; + } + +public: + BufferedFileInputStreamReader(std::FILE *file, const std::string &name) : file(file), name(name), line(1) { + buffer = new char[BUFFER_SIZE]; + isEof = new bool[BUFFER_SIZE]; + bufferSize = MAX_UNREAD_COUNT; + bufferPos = int(MAX_UNREAD_COUNT); + } + + ~BufferedFileInputStreamReader() { + if (NULL != buffer) { + delete[] buffer; + buffer = NULL; + } + if (NULL != isEof) { + delete[] isEof; + isEof = NULL; + } + } + + void setTestCase(int) { + __testlib_fail("setTestCase not implemented in BufferedFileInputStreamReader"); + } + + std::vector getReadChars() { + __testlib_fail("getReadChars not implemented in BufferedFileInputStreamReader"); + } + + int curChar() { + if (!refill()) + return EOFC; + + return isEof[bufferPos] ? EOFC : buffer[bufferPos]; + } + + int nextChar() { + if (!refill()) + return EOFC; + + return isEof[bufferPos] ? EOFC : increment(); + } + + void skipChar() { + increment(); + } + + void unreadChar(int c) { + bufferPos--; + if (bufferPos < 0) + __testlib_fail("BufferedFileInputStreamReader::unreadChar(int): bufferPos < 0"); + isEof[bufferPos] = (c == EOFC); + buffer[bufferPos] = char(c); + if (c == LF) + line--; + } + + std::string getName() { + return name; + } + + int getLine() { + return line; + } + + bool eof() { + return !refill() || EOFC == curChar(); + } + + void close() { + if (NULL != file) { + fclose(file); + file = NULL; + } + } +}; + +const size_t BufferedFileInputStreamReader::BUFFER_SIZE = 2000000; +const size_t BufferedFileInputStreamReader::MAX_UNREAD_COUNT = BufferedFileInputStreamReader::BUFFER_SIZE / 2; + +/* + * Streams to be used for reading data in checkers or validators. + * Each read*() method moves pointer to the next character after the + * read value. + */ +struct InStream { + /* Do not use them. */ + InStream(); + + ~InStream(); + + /* Wrap std::string with InStream. */ + InStream(const InStream &baseStream, std::string content); + + InputStreamReader *reader; + int lastLine; + + std::string name; + TMode mode; + bool opened; + bool stdfile; + bool strict; + + int wordReserveSize; + std::string _tmpReadToken; + + int readManyIteration; + size_t maxFileSize; + size_t maxTokenLength; + size_t maxMessageLength; + + void init(std::string fileName, TMode mode); + + void init(std::FILE *f, TMode mode); + + void setTestCase(int testCase); + std::vector getReadChars(); + + /* Moves stream pointer to the first non-white-space character or EOF. */ + void skipBlanks(); + + /* Returns current character in the stream. Doesn't remove it from stream. */ + char curChar(); + + /* Moves stream pointer one character forward. */ + void skipChar(); + + /* Returns current character and moves pointer one character forward. */ + char nextChar(); + + /* Returns current character and moves pointer one character forward. */ + char readChar(); + + /* As "readChar()" but ensures that the result is equal to given parameter. */ + char readChar(char c); + + /* As "readChar()" but ensures that the result is equal to the space (code=32). */ + char readSpace(); + + /* Puts back the character into the stream. */ + void unreadChar(char c); + + /* Reopens stream, you should not use it. */ + void reset(std::FILE *file = NULL); + + /* Checks that current position is EOF. If not it doesn't move stream pointer. */ + bool eof(); + + /* Moves pointer to the first non-white-space character and calls "eof()". */ + bool seekEof(); + + /* + * Checks that current position contains EOLN. + * If not it doesn't move stream pointer. + * In strict mode expects "#13#10" for windows or "#10" for other platforms. + */ + bool eoln(); + + /* Moves pointer to the first non-space and non-tab character and calls "eoln()". */ + bool seekEoln(); + + /* Moves stream pointer to the first character of the next line (if exists). */ + void nextLine(); + + /* + * Reads new token. Ignores white-spaces into the non-strict mode + * (strict mode is used in validators usually). + */ + std::string readWord(); + + /* The same as "readWord()", it is preffered to use "readToken()". */ + std::string readToken(); + + /* The same as "readWord()", but ensures that token matches to given pattern. */ + std::string readWord(const std::string &ptrn, const std::string &variableName = ""); + + std::string readWord(const pattern &p, const std::string &variableName = ""); + + std::vector + readWords(int size, const std::string &ptrn, const std::string &variablesName = "", int indexBase = 1); + + std::vector + readWords(int size, const pattern &p, const std::string &variablesName = "", int indexBase = 1); + + std::vector readWords(int size, int indexBase = 1); + + /* The same as "readToken()", but ensures that token matches to given pattern. */ + std::string readToken(const std::string &ptrn, const std::string &variableName = ""); + + std::string readToken(const pattern &p, const std::string &variableName = ""); + + std::vector + readTokens(int size, const std::string &ptrn, const std::string &variablesName = "", int indexBase = 1); + + std::vector + readTokens(int size, const pattern &p, const std::string &variablesName = "", int indexBase = 1); + + std::vector readTokens(int size, int indexBase = 1); + + void readWordTo(std::string &result); + + void readWordTo(std::string &result, const pattern &p, const std::string &variableName = ""); + + void readWordTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); + + void readTokenTo(std::string &result); + + void readTokenTo(std::string &result, const pattern &p, const std::string &variableName = ""); + + void readTokenTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); + + /* + * Reads new long long value. Ignores white-spaces into the non-strict mode + * (strict mode is used in validators usually). + */ + long long readLong(); + + unsigned long long readUnsignedLong(); + + /* + * Reads new int. Ignores white-spaces into the non-strict mode + * (strict mode is used in validators usually). + */ + int readInteger(); + + /* + * Reads new int. Ignores white-spaces into the non-strict mode + * (strict mode is used in validators usually). + */ + int readInt(); + + /* As "readLong()" but ensures that value in the range [minv,maxv]. */ + long long readLong(long long minv, long long maxv, const std::string &variableName = ""); + + /* Reads space-separated sequence of long longs. */ + std::vector + readLongs(int size, long long minv, long long maxv, const std::string &variablesName = "", int indexBase = 1); + + /* Reads space-separated sequence of long longs. */ + std::vector readLongs(int size, int indexBase = 1); + + unsigned long long + readUnsignedLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName = ""); + + std::vector + readUnsignedLongs(int size, unsigned long long minv, unsigned long long maxv, const std::string &variablesName = "", + int indexBase = 1); + + std::vector readUnsignedLongs(int size, int indexBase = 1); + + unsigned long long readLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName = ""); + + std::vector + readLongs(int size, unsigned long long minv, unsigned long long maxv, const std::string &variablesName = "", + int indexBase = 1); + + /* As "readInteger()" but ensures that value in the range [minv,maxv]. */ + int readInteger(int minv, int maxv, const std::string &variableName = ""); + + /* As "readInt()" but ensures that value in the range [minv,maxv]. */ + int readInt(int minv, int maxv, const std::string &variableName = ""); + + /* Reads space-separated sequence of integers. */ + std::vector + readIntegers(int size, int minv, int maxv, const std::string &variablesName = "", int indexBase = 1); + + /* Reads space-separated sequence of integers. */ + std::vector readIntegers(int size, int indexBase = 1); + + /* Reads space-separated sequence of integers. */ + std::vector readInts(int size, int minv, int maxv, const std::string &variablesName = "", int indexBase = 1); + + /* Reads space-separated sequence of integers. */ + std::vector readInts(int size, int indexBase = 1); + + /* + * Reads new double. Ignores white-spaces into the non-strict mode + * (strict mode is used in validators usually). + */ + double readReal(); + + /* + * Reads new double. Ignores white-spaces into the non-strict mode + * (strict mode is used in validators usually). + */ + double readDouble(); + + /* As "readReal()" but ensures that value in the range [minv,maxv]. */ + double readReal(double minv, double maxv, const std::string &variableName = ""); + + std::vector + readReals(int size, double minv, double maxv, const std::string &variablesName = "", int indexBase = 1); + + std::vector readReals(int size, int indexBase = 1); + + /* As "readDouble()" but ensures that value in the range [minv,maxv]. */ + double readDouble(double minv, double maxv, const std::string &variableName = ""); + + std::vector + readDoubles(int size, double minv, double maxv, const std::string &variablesName = "", int indexBase = 1); + + std::vector readDoubles(int size, int indexBase = 1); + + /* + * As "readReal()" but ensures that value in the range [minv,maxv] and + * number of digit after the decimal point is in range [minAfterPointDigitCount,maxAfterPointDigitCount] + * and number is in the form "[-]digit(s)[.digit(s)]". + */ + double readStrictReal(double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variableName = ""); + + std::vector readStrictReals(int size, double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variablesName = "", int indexBase = 1); + + /* + * As "readDouble()" but ensures that value in the range [minv,maxv] and + * number of digit after the decimal point is in range [minAfterPointDigitCount,maxAfterPointDigitCount] + * and number is in the form "[-]digit(s)[.digit(s)]". + */ + double readStrictDouble(double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variableName = ""); + + std::vector readStrictDoubles(int size, double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variablesName = "", int indexBase = 1); + + /* As readLine(). */ + std::string readString(); + + /* Read many lines. */ + std::vector readStrings(int size, int indexBase = 1); + + /* See readLine(). */ + void readStringTo(std::string &result); + + /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ + std::string readString(const pattern &p, const std::string &variableName = ""); + + /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ + std::string readString(const std::string &ptrn, const std::string &variableName = ""); + + /* Read many lines. */ + std::vector + readStrings(int size, const pattern &p, const std::string &variableName = "", int indexBase = 1); + + /* Read many lines. */ + std::vector + readStrings(int size, const std::string &ptrn, const std::string &variableName = "", int indexBase = 1); + + /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ + void readStringTo(std::string &result, const pattern &p, const std::string &variableName = ""); + + /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ + void readStringTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); + + /* + * Reads line from the current position to EOLN or EOF. Moves stream pointer to + * the first character of the new line (if possible). + */ + std::string readLine(); + + /* Read many lines. */ + std::vector readLines(int size, int indexBase = 1); + + /* See readLine(). */ + void readLineTo(std::string &result); + + /* The same as "readLine()", but ensures that line matches to the given pattern. */ + std::string readLine(const pattern &p, const std::string &variableName = ""); + + /* The same as "readLine()", but ensures that line matches to the given pattern. */ + std::string readLine(const std::string &ptrn, const std::string &variableName = ""); + + /* Read many lines. */ + std::vector + readLines(int size, const pattern &p, const std::string &variableName = "", int indexBase = 1); + + /* Read many lines. */ + std::vector + readLines(int size, const std::string &ptrn, const std::string &variableName = "", int indexBase = 1); + + /* The same as "readLine()", but ensures that line matches to the given pattern. */ + void readLineTo(std::string &result, const pattern &p, const std::string &variableName = ""); + + /* The same as "readLine()", but ensures that line matches to the given pattern. */ + void readLineTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); + + /* Reads EOLN or fails. Use it in validators. Calls "eoln()" method internally. */ + void readEoln(); + + /* Reads EOF or fails. Use it in validators. Calls "eof()" method internally. */ + void readEof(); + + /* + * Quit-functions aborts program with and : + * input/answer streams replace any result to FAIL. + */ + NORETURN void quit(TResult result, const char *msg); + /* + * Quit-functions aborts program with and : + * input/answer streams replace any result to FAIL. + */ + NORETURN void quitf(TResult result, const char *msg, ...); + + /* + * Quit-functions aborts program with and : + * input/answer streams replace any result to FAIL. + */ + void quitif(bool condition, TResult result, const char *msg, ...); + /* + * Quit-functions aborts program with and : + * input/answer streams replace any result to FAIL. + */ + NORETURN void quits(TResult result, std::string msg); + + /* + * Checks condition and aborts a program if codition is false. + * Returns _wa for ouf and _fail on any other streams. + */ +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))) +#endif + void ensuref(bool cond, const char *format, ...); + + void __testlib_ensure(bool cond, std::string message); + + void close(); + + const static int NO_INDEX = INT_MAX; + const static char OPEN_BRACKET = char(11); + const static char CLOSE_BRACKET = char(17); + + const static WORD LightGray = 0x07; + const static WORD LightRed = 0x0c; + const static WORD LightCyan = 0x0b; + const static WORD LightGreen = 0x0a; + const static WORD LightYellow = 0x0e; + + static void textColor(WORD color); + + static void quitscr(WORD color, const char *msg); + + static void quitscrS(WORD color, std::string msg); + + void xmlSafeWrite(std::FILE *file, const char *msg); + + /* Skips UTF-8 Byte Order Mark. */ + void skipBom(); + +private: + InStream(const InStream &); + + InStream &operator=(const InStream &); +}; + +InStream inf; +InStream ouf; +InStream ans; +bool appesMode; +std::string resultName; +std::string checkerName = "untitled checker"; +random_t rnd; +TTestlibMode testlibMode = _unknown; +double __testlib_points = std::numeric_limits::infinity(); + +struct ValidatorBoundsHit { + static const double EPS; + bool minHit; + bool maxHit; + + ValidatorBoundsHit(bool minHit = false, bool maxHit = false) : minHit(minHit), maxHit(maxHit) { + }; + + ValidatorBoundsHit merge(const ValidatorBoundsHit &validatorBoundsHit) { + return ValidatorBoundsHit( + __testlib_max(minHit, validatorBoundsHit.minHit), + __testlib_max(maxHit, validatorBoundsHit.maxHit) + ); + } +}; + +const double ValidatorBoundsHit::EPS = 1E-12; + +class Validator { +private: + const static std::string TEST_MARKUP_HEADER; + const static std::string TEST_CASE_OPEN_TAG; + const static std::string TEST_CASE_CLOSE_TAG; + + bool _initialized; + std::string _testset; + std::string _group; + + std::string _testOverviewLogFileName; + std::string _testMarkupFileName; + int _testCase = -1; + std::string _testCaseFileName; + + std::map _boundsHitByVariableName; + std::set _features; + std::set _hitFeatures; + + bool isVariableNameBoundsAnalyzable(const std::string &variableName) { + for (size_t i = 0; i < variableName.length(); i++) + if ((variableName[i] >= '0' && variableName[i] <= '9') || variableName[i] < ' ') + return false; + return true; + } + + bool isFeatureNameAnalyzable(const std::string &featureName) { + for (size_t i = 0; i < featureName.length(); i++) + if (featureName[i] < ' ') + return false; + return true; + } + +public: + Validator() : _initialized(false), _testset("tests"), _group() { + } + + void initialize() { + _initialized = true; + } + + std::string testset() const { + if (!_initialized) + __testlib_fail("Validator should be initialized with registerValidation(argc, argv) instead of registerValidation() to support validator.testset()"); + return _testset; + } + + std::string group() const { + if (!_initialized) + __testlib_fail("Validator should be initialized with registerValidation(argc, argv) instead of registerValidation() to support validator.group()"); + return _group; + } + + std::string testOverviewLogFileName() const { + return _testOverviewLogFileName; + } + + std::string testMarkupFileName() const { + return _testMarkupFileName; + } + + int testCase() const { + return _testCase; + } + + std::string testCaseFileName() const { + return _testCaseFileName; + } + + void setTestset(const char *const testset) { + _testset = testset; + } + + void setGroup(const char *const group) { + _group = group; + } + + void setTestOverviewLogFileName(const char *const testOverviewLogFileName) { + _testOverviewLogFileName = testOverviewLogFileName; + } + + void setTestMarkupFileName(const char *const testMarkupFileName) { + _testMarkupFileName = testMarkupFileName; + } + + void setTestCase(int testCase) { + _testCase = testCase; + } + + void setTestCaseFileName(const char *const testCaseFileName) { + _testCaseFileName = testCaseFileName; + } + + void addBoundsHit(const std::string &variableName, ValidatorBoundsHit boundsHit) { + if (isVariableNameBoundsAnalyzable(variableName)) { + _boundsHitByVariableName[variableName] + = boundsHit.merge(_boundsHitByVariableName[variableName]); + } + } + + std::string getBoundsHitLog() { + std::string result; + for (std::map::iterator i = _boundsHitByVariableName.begin(); + i != _boundsHitByVariableName.end(); + i++) { + result += "\"" + i->first + "\":"; + if (i->second.minHit) + result += " min-value-hit"; + if (i->second.maxHit) + result += " max-value-hit"; + result += "\n"; + } + return result; + } + + std::string getFeaturesLog() { + std::string result; + for (std::set::iterator i = _features.begin(); + i != _features.end(); + i++) { + result += "feature \"" + *i + "\":"; + if (_hitFeatures.count(*i)) + result += " hit"; + result += "\n"; + } + return result; + } + + void writeTestOverviewLog() { + if (!_testOverviewLogFileName.empty()) { + std::string fileName(_testOverviewLogFileName); + _testOverviewLogFileName = ""; + FILE *testOverviewLogFile = fopen(fileName.c_str(), "w"); + if (NULL == testOverviewLogFile) + __testlib_fail("Validator::writeTestOverviewLog: can't write test overview log to (" + fileName + ")"); + fprintf(testOverviewLogFile, "%s%s", getBoundsHitLog().c_str(), getFeaturesLog().c_str()); + if (fclose(testOverviewLogFile)) + __testlib_fail( + "Validator::writeTestOverviewLog: can't close test overview log file (" + fileName + ")"); + } + } + + void writeTestMarkup() { + if (!_testMarkupFileName.empty()) { + std::vector readChars = inf.getReadChars(); + if (!readChars.empty()) { + std::string markup(TEST_MARKUP_HEADER); + for (size_t i = 0; i < readChars.size(); i++) { + int c = readChars[i]; + if (i + 1 == readChars.size() && c == -1) + continue; + if (c <= 256) { + char cc = char(c); + if (cc == '\\' || cc == '!') + markup += '\\'; + markup += cc; + } else { + markup += TEST_CASE_OPEN_TAG; + markup += toString(c - 256); + markup += TEST_CASE_CLOSE_TAG; + } + } + FILE* f; + bool standard_file = false; + if (_testMarkupFileName == "stdout") + f = stdout, standard_file = true; + else if (_testMarkupFileName == "stderr") + f = stderr, standard_file = true; + else { + f = fopen(_testMarkupFileName.c_str(), "wb"); + if (NULL == f) + __testlib_fail("Validator::writeTestMarkup: can't write test markup to (" + _testMarkupFileName + ")"); + } + std::fprintf(f, "%s", markup.c_str()); + std::fflush(f); + if (!standard_file) + if (std::fclose(f)) + __testlib_fail("Validator::writeTestMarkup: can't close test markup file (" + _testCaseFileName + ")"); + } + } + } + + void writeTestCase() { + if (_testCase > 0) { + std::vector readChars = inf.getReadChars(); + if (!readChars.empty()) { + std::string content, testCaseContent; + bool matchedTestCase = false; + for (size_t i = 0; i < readChars.size(); i++) { + int c = readChars[i]; + if (i + 1 == readChars.size() && c == -1) + continue; + if (c <= 256) + content += char(c); + else { + if (matchedTestCase) { + testCaseContent = content; + matchedTestCase = false; + } + content = ""; + int testCase = c - 256; + if (testCase == _testCase) + matchedTestCase = true; + } + } + if (matchedTestCase) + testCaseContent = content; + + if (!testCaseContent.empty()) { + FILE* f; + bool standard_file = false; + if (_testCaseFileName.empty() || _testCaseFileName == "stdout") + f = stdout, standard_file = true; + else if (_testCaseFileName == "stderr") + f = stderr, standard_file = true; + else { + f = fopen(_testCaseFileName.c_str(), "wb"); + if (NULL == f) + __testlib_fail("Validator::writeTestCase: can't write test case to (" + _testCaseFileName + ")"); + } + std::fprintf(f, "%s", testCaseContent.c_str()); + std::fflush(f); + if (!standard_file) + if (std::fclose(f)) + __testlib_fail("Validator::writeTestCase: can't close test case file (" + _testCaseFileName + ")"); + } + } + } + } + + void addFeature(const std::string &feature) { + if (_features.count(feature)) + __testlib_fail("Feature " + feature + " registered twice."); + if (!isFeatureNameAnalyzable(feature)) + __testlib_fail("Feature name '" + feature + "' contains restricted characters."); + + _features.insert(feature); + } + + void feature(const std::string &feature) { + if (!isFeatureNameAnalyzable(feature)) + __testlib_fail("Feature name '" + feature + "' contains restricted characters."); + + if (!_features.count(feature)) + __testlib_fail("Feature " + feature + " didn't registered via addFeature(feature)."); + + _hitFeatures.insert(feature); + } +} validator; + +const std::string Validator::TEST_MARKUP_HEADER = "MU\xF3\x01"; +const std::string Validator::TEST_CASE_OPEN_TAG = "!c"; +const std::string Validator::TEST_CASE_CLOSE_TAG = ";"; + +struct TestlibFinalizeGuard { + static bool alive; + static bool registered; + + int quitCount, readEofCount; + + TestlibFinalizeGuard() : quitCount(0), readEofCount(0) { + // No operations. + } + + ~TestlibFinalizeGuard() { + bool _alive = alive; + alive = false; + + if (_alive) { + if (testlibMode == _checker && quitCount == 0) + __testlib_fail("Checker must end with quit or quitf call."); + + if (testlibMode == _validator && readEofCount == 0 && quitCount == 0) + __testlib_fail("Validator must end with readEof call."); + + /* opts */ + autoEnsureNoUnusedOpts(); + + if (!registered) + __testlib_fail("Call register-function in the first line of the main (registerTestlibCmd or other similar)"); + } + + if (__testlib_exitCode == 0) { + validator.writeTestOverviewLog(); + validator.writeTestMarkup(); + validator.writeTestCase(); + } + } + +private: + /* opts */ + void autoEnsureNoUnusedOpts(); +}; + +bool TestlibFinalizeGuard::alive = true; +bool TestlibFinalizeGuard::registered = false; +extern TestlibFinalizeGuard testlibFinalizeGuard; + +/* + * Call it to disable checks on finalization. + */ +void disableFinalizeGuard() { + TestlibFinalizeGuard::alive = false; +} + +/* Interactor streams. + */ +std::fstream tout; + +/* implementation + */ + +InStream::InStream() { + reader = NULL; + lastLine = -1; + opened = false; + name = ""; + mode = _input; + strict = false; + stdfile = false; + wordReserveSize = 4; + readManyIteration = NO_INDEX; + maxFileSize = 128 * 1024 * 1024; // 128MB. + maxTokenLength = 32 * 1024 * 1024; // 32MB. + maxMessageLength = 32000; +} + +InStream::InStream(const InStream &baseStream, std::string content) { + reader = new StringInputStreamReader(content); + lastLine = -1; + opened = true; + strict = baseStream.strict; + stdfile = false; + mode = baseStream.mode; + name = "based on " + baseStream.name; + readManyIteration = NO_INDEX; + maxFileSize = 128 * 1024 * 1024; // 128MB. + maxTokenLength = 32 * 1024 * 1024; // 32MB. + maxMessageLength = 32000; +} + +InStream::~InStream() { + if (NULL != reader) { + reader->close(); + delete reader; + reader = NULL; + } +} + +void InStream::setTestCase(int testCase) { + if (testlibMode != _validator || mode != _input || !stdfile || this != &inf) + __testlib_fail("InStream::setTestCase can be used only for inf in validator-mode." + " Actually, prefer setTestCase function instead of InStream member"); + reader->setTestCase(testCase); +} + +std::vector InStream::getReadChars() { + if (testlibMode != _validator || mode != _input || !stdfile || this != &inf) + __testlib_fail("InStream::getReadChars can be used only for inf in validator-mode."); + return reader == NULL ? std::vector() : reader->getReadChars(); +} + +void setTestCase(int testCase) { + static bool first_run = true; + static bool zero_based = false; + + if (first_run && testCase == 0) + zero_based = true; + + if (zero_based) + testCase++; + + __testlib_hasTestCase = true; + __testlib_testCase = testCase; + + if (testlibMode == _validator) + inf.setTestCase(testCase); + + first_run = false; +} + +#ifdef __GNUC__ +__attribute__((const)) +#endif +int resultExitCode(TResult r) { + if (r == _ok) + return OK_EXIT_CODE; + if (r == _wa) + return WA_EXIT_CODE; + if (r == _pe) + return PE_EXIT_CODE; + if (r == _fail) + return FAIL_EXIT_CODE; + if (r == _dirt) + return DIRT_EXIT_CODE; + if (r == _points) + return POINTS_EXIT_CODE; + if (r == _unexpected_eof) +#ifdef ENABLE_UNEXPECTED_EOF + return UNEXPECTED_EOF_EXIT_CODE; +#else + return PE_EXIT_CODE; +#endif + if (r >= _partially) + return PC_BASE_EXIT_CODE + (r - _partially); + return FAIL_EXIT_CODE; +} + +void InStream::textColor( +#if !(defined(ON_WINDOWS) && (!defined(_MSC_VER) || _MSC_VER > 1400)) && defined(__GNUC__) + __attribute__((unused)) +#endif + WORD color +) { +#if defined(ON_WINDOWS) && (!defined(_MSC_VER) || _MSC_VER > 1400) + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(handle, color); +#endif +#if !defined(ON_WINDOWS) && defined(__GNUC__) + if (isatty(2)) + { + switch (color) + { + case LightRed: + fprintf(stderr, "\033[1;31m"); + break; + case LightCyan: + fprintf(stderr, "\033[1;36m"); + break; + case LightGreen: + fprintf(stderr, "\033[1;32m"); + break; + case LightYellow: + fprintf(stderr, "\033[1;33m"); + break; + case LightGray: + default: + fprintf(stderr, "\033[0m"); + } + } +#endif +} + +#ifdef TESTLIB_THROW_EXIT_EXCEPTION_INSTEAD_OF_EXIT +class exit_exception: public std::exception { +private: + int exitCode; +public: + exit_exception(int exitCode): exitCode(exitCode) {} + int getExitCode() { return exitCode; } +}; +#endif + +NORETURN void halt(int exitCode) { +#ifdef FOOTER + InStream::textColor(InStream::LightGray); + std::fprintf(stderr, "Checker: \"%s\"\n", checkerName.c_str()); + std::fprintf(stderr, "Exit code: %d\n", exitCode); + InStream::textColor(InStream::LightGray); +#endif + __testlib_exitCode = exitCode; +#ifdef TESTLIB_THROW_EXIT_EXCEPTION_INSTEAD_OF_EXIT + throw exit_exception(exitCode); +#endif + std::exit(exitCode); +} + +static bool __testlib_shouldCheckDirt(TResult result) { + return result == _ok || result == _points || result >= _partially; +} + +static std::string __testlib_appendMessage(const std::string &message, const std::string &extra) { + int openPos = -1, closePos = -1; + for (size_t i = 0; i < message.length(); i++) { + if (message[i] == InStream::OPEN_BRACKET) { + if (openPos == -1) + openPos = int(i); + else + openPos = INT_MAX; + } + if (message[i] == InStream::CLOSE_BRACKET) { + if (closePos == -1) + closePos = int(i); + else + closePos = INT_MAX; + } + } + if (openPos != -1 && openPos != INT_MAX + && closePos != -1 && closePos != INT_MAX + && openPos < closePos) { + size_t index = message.find(extra, openPos); + if (index == std::string::npos || int(index) >= closePos) { + std::string result(message); + result.insert(closePos, ", " + extra); + return result; + } + return message; + } + + return message + " " + InStream::OPEN_BRACKET + extra + InStream::CLOSE_BRACKET; +} + +static std::string __testlib_toPrintableMessage(const std::string &message) { + int openPos = -1, closePos = -1; + for (size_t i = 0; i < message.length(); i++) { + if (message[i] == InStream::OPEN_BRACKET) { + if (openPos == -1) + openPos = int(i); + else + openPos = INT_MAX; + } + if (message[i] == InStream::CLOSE_BRACKET) { + if (closePos == -1) + closePos = int(i); + else + closePos = INT_MAX; + } + } + if (openPos != -1 && openPos != INT_MAX + && closePos != -1 && closePos != INT_MAX + && openPos < closePos) { + std::string result(message); + result[openPos] = '('; + result[closePos] = ')'; + return result; + } + + return message; +} + +NORETURN void InStream::quit(TResult result, const char *msg) { + if (TestlibFinalizeGuard::alive) + testlibFinalizeGuard.quitCount++; + + std::string message(msg); + message = trim(message); + + if (__testlib_hasTestCase) { + if (result != _ok) + message = __testlib_appendMessage(message, "test case " + vtos(__testlib_testCase)); + else { + if (__testlib_testCase == 1) + message = __testlib_appendMessage(message, vtos(__testlib_testCase) + " test case"); + else + message = __testlib_appendMessage(message, vtos(__testlib_testCase) + " test cases"); + } + } + + // You can change maxMessageLength. + // Example: 'inf.maxMessageLength = 1024 * 1024;'. + if (message.length() > maxMessageLength) { + std::string warn = "message length exceeds " + vtos(maxMessageLength) + + ", the message is truncated: "; + message = warn + message.substr(0, maxMessageLength - warn.length()); + } + +#ifndef ENABLE_UNEXPECTED_EOF + if (result == _unexpected_eof) + result = _pe; +#endif + + if (testlibMode == _scorer && result != _fail) + quits(_fail, "Scorer should return points only. Don't use a quit function."); + + if (mode != _output && result != _fail) { + if (mode == _input && testlibMode == _validator && lastLine != -1) + quits(_fail, __testlib_appendMessage(__testlib_appendMessage(message, name), "line " + vtos(lastLine))); + else + quits(_fail, __testlib_appendMessage(message, name)); + } + + std::FILE *resultFile; + std::string errorName; + + if (__testlib_shouldCheckDirt(result)) { + if (testlibMode != _interactor && !ouf.seekEof()) + quit(_dirt, "Extra information in the output file"); + } + + int pctype = result - _partially; + bool isPartial = false; + + switch (result) { + case _ok: + errorName = "ok "; + quitscrS(LightGreen, errorName); + break; + case _wa: + errorName = "wrong answer "; + quitscrS(LightRed, errorName); + break; + case _pe: + errorName = "wrong output format "; + quitscrS(LightRed, errorName); + break; + case _fail: + errorName = "FAIL "; + quitscrS(LightRed, errorName); + break; + case _dirt: + errorName = "wrong output format "; + quitscrS(LightCyan, errorName); + result = _pe; + break; + case _points: + errorName = "points "; + quitscrS(LightYellow, errorName); + break; + case _unexpected_eof: + errorName = "unexpected eof "; + quitscrS(LightCyan, errorName); + break; + default: + if (result >= _partially) { + errorName = format("partially correct (%d) ", pctype); + isPartial = true; + quitscrS(LightYellow, errorName); + } else + quit(_fail, "What is the code ??? "); + } + + if (resultName != "") { + resultFile = std::fopen(resultName.c_str(), "w"); + if (resultFile == NULL) { + resultName = ""; + quit(_fail, "Can not write to the result file"); + } + if (appesMode) { + std::fprintf(resultFile, ""); + if (isPartial) + std::fprintf(resultFile, "", + outcomes[(int) _partially].c_str(), pctype); + else { + if (result != _points) + std::fprintf(resultFile, "", outcomes[(int) result].c_str()); + else { + if (__testlib_points == std::numeric_limits::infinity()) + quit(_fail, "Expected points, but infinity found"); + std::string stringPoints = removeDoubleTrailingZeroes(format("%.10f", __testlib_points)); + std::fprintf(resultFile, "", + outcomes[(int) result].c_str(), stringPoints.c_str()); + } + } + xmlSafeWrite(resultFile, __testlib_toPrintableMessage(message).c_str()); + std::fprintf(resultFile, "\n"); + } else + std::fprintf(resultFile, "%s", __testlib_toPrintableMessage(message).c_str()); + if (NULL == resultFile || fclose(resultFile) != 0) { + resultName = ""; + quit(_fail, "Can not write to the result file"); + } + } + + quitscr(LightGray, __testlib_toPrintableMessage(message).c_str()); + std::fprintf(stderr, "\n"); + + inf.close(); + ouf.close(); + ans.close(); + if (tout.is_open()) + tout.close(); + + textColor(LightGray); + + if (resultName != "") + std::fprintf(stderr, "See file to check exit message\n"); + + halt(resultExitCode(result)); +} + +#ifdef __GNUC__ +__attribute__ ((format (printf, 3, 4))) +#endif +NORETURN void InStream::quitf(TResult result, const char *msg, ...) { + FMT_TO_RESULT(msg, msg, message); + InStream::quit(result, message.c_str()); +} + +#ifdef __GNUC__ +__attribute__ ((format (printf, 4, 5))) +#endif +void InStream::quitif(bool condition, TResult result, const char *msg, ...) { + if (condition) { + FMT_TO_RESULT(msg, msg, message); + InStream::quit(result, message.c_str()); + } +} + +NORETURN void InStream::quits(TResult result, std::string msg) { + InStream::quit(result, msg.c_str()); +} + +void InStream::xmlSafeWrite(std::FILE *file, const char *msg) { + size_t lmsg = strlen(msg); + for (size_t i = 0; i < lmsg; i++) { + if (msg[i] == '&') { + std::fprintf(file, "%s", "&"); + continue; + } + if (msg[i] == '<') { + std::fprintf(file, "%s", "<"); + continue; + } + if (msg[i] == '>') { + std::fprintf(file, "%s", ">"); + continue; + } + if (msg[i] == '"') { + std::fprintf(file, "%s", """); + continue; + } + if (0 <= msg[i] && msg[i] <= 31) { + std::fprintf(file, "%c", '.'); + continue; + } + std::fprintf(file, "%c", msg[i]); + } +} + +void InStream::quitscrS(WORD color, std::string msg) { + quitscr(color, msg.c_str()); +} + +void InStream::quitscr(WORD color, const char *msg) { + if (resultName == "") { + textColor(color); + std::fprintf(stderr, "%s", msg); + textColor(LightGray); + } +} + +void InStream::reset(std::FILE *file) { + if (opened && stdfile) + quit(_fail, "Can't reset standard handle"); + + if (opened) + close(); + + if (!stdfile && NULL == file) + if (NULL == (file = std::fopen(name.c_str(), "rb"))) { + if (mode == _output) + quits(_pe, std::string("Output file not found: \"") + name + "\""); + + if (mode == _answer) + quits(_fail, std::string("Answer file not found: \"") + name + "\""); + } + + if (NULL != file) { + opened = true; + __testlib_set_binary(file); + + if (stdfile) + reader = new FileInputStreamReader(file, name); + else + reader = new BufferedFileInputStreamReader(file, name); + } else { + opened = false; + reader = NULL; + } +} + +void InStream::init(std::string fileName, TMode mode) { + opened = false; + name = fileName; + stdfile = false; + this->mode = mode; + + std::ifstream stream; + stream.open(fileName.c_str(), std::ios::in); + if (stream.is_open()) { + std::streampos start = stream.tellg(); + stream.seekg(0, std::ios::end); + std::streampos end = stream.tellg(); + size_t fileSize = size_t(end - start); + stream.close(); + + // You can change maxFileSize. + // Example: 'inf.maxFileSize = 256 * 1024 * 1024;'. + if (fileSize > maxFileSize) + quitf(_pe, "File size exceeds %d bytes, size is %d", int(maxFileSize), int(fileSize)); + } + + reset(); + skipBom(); +} + +void InStream::init(std::FILE *f, TMode mode) { + opened = false; + name = "untitled"; + this->mode = mode; + + if (f == stdin) + name = "stdin", stdfile = true; + if (f == stdout) + name = "stdout", stdfile = true; + if (f == stderr) + name = "stderr", stdfile = true; + + reset(f); + skipBom(); +} + +void InStream::skipBom() { + const std::string utf8Bom = "\xEF\xBB\xBF"; + size_t index = 0; + while (index < utf8Bom.size() && curChar() == utf8Bom[index]) { + index++; + skipChar(); + } + if (index < utf8Bom.size()) { + while (index != 0) { + unreadChar(utf8Bom[index - 1]); + index--; + } + } +} + +char InStream::curChar() { + return char(reader->curChar()); +} + +char InStream::nextChar() { + return char(reader->nextChar()); +} + +char InStream::readChar() { + return nextChar(); +} + +char InStream::readChar(char c) { + lastLine = reader->getLine(); + char found = readChar(); + if (c != found) { + if (!isEoln(found)) + quit(_pe, ("Unexpected character '" + std::string(1, found) + "', but '" + std::string(1, c) + + "' expected").c_str()); + else + quit(_pe, ("Unexpected character " + ("#" + vtos(int(found))) + ", but '" + std::string(1, c) + + "' expected").c_str()); + } + return found; +} + +char InStream::readSpace() { + return readChar(' '); +} + +void InStream::unreadChar(char c) { + reader->unreadChar(c); +} + +void InStream::skipChar() { + reader->skipChar(); +} + +void InStream::skipBlanks() { + while (isBlanks(reader->curChar())) + reader->skipChar(); +} + +std::string InStream::readWord() { + readWordTo(_tmpReadToken); + return _tmpReadToken; +} + +void InStream::readWordTo(std::string &result) { + if (!strict) + skipBlanks(); + + lastLine = reader->getLine(); + int cur = reader->nextChar(); + + if (cur == EOFC) + quit(_unexpected_eof, "Unexpected end of file - token expected"); + + if (isBlanks(cur)) + quit(_pe, "Unexpected white-space - token expected"); + + result.clear(); + + while (!(isBlanks(cur) || cur == EOFC)) { + result += char(cur); + + // You can change maxTokenLength. + // Example: 'inf.maxTokenLength = 128 * 1024 * 1024;'. + if (result.length() > maxTokenLength) + quitf(_pe, "Length of token exceeds %d, token is '%s...'", int(maxTokenLength), + __testlib_part(result).c_str()); + + cur = reader->nextChar(); + } + + reader->unreadChar(cur); + + if (result.length() == 0) + quit(_unexpected_eof, "Unexpected end of file or white-space - token expected"); +} + +std::string InStream::readToken() { + return readWord(); +} + +void InStream::readTokenTo(std::string &result) { + readWordTo(result); +} + +static std::string __testlib_part(const std::string &s) { + std::string t; + for (size_t i = 0; i < s.length(); i++) + if (s[i] != '\0') + t += s[i]; + else + t += '~'; + if (t.length() <= 64) + return t; + else + return t.substr(0, 30) + "..." + t.substr(s.length() - 31, 31); +} + +#define __testlib_readMany(readMany, readOne, typeName, space) \ + if (size < 0) \ + quit(_fail, #readMany ": size should be non-negative."); \ + if (size > 100000000) \ + quit(_fail, #readMany ": size should be at most 100000000."); \ + \ + std::vector result(size); \ + readManyIteration = indexBase; \ + \ + for (int i = 0; i < size; i++) \ + { \ + result[i] = readOne; \ + readManyIteration++; \ + if (strict && space && i + 1 < size) \ + readSpace(); \ + } \ + \ + readManyIteration = NO_INDEX; \ + return result; \ + + +std::string InStream::readWord(const pattern &p, const std::string &variableName) { + readWordTo(_tmpReadToken); + if (!p.matches(_tmpReadToken)) { + if (readManyIteration == NO_INDEX) { + if (variableName.empty()) + quit(_wa, + ("Token \"" + __testlib_part(_tmpReadToken) + "\" doesn't correspond to pattern \"" + p.src() + + "\"").c_str()); + else + quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(_tmpReadToken) + + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + } else { + if (variableName.empty()) + quit(_wa, ("Token element [index=" + vtos(readManyIteration) + "] equals to \"" + + __testlib_part(_tmpReadToken) + "\" doesn't correspond to pattern \"" + p.src() + + "\"").c_str()); + else + quit(_wa, ("Token element " + variableName + "[" + vtos(readManyIteration) + "] equals to \"" + + __testlib_part(_tmpReadToken) + "\", doesn't correspond to pattern \"" + p.src() + + "\"").c_str()); + } + } + return _tmpReadToken; +} + +std::vector +InStream::readWords(int size, const pattern &p, const std::string &variablesName, int indexBase) { + __testlib_readMany(readWords, readWord(p, variablesName), std::string, true); +} + +std::vector InStream::readWords(int size, int indexBase) { + __testlib_readMany(readWords, readWord(), std::string, true); +} + +std::string InStream::readWord(const std::string &ptrn, const std::string &variableName) { + return readWord(pattern(ptrn), variableName); +} + +std::vector +InStream::readWords(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { + pattern p(ptrn); + __testlib_readMany(readWords, readWord(p, variablesName), std::string, true); +} + +std::string InStream::readToken(const pattern &p, const std::string &variableName) { + return readWord(p, variableName); +} + +std::vector +InStream::readTokens(int size, const pattern &p, const std::string &variablesName, int indexBase) { + __testlib_readMany(readTokens, readToken(p, variablesName), std::string, true); +} + +std::vector InStream::readTokens(int size, int indexBase) { + __testlib_readMany(readTokens, readToken(), std::string, true); +} + +std::string InStream::readToken(const std::string &ptrn, const std::string &variableName) { + return readWord(ptrn, variableName); +} + +std::vector +InStream::readTokens(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { + pattern p(ptrn); + __testlib_readMany(readTokens, readWord(p, variablesName), std::string, true); +} + +void InStream::readWordTo(std::string &result, const pattern &p, const std::string &variableName) { + readWordTo(result); + if (!p.matches(result)) { + if (variableName.empty()) + quit(_wa, ("Token \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() + + "\"").c_str()); + else + quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(result) + + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + } +} + +void InStream::readWordTo(std::string &result, const std::string &ptrn, const std::string &variableName) { + return readWordTo(result, pattern(ptrn), variableName); +} + +void InStream::readTokenTo(std::string &result, const pattern &p, const std::string &variableName) { + return readWordTo(result, p, variableName); +} + +void InStream::readTokenTo(std::string &result, const std::string &ptrn, const std::string &variableName) { + return readWordTo(result, ptrn, variableName); +} + +#ifdef __GNUC__ +__attribute__((pure)) +#endif +static inline bool equals(long long integer, const char *s) { + if (integer == LLONG_MIN) + return strcmp(s, "-9223372036854775808") == 0; + + if (integer == 0LL) + return strcmp(s, "0") == 0; + + size_t length = strlen(s); + + if (length == 0) + return false; + + if (integer < 0 && s[0] != '-') + return false; + + if (integer < 0) + s++, length--, integer = -integer; + + if (length == 0) + return false; + + while (integer > 0) { + int digit = int(integer % 10); + + if (s[length - 1] != '0' + digit) + return false; + + length--; + integer /= 10; + } + + return length == 0; +} + +#ifdef __GNUC__ +__attribute__((pure)) +#endif +static inline bool equals(unsigned long long integer, const char *s) { + if (integer == ULLONG_MAX) + return strcmp(s, "18446744073709551615") == 0; + + if (integer == 0ULL) + return strcmp(s, "0") == 0; + + size_t length = strlen(s); + + if (length == 0) + return false; + + while (integer > 0) { + int digit = int(integer % 10); + + if (s[length - 1] != '0' + digit) + return false; + + length--; + integer /= 10; + } + + return length == 0; +} + +static inline double stringToDouble(InStream &in, const char *buffer) { + double retval; + + size_t length = strlen(buffer); + + int minusCount = 0; + int plusCount = 0; + int decimalPointCount = 0; + int digitCount = 0; + int eCount = 0; + + for (size_t i = 0; i < length; i++) { + if (('0' <= buffer[i] && buffer[i] <= '9') || buffer[i] == '.' + || buffer[i] == 'e' || buffer[i] == 'E' + || buffer[i] == '-' || buffer[i] == '+') { + if ('0' <= buffer[i] && buffer[i] <= '9') + digitCount++; + if (buffer[i] == 'e' || buffer[i] == 'E') + eCount++; + if (buffer[i] == '-') + minusCount++; + if (buffer[i] == '+') + plusCount++; + if (buffer[i] == '.') + decimalPointCount++; + } else + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + } + + // If for sure is not a number in standard notation or in e-notation. + if (digitCount == 0 || minusCount > 2 || plusCount > 2 || decimalPointCount > 1 || eCount > 1) + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + char *suffix = new char[length + 1]; + std::memset(suffix, 0, length + 1); + int scanned = std::sscanf(buffer, "%lf%s", &retval, suffix); + bool empty = strlen(suffix) == 0; + delete[] suffix; + + if (scanned == 1 || (scanned == 2 && empty)) { + if (__testlib_isNaN(retval)) + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + return retval; + } else + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); +} + +static inline double stringToDouble(InStream &in, const std::string& buffer) { + for (size_t i = 0; i < buffer.length(); i++) + if (buffer[i] == '\0') + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); + return stringToDouble(in, buffer.c_str()); +} + +static inline double stringToStrictDouble(InStream &in, const char *buffer, + int minAfterPointDigitCount, int maxAfterPointDigitCount) { + if (minAfterPointDigitCount < 0) + in.quit(_fail, "stringToStrictDouble: minAfterPointDigitCount should be non-negative."); + + if (minAfterPointDigitCount > maxAfterPointDigitCount) + in.quit(_fail, + "stringToStrictDouble: minAfterPointDigitCount should be less or equal to maxAfterPointDigitCount."); + + double retval; + + size_t length = strlen(buffer); + + if (length == 0 || length > 1000) + in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + if (buffer[0] != '-' && (buffer[0] < '0' || buffer[0] > '9')) + in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + int pointPos = -1; + for (size_t i = 1; i + 1 < length; i++) { + if (buffer[i] == '.') { + if (pointPos > -1) + in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + pointPos = int(i); + } + if (buffer[i] != '.' && (buffer[i] < '0' || buffer[i] > '9')) + in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + } + + if (buffer[length - 1] < '0' || buffer[length - 1] > '9') + in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + int afterDigitsCount = (pointPos == -1 ? 0 : int(length) - pointPos - 1); + if (afterDigitsCount < minAfterPointDigitCount || afterDigitsCount > maxAfterPointDigitCount) + in.quit(_pe, ("Expected strict double with number of digits after point in range [" + + vtos(minAfterPointDigitCount) + + "," + + vtos(maxAfterPointDigitCount) + + "], but \"" + __testlib_part(buffer) + "\" found").c_str() + ); + + int firstDigitPos = -1; + for (size_t i = 0; i < length; i++) + if (buffer[i] >= '0' && buffer[i] <= '9') { + firstDigitPos = int(i); + break; + } + + if (firstDigitPos > 1 || firstDigitPos == -1) + in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + if (buffer[firstDigitPos] == '0' && firstDigitPos + 1 < int(length) + && buffer[firstDigitPos + 1] >= '0' && buffer[firstDigitPos + 1] <= '9') + in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + char *suffix = new char[length + 1]; + std::memset(suffix, 0, length + 1); + int scanned = std::sscanf(buffer, "%lf%s", &retval, suffix); + bool empty = strlen(suffix) == 0; + delete[] suffix; + + if (scanned == 1 || (scanned == 2 && empty)) { + if (__testlib_isNaN(retval) || __testlib_isInfinite(retval)) + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); + if (buffer[0] == '-' && retval >= 0) + in.quit(_pe, ("Redundant minus in \"" + __testlib_part(buffer) + "\" found").c_str()); + return retval; + } else + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); +} + +static inline double stringToStrictDouble(InStream &in, const std::string& buffer, + int minAfterPointDigitCount, int maxAfterPointDigitCount) { + for (size_t i = 0; i < buffer.length(); i++) + if (buffer[i] == '\0') + in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); + return stringToStrictDouble(in, buffer.c_str(), minAfterPointDigitCount, maxAfterPointDigitCount); +} + +static inline long long stringToLongLong(InStream &in, const char *buffer) { + if (strcmp(buffer, "-9223372036854775808") == 0) + return LLONG_MIN; + + bool minus = false; + size_t length = strlen(buffer); + + if (length > 1 && buffer[0] == '-') + minus = true; + + if (length > 20) + in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + long long retval = 0LL; + + int zeroes = 0; + bool processingZeroes = true; + + for (int i = (minus ? 1 : 0); i < int(length); i++) { + if (buffer[i] == '0' && processingZeroes) + zeroes++; + else + processingZeroes = false; + + if (buffer[i] < '0' || buffer[i] > '9') + in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); + retval = retval * 10 + (buffer[i] - '0'); + } + + if (retval < 0) + in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + if ((zeroes > 0 && (retval != 0 || minus)) || zeroes > 1) + in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + retval = (minus ? -retval : +retval); + + if (length < 19) + return retval; + + if (equals(retval, buffer)) + return retval; + else + in.quit(_pe, ("Expected int64, but \"" + __testlib_part(buffer) + "\" found").c_str()); +} + +static inline long long stringToLongLong(InStream &in, const std::string& buffer) { + for (size_t i = 0; i < buffer.length(); i++) + if (buffer[i] == '\0') + in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); + return stringToLongLong(in, buffer.c_str()); +} + +static inline unsigned long long stringToUnsignedLongLong(InStream &in, const char *buffer) { + size_t length = strlen(buffer); + + if (length > 20) + in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); + if (length > 1 && buffer[0] == '0') + in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + unsigned long long retval = 0LL; + for (int i = 0; i < int(length); i++) { + if (buffer[i] < '0' || buffer[i] > '9') + in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); + retval = retval * 10 + (buffer[i] - '0'); + } + + if (length < 19) + return retval; + + if (length == 20 && strcmp(buffer, "18446744073709551615") > 0) + in.quit(_pe, ("Expected unsigned int64, but \"" + __testlib_part(buffer) + "\" found").c_str()); + + if (equals(retval, buffer)) + return retval; + else + in.quit(_pe, ("Expected unsigned int64, but \"" + __testlib_part(buffer) + "\" found").c_str()); +} + +static inline long long stringToUnsignedLongLong(InStream &in, const std::string& buffer) { + for (size_t i = 0; i < buffer.length(); i++) + if (buffer[i] == '\0') + in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); + return stringToUnsignedLongLong(in, buffer.c_str()); +} + +int InStream::readInteger() { + if (!strict && seekEof()) + quit(_unexpected_eof, "Unexpected end of file - int32 expected"); + + readWordTo(_tmpReadToken); + + long long value = stringToLongLong(*this, _tmpReadToken); + if (value < INT_MIN || value > INT_MAX) + quit(_pe, ("Expected int32, but \"" + __testlib_part(_tmpReadToken) + "\" found").c_str()); + + return int(value); +} + +long long InStream::readLong() { + if (!strict && seekEof()) + quit(_unexpected_eof, "Unexpected end of file - int64 expected"); + + readWordTo(_tmpReadToken); + + return stringToLongLong(*this, _tmpReadToken); +} + +unsigned long long InStream::readUnsignedLong() { + if (!strict && seekEof()) + quit(_unexpected_eof, "Unexpected end of file - int64 expected"); + + readWordTo(_tmpReadToken); + + return stringToUnsignedLongLong(*this, _tmpReadToken); +} + +long long InStream::readLong(long long minv, long long maxv, const std::string &variableName) { + long long result = readLong(); + + if (result < minv || result > maxv) { + if (readManyIteration == NO_INDEX) { + if (variableName.empty()) + quit(_wa, ("Integer " + vtos(result) + " violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + + "]").c_str()); + else + quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + } else { + if (variableName.empty()) + quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + else + quit(_wa, + ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " + + vtos(result) + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + } + } + + if (strict && !variableName.empty()) + validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result)); + + return result; +} + +std::vector +InStream::readLongs(int size, long long minv, long long maxv, const std::string &variablesName, int indexBase) { + __testlib_readMany(readLongs, readLong(minv, maxv, variablesName), long long, true) +} + +std::vector InStream::readLongs(int size, int indexBase) { + __testlib_readMany(readLongs, readLong(), long long, true) +} + +unsigned long long +InStream::readUnsignedLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName) { + unsigned long long result = readUnsignedLong(); + + if (result < minv || result > maxv) { + if (readManyIteration == NO_INDEX) { + if (variableName.empty()) + quit(_wa, + ("Unsigned integer " + vtos(result) + " violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + + "]").c_str()); + else + quit(_wa, + ("Unsigned integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + } else { + if (variableName.empty()) + quit(_wa, + ("Unsigned integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + else + quit(_wa, ("Unsigned integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + + "] equals to " + vtos(result) + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + + "]").c_str()); + } + } + + if (strict && !variableName.empty()) + validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result)); + + return result; +} + +std::vector InStream::readUnsignedLongs(int size, unsigned long long minv, unsigned long long maxv, + const std::string &variablesName, int indexBase) { + __testlib_readMany(readUnsignedLongs, readUnsignedLong(minv, maxv, variablesName), unsigned long long, true) +} + +std::vector InStream::readUnsignedLongs(int size, int indexBase) { + __testlib_readMany(readUnsignedLongs, readUnsignedLong(), unsigned long long, true) +} + +unsigned long long +InStream::readLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName) { + return readUnsignedLong(minv, maxv, variableName); +} + +int InStream::readInt() { + return readInteger(); +} + +int InStream::readInt(int minv, int maxv, const std::string &variableName) { + int result = readInt(); + + if (result < minv || result > maxv) { + if (readManyIteration == NO_INDEX) { + if (variableName.empty()) + quit(_wa, ("Integer " + vtos(result) + " violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + + "]").c_str()); + else + quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + } else { + if (variableName.empty()) + quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + else + quit(_wa, + ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " + + vtos(result) + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); + } + } + + if (strict && !variableName.empty()) + validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result)); + + return result; +} + +int InStream::readInteger(int minv, int maxv, const std::string &variableName) { + return readInt(minv, maxv, variableName); +} + +std::vector InStream::readInts(int size, int minv, int maxv, const std::string &variablesName, int indexBase) { + __testlib_readMany(readInts, readInt(minv, maxv, variablesName), int, true) +} + +std::vector InStream::readInts(int size, int indexBase) { + __testlib_readMany(readInts, readInt(), int, true) +} + +std::vector InStream::readIntegers(int size, int minv, int maxv, const std::string &variablesName, int indexBase) { + __testlib_readMany(readIntegers, readInt(minv, maxv, variablesName), int, true) +} + +std::vector InStream::readIntegers(int size, int indexBase) { + __testlib_readMany(readIntegers, readInt(), int, true) +} + +double InStream::readReal() { + if (!strict && seekEof()) + quit(_unexpected_eof, "Unexpected end of file - double expected"); + + return stringToDouble(*this, readWord()); +} + +double InStream::readDouble() { + return readReal(); +} + +double InStream::readReal(double minv, double maxv, const std::string &variableName) { + double result = readReal(); + + if (result < minv || result > maxv) { + if (readManyIteration == NO_INDEX) { + if (variableName.empty()) + quit(_wa, ("Double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + + "]").c_str()); + else + quit(_wa, ("Double parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + } else { + if (variableName.empty()) + quit(_wa, ("Double element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + else + quit(_wa, + ("Double element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " + + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + } + } + + if (strict && !variableName.empty()) + validator.addBoundsHit(variableName, ValidatorBoundsHit( + doubleDelta(minv, result) < ValidatorBoundsHit::EPS, + doubleDelta(maxv, result) < ValidatorBoundsHit::EPS + )); + + return result; +} + +std::vector +InStream::readReals(int size, double minv, double maxv, const std::string &variablesName, int indexBase) { + __testlib_readMany(readReals, readReal(minv, maxv, variablesName), double, true) +} + +std::vector InStream::readReals(int size, int indexBase) { + __testlib_readMany(readReals, readReal(), double, true) +} + +double InStream::readDouble(double minv, double maxv, const std::string &variableName) { + return readReal(minv, maxv, variableName); +} + +std::vector +InStream::readDoubles(int size, double minv, double maxv, const std::string &variablesName, int indexBase) { + __testlib_readMany(readDoubles, readDouble(minv, maxv, variablesName), double, true) +} + +std::vector InStream::readDoubles(int size, int indexBase) { + __testlib_readMany(readDoubles, readDouble(), double, true) +} + +double InStream::readStrictReal(double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variableName) { + if (!strict && seekEof()) + quit(_unexpected_eof, "Unexpected end of file - strict double expected"); + + double result = stringToStrictDouble(*this, readWord(), minAfterPointDigitCount, maxAfterPointDigitCount); + + if (result < minv || result > maxv) { + if (readManyIteration == NO_INDEX) { + if (variableName.empty()) + quit(_wa, ("Strict double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + + "]").c_str()); + else + quit(_wa, + ("Strict double parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + } else { + if (variableName.empty()) + quit(_wa, ("Strict double element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + else + quit(_wa, ("Strict double element " + std::string(variableName) + "[" + vtos(readManyIteration) + + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + + "]").c_str()); + } + } + + if (strict && !variableName.empty()) + validator.addBoundsHit(variableName, ValidatorBoundsHit( + doubleDelta(minv, result) < ValidatorBoundsHit::EPS, + doubleDelta(maxv, result) < ValidatorBoundsHit::EPS + )); + + return result; +} + +std::vector InStream::readStrictReals(int size, double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variablesName, int indexBase) { + __testlib_readMany(readStrictReals, + readStrictReal(minv, maxv, minAfterPointDigitCount, maxAfterPointDigitCount, variablesName), + double, true) +} + +double InStream::readStrictDouble(double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variableName) { + return readStrictReal(minv, maxv, + minAfterPointDigitCount, maxAfterPointDigitCount, + variableName); +} + +std::vector InStream::readStrictDoubles(int size, double minv, double maxv, + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string &variablesName, int indexBase) { + __testlib_readMany(readStrictDoubles, + readStrictDouble(minv, maxv, minAfterPointDigitCount, maxAfterPointDigitCount, variablesName), + double, true) +} + +bool InStream::eof() { + if (!strict && NULL == reader) + return true; + + return reader->eof(); +} + +bool InStream::seekEof() { + if (!strict && NULL == reader) + return true; + skipBlanks(); + return eof(); +} + +bool InStream::eoln() { + if (!strict && NULL == reader) + return true; + + int c = reader->nextChar(); + + if (!strict) { + if (c == EOFC) + return true; + + if (c == CR) { + c = reader->nextChar(); + + if (c != LF) { + reader->unreadChar(c); + reader->unreadChar(CR); + return false; + } else + return true; + } + + if (c == LF) + return true; + + reader->unreadChar(c); + return false; + } else { + bool returnCr = false; + +#if (defined(ON_WINDOWS) && !defined(FOR_LINUX)) || defined(FOR_WINDOWS) + if (c != CR) { + reader->unreadChar(c); + return false; + } else { + if (!returnCr) + returnCr = true; + c = reader->nextChar(); + } +#endif + if (c != LF) { + reader->unreadChar(c); + if (returnCr) + reader->unreadChar(CR); + return false; + } + + return true; + } +} + +void InStream::readEoln() { + lastLine = reader->getLine(); + if (!eoln()) + quit(_pe, "Expected EOLN"); +} + +void InStream::readEof() { + lastLine = reader->getLine(); + if (!eof()) + quit(_pe, "Expected EOF"); + + if (TestlibFinalizeGuard::alive && this == &inf) + testlibFinalizeGuard.readEofCount++; +} + +bool InStream::seekEoln() { + if (!strict && NULL == reader) + return true; + + int cur; + do { + cur = reader->nextChar(); + } while (cur == SPACE || cur == TAB); + + reader->unreadChar(cur); + return eoln(); +} + +void InStream::nextLine() { + readLine(); +} + +void InStream::readStringTo(std::string &result) { + if (NULL == reader) + quit(_pe, "Expected line"); + + result.clear(); + + for (;;) { + int cur = reader->curChar(); + + if (cur == LF || cur == EOFC) + break; + + if (cur == CR) { + cur = reader->nextChar(); + if (reader->curChar() == LF) { + reader->unreadChar(cur); + break; + } + } + + lastLine = reader->getLine(); + result += char(reader->nextChar()); + } + + if (strict) + readEoln(); + else + eoln(); +} + +std::string InStream::readString() { + readStringTo(_tmpReadToken); + return _tmpReadToken; +} + +std::vector InStream::readStrings(int size, int indexBase) { + __testlib_readMany(readStrings, readString(), std::string, false) +} + +void InStream::readStringTo(std::string &result, const pattern &p, const std::string &variableName) { + readStringTo(result); + if (!p.matches(result)) { + if (readManyIteration == NO_INDEX) { + if (variableName.empty()) + quit(_wa, ("Line \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() + + "\"").c_str()); + else + quit(_wa, ("Line [name=" + variableName + "] equals to \"" + __testlib_part(result) + + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + } else { + if (variableName.empty()) + quit(_wa, + ("Line element [index=" + vtos(readManyIteration) + "] equals to \"" + __testlib_part(result) + + "\" doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + else + quit(_wa, + ("Line element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to \"" + + __testlib_part(result) + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + } + } +} + +void InStream::readStringTo(std::string &result, const std::string &ptrn, const std::string &variableName) { + readStringTo(result, pattern(ptrn), variableName); +} + +std::string InStream::readString(const pattern &p, const std::string &variableName) { + readStringTo(_tmpReadToken, p, variableName); + return _tmpReadToken; +} + +std::vector +InStream::readStrings(int size, const pattern &p, const std::string &variablesName, int indexBase) { + __testlib_readMany(readStrings, readString(p, variablesName), std::string, false) +} + +std::string InStream::readString(const std::string &ptrn, const std::string &variableName) { + readStringTo(_tmpReadToken, ptrn, variableName); + return _tmpReadToken; +} + +std::vector +InStream::readStrings(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { + pattern p(ptrn); + __testlib_readMany(readStrings, readString(p, variablesName), std::string, false) +} + +void InStream::readLineTo(std::string &result) { + readStringTo(result); +} + +std::string InStream::readLine() { + return readString(); +} + +std::vector InStream::readLines(int size, int indexBase) { + __testlib_readMany(readLines, readString(), std::string, false) +} + +void InStream::readLineTo(std::string &result, const pattern &p, const std::string &variableName) { + readStringTo(result, p, variableName); +} + +void InStream::readLineTo(std::string &result, const std::string &ptrn, const std::string &variableName) { + readStringTo(result, ptrn, variableName); +} + +std::string InStream::readLine(const pattern &p, const std::string &variableName) { + return readString(p, variableName); +} + +std::vector +InStream::readLines(int size, const pattern &p, const std::string &variablesName, int indexBase) { + __testlib_readMany(readLines, readString(p, variablesName), std::string, false) +} + +std::string InStream::readLine(const std::string &ptrn, const std::string &variableName) { + return readString(ptrn, variableName); +} + +std::vector +InStream::readLines(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { + pattern p(ptrn); + __testlib_readMany(readLines, readString(p, variablesName), std::string, false) +} + +#ifdef __GNUC__ +__attribute__ ((format (printf, 3, 4))) +#endif +void InStream::ensuref(bool cond, const char *format, ...) { + if (!cond) { + FMT_TO_RESULT(format, format, message); + this->__testlib_ensure(cond, message); + } +} + +void InStream::__testlib_ensure(bool cond, std::string message) { + if (!cond) + this->quit(_wa, message.c_str()); +} + +void InStream::close() { + if (NULL != reader) { + reader->close(); + delete reader; + reader = NULL; + } + + opened = false; +} + +NORETURN void quit(TResult result, const std::string &msg) { + ouf.quit(result, msg.c_str()); +} + +NORETURN void quit(TResult result, const char *msg) { + ouf.quit(result, msg); +} + +NORETURN void __testlib_quitp(double points, const char *message) { + __testlib_points = points; + std::string stringPoints = removeDoubleTrailingZeroes(format("%.10f", points)); + + std::string quitMessage; + if (NULL == message || 0 == strlen(message)) + quitMessage = stringPoints; + else + quitMessage = stringPoints + " " + message; + + quit(_points, quitMessage.c_str()); +} + +NORETURN void __testlib_quitp(int points, const char *message) { + __testlib_points = points; + std::string stringPoints = format("%d", points); + + std::string quitMessage; + if (NULL == message || 0 == strlen(message)) + quitMessage = stringPoints; + else + quitMessage = stringPoints + " " + message; + + quit(_points, quitMessage.c_str()); +} + +NORETURN void quitp(float points, const std::string &message = "") { + __testlib_quitp(double(points), message.c_str()); +} + +NORETURN void quitp(double points, const std::string &message = "") { + __testlib_quitp(points, message.c_str()); +} + +NORETURN void quitp(long double points, const std::string &message = "") { + __testlib_quitp(double(points), message.c_str()); +} + +NORETURN void quitp(int points, const std::string &message = "") { + __testlib_quitp(points, message.c_str()); +} + +NORETURN void quitpi(const std::string &points_info, const std::string &message = "") { + if (points_info.find(' ') != std::string::npos) + quit(_fail, "Parameter 'points_info' can't contain spaces"); + if (message.empty()) + quit(_points, ("points_info=" + points_info).c_str()); + else + quit(_points, ("points_info=" + points_info + " " + message).c_str()); +} + +template +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +NORETURN void quitp(F points, const char *format, ...) { + FMT_TO_RESULT(format, format, message); + quitp(points, message); +} + +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +NORETURN void quitf(TResult result, const char *format, ...) { + FMT_TO_RESULT(format, format, message); + quit(result, message); +} + +#ifdef __GNUC__ +__attribute__ ((format (printf, 3, 4))) +#endif +void quitif(bool condition, TResult result, const char *format, ...) { + if (condition) { + FMT_TO_RESULT(format, format, message); + quit(result, message); + } +} + +NORETURN void __testlib_help() { + InStream::textColor(InStream::LightCyan); + std::fprintf(stderr, "TESTLIB %s, https://github.com/MikeMirzayanov/testlib/ ", VERSION); + std::fprintf(stderr, "by Mike Mirzayanov, copyright(c) 2005-2020\n"); + std::fprintf(stderr, "Checker name: \"%s\"\n", checkerName.c_str()); + InStream::textColor(InStream::LightGray); + + std::fprintf(stderr, "\n"); + std::fprintf(stderr, "Latest features: \n"); + for (size_t i = 0; i < sizeof(latestFeatures) / sizeof(char *); i++) { + std::fprintf(stderr, "*) %s\n", latestFeatures[i]); + } + std::fprintf(stderr, "\n"); + + std::fprintf(stderr, "Program must be run with the following arguments: \n"); + std::fprintf(stderr, " [--testset testset] [--group group] [ [<-appes>]]\n\n"); + + __testlib_exitCode = FAIL_EXIT_CODE; + std::exit(FAIL_EXIT_CODE); +} + +static void __testlib_ensuresPreconditions() { + // testlib assumes: sizeof(int) = 4. + __TESTLIB_STATIC_ASSERT(sizeof(int) == 4); + + // testlib assumes: INT_MAX == 2147483647. + __TESTLIB_STATIC_ASSERT(INT_MAX == 2147483647); + + // testlib assumes: sizeof(long long) = 8. + __TESTLIB_STATIC_ASSERT(sizeof(long long) == 8); + + // testlib assumes: sizeof(double) = 8. + __TESTLIB_STATIC_ASSERT(sizeof(double) == 8); + + // testlib assumes: no -ffast-math. + if (!__testlib_isNaN(+__testlib_nan())) + quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'"); + if (!__testlib_isNaN(-__testlib_nan())) + quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'"); +} + +std::string __testlib_testset; + +std::string getTestset() { + return __testlib_testset; +} + +std::string __testlib_group; + +std::string getGroup() { + return __testlib_group; +} + +static void __testlib_set_testset_and_group(int argc, char* argv[]) { + for (int i = 1; i < argc; i++) { + if (!strcmp("--testset", argv[i])) { + if (i + 1 < argc && strlen(argv[i + 1]) > 0) + __testlib_testset = argv[++i]; + else + quit(_fail, std::string("Expected non-empty testset after --testset command line parameter")); + } else if (!strcmp("--group", argv[i])) { + if (i + 1 < argc) + __testlib_group = argv[++i]; + else + quit(_fail, std::string("Expected group after --group command line parameter")); + } + } +} + +void registerGen(int argc, char *argv[], int randomGeneratorVersion) { + if (randomGeneratorVersion < 0 || randomGeneratorVersion > 1) + quitf(_fail, "Random generator version is expected to be 0 or 1."); + random_t::version = randomGeneratorVersion; + + __testlib_ensuresPreconditions(); + TestlibFinalizeGuard::registered = true; + + testlibMode = _generator; + __testlib_set_binary(stdin); + rnd.setSeed(argc, argv); + +#if __cplusplus > 199711L || defined(_MSC_VER) + prepareOpts(argc, argv); +#endif +} + +#ifdef USE_RND_AS_BEFORE_087 +void registerGen(int argc, char* argv[]) +{ + registerGen(argc, argv, 0); +} +#else +#ifdef __GNUC__ +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 4)) +__attribute__ ((deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)." +" The third parameter stands for the random generator version." +" If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)." +" Version 1 has been released on Spring, 2013. Use it to write new generators."))) +#else +__attribute__ ((deprecated)) +#endif +#endif +#ifdef _MSC_VER +__declspec(deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)." + " The third parameter stands for the random generator version." + " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)." + " Version 1 has been released on Spring, 2013. Use it to write new generators.")) +#endif +void registerGen(int argc, char *argv[]) { + std::fprintf(stderr, "Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)." + " The third parameter stands for the random generator version." + " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)." + " Version 1 has been released on Spring, 2013. Use it to write new generators.\n\n"); + registerGen(argc, argv, 0); +} +#endif + +void registerInteraction(int argc, char *argv[]) { + __testlib_ensuresPreconditions(); + __testlib_set_testset_and_group(argc, argv); + TestlibFinalizeGuard::registered = true; + + testlibMode = _interactor; + __testlib_set_binary(stdin); + + if (argc > 1 && !strcmp("--help", argv[1])) + __testlib_help(); + + if (argc < 3 || argc > 6) { + quit(_fail, std::string("Program must be run with the following arguments: ") + + std::string(" [ [ [<-appes>]]]") + + "\nUse \"--help\" to get help information"); + } + + if (argc <= 4) { + resultName = ""; + appesMode = false; + } + +#ifndef EJUDGE + if (argc == 5) { + resultName = argv[4]; + appesMode = false; + } + + if (argc == 6) { + if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5])) { + quit(_fail, std::string("Program must be run with the following arguments: ") + + " [ [<-appes>]]"); + } else { + resultName = argv[4]; + appesMode = true; + } + } +#endif + + inf.init(argv[1], _input); + + tout.open(argv[2], std::ios_base::out); + if (tout.fail() || !tout.is_open()) + quit(_fail, std::string("Can not write to the test-output-file '") + argv[2] + std::string("'")); + + ouf.init(stdin, _output); + + if (argc >= 4) + ans.init(argv[3], _answer); + else + ans.name = "unopened answer stream"; +} + +void registerValidation() { + __testlib_ensuresPreconditions(); + TestlibFinalizeGuard::registered = true; + + testlibMode = _validator; + + __testlib_set_binary(stdin); + __testlib_set_binary(stdout); + __testlib_set_binary(stderr); + + inf.init(stdin, _input); + inf.strict = true; +} + +void registerValidation(int argc, char *argv[]) { + registerValidation(); + __testlib_set_testset_and_group(argc, argv); + + validator.initialize(); + TestlibFinalizeGuard::registered = true; + + std::string comment = "Validator must be run with the following arguments:" + " [--testset testset]" + " [--group group]" + " [--testOverviewLogFileName fileName]" + " [--testMarkupFileName fileName]" + " [--testCase testCase]" + " [--testCaseFileName fileName]" + ; + + for (int i = 1; i < argc; i++) { + if (!strcmp("--testset", argv[i])) { + if (i + 1 < argc && strlen(argv[i + 1]) > 0) + validator.setTestset(argv[++i]); + else + quit(_fail, comment); + } + if (!strcmp("--group", argv[i])) { + if (i + 1 < argc) + validator.setGroup(argv[++i]); + else + quit(_fail, comment); + } + if (!strcmp("--testOverviewLogFileName", argv[i])) { + if (i + 1 < argc) + validator.setTestOverviewLogFileName(argv[++i]); + else + quit(_fail, comment); + } + if (!strcmp("--testMarkupFileName", argv[i])) { + if (i + 1 < argc) + validator.setTestMarkupFileName(argv[++i]); + else + quit(_fail, comment); + } + if (!strcmp("--testCase", argv[i])) { + if (i + 1 < argc) { + long long testCase = stringToLongLong(inf, argv[++i]); + if (testCase < 1 || testCase >= __TESTLIB_MAX_TEST_CASE) + quit(_fail, format("Argument testCase should be between 1 and %d, but ", __TESTLIB_MAX_TEST_CASE) + + toString(testCase) + " found"); + validator.setTestCase(int(testCase)); + } else + quit(_fail, comment); + } + if (!strcmp("--testCaseFileName", argv[i])) { + if (i + 1 < argc) { + validator.setTestCaseFileName(argv[++i]); + } else + quit(_fail, comment); + } + } +} + +void addFeature(const std::string &feature) { + if (testlibMode != _validator) + quit(_fail, "Features are supported in validators only."); + validator.addFeature(feature); +} + +void feature(const std::string &feature) { + if (testlibMode != _validator) + quit(_fail, "Features are supported in validators only."); + validator.feature(feature); +} + +class Checker { +private: + bool _initialized; + std::string _testset; + std::string _group; + +public: + Checker() : _initialized(false), _testset("tests"), _group() { + } + + void initialize() { + _initialized = true; + } + + std::string testset() const { + if (!_initialized) + __testlib_fail("Checker should be initialized with registerTestlibCmd(argc, argv) instead of registerTestlibCmd() to support checker.testset()"); + return _testset; + } + + std::string group() const { + if (!_initialized) + __testlib_fail("Checker should be initialized with registerTestlibCmd(argc, argv) instead of registerTestlibCmd() to support checker.group()"); + return _group; + } + + void setTestset(const char *const testset) { + _testset = testset; + } + + void setGroup(const char *const group) { + _group = group; + } +} checker; + +void registerTestlibCmd(int argc, char *argv[]) { + __testlib_ensuresPreconditions(); + __testlib_set_testset_and_group(argc, argv); + TestlibFinalizeGuard::registered = true; + + testlibMode = _checker; + __testlib_set_binary(stdin); + + std::vector args(1, argv[0]); + checker.initialize(); + + for (int i = 1; i < argc; i++) { + if (!strcmp("--testset", argv[i])) { + if (i + 1 < argc && strlen(argv[i + 1]) > 0) + checker.setTestset(argv[++i]); + else + quit(_fail, std::string("Expected testset after --testset command line parameter")); + } else if (!strcmp("--group", argv[i])) { + if (i + 1 < argc) + checker.setGroup(argv[++i]); + else + quit(_fail, std::string("Expected group after --group command line parameter")); + } else + args.push_back(argv[i]); + } + + argc = int(args.size()); + if (argc > 1 && "--help" == args[1]) + __testlib_help(); + + if (argc < 4 || argc > 6) { + quit(_fail, std::string("Program must be run with the following arguments: ") + + std::string("[--testset testset] [--group group] [ [<-appes>]]") + + "\nUse \"--help\" to get help information"); + } + + if (argc == 4) { + resultName = ""; + appesMode = false; + } + + if (argc == 5) { + resultName = args[4]; + appesMode = false; + } + + if (argc == 6) { + if ("-APPES" != args[5] && "-appes" != args[5]) { + quit(_fail, std::string("Program must be run with the following arguments: ") + + " [ [<-appes>]]"); + } else { + resultName = args[4]; + appesMode = true; + } + } + + #ifdef BOCA_SUPPORT + inf.init(argv[3], _input); + ouf.init(argv[1], _output); + ans.init(argv[2], _answer); + #else + inf.init(argv[1], _input); + ouf.init(argv[2], _output); + ans.init(argv[3], _answer); + #endif +} + +void registerTestlib(int argc, ...) { + if (argc < 3 || argc > 5) + quit(_fail, std::string("Program must be run with the following arguments: ") + + " [ [<-appes>]]"); + + char **argv = new char *[argc + 1]; + + va_list ap; + va_start(ap, argc); + argv[0] = NULL; + for (int i = 0; i < argc; i++) { + argv[i + 1] = va_arg(ap, char*); + } + va_end(ap); + + registerTestlibCmd(argc + 1, argv); + delete[] argv; +} + +static inline void __testlib_ensure(bool cond, const std::string &msg) { + if (!cond) + quit(_fail, msg.c_str()); +} + +#ifdef __GNUC__ +__attribute__((unused)) +#endif +static inline void __testlib_ensure(bool cond, const char *msg) { + if (!cond) + quit(_fail, msg); +} + +#define ensure(cond) __testlib_ensure(cond, "Condition failed: \"" #cond "\"") +#define STRINGIZE_DETAIL(x) #x +#define STRINGIZE(x) STRINGIZE_DETAIL(x) +#define ensure_ext(cond) __testlib_ensure(cond, "Line " STRINGIZE(__LINE__) ": Condition failed: \"" #cond "\"") + +#ifdef __GNUC__ +__attribute__ ((format (printf, 2, 3))) +#endif +inline void ensuref(bool cond, const char *format, ...) { + if (!cond) { + FMT_TO_RESULT(format, format, message); + __testlib_ensure(cond, message); + } +} + +NORETURN static void __testlib_fail(const std::string &message) { + quitf(_fail, "%s", message.c_str()); +} + +#ifdef __GNUC__ +__attribute__ ((format (printf, 1, 2))) +#endif +void setName(const char *format, ...) { + FMT_TO_RESULT(format, format, name); + checkerName = name; +} + +/* + * Do not use random_shuffle, because it will produce different result + * for different C++ compilers. + * + * This implementation uses testlib random_t to produce random numbers, so + * it is stable. + */ +template +void shuffle(_RandomAccessIter __first, _RandomAccessIter __last) { + if (__first == __last) return; + for (_RandomAccessIter __i = __first + 1; __i != __last; ++__i) + std::iter_swap(__i, __first + rnd.next(int(__i - __first) + 1)); +} + + +template +#if defined(__GNUC__) && !defined(__clang__) +__attribute__ ((error("Don't use random_shuffle(), use shuffle() instead"))) +#endif +void random_shuffle(_RandomAccessIter, _RandomAccessIter) { + quitf(_fail, "Don't use random_shuffle(), use shuffle() instead"); +} + +#ifdef __GLIBC__ +# define RAND_THROW_STATEMENT throw() +#else +# define RAND_THROW_STATEMENT +#endif + +#if defined(__GNUC__) && !defined(__clang__) + +__attribute__ ((error("Don't use rand(), use rnd.next() instead"))) +#endif +#ifdef _MSC_VER +# pragma warning( disable : 4273 ) +#endif +int rand() RAND_THROW_STATEMENT +{ + quitf(_fail, "Don't use rand(), use rnd.next() instead"); + + /* This line never runs. */ + //throw "Don't use rand(), use rnd.next() instead"; +} + +#if defined(__GNUC__) && !defined(__clang__) + +__attribute__ ((error("Don't use srand(), you should use " +"'registerGen(argc, argv, 1);' to initialize generator seed " +"by hash code of the command line params. The third parameter " +"is randomGeneratorVersion (currently the latest is 1)."))) +#endif +#ifdef _MSC_VER +# pragma warning( disable : 4273 ) +#endif +void srand(unsigned int seed) RAND_THROW_STATEMENT +{ + quitf(_fail, "Don't use srand(), you should use " + "'registerGen(argc, argv, 1);' to initialize generator seed " + "by hash code of the command line params. The third parameter " + "is randomGeneratorVersion (currently the latest is 1) [ignored seed=%u].", seed); +} + +void startTest(int test) { + const std::string testFileName = vtos(test); + if (NULL == freopen(testFileName.c_str(), "wt", stdout)) + __testlib_fail("Unable to write file '" + testFileName + "'"); +} + +inline std::string upperCase(std::string s) { + for (size_t i = 0; i < s.length(); i++) + if ('a' <= s[i] && s[i] <= 'z') + s[i] = char(s[i] - 'a' + 'A'); + return s; +} + +inline std::string lowerCase(std::string s) { + for (size_t i = 0; i < s.length(); i++) + if ('A' <= s[i] && s[i] <= 'Z') + s[i] = char(s[i] - 'A' + 'a'); + return s; +} + +inline std::string compress(const std::string &s) { + return __testlib_part(s); +} + +inline std::string englishEnding(int x) { + x %= 100; + if (x / 10 == 1) + return "th"; + if (x % 10 == 1) + return "st"; + if (x % 10 == 2) + return "nd"; + if (x % 10 == 3) + return "rd"; + return "th"; +} + +template +std::string join(_ForwardIterator first, _ForwardIterator last, _Separator separator) { + std::stringstream ss; + bool repeated = false; + for (_ForwardIterator i = first; i != last; i++) { + if (repeated) + ss << separator; + else + repeated = true; + ss << *i; + } + return ss.str(); +} + +template +std::string join(_ForwardIterator first, _ForwardIterator last) { + return join(first, last, ' '); +} + +template +std::string join(const _Collection &collection, _Separator separator) { + return join(collection.begin(), collection.end(), separator); +} + +template +std::string join(const _Collection &collection) { + return join(collection, ' '); +} + +/** + * Splits string s by character separator returning exactly k+1 items, + * where k is the number of separator occurences. + */ +std::vector split(const std::string &s, char separator) { + std::vector result; + std::string item; + for (size_t i = 0; i < s.length(); i++) + if (s[i] == separator) { + result.push_back(item); + item = ""; + } else + item += s[i]; + result.push_back(item); + return result; +} + +/** + * Splits string s by character separators returning exactly k+1 items, + * where k is the number of separator occurences. + */ +std::vector split(const std::string &s, const std::string &separators) { + if (separators.empty()) + return std::vector(1, s); + + std::vector isSeparator(256); + for (size_t i = 0; i < separators.size(); i++) + isSeparator[(unsigned char) (separators[i])] = true; + + std::vector result; + std::string item; + for (size_t i = 0; i < s.length(); i++) + if (isSeparator[(unsigned char) (s[i])]) { + result.push_back(item); + item = ""; + } else + item += s[i]; + result.push_back(item); + return result; +} + +/** + * Splits string s by character separator returning non-empty items. + */ +std::vector tokenize(const std::string &s, char separator) { + std::vector result; + std::string item; + for (size_t i = 0; i < s.length(); i++) + if (s[i] == separator) { + if (!item.empty()) + result.push_back(item); + item = ""; + } else + item += s[i]; + if (!item.empty()) + result.push_back(item); + return result; +} + +/** + * Splits string s by character separators returning non-empty items. + */ +std::vector tokenize(const std::string &s, const std::string &separators) { + if (separators.empty()) + return std::vector(1, s); + + std::vector isSeparator(256); + for (size_t i = 0; i < separators.size(); i++) + isSeparator[(unsigned char) (separators[i])] = true; + + std::vector result; + std::string item; + for (size_t i = 0; i < s.length(); i++) + if (isSeparator[(unsigned char) (s[i])]) { + if (!item.empty()) + result.push_back(item); + item = ""; + } else + item += s[i]; + + if (!item.empty()) + result.push_back(item); + + return result; +} + +NORETURN void __testlib_expectedButFound(TResult result, std::string expected, std::string found, const char *prepend) { + std::string message; + if (strlen(prepend) != 0) + message = format("%s: expected '%s', but found '%s'", + compress(prepend).c_str(), compress(expected).c_str(), compress(found).c_str()); + else + message = format("expected '%s', but found '%s'", + compress(expected).c_str(), compress(found).c_str()); + quit(result, message); +} + +NORETURN void __testlib_expectedButFound(TResult result, double expected, double found, const char *prepend) { + std::string expectedString = removeDoubleTrailingZeroes(format("%.12f", expected)); + std::string foundString = removeDoubleTrailingZeroes(format("%.12f", found)); + __testlib_expectedButFound(result, expectedString, foundString, prepend); +} + +template +#ifdef __GNUC__ +__attribute__ ((format (printf, 4, 5))) +#endif +NORETURN void expectedButFound(TResult result, T expected, T found, const char *prependFormat = "", ...) { + FMT_TO_RESULT(prependFormat, prependFormat, prepend); + std::string expectedString = vtos(expected); + std::string foundString = vtos(found); + __testlib_expectedButFound(result, expectedString, foundString, prepend.c_str()); +} + +template<> +#ifdef __GNUC__ +__attribute__ ((format (printf, 4, 5))) +#endif +NORETURN void +expectedButFound(TResult result, std::string expected, std::string found, const char *prependFormat, ...) { + FMT_TO_RESULT(prependFormat, prependFormat, prepend); + __testlib_expectedButFound(result, expected, found, prepend.c_str()); +} + +template<> +#ifdef __GNUC__ +__attribute__ ((format (printf, 4, 5))) +#endif +NORETURN void expectedButFound(TResult result, double expected, double found, const char *prependFormat, ...) { + FMT_TO_RESULT(prependFormat, prependFormat, prepend); + std::string expectedString = removeDoubleTrailingZeroes(format("%.12f", expected)); + std::string foundString = removeDoubleTrailingZeroes(format("%.12f", found)); + __testlib_expectedButFound(result, expectedString, foundString, prepend.c_str()); +} + +template<> +#ifdef __GNUC__ +__attribute__ ((format (printf, 4, 5))) +#endif +NORETURN void +expectedButFound(TResult result, const char *expected, const char *found, const char *prependFormat, + ...) { + FMT_TO_RESULT(prependFormat, prependFormat, prepend); + __testlib_expectedButFound(result, std::string(expected), std::string(found), prepend.c_str()); +} + +template<> +#ifdef __GNUC__ +__attribute__ ((format (printf, 4, 5))) +#endif +NORETURN void expectedButFound(TResult result, float expected, float found, const char *prependFormat, ...) { + FMT_TO_RESULT(prependFormat, prependFormat, prepend); + __testlib_expectedButFound(result, double(expected), double(found), prepend.c_str()); +} + +template<> +#ifdef __GNUC__ +__attribute__ ((format (printf, 4, 5))) +#endif +NORETURN void +expectedButFound(TResult result, long double expected, long double found, const char *prependFormat, ...) { + FMT_TO_RESULT(prependFormat, prependFormat, prepend); + __testlib_expectedButFound(result, double(expected), double(found), prepend.c_str()); +} + +#if __cplusplus > 199711L || defined(_MSC_VER) +template +struct is_iterable { + template + static char test(typename U::iterator *x); + + template + static long test(U *x); + + static const bool value = sizeof(test(0)) == 1; +}; + +template +struct __testlib_enable_if { +}; + +template +struct __testlib_enable_if { + typedef T type; +}; + +template +typename __testlib_enable_if::value, void>::type __testlib_print_one(const T &t) { + std::cout << t; +} + +template +typename __testlib_enable_if::value, void>::type __testlib_print_one(const T &t) { + bool first = true; + for (typename T::const_iterator i = t.begin(); i != t.end(); i++) { + if (first) + first = false; + else + std::cout << " "; + std::cout << *i; + } +} + +template<> +typename __testlib_enable_if::value, void>::type +__testlib_print_one(const std::string &t) { + std::cout << t; +} + +template +void __println_range(A begin, B end) { + bool first = true; + for (B i = B(begin); i != end; i++) { + if (first) + first = false; + else + std::cout << " "; + __testlib_print_one(*i); + } + std::cout << std::endl; +} + +template +struct is_iterator { + static T makeT(); + + typedef void *twoptrs[2]; + + static twoptrs &test(...); + + template + static typename R::iterator_category *test(R); + + template + static void *test(R *); + + static const bool value = sizeof(test(makeT())) == sizeof(void *); +}; + +template +struct is_iterator::value>::type> { + static const bool value = false; +}; + +template +typename __testlib_enable_if::value, void>::type println(const A &a, const B &b) { + __testlib_print_one(a); + std::cout << " "; + __testlib_print_one(b); + std::cout << std::endl; +} + +template +typename __testlib_enable_if::value, void>::type println(const A &a, const B &b) { + __println_range(a, b); +} + +template +void println(const A *a, const A *b) { + __println_range(a, b); +} + +template<> +void println(const char *a, const char *b) { + __testlib_print_one(a); + std::cout << " "; + __testlib_print_one(b); + std::cout << std::endl; +} + +template +void println(const T &x) { + __testlib_print_one(x); + std::cout << std::endl; +} + +template +void println(const A &a, const B &b, const C &c) { + __testlib_print_one(a); + std::cout << " "; + __testlib_print_one(b); + std::cout << " "; + __testlib_print_one(c); + std::cout << std::endl; +} + +template +void println(const A &a, const B &b, const C &c, const D &d) { + __testlib_print_one(a); + std::cout << " "; + __testlib_print_one(b); + std::cout << " "; + __testlib_print_one(c); + std::cout << " "; + __testlib_print_one(d); + std::cout << std::endl; +} + +template +void println(const A &a, const B &b, const C &c, const D &d, const E &e) { + __testlib_print_one(a); + std::cout << " "; + __testlib_print_one(b); + std::cout << " "; + __testlib_print_one(c); + std::cout << " "; + __testlib_print_one(d); + std::cout << " "; + __testlib_print_one(e); + std::cout << std::endl; +} + +template +void println(const A &a, const B &b, const C &c, const D &d, const E &e, const F &f) { + __testlib_print_one(a); + std::cout << " "; + __testlib_print_one(b); + std::cout << " "; + __testlib_print_one(c); + std::cout << " "; + __testlib_print_one(d); + std::cout << " "; + __testlib_print_one(e); + std::cout << " "; + __testlib_print_one(f); + std::cout << std::endl; +} + +template +void println(const A &a, const B &b, const C &c, const D &d, const E &e, const F &f, const G &g) { + __testlib_print_one(a); + std::cout << " "; + __testlib_print_one(b); + std::cout << " "; + __testlib_print_one(c); + std::cout << " "; + __testlib_print_one(d); + std::cout << " "; + __testlib_print_one(e); + std::cout << " "; + __testlib_print_one(f); + std::cout << " "; + __testlib_print_one(g); + std::cout << std::endl; +} + +/* opts */ + +/** + * A struct for a singular testlib opt, containing the raw string value, + * and a boolean value for marking whether the opt is used. + */ +struct TestlibOpt { + std::string value; + bool used; + + TestlibOpt() : value(), used(false) {} +}; + +/** + * Get the type of opt based on the number of `-` at the beginning and the + * _validity_ of the key name. + * + * A valid key name must start with an alphabetical character. + * + * Returns: 1 if s has one `-` at the beginning, that is, "-keyName". + * 2 if s has two `-` at the beginning, that is, "--keyName". + * 0 otherwise. That is, if s has no `-` at the beginning, or has more + * than 2 at the beginning ("---keyName", "----keyName", ...), or the + * keyName is invalid (the first character is not an alphabetical + * character). + */ +size_t getOptType(char *s) { + if (!s || strlen(s) <= 1) + return 0; + + if (s[0] == '-') { + if (isalpha(s[1])) + return 1; + else if (s[1] == '-') + return isalpha(s[2]) ? 2 : 0; + } + + return 0; +} + +/** + * Parse the opt at a given index, and put it into the opts maps. + * + * An opt can has the following form: + * 1) -keyName=value or --keyName=value (ex. -n=10 --test-count=20) + * 2) -keyName value or --keyName value (ex. -n 10 --test-count 20) + * 3) -kNumval or --kNumval (ex. -n10 --t20) + * 4) -boolProperty or --boolProperty (ex. -sorted --tree-only) + * + * Only the second form consumes 2 arguments. The other consumes only 1 + * argument. + * + * In the third form, the key is a single character, and after the key is the + * value. The value _should_ be a number. + * + * In the forth form, the value is true. + * + * Params: + * - argc and argv: the number of command line arguments and the command line + * arguments themselves. + * - index: the starting index of the opts. + * - opts: the map containing the resulting opt. + * + * Returns: the number of consumed arguments to parse the opt. + * 0 if there is no arguments to parse. + * + * Algorithm details: + * TODO. Please refer to the implementation to see how the code handles the 3rd and 4th forms separately. + */ +size_t parseOpt(size_t argc, char *argv[], size_t index, std::map &opts) { + if (index >= argc) + return 0; + + size_t type = getOptType(argv[index]), inc = 1; + if (type > 0) { + std::string key(argv[index] + type), val; + size_t sep = key.find('='); + if (sep != std::string::npos) { + val = key.substr(sep + 1); + key = key.substr(0, sep); + } else { + if (index + 1 < argc && getOptType(argv[index + 1]) == 0) { + val = argv[index + 1]; + inc = 2; + } else { + if (key.length() > 1 && isdigit(key[1])) { + val = key.substr(1); + key = key.substr(0, 1); + } else { + val = "true"; + } + } + } + opts[key].value = val; + } else { + return inc; + } + + return inc; +} + +/** + * Global list containing all the arguments in the order given in the command line. + */ +std::vector __testlib_argv; + +/** + * Global dictionary containing all the parsed opts. + */ +std::map __testlib_opts; + +/** + * Whether automatic no unused opts ensurement should be done. This flag will + * be turned on when `has_opt` or `opt(key, default_value)` is called. + * + * The automatic ensurement can be suppressed when + * __testlib_ensureNoUnusedOptsSuppressed is true. + */ +bool __testlib_ensureNoUnusedOptsFlag = false; + +/** + * Suppress no unused opts automatic ensurement. Can be set to true with + * `suppressEnsureNoUnusedOpts()`. + */ +bool __testlib_ensureNoUnusedOptsSuppressed = false; + +/** + * Parse command line arguments into opts. + * The results are stored into __testlib_argv and __testlib_opts. + */ +void prepareOpts(int argc, char *argv[]) { + if (argc <= 0) + __testlib_fail("Opts: expected argc>=0 but found " + toString(argc)); + size_t n = static_cast(argc); // NOLINT(hicpp-use-auto,modernize-use-auto) + __testlib_opts = std::map(); + for (size_t index = 1; index < n; index += parseOpt(n, argv, index, __testlib_opts)); + __testlib_argv = std::vector(n); + for (size_t index = 0; index < n; index++) + __testlib_argv[index] = argv[index]; +} + +/** + * An utility function to get the argument with a given index. This function + * also print a readable message when no arguments are found. + */ +std::string __testlib_indexToArgv(int index) { + if (index < 0 || index >= int(__testlib_argv.size())) + __testlib_fail("Opts: index '" + toString(index) + "' is out of range [0," + + toString(__testlib_argv.size()) + ")"); + return __testlib_argv[size_t(index)]; +} + +/** + * An utility function to get the opt with a given key . This function + * also print a readable message when no opts are found. + */ +std::string __testlib_keyToOpts(const std::string &key) { + auto it = __testlib_opts.find(key); + if (it == __testlib_opts.end()) + __testlib_fail("Opts: unknown key '" + compress(key) + "'"); + it->second.used = true; + return it->second.value; +} + +template +T optValueToIntegral(const std::string &s, bool nonnegative); + +long double optValueToLongDouble(const std::string &s); + +std::string parseExponentialOptValue(const std::string &s) { + size_t pos = std::string::npos; + for (size_t i = 0; i < s.length(); i++) + if (s[i] == 'e' || s[i] == 'E') { + if (pos != std::string::npos) + __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); + pos = i; + } + if (pos == std::string::npos) + return s; + std::string e = s.substr(pos + 1); + if (!e.empty() && e[0] == '+') + e = e.substr(1); + if (e.empty()) + __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); + if (e.length() > 20) + __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); + int ne = optValueToIntegral(e, false); + std::string num = s.substr(0, pos); + if (num.length() > 20) + __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); + if (!num.empty() && num[0] == '+') + num = num.substr(1); + optValueToLongDouble(num); + bool minus = false; + if (num[0] == '-') { + minus = true; + num = num.substr(1); + } + for (int i = 0; i < +ne; i++) { + size_t sep = num.find('.'); + if (sep == std::string::npos) + num += '0'; + else { + if (sep + 1 == num.length()) + num[sep] = '0'; + else + std::swap(num[sep], num[sep + 1]); + } + } + for (int i = 0; i < -ne; i++) { + size_t sep = num.find('.'); + if (sep == std::string::npos) + num.insert(num.begin() + int(num.length()) - 1, '.'); + else { + if (sep == 0) + num.insert(num.begin() + 1, '0'); + else + std::swap(num[sep - 1], num[sep]); + } + } + while (!num.empty() && num[0] == '0') + num = num.substr(1); + while (num.find('.') != std::string::npos && num.back() == '0') + num = num.substr(0, num.length() - 1); + if (!num.empty() && num.back() == '.') + num = num.substr(0, num.length() - 1); + if ((!num.empty() && num[0] == '.') || num.empty()) + num.insert(num.begin(), '0'); + return (minus ? "-" : "") + num; +} + +template +T optValueToIntegral(const std::string &s_, bool nonnegative) { + std::string s(parseExponentialOptValue(s_)); + if (s.empty()) + __testlib_fail("Opts: expected integer but '" + compress(s_) + "' found"); + T value = 0; + long double about = 0.0; + signed char sign = +1; + size_t pos = 0; + if (s[pos] == '-') { + if (nonnegative) + __testlib_fail("Opts: expected non-negative integer but '" + compress(s_) + "' found"); + sign = -1; + pos++; + } + for (size_t i = pos; i < s.length(); i++) { + if (s[i] < '0' || s[i] > '9') + __testlib_fail("Opts: expected integer but '" + compress(s_) + "' found"); + value = value * 10 + s[i] - '0'; + about = about * 10 + s[i] - '0'; + } + value *= sign; + about *= sign; + if (fabsl(value - about) > 0.1) + __testlib_fail("Opts: integer overflow: expected integer but '" + compress(s_) + "' found"); + return value; +} + +long double optValueToLongDouble(const std::string &s_) { + std::string s(parseExponentialOptValue(s_)); + if (s.empty()) + __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found"); + long double value = 0.0; + signed char sign = +1; + size_t pos = 0; + if (s[pos] == '-') { + sign = -1; + pos++; + } + bool period = false; + long double mul = 1.0; + for (size_t i = pos; i < s.length(); i++) { + if (s[i] == '.') { + if (period) + __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found"); + else { + period = true; + continue; + } + } + if (period) + mul *= 10.0; + if (s[i] < '0' || s[i] > '9') + __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found"); + if (period) + value += (s[i] - '0') / mul; + else + value = value * 10 + s[i] - '0'; + } + value *= sign; + return value; +} + +/** + * Return true if there is an opt with a given key. + * + * By calling this function, automatic ensurement for no unused opts will be + * done when the program is finalized. Call suppressEnsureNoUnusedOpts() to + * turn it off. + */ +bool has_opt(const std::string &key) { + __testlib_ensureNoUnusedOptsFlag = true; + return __testlib_opts.count(key) != 0; +} + +/* About the followings part for opt with 2 and 3 arguments. + * + * To parse the argv/opts correctly for a give type (integer, floating point or + * string), some meta programming must be done to determine the type of + * the type, and use the correct parsing function accordingly. + * + * The pseudo algorithm for determining the type of T and parse it accordingly + * is as follows: + * + * if (T is integral type) { + * if (T is unsigned) { + * parse the argv/opt as an **unsigned integer** of type T. + * } else { + * parse the argv/opt as an **signed integer** of type T. + * } else { + * if (T is floating point type) { + * parse the argv/opt as an **floating point** of type T. + * } else { + * // T should be std::string + * just the raw content of the argv/opts. + * } + * } + * + * To help with meta programming, some `opt` function with 2 or 3 arguments are + * defined. + * + * Opt with 3 arguments: T opt(true/false is_integral, true/false is_unsigned, index/key) + * + * + The first argument is for determining whether the type T is an integral + * type. That is, the result of std::is_integral() should be passed to + * this argument. When false, the type _should_ be either floating point or a + * std::string. + * + * + The second argument is for determining whether the signedness of the type + * T (if it is unsigned or signed). That is, the result of + * std::is_unsigned() should be passed to this argument. This argument can + * be ignored if the first one is false, because it only applies to integer. + * + * Opt with 2 arguments: T opt(true/false is_floating_point, index/key) + * + The first argument is for determining whether the type T is a floating + * point type. That is, the result of std::is_floating_point() should be + * passed to this argument. When false, the type _should_ be a std::string. + */ + +template +T opt(std::false_type is_floating_point, int index); + +template<> +std::string opt(std::false_type /*is_floating_point*/, int index) { + return __testlib_indexToArgv(index); +} + +template +T opt(std::true_type /*is_floating_point*/, int index) { + return T(optValueToLongDouble(__testlib_indexToArgv(index))); +} + +template +T opt(std::false_type /*is_integral*/, U /*is_unsigned*/, int index) { + return opt(std::is_floating_point(), index); +} + +template +T opt(std::true_type /*is_integral*/, std::false_type /*is_unsigned*/, int index) { + return optValueToIntegral(__testlib_indexToArgv(index), false); +} + +template +T opt(std::true_type /*is_integral*/, std::true_type /*is_unsigned*/, int index) { + return optValueToIntegral(__testlib_indexToArgv(index), true); +} + +template<> +bool opt(std::true_type /*is_integral*/, std::true_type /*is_unsigned*/, int index) { + std::string value = __testlib_indexToArgv(index); + if (value == "true" || value == "1") + return true; + if (value == "false" || value == "0") + return false; + __testlib_fail("Opts: opt by index '" + toString(index) + "': expected bool true/false or 0/1 but '" + + compress(value) + "' found"); +} + +/** + * Return the parsed argv by a given index. + */ +template +T opt(int index) { + return opt(std::is_integral(), std::is_unsigned(), index); +} + +/** + * Return the raw string value of an argv by a given index. + */ +std::string opt(int index) { + return opt(index); +} + +/** + * Return the parsed argv by a given index. If the index is bigger than + * the number of argv, return the given default_value. + */ +template +T opt(int index, const T &default_value) { + if (index >= int(__testlib_argv.size())) { + return default_value; + } + return opt(index); +} + +/** + * Return the raw string value of an argv by a given index. If the index is + * bigger than the number of argv, return the given default_value. + */ +std::string opt(int index, const std::string &default_value) { + return opt(index, default_value); +} + +template +T opt(std::false_type is_floating_point, const std::string &key); + +template<> +std::string opt(std::false_type /*is_floating_point*/, const std::string &key) { + return __testlib_keyToOpts(key); +} + +template +T opt(std::true_type /*is_integral*/, const std::string &key) { + return T(optValueToLongDouble(__testlib_keyToOpts(key))); +} + +template +T opt(std::false_type /*is_integral*/, U, const std::string &key) { + return opt(std::is_floating_point(), key); +} + +template +T opt(std::true_type /*is_integral*/, std::false_type /*is_unsigned*/, const std::string &key) { + return optValueToIntegral(__testlib_keyToOpts(key), false); +} + +template +T opt(std::true_type /*is_integral*/, std::true_type /*is_unsigned*/, const std::string &key) { + return optValueToIntegral(__testlib_keyToOpts(key), true); +} + +template<> +bool opt(std::true_type /*is_integral*/, std::true_type /*is_unsigned*/, const std::string &key) { + if (!has_opt(key)) + return false; + std::string value = __testlib_keyToOpts(key); + if (value == "true" || value == "1") + return true; + if (value == "false" || value == "0") + return false; + __testlib_fail("Opts: key '" + compress(key) + "': expected bool true/false or 0/1 but '" + + compress(value) + "' found"); +} + +/** + * Return the parsed opt by a given key. + */ +template +T opt(const std::string &key) { + return opt(std::is_integral(), std::is_unsigned(), key); +} + +/** + * Return the raw string value of an opt by a given key + */ +std::string opt(const std::string &key) { + return opt(key); +} + +/* Scorer started. */ + +enum TestResultVerdict { + SKIPPED, + OK, + WRONG_ANSWER, + RUNTIME_ERROR, + TIME_LIMIT_EXCEEDED, + IDLENESS_LIMIT_EXCEEDED, + MEMORY_LIMIT_EXCEEDED, + COMPILATION_ERROR, + CRASHED, + FAILED +}; + +std::string serializeVerdict(TestResultVerdict verdict) { + switch (verdict) { + case SKIPPED: return "SKIPPED"; + case OK: return "OK"; + case WRONG_ANSWER: return "WRONG_ANSWER"; + case RUNTIME_ERROR: return "RUNTIME_ERROR"; + case TIME_LIMIT_EXCEEDED: return "TIME_LIMIT_EXCEEDED"; + case IDLENESS_LIMIT_EXCEEDED: return "IDLENESS_LIMIT_EXCEEDED"; + case MEMORY_LIMIT_EXCEEDED: return "MEMORY_LIMIT_EXCEEDED"; + case COMPILATION_ERROR: return "COMPILATION_ERROR"; + case CRASHED: return "CRASHED"; + case FAILED: return "FAILED"; + } + throw "Unexpected verdict"; +} + +TestResultVerdict deserializeTestResultVerdict(std::string s) { + if (s == "SKIPPED") + return SKIPPED; + else if (s == "OK") + return OK; + else if (s == "WRONG_ANSWER") + return WRONG_ANSWER; + else if (s == "RUNTIME_ERROR") + return RUNTIME_ERROR; + else if (s == "TIME_LIMIT_EXCEEDED") + return TIME_LIMIT_EXCEEDED; + else if (s == "IDLENESS_LIMIT_EXCEEDED") + return IDLENESS_LIMIT_EXCEEDED; + else if (s == "MEMORY_LIMIT_EXCEEDED") + return MEMORY_LIMIT_EXCEEDED; + else if (s == "COMPILATION_ERROR") + return COMPILATION_ERROR; + else if (s == "CRASHED") + return CRASHED; + else if (s == "FAILED") + return FAILED; + ensuref(false, "Unexpected serialized TestResultVerdict"); + // No return actually. + return FAILED; +} + +struct TestResult { + int testIndex; + std::string testset; + std::string group; + TestResultVerdict verdict; + double points; + long long timeConsumed; + long long memoryConsumed; + std::string input; + std::string output; + std::string answer; + int exitCode; + std::string checkerComment; +}; + +std::string serializePoints(double points) { + if (std::isnan(points)) + return ""; + else { + char c[64]; + sprintf(c, "%.03lf", points); + return c; + } +} + +double deserializePoints(std::string s) { + if (s.empty()) + return std::numeric_limits::quiet_NaN(); + else { + double result; + ensuref(sscanf(s.c_str(), "%lf", &result) == 1, "Invalid serialized points"); + return result; + } +} + +std::string escapeTestResultString(std::string s) { + std::string result; + for (size_t i = 0; i < s.length(); i++) { + if (s[i] == '\r') + continue; + if (s[i] == '\n') { + result += "\\n"; + continue; + } + if (s[i] == '\\' || s[i] == ';') + result += '\\'; + result += s[i]; + } + return result; +} + +std::string unescapeTestResultString(std::string s) { + std::string result; + for (size_t i = 0; i < s.length(); i++) { + if (s[i] == '\\' && i + 1 < s.length()) { + if (s[i + 1] == 'n') { + result += '\n'; + i++; + continue; + } else if (s[i + 1] == ';' || s[i + 1] == '\\') { + result += s[i + 1]; + i++; + continue; + } + } + result += s[i]; + } + return result; +} + +std::string serializeTestResult(TestResult tr) { + std::string result; + result += std::to_string(tr.testIndex); + result += ";"; + result += escapeTestResultString(tr.testset); + result += ";"; + result += escapeTestResultString(tr.group); + result += ";"; + result += serializeVerdict(tr.verdict); + result += ";"; + result += serializePoints(tr.points); + result += ";"; + result += std::to_string(tr.timeConsumed); + result += ";"; + result += std::to_string(tr.memoryConsumed); + result += ";"; + result += escapeTestResultString(tr.input); + result += ";"; + result += escapeTestResultString(tr.output); + result += ";"; + result += escapeTestResultString(tr.answer); + result += ";"; + result += std::to_string(tr.exitCode); + result += ";"; + result += escapeTestResultString(tr.checkerComment); + return result; +} + +TestResult deserializeTestResult(std::string s) { + std::vector items; + std::string t; + for (size_t i = 0; i < s.length(); i++) { + if (s[i] == '\\') { + t += s[i]; + if (i + 1 < s.length()) + t += s[i + 1]; + i++; + continue; + } else { + if (s[i] == ';') { + items.push_back(t); + t = ""; + } else + t += s[i]; + } + } + items.push_back(t); + + ensuref(items.size() == 12, "Invalid TestResult serialization: expected exactly 12 items"); + + TestResult tr; + size_t pos = 0; + tr.testIndex = stoi(items[pos++]); + tr.testset = unescapeTestResultString(items[pos++]); + tr.group = unescapeTestResultString(items[pos++]); + tr.verdict = deserializeTestResultVerdict(items[pos++]); + tr.points = deserializePoints(items[pos++]); + tr.timeConsumed = stoll(items[pos++]); + tr.memoryConsumed = stoll(items[pos++]); + tr.input = unescapeTestResultString(items[pos++]); + tr.output = unescapeTestResultString(items[pos++]); + tr.answer = unescapeTestResultString(items[pos++]); + tr.exitCode = stoi(items[pos++]); + tr.checkerComment = unescapeTestResultString(items[pos++]); + + return tr; +} + +std::vector readTestResults(std::string fileName) { + std::ifstream stream; + stream.open(fileName.c_str(), std::ios::in); + ensuref(stream.is_open(), "Can't read test results file '%s'", fileName.c_str()); + std::vector result; + std::string line; + while (getline(stream, line)) + if (!line.empty()) + result.push_back(deserializeTestResult(line)); + stream.close(); + return result; +} + +std::function)> __testlib_scorer; + +struct TestlibScorerGuard { + ~TestlibScorerGuard() { + if (testlibMode == _scorer) { + std::vector testResults; + while (!inf.eof()) { + std::string line = inf.readLine(); + if (!line.empty()) + testResults.push_back(deserializeTestResult(line)); + } + inf.readEof(); + printf("%.3f\n", __testlib_scorer(testResults)); + } + } +} __testlib_scorer_guard; + +void registerScorer(int argc, char *argv[], std::function)> scorer) { + /* Supress unused. */ + (void)(argc), (void)(argv); + + __testlib_ensuresPreconditions(); + + testlibMode = _scorer; + __testlib_set_binary(stdin); + + inf.init(stdin, _input); + inf.strict = false; + + __testlib_scorer = scorer; +} + +/* Scorer ended. */ + +/** + * Return the parsed opt by a given key. If no opts with the given key are + * found, return the given default_value. + * + * By calling this function, automatic ensurement for no unused opts will be + * done when the program is finalized. Call suppressEnsureNoUnusedOpts() to + * turn it off. + */ +template +T opt(const std::string &key, const T &default_value) { + if (!has_opt(key)) { + return default_value; + } + return opt(key); +} + +/** + * Return the raw string value of an opt by a given key. If no opts with the + * given key are found, return the given default_value. + * + * By calling this function, automatic ensurement for no unused opts will be + * done when the program is finalized. Call suppressEnsureNoUnusedOpts() to + * turn it off. + */ +std::string opt(const std::string &key, const std::string &default_value) { + return opt(key, default_value); +} + +/** + * Check if all opts are used. If not, __testlib_fail is called. + * Should be used after calling all opt() function calls. + * + * This function is useful when opt() with default_value for checking typos + * in the opt's key. + */ +void ensureNoUnusedOpts() { + for (const auto &opt: __testlib_opts) { + if (!opt.second.used) { + __testlib_fail(format("Opts: unused key '%s'", compress(opt.first).c_str())); + } + } +} + +void suppressEnsureNoUnusedOpts() { + __testlib_ensureNoUnusedOptsSuppressed = true; +} + +void TestlibFinalizeGuard::autoEnsureNoUnusedOpts() { + if (__testlib_ensureNoUnusedOptsFlag && !__testlib_ensureNoUnusedOptsSuppressed) { + ensureNoUnusedOpts(); + } +} + +TestlibFinalizeGuard testlibFinalizeGuard; + +#endif +#endif diff --git a/dominancia-de-pontos/src/validator.cpp b/dominancia-de-pontos/src/validator.cpp new file mode 100644 index 0000000..cc98c5e --- /dev/null +++ b/dominancia-de-pontos/src/validator.cpp @@ -0,0 +1,23 @@ +#include "testlib.h" +#include + +using namespace std; + +const int MIN_N = 1; +const int MAX_N = 1e5; +const int MIN_COORD = -1e9; +const int MAX_COORD = 1e9; + +int main(int argc, char* argv[]) { + registerValidation(argc, argv); + int n = inf.readInt(MIN_N, MAX_N, "n"); + inf.readEoln(); + for (int i = 0; i < n; i++) { + inf.readInt(MIN_COORD, MAX_COORD, "xi"); + inf.readSpace(); + inf.readInt(MIN_COORD, MAX_COORD, "yi"); + inf.readEoln(); + } + inf.readEof(); + return 0; +} \ No newline at end of file diff --git a/dominancia-de-pontos/statement/description.tex b/dominancia-de-pontos/statement/description.tex new file mode 100644 index 0000000..0603bfe --- /dev/null +++ b/dominancia-de-pontos/statement/description.tex @@ -0,0 +1,9 @@ +Considere um conjunto de \( n \) pontos \( P = \{(x_0, y_0), (x_1, y_1), \ldots, (x_{n-1}, y_{n-1}) \} \) no plano cartesiano \( \mathbb{R}^2 \). + +Dizemos que um ponto \( A = (x_a, y_a) \) \textbf{domina} outro ponto \( B = (x_b, y_b) \) se, e somente se, as seguintes condições forem satisfeitas simultaneamente: +\begin{itemize} + \item \( x_a < x_b \) + \item \( y_a < y_b \) +\end{itemize} + +O objetivo é contar o número total de relações de dominância existentes no conjunto \( P \). Uma relação de dominância é definida por um par ordenado de pontos \( (P_i, P_j) \) tal que \( P_i \) domina \( P_j \). \ No newline at end of file diff --git a/dominancia-de-pontos/statement/input.tex b/dominancia-de-pontos/statement/input.tex new file mode 100644 index 0000000..7b81cee --- /dev/null +++ b/dominancia-de-pontos/statement/input.tex @@ -0,0 +1,5 @@ +A entrada consiste em um único caso de teste. + +A primeira linha contém um inteiro \( n \) (\( 1 \le n \le 10^5 \)), representando o número de pontos no conjunto. + +As próximas \( n \) linhas contêm, cada uma, dois inteiros \( x_i \) e \( y_i \) (\( -10^9 \le x_i, y_i \le 10^9 \)), representando as coordenadas de cada ponto. \ No newline at end of file diff --git a/dominancia-de-pontos/statement/notes.tex b/dominancia-de-pontos/statement/notes.tex new file mode 100644 index 0000000..e69de29 diff --git a/dominancia-de-pontos/statement/output.tex b/dominancia-de-pontos/statement/output.tex new file mode 100644 index 0000000..3eb128e --- /dev/null +++ b/dominancia-de-pontos/statement/output.tex @@ -0,0 +1 @@ +A saída deve conter uma única linha com um número inteiro representando o total de relações de dominância encontradas no conjunto de pontos dado. \ No newline at end of file diff --git a/dominancia-de-pontos/statement/preamble.tex b/dominancia-de-pontos/statement/preamble.tex new file mode 100644 index 0000000..e69de29 diff --git a/dominancia-de-pontos/statement/tutorial.tex b/dominancia-de-pontos/statement/tutorial.tex new file mode 100644 index 0000000..e69de29