API Reference


Problem spec

ProblemSpec must inherit tcframe::BaseProblemSpec:

class ProblemSpec : public BaseProblemSpec {};

Except for private helper functions, every member of ProblemSpec listed below must be protected.


Input/output variables

Defined as instance member variables of ProblemSpec, which will be referenced in other methods of ProblemSpec and TestSpec.

There are three supported types of variables:

Scalar
Variables of built-in integral types (int, long long, char, etc.), built-in floating-point types (float, double), and std::string.
Vector
std::vector<T>, where T is a scalar type as defined above. Arrays (T[]) are not supported.
Matrix
std::vector<std::vector<T>>, where T is a scalar type as defined above. 2D arrays (T[][]) are not supported.

Example:

class ProblemSpec : public BaseProblemSpec {
protected:
    int A, B;
    vector<int> parent;
    vector<vector<int>> values;
};

Input/output formats

virtual void InputFormat() = 0;

Defines the input format. It is mandatory.

virtual void OutputFormat() {}

Defines the output format. It is optional; if not implemented, then the output will not be validated.

Defining format

The following macros are exposed to define input/output formats:

EMPTY_LINE()

Defines an empty line.

RAW_LINE(string variable name)

Defines a line of raw string. The variable must be of std::string type.

Example:

void InputFormat() {
    RAW_LINE(S);
}

With S = “Hello, world!”, the above format will produce:

Hello, world!
RAW_LINES(vector of string variable name)
RAW_LINES(vector of string variable name) % SIZE(number of elements)

Defines multiple lines, each consisting of raw string. The variable must be of std::vector<std::string> type.

If the size is not given, then this must be the last segment in the I/O format.

Example:

void InputFormat() {
    RAW_LINES(X) % SIZE(2);
    RAW_LINES(Y);
}

With X = {“Hello, world!”, “Happy new year.”}, Y = {“lorem”, “ipsum”, “dolor sit amet”}, the above format will produce:

Hello, world!
Happy new year.
lorem
ipsum
dolor sit amet
LINE(comma-separated elements)

Defines a single line containing space-separated scalar or vector variables. In case of vector variables, the elements are separated by spaces as well.

element is one of:

  • <scalar variable name>.
  • <vector variable name> % SIZE(<number of elements>). The number of elements can be a constant or a scalar variable.
  • <vector variable name>. Here, the number of elements is unspecified. This kind of element must occur last in a line segment, if any. Elements will be considered until new line is found.

Example:

void InputFormat() {
    LINE(N);
    LINE(A % SIZE(3), B);
    LINE(M, C % SIZE(M));
}

With N = 2, A = {1, 2, 3}, B = {100, 200, 300, 400}, M = 2, C = {7, 8}, the above format will produce:

2
1 2 3 100 200 300 400
2 7 8
LINES(comma-separated vector/matrix variable names)
LINES(comma-separated vector/matrix variable names) % SIZE(number of elements)

Defines multiple lines, each consisting of space-separated elements of given vector/matrix variables.

If the size is not given, this must be the last segment in the I/O format.

Example:

void InputFormat() {
    LINES(V) % SIZE(2);
    LINES(X, Y) % SIZE(N);
    LINES(Z);
}

With V = {1, 2}, X = {100, 110, 120}, Y = {200, 210, 220} N = 3, Z = {1, 2, 3, 4} the above format will produce:

1
2
100 200
110 210
120 220
1
2
3
4

If a matrix variable is given, it must occur as the last argument, and the number of rows must match with the number of elements of the other vector variables (if any). It is not required that each row of the matrix consists of the same number of columns.

Example:

void InputFormat() {
    LINES(op, data) % SIZE(2);
}

With op = {“UPDATE”, “QUERY”}, data = {{3, 5}, {7}}, the above format will produce:

UPDATE 3 5
QUERY 7
GRID(matrix variable name) % SIZE(number of rows, number of columns)

Defines a grid consisting elements of a given matrix variable. If the given matrix variable is of type char, the elements in each row is not space-separated, otherwise they are space-separated.

Example:

void InputFormat() {
    GRID(G) % SIZE(2, 2);
    GRID(H) % SIZE(R, C);
}

With G = {{‘a’, ‘b’}, {‘c’, ‘d’}}, H = {{1, 2, 3}, {4, 5, 6}}, R = 2, C = 3, the above format will produce:

ab
cd
1 2 3
4 5 6

Problem styles

virtual void StyleConfig() {}

Defines the options to enable for problem styles. The following methods are exposed:

CustomScorer()

Declares that the problem needs a custom scorer.

NoOutput()

Declares that the problem does not need test case output files.

See Problem Styles for more details.

Example:

void StyleConfig() {
    CustomScorer();
    NoOutput();
}

Constraints and subtasks

virtual void MultipleTestCasesConstraints() {}

Defines the constraints to be imposed to the multiple test cases counter.

virtual void Constraints() {}

Defines the constraints to be imposed to the input/output variables.

virtual void Subtask1() {}
virtual void Subtask2() {}
// ...
virtual void Subtask25() {}

Defines the constraints to be imposed to the input/output variables for each subtask (up to 25).

Defining constraints

The following macro is exposed to define constraints:

CONS(predicate)

Defines a constraint. predicate is a boolean expression, whose value must be completely determined by the values of the input variables (only).

Example:

void Subtask1() {
    CONS(A <= B && B <= 1000);
    CONS(graphDoesNotHaveCycles());
}

