From 8e1f5309bacf1666f400192b1a2d191bd6febd2b Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Sun, 29 Mar 2015 20:20:35 +0700 Subject: [PATCH] Support output variable and format Resolve #24 Resolve #25 --- docs/api_ref.rst | 14 ++++----- docs/basic_concepts.rst | 54 +++++++++++++++++++++++++++-------- include/tcframe/generator.hpp | 4 +++ include/tcframe/os.hpp | 2 +- include/tcframe/problem.hpp | 1 + test/generator_test.cpp | 14 ++++++++- 6 files changed, 68 insertions(+), 21 deletions(-) diff --git a/docs/api_ref.rst b/docs/api_ref.rst index c581da15..5fbedabb 100644 --- a/docs/api_ref.rst +++ b/docs/api_ref.rst @@ -14,10 +14,10 @@ The following methods can be called inside the overridden method **BaseProblem:: If not specified, the default slug is "problem". -.. _api-ref-input-variables: +.. _api-ref-io-variables: -Input variables ---------------- +Input/output variables +---------------------- There are three types of variables that are supported: @@ -30,12 +30,12 @@ Vector Matrix std::vector>, where T is a scalar type as defined above. Note that you cannot use 2D arrays (\ **T**\ [][]). -.. _api-ref-input-segments: +.. _api-ref-io-segments: -Input segments --------------- +Input/output segments +--------------------- -The following macros can be called inside the overridden method **BaseProblem::InputFormat()**. +The following macros can be called inside the overridden method **BaseProblem::InputFormat()** or **BaseProblem::OutputFormat()**. .. py:function:: EMPTY_LINE() diff --git a/docs/basic_concepts.rst b/docs/basic_concepts.rst index 13693269..78c5247d 100644 --- a/docs/basic_concepts.rst +++ b/docs/basic_concepts.rst @@ -88,10 +88,10 @@ This component consists of configuration of several aspects of the problem. To d The complete reference of configurable aspects of a problem can be found here: :ref:`Problem configuration API reference `. -Input variables ---------------- +Input/output variables +---------------------- -This component is a collection of variables which compose test cases inputs. They can be usually found in the input format section in the problem statement. For this problem, we have three input variables: **N**, **K**, and **A**. The input variables are defined as protected member variables. +**Input variables** are a collection of variables which compose test cases inputs. They can be usually found in the input format section in the problem statement. For this problem, we have three input variables: **N**, **K**, and **A**. The input variables are defined as protected member variables. In this problem, we have two scalars (**N**, **K**) and one vector (**A**) as the input variables. We define them as follows: @@ -101,12 +101,22 @@ In this problem, we have two scalars (**N**, **K**) and one vector (**A**) as th int K; vector A; -The complete reference of input variables can be found here: :ref:`Input variables API reference `. +Similarly, **output variables** are a collection of variables which compose test cases inputs. Most of the cases, this is just a single variable and does not have a particular name in the problem statement. Let's just call it result. -Input format ------------- +.. sourcecode:: cpp + + int N; + int K; + vector A; -This component specifies how the input variables should be printed in test case input files. To define this component, override the method **void BaseProblem::InputFormat()**. The format is specified in terms of consecutive input **segment**\ s. Basically an input segment arranges the layout of several input variables. + int result; + +The complete reference of input/output variables can be found here: :ref:`Input/output variables API reference `. + +Input/output format +------------------- + +**Input format** specifies how the input variables should be printed in test case input files. To define this component, override the method **void BaseProblem::InputFormat()**. The format is specified in terms of consecutive input **segment**\ s. Basically an input segment arranges the layout of several input variables. A test case input file for this problem consists of a single containing **N** and **K**, followed by a single line containing space-separated elements of **A**. We can define that format as follows: @@ -117,12 +127,20 @@ A test case input file for this problem consists of a single containing **N** an LINE(A % SIZE(N)); } -The complete reference of input segments can be found here: :ref:`Input segments API reference `. +Similarly, **output format** specifies how the input variables should be printed in test case input files. To define this component, override the method **void BaseProblem::OutputFormat()**. + +.. sourcecode:: cpp + + void OutputFormat() { + LINE(result); + } + +The complete reference of input/output segments can be found here: :ref:`Input/output segments API reference `. Constraints ----------- -This components specifies the constraints of the problem; i.e., the conditions that must be satisfied by the input +This components specifies the constraints of the problem; i.e., the conditions that must be satisfied by the input/output variables. Two types of problems are supported: the ones without subtasks, and the ones with subtasks. **For problems without subtasks**\ : Override the method **void BaseProblem::Constraints()**. @@ -174,7 +192,7 @@ where **eachElementBetween()** is a private helper method, defined as follows: As of this version, there is currently no easy way to test a predicate for each element of a vector. The workaround is to write a helper method ourselves, like what we did above. -The complete reference of input segments can be found here: :ref:`Constraints API reference `. +The complete reference of constraints can be found here: :ref:`Constraints API reference `. We have now completed writing a problem specification class. In summary, our class should look like this: @@ -186,6 +204,8 @@ We have now completed writing a problem specification class. In summary, our cla int K; vector A; + int result; + void Config() { setSlug("k-product"); } @@ -195,6 +215,10 @@ We have now completed writing a problem specification class. In summary, our cla LINE(A % SIZE(N)); } + void OutputFormat() { + LINE(result); + } + void Subtask1() { CONS(N == 1); CONS(1 <= K && K <= 100); @@ -342,6 +366,8 @@ Note that for vector input variables, don't forget to clear them before assignin int K; vector A; + int result; + void Config() { setSlug("k-product"); } @@ -351,6 +377,10 @@ Note that for vector input variables, don't forget to clear them before assignin LINE(A % SIZE(N)); } + void OutputFormat() { + LINE(result); + } + void Subtask1() { CONS(N == 1); CONS(1 <= K && K <= 100); @@ -487,8 +517,8 @@ the input-output file pair will be stored in the specified base directory (by de Generation can fail due to several reasons: -Invalid input format - In this case, no test cases will be generated. For example: using scalar variable for a grid segment. +Invalid input/output format + For example: using scalar variable for a grid segment. Invalid input variable states For example: a grid segment requires that the size is 2 x 3, but after applying the test case definition, the matrix consists of 3 x 4 elements. diff --git a/include/tcframe/generator.hpp b/include/tcframe/generator.hpp index f7d444ec..535ff668 100644 --- a/include/tcframe/generator.hpp +++ b/include/tcframe/generator.hpp @@ -253,6 +253,10 @@ class BaseGenerator : protected TProblem, protected TestCasesCollector { Failure("Standard error: " + string(istreambuf_iterator(*result.errorStream), istreambuf_iterator()), 1) }); } + + TProblem::beginParsingFormat(result.outputStream); + TProblem::OutputFormat(); + TProblem::endParsingFormat(); } }; diff --git a/include/tcframe/os.hpp b/include/tcframe/os.hpp index 8c8a4e61..f5600790 100644 --- a/include/tcframe/os.hpp +++ b/include/tcframe/os.hpp @@ -67,7 +67,7 @@ class UnixOperatingSystem : public OperatingSystem { ExecutionResult result; int exitStatus = system((command + " < " + inputFilename + " > " + outputFilename + " 2> " + errorFilename).c_str()); result.exitCode = WEXITSTATUS(exitStatus); - result.outputStream = openForReading(outputFilename); + result.outputStream = openForReading(outputName); result.errorStream = openForReadingAsStringStream(errorFilename); return result; diff --git a/include/tcframe/problem.hpp b/include/tcframe/problem.hpp index f4e11547..3c7588ab 100644 --- a/include/tcframe/problem.hpp +++ b/include/tcframe/problem.hpp @@ -30,6 +30,7 @@ class BaseProblem : protected ConstraintsCollector, protected IOFormatProvider { } virtual void InputFormat() = 0; + virtual void OutputFormat() = 0; virtual void Constraints() { throw NotImplementedException(); } virtual void Subtask1() { throw NotImplementedException(); } diff --git a/test/generator_test.cpp b/test/generator_test.cpp index e603c24d..61134165 100644 --- a/test/generator_test.cpp +++ b/test/generator_test.cpp @@ -82,7 +82,7 @@ class FakeOperatingSystem : public OperatingSystem { int A, B, K; input >> A >> B >> K; - string output = Util::toString(A + B * K); + string output = Util::toString(A + B * K) + "\n"; ExecutionResult result; result.exitCode = 0; @@ -102,6 +102,8 @@ class ProblemWithSubtasks : public BaseProblem { int A, B; int K; + int result; + void Config() { setSlug("problem"); } @@ -111,6 +113,10 @@ class ProblemWithSubtasks : public BaseProblem { applyLastSegment(), (applyLineSegment("K"), K); } + void OutputFormat() { + applyLastSegment(), (applyLineSegment("result"), result); + } + void Subtask1() { addConstraint([this] { return 1 <= A && A <= 1000; }, "1 <= A && A <= 1000"); addConstraint([this] { return 1 <= B && B <= 1000; }, "1 <= B && B <= 1000"); @@ -162,6 +168,8 @@ class ProblemWithoutSubtasks : public BaseProblem { int A, B; int K; + int result; + void Config() { setSlug("problem"); } @@ -171,6 +179,10 @@ class ProblemWithoutSubtasks : public BaseProblem { applyLastSegment(), (applyLineSegment("K"), K); } + void OutputFormat() { + applyLastSegment(), (applyLineSegment("result"), result); + } + void Constraints() { addConstraint([this] { return 1 <= A && A <= 1000; }, "1 <= A && A <= 1000"); addConstraint([this] { return 1 <= B && B <= 1000; }, "1 <= B && B <= 1000");