Multiple test cases config

virtual void MultipleTestCasesConfig() {}

Defines the config for multiple test cases per file problems. The following methods are exposed:

Counter(int &var)

Sets the input variable that will hold the number of test cases in a file.

OutputPrefix(std::string prefix)

Sets the prefix to be prepended to the output of each test case. It can include %d, which will be replaced by the actual test case number (1-based).

Example:

void MultipleTestCasesConfig() {
    Counter(T);
    OutputPrefix("Case #%d: ");
}

Grading config

virtual void GradingConfig() {}

Defines the config for local grading. The following methods are exposed:

TimeLimit(int timeLimitInSeconds)

Sets the time limit in seconds. If not specified, the default value is 2 seconds.

MemoryLimit(int memoryLimitInMegabytes)

Sets the memory limit in MB. If not specified, the default value is 64 MB.

Example:

void GradingConfig() {
    TimeLimit(3);
    MemoryLimit(256);
}

Test spec

TestSpec must inherit tcframe::BaseTestSpec<ProblemSpec>:

class TestSpec : public BaseTestSpec<ProblemSpec> {};

Except for private helper functions, every member of TestSpec listed below must be protected.


Sample test cases

virtual void SampleTestCase1() {}
virtual void SampleTestCase2() {}
// ...
virtual void SampleTestCase25() {}

Defines the sample test cases (up to 25). The following methods are exposed:

Subtasks(std::set<int> subtaskNumbers)

Assigns the current sample test case to a set of subtasks, if the problem has subtasks. If used, this should be the first call in a sample test case.

Input(std::vector<std::string> lines)

Defines the input as exact literal string, given as list of lines.

Output(std::vector<std::string> lines)

Defines the input as exact literal string, given as list of lines. It is optional; if not specified, the solution will be run against the sample input to produce the corresponding sample output.

Example:

void SampleTestCase1() {
    Input({
        "4 6",
        "a b c"
    });
    Output({
        "10"
    });
}

Test cases and test groups

virtual void TestCases() {}

Defines the test cases.

virtual void TestGroup1() {}
virtual void TestGroup2() {}
// ...
virtual void TestGroup25() {}

Defines the test cases on each test group (up to 25). The following method is exposed:

Subtasks(std::set<int> subtaskNumbers)

Assigns the current test group to a set of subtasks. This should be the first call in a test group.

void TestGroup1() {
    Subtasks({1, 3});

    // test case definitions follow
}

Defining test cases

The following macro is exposed to define test cases:

CASE(comma-separated statements)

Defines a test case.

statement should be one of:

  • assignment to an input variables
  • private method call that assigns values to one or more input variables

Example:

void TestCases() {
    CASE(N = 42, M = 100, randomArray());
    CASE(N = 1000, M = 1000, randomArray());
    CASE(randomEqualNandM(), randomArray());
}

Test case lifecycle

virtual void BeforeTestCase() {}
virtual void AfterTestCase() {}

Hook up additional logic to run during in a test case lifecycle.

For each test case, the following things will happen in order:

  1. BeforeTestCase() is executed.
  2. The assignments/method calls inside CASE() are executed.
  3. AfterTestCase() is executed.
  4. Input variable values are printed according to the input format.

Random number generator

BaseTestSpec exposes a random number generator object rnd that can be utilized to define test cases. The following methods are available on it:

int nextInt(int minNum, int maxNum)

Returns a uniformly distributed random integer (int) between minNum and maxNum, inclusive.

int nextInt(int maxNumEx)

Returns a uniformly distributed random integer (int) between 0 and maxNumEx - 1, inclusive.

long long nextLongLong(long long minNum, long long maxNum)

Returns a uniformly distributed random integer (long long) between minNum and maxNum, inclusive.

long long nextLongLong(long long maxNumEx)

Returns a uniformly distributed random integer (long long) between 0 and maxNumEx - 1, inclusive.

double nextDouble(double minNum, double maxNum)

Returns a uniformly distributed random real number (double) between minNum and maxNum, inclusive.

double nextDouble(double maxNum)

Returns a uniformly distributed random real number (double) between 0 and maxNum, inclusive.

void shuffle(std::RandomAccessIterator first, std::RandomAccessIterator last)

Randomly shuffles the elements in [first, last). Use this instead of std::random_shuffle().


Runner program

A runner is the compiled version of a spec file, and is capable of two things:

Test cases generation

./runner [options]
--output=<dir>

The output directory to which the test cases will be generated. Default: tc.

--solution=<command>

The solution command to use for generating output files. Default: ./solution.

--scorer=<command>

The custom scorer command to use for checking sample output strings in problem spec class. Default: ./scorer.

--seed=<seed>

The seed for random number generator rnd in the test spec. Default: 0.

Local grading

./runner grade [options]
--output=<dir>

The output directory from which the generated test cases will be read. Default: tc.

--solution=<command>

The solution command to grade. Default: ./solution.

--scorer=<command>

The custom scorer command to use. Default: ./scorer.

--time-limit=<time-limit-in-seconds>

Overrides the time limit specified by TimeLimit() in grading config.

--memory-limit=<memory-limit-in-megabytes>

Overrides the memory limit specified by MemoryLimit() in grading config.

--no-time-limit

Unsets the time limit specified by TimeLimit() in grading config.

--no-memory-limit

Unsets the memory limit specified by MemoryLimit() in grading config.