├── .classpath
├── .gitignore
├── .project
├── BUILD
├── README
├── deploy
├── moj_instructions.txt
├── template.cpp
└── template.java
├── deps
├── CodeProcessor.jar
├── ContestApplet.jar
└── FileEdit.jar
├── package.sh
├── src
└── moj
│ ├── CPPHarnessGenerator.java
│ ├── Common.java
│ ├── ConfigurationDialog.java
│ ├── ConstantFormatting.java
│ ├── HarnessGenerator.java
│ ├── JavaHarnessGenerator.java
│ ├── LanguageAutoDetection.java
│ ├── Preferences.java
│ └── moj.java
└── test
└── moj
├── CPPParameterTest.java
├── JavaParameterTest.java
└── mocks
├── DataTypeFactoryMock.java
├── PreferencesMock.java
└── ProblemComponentModelMock.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .settings
2 | bin
3 | deploy/moj*.zip
4 | deploy/moj.jar
5 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | moj
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/BUILD:
--------------------------------------------------------------------------------
1 | The easiest way to build moj is to import the project into Eclipse
2 | (.classpath and .project files are provided). The script package.sh
3 | creates:
4 | - deploy/moj.jar (load this in the Arena)
5 | - deploy/moj.zip (suitable for distribution)
6 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Hello!
2 |
3 | moj is a plug-in for the TopCoder arena that helps you compete
4 | locally. When you open a problem, moj pulls the example test cases
5 | from the problem statement and adds test code to your source
6 | file. When you run your solution locally, it will be run on the
7 | examples. The test code will be stripped when you submit your solution
8 | in the Arena.
9 |
10 | moj supports C++ and Java.
11 |
12 | moj extends the functionality of TZTester. It requires CodeProcessor
13 | (v2.0 recommended) and FileEdit, both of which are bundled with
14 | moj. Improvements over TZTester are:
15 |
16 | 1. Output is prettier. Numbers are no longer surrounded by quotes. A
17 | small summary is output after the results for individual cases. If
18 | this summary does not appear, your program crashed while solving
19 | one of the cases.
20 |
21 | 2a. Doubles and arrays of doubles are compared correctly (with an
22 | absolute and relative tolerance of 1e-9).
23 |
24 | 2b. If the result is a double or array of doubles and your output is
25 | not exact, moj will display the relative error of your output.
26 | For example:
27 |
28 | Test Case #0... PASSED (relative error 5.44969e-014)
29 | Test Case #1... PASSED (relative error 5.68462e-012)
30 | Test Case #2... PASSED (relative error 4.05691e-008)
31 | Test Case #3... PASSED
32 | All 4 tests passed!
33 |
34 | The purpose of this feature is to alert you when your solution may
35 | be insufficiently precise. Test case 2 passes because the absolute
36 | error is small enough. However, it is a good indicator that your
37 | solution is not precise enough for systests. Indeed, modifying the
38 | code to use 80-bit floating-point variables changes the report to:
39 |
40 | Test Case #0... PASSED (relative error 2.88899e-015)
41 | Test Case #1... PASSED (relative error 3.82932e-015)
42 | Test Case #2... PASSED (relative error 4.63219e-012)
43 | Test Case #3... PASSED
44 | All 4 tests passed!
45 |
46 | 3. Restructured test harness. Adding custom cases is much easier.
47 | Placeholders are in place at the bottom of the test code, just
48 | scroll down and uncomment one of them. You may also add additional
49 | cases.
50 |
51 | 4. Java support. Through a hack (language auto-detection from source
52 | code, based on heuristics) you may switch between Java and C++
53 | after opening a problem.
54 |
55 | 5. Timing per test case.
56 |
57 | 6. Visual C++ support.
58 |
59 |
60 | Setup:
61 |
62 | 1. Unzip the three .jar files in the moj package to a folder of your
63 | liking.
64 |
65 | 2. Launch the Arena. Open the "Options" menu and select "Editors".
66 |
67 | 3. Add CodeProcessor, with entry point codeprocessor.EntryPoint. Add
68 | codeprocessor.jar, moj.jar and fileedit.jar to the classpath.
69 |
70 | 4. In the "Editor preferences" window, select CodeProcessor then
71 | "Configure". Under "Editor EntryPoint" write fileedit.EntryPoint.
72 | Configure FileEdit.
73 |
74 | The most important part is to update the code template(s). Under
75 | the "Code Template" tab, select your language and paste in the code
76 | template. Included in the moj package are minimal code templates
77 | for C++ and Java.
78 |
79 | On the "General" tab I recommend turning off "Backup existing file
80 | then overwrite".
81 |
82 | 5. In the CodeProcessor configuration window, write moj.moj as the
83 | processor class and verify. If all is good, you'll get 4 "found"
84 | reports. Configure moj to your liking.
85 |
86 |
87 | For bug reports, suggestions and questions, please use the member
88 | contact feature at the TopCoder website or the forum thread. My handle
89 | is lovro.
90 |
--------------------------------------------------------------------------------
/deploy/moj_instructions.txt:
--------------------------------------------------------------------------------
1 | moj 4.18
2 | ========
3 |
4 | Hello!
5 |
6 | moj is a plug-in for the TopCoder arena that helps you compete
7 | locally. When you open a problem, moj pulls the example test cases
8 | from the problem statement and adds test code to your source
9 | file. When you run your solution locally, it will be run on the
10 | examples. The test code will be stripped when you submit your solution
11 | in the Arena.
12 |
13 | moj supports C++ and Java.
14 |
15 | moj extends the functionality of TZTester. It requires CodeProcessor
16 | (v2.0 recommended) and FileEdit, both of which are bundled with
17 | moj. Improvements over TZTester are:
18 |
19 | 1. Output is prettier. Numbers are no longer surrounded by quotes. A
20 | small summary is output after the results for individual cases. If
21 | this summary does not appear, your program crashed while solving
22 | one of the cases.
23 |
24 | 2a. Doubles and arrays of doubles are compared correctly (with an
25 | absolute and relative tolerance of 1e-9).
26 |
27 | 2b. If the result is a double or array of doubles and your output is
28 | not exact, moj will display the relative error of your output.
29 | For example:
30 |
31 | Test Case #0... PASSED (relative error 5.44969e-014)
32 | Test Case #1... PASSED (relative error 5.68462e-012)
33 | Test Case #2... PASSED (relative error 4.05691e-008)
34 | Test Case #3... PASSED
35 | All 4 tests passed!
36 |
37 | The purpose of this feature is to alert you when your solution may
38 | be insufficiently precise. Test case 2 passes because the absolute
39 | error is small enough. However, it is a good indicator that your
40 | solution is not precise enough for systests. Indeed, modifying the
41 | code to use 80-bit floating-point variables changes the report to:
42 |
43 | Test Case #0... PASSED (relative error 2.88899e-015)
44 | Test Case #1... PASSED (relative error 3.82932e-015)
45 | Test Case #2... PASSED (relative error 4.63219e-012)
46 | Test Case #3... PASSED
47 | All 4 tests passed!
48 |
49 | 3. Restructured test harness. Adding custom cases is much easier.
50 | Placeholders are in place at the bottom of the test code, just
51 | scroll down and uncomment one of them. You may also add additional
52 | cases.
53 |
54 | 4. Java support. Through a hack (language auto-detection from source
55 | code, based on heuristics) you may switch between Java and C++
56 | after opening a problem.
57 |
58 | 5. Timing per test case.
59 |
60 | 6. Visual C++ support.
61 |
62 |
63 | Setup:
64 |
65 | 1. Unzip the three .jar files in the moj package to a folder of your
66 | liking.
67 |
68 | 2. Launch the Arena. Open the "Options" menu and select "Editors".
69 |
70 | 3. Add CodeProcessor, with entry point codeprocessor.EntryPoint. Add
71 | codeprocessor.jar, moj.jar and fileedit.jar to the classpath.
72 |
73 | 4. In the "Editor preferences" window, select CodeProcessor then
74 | "Configure". Under "Editor EntryPoint" write fileedit.EntryPoint.
75 | Configure FileEdit.
76 |
77 | The most important part is to update the code template(s). Under
78 | the "Code Template" tab, select your language and paste in the code
79 | template. Included in the moj package are minimal code templates
80 | for C++ and Java.
81 |
82 | On the "General" tab I recommend turning off "Backup existing file
83 | then overwrite".
84 |
85 | 5. In the CodeProcessor configuration window, write moj.moj as the
86 | processor class and verify. If all is good, you'll get 4 "found"
87 | reports. Configure moj to your liking.
88 |
89 |
90 | For bug reports, suggestions and questions, please use the member
91 | contact feature at the TopCoder website or the forum thread. My handle
92 | is lovro.
93 |
--------------------------------------------------------------------------------
/deploy/template.cpp:
--------------------------------------------------------------------------------
1 | // Paste me into the FileEdit configuration dialog
2 |
3 | #include
4 | #include
5 | using namespace std;
6 |
7 | class $CLASSNAME$ {
8 | public:
9 | $RC$ $METHODNAME$( $METHODPARMS$ ) {
10 |
11 | }
12 | };
13 |
14 | $BEGINCUT$
15 | $TESTCODE$
16 |
17 | $DEFAULTMAIN$
18 | $ENDCUT$
19 |
--------------------------------------------------------------------------------
/deploy/template.java:
--------------------------------------------------------------------------------
1 | // Paste me into the FileEdit configuration dialog
2 |
3 | public class $CLASSNAME$ {
4 | public $RC$ $METHODNAME$($METHODPARMS$) {
5 |
6 | }
7 |
8 |
9 | $BEGINCUT$
10 | $DEFAULTMAIN$
11 | $ENDCUT$
12 | }
13 |
14 | $BEGINCUT$
15 | $TESTCODE$
16 | $ENDCUT$
17 |
--------------------------------------------------------------------------------
/deps/CodeProcessor.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lovrop/topcoder-moj/793a56d8e21d821000f9293bc3e0dc8f7d940895/deps/CodeProcessor.jar
--------------------------------------------------------------------------------
/deps/ContestApplet.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lovrop/topcoder-moj/793a56d8e21d821000f9293bc3e0dc8f7d940895/deps/ContestApplet.jar
--------------------------------------------------------------------------------
/deps/FileEdit.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lovrop/topcoder-moj/793a56d8e21d821000f9293bc3e0dc8f7d940895/deps/FileEdit.jar
--------------------------------------------------------------------------------
/package.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cd $(dirname $0)
4 |
5 | VERSION=$(egrep -o 'Powered by moj [0-9][0-9a-zA-Z.]+' src/moj/moj.java | egrep -o '[0-9][0-9a-zA-Z.]+')
6 | echo "Version is $VERSION."
7 |
8 | echo "Creating jar..."
9 | rm -f deploy/moj.jar
10 | cd bin
11 | jar cf ../deploy/moj.jar moj/*.class
12 | cd ..
13 |
14 | rm -f deploy/moj_instructions.txt
15 | echo "moj $VERSION" >> deploy/moj_instructions.txt
16 | echo "moj $VERSION" | sed -e 's/./=/g' >> deploy/moj_instructions.txt
17 | echo >> deploy/moj_instructions.txt
18 | cat README >> deploy/moj_instructions.txt
19 |
20 | echo "Creating zip..."
21 | target=deploy/moj_$VERSION.zip
22 | rm -f $target
23 | zip -j $target deploy/moj.jar deps/CodeProcessor.jar deps/FileEdit.jar deploy/moj_instructions.txt deploy/template.cpp deploy/template.java
24 |
25 | echo "Done."
26 |
--------------------------------------------------------------------------------
/src/moj/CPPHarnessGenerator.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Set;
5 | import java.util.TreeSet;
6 | import java.util.regex.Pattern;
7 |
8 | import com.topcoder.client.contestant.ProblemComponentModel;
9 | import com.topcoder.shared.language.Language;
10 | import com.topcoder.shared.problem.*;
11 |
12 | public class CPPHarnessGenerator implements HarnessGenerator {
13 | final ProblemComponentModel m_problem;
14 | final Language m_lang;
15 |
16 | final Preferences m_pref;
17 | final String m_targetCompiler;
18 |
19 | static class TestCodeGenerationState {
20 | public Set headers = new TreeSet();
21 | public ArrayList lines = new ArrayList();
22 |
23 | public void add(String line) {
24 | lines.add(line);
25 | }
26 |
27 | public void addHeader(String header) {
28 | headers.add(header);
29 | }
30 | }
31 |
32 | public CPPHarnessGenerator(ProblemComponentModel problem, Language lang, Preferences pref) {
33 | m_problem = problem;
34 | m_lang = lang;
35 | m_pref = pref;
36 | m_targetCompiler = pref.getTargetCompiler();
37 | }
38 |
39 | public String generateDefaultMain() {
40 | return
41 | "#include \n" +
42 | "int main(int argc, char *argv[]) {\n" +
43 | "\tif (argc == 1) {\n" +
44 | "\t\tmoj_harness::run_test();\n" +
45 | "\t} else {\n" +
46 | "\t\tfor (int i=1; i= 100) break;");
84 | code.add(" continue;");
85 | code.add(" }");
86 | code.add(" correct += x;");
87 | code.add(" ++total;");
88 | code.add(" }");
89 | code.add(" ");
90 | code.add(" if (total == 0) {");
91 | code.add(" std::cerr << \"No test cases run.\" << std::endl;");
92 | code.add(" } else if (correct < total) {");
93 | code.add(" std::cerr << \"Some cases FAILED (passed \" << correct << \" of \" << total << \").\" << std::endl;");
94 | code.add(" } else {");
95 | code.add(" std::cerr << \"All \" << total << \" tests passed!\" << std::endl;");
96 | code.add(" }");
97 | code.add(" }");
98 | code.add(" ");
99 | }
100 |
101 | void generateOutputComparison(TestCodeGenerationState code) {
102 | DataType returnType = m_problem.getReturnType();
103 | if (returnType.getBaseName().equals("double")) {
104 | code.addHeader("algorithm"); // min, max
105 | code.addHeader("cmath"); // isinf, isnan, fabs
106 | String isinf, isnan;
107 | if (m_targetCompiler.equals(Preferences.TARGETCOMPILER_VC)) {
108 | isinf = "!_finite";
109 | isnan = "_isnan";
110 | } else if (m_targetCompiler.equals(Preferences.TARGETCOMPILER_GCC11)) {
111 | isinf = "std::isinf";
112 | isnan = "std::isnan";
113 | } else {
114 | isinf = "isinf";
115 | isnan = "isnan";
116 | }
117 |
118 | code.add(" static const double MAX_DOUBLE_ERROR = 1e-9;");
119 | code.add(" static bool topcoder_fequ(double expected, double result) {");
120 | code.add(" if (" + isnan + "(expected)) {");
121 | code.add(" return " + isnan + "(result);");
122 | code.add(" } else if (" + isinf + "(expected)) {");
123 | code.add(" if (expected > 0) {");
124 | code.add(" return result > 0 && " + isinf + "(result);");
125 | code.add(" } else {");
126 | code.add(" return result < 0 && " + isinf + "(result);");
127 | code.add(" }");
128 | code.add(" } else if (" + isnan + "(result) || " + isinf + "(result)) {");
129 | code.add(" return false;");
130 | code.add(" } else if (std::fabs(result - expected) < MAX_DOUBLE_ERROR) {");
131 | code.add(" return true;");
132 | code.add(" } else {");
133 | code.add(" double mmin = std::min(expected * (1.0 - MAX_DOUBLE_ERROR), expected * (1.0 + MAX_DOUBLE_ERROR));");
134 | code.add(" double mmax = std::max(expected * (1.0 - MAX_DOUBLE_ERROR), expected * (1.0 + MAX_DOUBLE_ERROR));");
135 | code.add(" return result > mmin && result < mmax;");
136 | code.add(" }");
137 | code.add(" }");
138 | code.add(" double moj_relative_error(double expected, double result) {");
139 | code.add(" if (" + isnan + "(expected) || " + isinf + "(expected) || " + isnan + "(result) || " + isinf + "(result) || expected == 0) {");
140 | code.add(" return 0;");
141 | code.add(" }");
142 | code.add(" return std::fabs(result-expected) / std::fabs(expected);");
143 | code.add(" }");
144 | if (returnType.getDimension() > 0) {
145 | code.addHeader("vector");
146 | code.add(" static bool topcoder_fequ(const vector &a, const vector &b) { if (a.size() != b.size()) return false; for (size_t i=0; i &expected, const vector &result) { double ret = 0.0; for (size_t i=0; i 0) {
156 | code.addHeader("vector");
157 | code.add(" template std::ostream& operator<<(std::ostream &os, const vector &v) { os << \"{\"; for (typename vector::const_iterator vi=v.begin(); vi!=v.end(); ++vi) { if (vi != v.begin()) os << \",\"; os << \" \" << *vi; } os << \" }\"; return os; }");
158 | if (returnType.getBaseName().equals("String")) {
159 | code.addHeader("string");
160 | code.add(" template<> std::ostream& operator<<(std::ostream &os, const vector &v) { os << \"{\"; for (vector::const_iterator vi=v.begin(); vi!=v.end(); ++vi) { if (vi != v.begin()) os << \",\"; os << \" \\\"\" << *vi << \"\\\"\"; } os << \" }\"; return os; }");
161 | }
162 | code.add("");
163 | }
164 | }
165 |
166 | void generateVerifyCase(TestCodeGenerationState code) {
167 | DataType returnType = m_problem.getReturnType();
168 | String typeName = returnType.getDescriptor(m_lang);
169 |
170 | code.addHeader("cstdio");
171 | code.addHeader("ctime");
172 | code.addHeader("iostream");
173 | code.addHeader("string");
174 | code.addHeader("vector");
175 | code.add(" int verify_case(int casenum, const " + typeName + " &expected, const " + typeName + " &received, std::clock_t elapsed) { ");
176 | code.add(" std::cerr << \"Example \" << casenum << \"... \"; ");
177 | code.add(" ");
178 | code.add(" string verdict;");
179 | code.add(" vector info;");
180 | code.add(" char buf[100];");
181 | code.add(" ");
182 | code.add(" if (elapsed > CLOCKS_PER_SEC / 200) {");
183 | code.add(" std::sprintf(buf, \"time %.2fs\", elapsed * (1.0/CLOCKS_PER_SEC));");
184 | code.add(" info.push_back(buf);");
185 | code.add(" }");
186 | code.add(" ");
187 |
188 | // Print "PASSED" or "FAILED" based on the result
189 | if (returnType.getBaseName().equals("double")) {
190 | code.add(" if (topcoder_fequ(expected, received)) {");
191 | code.add(" verdict = \"PASSED\";");
192 | code.add(" double rerr = moj_relative_error(expected, received); ");
193 | code.add(" if (rerr > 0) {");
194 | code.add(" std::sprintf(buf, \"relative error %.3e\", rerr);");
195 | code.add(" info.push_back(buf);");
196 | code.add(" }");
197 | } else {
198 | code.add(" if (expected == received) {");
199 | code.add(" verdict = \"PASSED\";");
200 | }
201 | code.add(" } else {");
202 | code.add(" verdict = \"FAILED\";");
203 | code.add(" }");
204 | code.add(" ");
205 | code.add(" std::cerr << verdict;");
206 | code.add(" if (!info.empty()) {");
207 | code.add(" std::cerr << \" (\";");
208 | code.add(" for (size_t i=0; i 0) std::cerr << \", \";");
210 | code.add(" std::cerr << info[i];");
211 | code.add(" }");
212 | code.add(" std::cerr << \")\";");
213 | code.add(" }");
214 | code.add(" std::cerr << std::endl;");
215 | code.add(" ");
216 |
217 | code.add(" if (verdict == \"FAILED\") {");
218 | if (returnType.getBaseName().equals("String") && returnType.getDimension() == 0) {
219 | code.add(" std::cerr << \" Expected: \\\"\" << expected << \"\\\"\" << std::endl; ");
220 | code.add(" std::cerr << \" Received: \\\"\" << received << \"\\\"\" << std::endl; ");
221 | } else {
222 | code.add(" std::cerr << \" Expected: \" << expected << std::endl; ");
223 | code.add(" std::cerr << \" Received: \" << received << std::endl; ");
224 | }
225 | code.add(" }");
226 | code.add(" ");
227 | code.add(" return verdict == \"PASSED\";");
228 | code.add(" }");
229 | code.add("");
230 | }
231 |
232 | static boolean representsEmptyArray(String s) {
233 | return s.replaceAll("\\s+", "").equals("{}");
234 | }
235 |
236 | void generateParameter(TestCodeGenerationState code, DataType paramType, String name, String contents, boolean isPlaceholder) {
237 | if (isPlaceholder) {
238 | contents = "";
239 | }
240 |
241 | String baseName = paramType.getBaseName();
242 | boolean isLong = baseName.equals("long");
243 | String typeName = "";
244 | if (paramType.getDimension() == 0) {
245 | // Scalar
246 | typeName = paramType.getDescriptor(m_lang) + " " + name;
247 | if (isLong) {
248 | contents = ConstantFormatting.formatLongForCPP(contents);
249 | }
250 | } else {
251 | typeName = (isLong ? "long long" : baseName.toLowerCase()) + " " + name + "[]";
252 |
253 | if (!isPlaceholder) {
254 | if (m_targetCompiler.equals(Preferences.TARGETCOMPILER_VC) &&
255 | representsEmptyArray(contents)) {
256 | typeName = "// " + typeName;
257 | contents = "empty, commented out for VC++";
258 | } else if (isLong) {
259 | // Vector of longs, add LL to constants
260 | contents = ConstantFormatting.formatLongArrayForCPP(contents);
261 | }
262 | }
263 | }
264 |
265 | while (typeName.length() < 25) {
266 | typeName = typeName + " ";
267 | }
268 |
269 | if (!baseName.equals("String")) {
270 | // Compress spaces in non-strings
271 | contents = contents.replaceAll("\\s+", " ");
272 | }
273 |
274 | code.add(" " + typeName + " = " + contents + ";");
275 | }
276 |
277 | String vectorize(DataType type, String name, String contents, boolean isPlaceholder) {
278 | if (type.getDimension() == 0) {
279 | return name;
280 | } else {
281 | if (!isPlaceholder &&
282 | m_targetCompiler.equals(Preferences.TARGETCOMPILER_VC) &&
283 | representsEmptyArray(contents)) {
284 | // Visual C++ empty array hack
285 | return type.getDescriptor(m_lang) + "()";
286 | }
287 |
288 | return type.getDescriptor(m_lang) + "(" + name + ", " + name + " + (sizeof " + name + " / sizeof " + name + "[0]))";
289 | }
290 | }
291 |
292 | void generateTestCase(TestCodeGenerationState code, int index, TestCase testCase, boolean isPlaceholder) {
293 | DataType[] paramTypes = m_problem.getParamTypes();
294 | String[] paramNames = m_problem.getParamNames();
295 | DataType returnType = m_problem.getReturnType();
296 |
297 | String[] inputs = testCase.getInput();
298 | String output = testCase.getOutput();
299 |
300 | /*
301 | * Generate code for setting up individual test cases
302 | * and calling the method with these parameters.
303 | */
304 | // Generate each input variable separately
305 | for (int i = 0; i < inputs.length; ++i) {
306 | generateParameter(code, paramTypes[i], paramNames[i], inputs[i], isPlaceholder);
307 | }
308 |
309 | // Generate the output variable as the last variable
310 | generateParameter(code, returnType, "expected__", output, isPlaceholder);
311 |
312 | code.add("");
313 | code.add(" std::clock_t start__ = std::clock();");
314 |
315 | // Generate the function call
316 | StringBuffer call = new StringBuffer();
317 | call.append(returnType.getDescriptor(m_lang) + " received__");
318 | while (call.length() < 25) {
319 | call.append(' ');
320 | }
321 | call.append(" = " + m_problem.getClassName() + "()." + m_problem.getMethodName() + "(");
322 | for (int i = 0; i < inputs.length; ++i) {
323 | call.append(vectorize(paramTypes[i], paramNames[i], inputs[i], isPlaceholder));
324 | if (i < inputs.length-1) {
325 | call.append(", ");
326 | }
327 | }
328 | call.append(");");
329 | code.add(" " + call);
330 |
331 | code.add(" return verify_case(casenum__, " + vectorize(returnType, "expected__", output, isPlaceholder) + ", received__, clock()-start__);");
332 | }
333 |
334 | void generateRunTestCase(TestCodeGenerationState code) {
335 | TestCase[] testCases = m_problem.getTestCases();
336 |
337 | code.add(" int run_test_case(int casenum__) {");
338 | code.add(" switch (casenum__) {");
339 | // Generate the individual test cases
340 | int totalCases = testCases.length + m_pref.getNumPlaceholders();
341 | for (int i = 0; i < totalCases; ++i) {
342 | if (i == testCases.length) {
343 | code.add("");
344 | code.add(" // custom cases");
345 | code.add("");
346 | }
347 | code.add((i >= testCases.length ? "/*" : "") + " case " + i + ": {");
348 | generateTestCase(code, i, testCases[i < testCases.length ? i : 0], i >= testCases.length);
349 | code.add(" }" + (i >= testCases.length ? "*/" : ""));
350 | }
351 |
352 | // next
353 | code.add(" default:");
354 | code.add(" return -1;");
355 | code.add(" }");
356 | code.add(" }");
357 | }
358 |
359 | public String generateTestCode() {
360 | TestCodeGenerationState code = new TestCodeGenerationState();
361 |
362 | generateNamespaceStart(code);
363 | generateRunTest(code);
364 |
365 | generateOutputComparison(code);
366 | generateFormatResult(code);
367 | generateVerifyCase(code);
368 | generateRunTestCase(code);
369 | code.add("}");
370 |
371 | StringBuilder sb = new StringBuilder();
372 | for (String header : code.headers) {
373 | sb.append("#include <");
374 | sb.append(header);
375 | sb.append(">\n");
376 | }
377 | for (String s : code.lines) {
378 | sb.append(s);
379 | sb.append('\n');
380 | }
381 | String ret = sb.toString();
382 | ret = Pattern.compile("^ ", Pattern.MULTILINE).matcher(ret).replaceAll("\t\t\t\t\t");
383 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t\t\t\t");
384 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t\t\t");
385 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t\t");
386 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t");
387 | return ret;
388 | }
389 | }
390 |
--------------------------------------------------------------------------------
/src/moj/Common.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | import java.awt.*;
4 | import java.awt.event.FocusAdapter;
5 | import java.awt.event.FocusEvent;
6 |
7 | import javax.swing.*;
8 | import javax.swing.border.Border;
9 | import javax.swing.text.JTextComponent;
10 |
11 | class Common {
12 | public static final Color FG_COLOR = Color.white;
13 | public static final Color BG_COLOR = Color.black;
14 | public static final Color WPB_COLOR = Color.decode("0x333333");
15 | public static final Color TF_COLOR = Color.white;
16 | public static final Color TB_COLOR = Color.black;
17 | public static final Color HF_COLOR = Color.white;
18 | public static final Color HB_COLOR = Color.decode("0x003300");
19 | public final static Font DEFAULTFONT = new Font("SansSerif", Font.PLAIN, 12);
20 | public final static Box createHorizontalBox(Component[] a) {
21 | return Common.createHorizontalBox(a, true);
22 | }
23 | public final static Box createHorizontalBox(Component[] a, boolean endGlue) {
24 | Box temp = Box.createHorizontalBox();
25 | if (a.length == 0)
26 | return temp;
27 | // Add all but the last one
28 | for (int x = 0; x < a.length - 1; x++) {
29 | temp.add(a[x]);
30 | temp.add(Box.createHorizontalStrut(5));
31 | }
32 | // Add the last one
33 | temp.add(a[a.length - 1]);
34 | if (endGlue)
35 | temp.add(Box.createHorizontalGlue());
36 | return temp;
37 | }
38 |
39 | public final static JTable createJTable() {
40 | JTable table = new JTable();
41 | table.setBackground(Common.TB_COLOR);
42 | table.setForeground(Common.TF_COLOR);
43 | table.setSelectionBackground(Common.HB_COLOR);
44 | table.setSelectionForeground(Common.HF_COLOR);
45 | table.setShowGrid(false);
46 | return table;
47 | }
48 | public final static JLabel createJLabel(String text) {
49 | return Common.createJLabel(text, DEFAULTFONT);
50 | }
51 | public final static JLabel createJLabel(String text, Font font) {
52 | return Common.createJLabel(text, null, SwingConstants.LEFT, font);
53 | }
54 | public final static JLabel createJLabel(String text, Dimension size) {
55 | return Common.createJLabel(text, size, SwingConstants.LEFT, DEFAULTFONT);
56 | }
57 | public final static JLabel createJLabel(
58 | String text,
59 | Dimension size,
60 | int alignment) {
61 | return Common.createJLabel(text, size, alignment, DEFAULTFONT);
62 | }
63 | public final static JLabel createJLabel(
64 | String text,
65 | Dimension size,
66 | int alignment,
67 | Font font) {
68 | JLabel temp = new JLabel(text);
69 | temp.setForeground(Common.FG_COLOR);
70 | temp.setBackground(WPB_COLOR);
71 | temp.setFont(font);
72 | temp.setHorizontalAlignment(alignment);
73 | if (size != null) {
74 | temp.setMinimumSize(size);
75 | temp.setPreferredSize(size);
76 | temp.setMaximumSize(size);
77 | }
78 | return temp;
79 | }
80 | public final static JTextField createJTextField(int size, Dimension max) {
81 | return Common.createJTextField(size, max, DEFAULTFONT);
82 | }
83 | public final static JTextField createJTextField(
84 | int size,
85 | Dimension max,
86 | Font font) {
87 | JTextField temp = new JTextField(size);
88 | temp.setForeground(FG_COLOR);
89 | temp.setBackground(BG_COLOR);
90 | temp.setCaretColor(FG_COLOR);
91 | temp.setFont(font);
92 | temp.setBorder(BorderFactory.createLineBorder(FG_COLOR, 1));
93 | temp.setMaximumSize(max);
94 | temp.addFocusListener(new Common.SelectAll(temp));
95 | return temp;
96 | }
97 | public final static JScrollPane createJScrollPane(Component a) {
98 | return Common.createJScrollPane(a, null, null);
99 | }
100 |
101 | public final static JScrollPane createJScrollPane(Component a, Dimension size) {
102 | return Common.createJScrollPane(a, size, null);
103 | }
104 |
105 | public final static JScrollPane createJScrollPane(Component a, Dimension size, Border border) {
106 |
107 | JScrollPane temp = new JScrollPane(a);
108 | temp.setBackground(WPB_COLOR);
109 | temp.getViewport().setBackground(WPB_COLOR);
110 | if (size!=null) temp.getViewport().setPreferredSize(size);
111 | if (border!=null) temp.setBorder(border);
112 |
113 | return temp;
114 | }
115 |
116 | public final static void setDefaultAttributes(Container panel) {
117 | Common.setDefaultAttributes(panel, new BorderLayout());
118 | }
119 | public final static void setDefaultAttributes(
120 | Container panel,
121 | LayoutManager layout) {
122 | panel.setLayout(layout);
123 | panel.setBackground(WPB_COLOR);
124 | }
125 | private static class SelectAll extends FocusAdapter {
126 | JTextComponent parent;
127 | public SelectAll(JTextComponent parent) {
128 | this.parent = parent;
129 | }
130 | public void focusGained(FocusEvent e) {
131 | parent.selectAll();
132 | }
133 | }
134 | public final static JButton createJButton(String text) {
135 | return Common.createJButton(text, null, DEFAULTFONT);
136 | }
137 | public final static JButton createJButton(String text, Dimension size) {
138 | return Common.createJButton(text, size, DEFAULTFONT);
139 | }
140 | public final static JButton createJButton(String text, Font font) {
141 | return Common.createJButton(text, null, font);
142 | }
143 | public final static JButton createJButton(
144 | String text,
145 | Dimension size,
146 | Font font) {
147 | JButton temp = new JButton(text);
148 | temp.setFont(font);
149 | if (size != null) {
150 | temp.setMinimumSize(size);
151 | temp.setPreferredSize(size);
152 | temp.setMaximumSize(size);
153 | }
154 | return temp;
155 | }
156 | public static void showMessage(String title, String msg, Component comp) {
157 | JOptionPane.showMessageDialog(
158 | comp,
159 | msg,
160 | title,
161 | JOptionPane.INFORMATION_MESSAGE);
162 | }
163 | public static void showError(String title, String msg, Component comp) {
164 | JOptionPane.showMessageDialog(
165 | comp,
166 | msg,
167 | title,
168 | JOptionPane.INFORMATION_MESSAGE);
169 | }
170 | public static boolean confirm(String title, String msg, Component comp) {
171 | int choice =
172 | JOptionPane.showConfirmDialog(
173 | comp,
174 | msg,
175 | title,
176 | JOptionPane.YES_NO_OPTION,
177 | JOptionPane.WARNING_MESSAGE);
178 | if (choice == JOptionPane.YES_OPTION) {
179 | return (true);
180 | }
181 | return (false);
182 | }
183 | public static String input(String title, String msg, Component comp) {
184 | String value =
185 | JOptionPane.showInputDialog(comp, msg, title, JOptionPane.QUESTION_MESSAGE);
186 | return (value);
187 | }
188 | }
189 | /* @(#)Common.java */
--------------------------------------------------------------------------------
/src/moj/ConfigurationDialog.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | import java.awt.*;
4 | import java.awt.event.*;
5 | import java.io.IOException;
6 | import java.util.Enumeration;
7 |
8 | import javax.swing.*;
9 |
10 | class ConfigurationDialog extends JDialog implements ActionListener {
11 |
12 | private static final long serialVersionUID = 6205878572134421087L;
13 |
14 | private Preferences pref;
15 |
16 | private ButtonGroup compilerButtonGroup = new ButtonGroup();
17 | private JRadioButton gcc11RadioButton = new JRadioButton("GCC -std=c++11");
18 | private JRadioButton gcc98RadioButton = new JRadioButton("Older GCC");
19 | private JRadioButton vcRadioButton = new JRadioButton("Visual C++");
20 |
21 | private JTextField placeholdersTextField = new JTextField();
22 |
23 | private JCheckBox switchCheckBox = new JCheckBox();
24 | private JCheckBox javaSupportCheckBox = new JCheckBox();
25 |
26 | private JButton saveButton = new JButton("Save");
27 | private JButton closeButton = new JButton("Close");
28 |
29 | private WindowHandler windowHandler = new WindowHandler();
30 |
31 | public ConfigurationDialog(Preferences pref) {
32 |
33 | super((JFrame)null, "moj configuration", true);
34 |
35 | this.pref = pref;
36 | setSize(new Dimension(600, 400));
37 |
38 | // Configure the content pane
39 | Container contentPane = getContentPane();
40 | contentPane.setLayout(new GridBagLayout());
41 | contentPane.setForeground(Common.FG_COLOR);
42 | contentPane.setBackground(Common.WPB_COLOR);
43 |
44 | // Target compiler
45 | JLabel compilerLabel = new JLabel("Target compiler:");
46 | compilerLabel.setForeground(Common.FG_COLOR);
47 | compilerLabel.setBackground(Common.WPB_COLOR);
48 | compilerLabel.setToolTipText("Select the compiler you will be using. moj needs to make adjustments to the testing code for it to compile under Visual C++.");
49 |
50 | compilerButtonGroup.add(gcc11RadioButton);
51 | compilerButtonGroup.add(gcc98RadioButton);
52 | compilerButtonGroup.add(vcRadioButton);
53 |
54 | String current_compiler = pref.getTargetCompiler();
55 | if (current_compiler.equals(Preferences.TARGETCOMPILER_GCC11)) {
56 | gcc11RadioButton.setSelected(true);
57 | } else if (current_compiler.equals(Preferences.TARGETCOMPILER_GCC98)) {
58 | gcc98RadioButton.setSelected(true);
59 | } else {
60 | vcRadioButton.setSelected(true);
61 | }
62 |
63 | for (Enumeration eRadio=compilerButtonGroup.getElements(); eRadio.hasMoreElements(); ) {
64 | //Iterating over the Radio Buttons
65 | JRadioButton button = (JRadioButton)eRadio.nextElement();
66 | button.setForeground(Common.FG_COLOR);
67 | button.setBackground(Common.WPB_COLOR);
68 | button.setText("" + button.getText() + "");
69 | }
70 |
71 | // Test case placeholders
72 | JLabel placeholdersLabel = new JLabel("Test case placeholders:");
73 | placeholdersLabel.setForeground(Common.FG_COLOR);
74 | placeholdersLabel.setBackground(Common.WPB_COLOR);
75 | placeholdersLabel.setToolTipText("Set the number of empty test case placeholders to be generated. You can use these to enter your own test cases while solving the problem.");
76 | placeholdersTextField.setText("" + pref.getNumPlaceholders());
77 |
78 | // Language-switch workaround checkbox
79 | switchCheckBox.setText("Enable workaround for FileEdit language switching issue");
80 | switchCheckBox.setForeground(Common.FG_COLOR);
81 | switchCheckBox.setBackground(Common.WPB_COLOR);
82 | switchCheckBox.setToolTipText("If checked, moj will try to allow you to switch between languages mid-contest.");
83 | switchCheckBox.setSelected(pref.getLanguageSwitchWorkaround());
84 |
85 | // Java support checkbox
86 | javaSupportCheckBox.setText("Enable Java test code generation");
87 | javaSupportCheckBox.setForeground(Common.FG_COLOR);
88 | javaSupportCheckBox.setBackground(Common.WPB_COLOR);
89 | javaSupportCheckBox.setToolTipText("Uncheck if you want to use a different plug-in to generate Java test code.");
90 | javaSupportCheckBox.setSelected(pref.getEnableJavaSupport());
91 |
92 | contentPane.add(compilerLabel, new GridBagConstraints(0,0,1,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
93 | contentPane.add(gcc11RadioButton, new GridBagConstraints(1,0,1,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
94 | contentPane.add(gcc98RadioButton, new GridBagConstraints(1,1,1,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
95 | contentPane.add(vcRadioButton, new GridBagConstraints(1,2,1,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
96 | contentPane.add(placeholdersLabel, new GridBagConstraints(0,3,1,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
97 | contentPane.add(placeholdersTextField, new GridBagConstraints(1,3,1,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
98 | contentPane.add(switchCheckBox, new GridBagConstraints(0,4,3,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
99 | contentPane.add(javaSupportCheckBox, new GridBagConstraints(0,5,3,1,1,1,GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10,10,4,4),0,0));
100 | contentPane.add(saveButton, new GridBagConstraints(1,6,1,1,0,0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10,10,10,10),0,0));
101 | contentPane.add(closeButton, new GridBagConstraints(2,6,1,1,0,0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10,0,10,10),0,0));
102 |
103 | // Add listeners
104 | saveButton.addActionListener(this);
105 | closeButton.addActionListener(this);
106 |
107 | // Set the close operations
108 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
109 | addWindowListener(windowHandler);
110 |
111 | this.pack();
112 | }
113 |
114 | public void actionPerformed(ActionEvent e) {
115 | Object src = e.getSource();
116 | if (src == saveButton) {
117 | save();
118 | } else if (src == closeButton) {
119 | windowHandler.windowClosing(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
120 | }
121 | }
122 |
123 | private String getSelectedCompiler() {
124 | return
125 | gcc11RadioButton.isSelected() ? Preferences.TARGETCOMPILER_GCC11 :
126 | gcc98RadioButton.isSelected() ? Preferences.TARGETCOMPILER_GCC98 :
127 | Preferences.TARGETCOMPILER_VC;
128 | }
129 |
130 | public boolean save() {
131 | // Parse
132 | int numPlaceholders;
133 | try {
134 | numPlaceholders = Integer.valueOf(placeholdersTextField.getText());
135 | } catch(NumberFormatException e) {
136 | Common.showError("Parse error", "\"" + placeholdersTextField.getText() + "\" is not a valid integer.", null);
137 | return false;
138 | }
139 |
140 | if (numPlaceholders < 0 || numPlaceholders > 50) {
141 | Common.showError("Parse error", "The number of placeholders must be between 0 and 50.", null);
142 | return false;
143 | }
144 |
145 | // Write out the preferences
146 | try {
147 | pref.setTargetCompiler(getSelectedCompiler());
148 | pref.setNumPlaceholders(numPlaceholders);
149 | pref.save();
150 | Common.showMessage("Save", "Preferences were saved successfully", null);
151 | return true;
152 | } catch (IOException e) {
153 | Common.showError("Error saving preferences", e.toString(), null);
154 | return false;
155 | }
156 |
157 | }
158 |
159 | private class WindowHandler extends WindowAdapter {
160 | public void windowClosing(WindowEvent e) {
161 |
162 | // Find out if anything has save's pending
163 | boolean savePending=false;
164 |
165 | savePending =
166 | !getSelectedCompiler().equals(pref.getTargetCompiler())
167 | || !placeholdersTextField.getText().equals("" + pref.getNumPlaceholders())
168 | || switchCheckBox.isSelected() != pref.getLanguageSwitchWorkaround()
169 | || javaSupportCheckBox.isSelected() != pref.getEnableJavaSupport();
170 |
171 | // If so...
172 | if (savePending) {
173 |
174 | // Should we save?
175 | if (Common.confirm("Save Pending", "Changes are pending. Do you want to save before closing?", null)) {
176 | // Try to save
177 | if (!save()) return;
178 | }
179 | }
180 | // Close the window
181 | dispose();
182 | }
183 | }
184 |
185 | public static void main(String[] args) {
186 | new ConfigurationDialog(null).setVisible(true);
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/moj/ConstantFormatting.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | class ConstantFormatting {
4 | public static String formatLongForCPP(String str) {
5 | return formatSingleLong(str, "LL");
6 | }
7 |
8 | public static String formatLongArrayForCPP(String str) {
9 | return formatLongArray(str, "LL");
10 | }
11 |
12 | public static String formatLongForJava(String str) {
13 | return formatSingleLong(str, "L");
14 | }
15 |
16 | public static String formatLongArrayForJava(String str) {
17 | return formatLongArray(str, "L");
18 | }
19 |
20 | private static String formatSingleLong(String str, String suffix) {
21 | if (str.trim().equals("")) {
22 | return "";
23 | }
24 | long value = Long.valueOf(str);
25 | if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
26 | str += suffix;
27 | }
28 | return str;
29 | }
30 |
31 | private static String formatLongArray(String str, String suffix) {
32 | // Vector of longs, add LL to constants
33 | String[] tokens = str.split("[^0-9-]");
34 | StringBuffer fixed = new StringBuffer();
35 | boolean first = true;
36 | for (String token : tokens) {
37 | if (token.isEmpty()) {
38 | continue;
39 | }
40 | if (!first) {
41 | fixed.append(", ");
42 | }
43 | first = false;
44 | fixed.append(formatSingleLong(token, suffix));
45 | }
46 | return "{" + fixed.toString() + "}";
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/moj/HarnessGenerator.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | public interface HarnessGenerator {
4 | String generateTestCode();
5 | String generateDefaultMain();
6 | String generateRunTest();
7 | }
8 |
--------------------------------------------------------------------------------
/src/moj/JavaHarnessGenerator.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.regex.Pattern;
7 |
8 | import com.topcoder.client.contestant.ProblemComponentModel;
9 | import com.topcoder.shared.language.Language;
10 | import com.topcoder.shared.problem.*;
11 |
12 | public class JavaHarnessGenerator implements HarnessGenerator {
13 | final ProblemComponentModel m_problem;
14 | final Language m_lang;
15 | final Preferences m_pref;
16 |
17 | public JavaHarnessGenerator(ProblemComponentModel problem, Language lang, Preferences pref) {
18 | m_problem = problem;
19 | m_lang = lang;
20 | m_pref = pref;
21 | }
22 |
23 | public String generateDefaultMain() {
24 | return
25 | "public static void main(String[] args) {\n" +
26 | "\t\tif (args.length == 0) {\n" +
27 | "\t\t\t" + m_problem.getClassName() + "Harness.run_test(-1);\n" +
28 | "\t\t} else {\n" +
29 | "\t\t\tfor (int i=0; i code) {
40 | code.add("class " + m_problem.getClassName() + "Harness {");
41 | }
42 |
43 | void generateRunTest(ArrayList code) {
44 | code.add(" public static void run_test(int casenum) {");
45 |
46 | code.add(" if (casenum != -1) {");
47 | code.add(" if (runTestCase(casenum) == -1)");
48 | code.add(" System.err.println(\"Illegal input! Test case \" + casenum + \" does not exist.\");");
49 | code.add(" return;");
50 | code.add(" }");
51 | code.add(" ");
52 | code.add(" int correct = 0, total = 0;");
53 | code.add(" for (int i=0;; ++i) {");
54 | code.add(" int x = runTestCase(i);");
55 | code.add(" if (x == -1) {");
56 | code.add(" if (i >= 100) break;");
57 | code.add(" continue;");
58 | code.add(" }");
59 | code.add(" correct += x;");
60 | code.add(" ++total;");
61 | code.add(" }");
62 | code.add(" ");
63 | code.add(" if (total == 0) {");
64 | code.add(" System.err.println(\"No test cases run.\");");
65 | code.add(" } else if (correct < total) {");
66 | code.add(" System.err.println(\"Some cases FAILED (passed \" + correct + \" of \" + total + \").\");");
67 | code.add(" } else {");
68 | code.add(" System.err.println(\"All \" + total + \" tests passed!\");");
69 | code.add(" }");
70 | code.add(" }");
71 | code.add(" ");
72 | }
73 |
74 | void generateOutputComparison(ArrayList code) {
75 | DataType returnType = m_problem.getReturnType();
76 | if (returnType.getBaseName().equals("double")) {
77 | code.add(" static final double MAX_DOUBLE_ERROR = 1E-9;");
78 | code.add(" static boolean compareOutput(double expected, double result){ if(Double.isNaN(expected)){ return Double.isNaN(result); }else if(Double.isInfinite(expected)){ if(expected > 0){ return result > 0 && Double.isInfinite(result); }else{ return result < 0 && Double.isInfinite(result); } }else if(Double.isNaN(result) || Double.isInfinite(result)){ return false; }else if(Math.abs(result - expected) < MAX_DOUBLE_ERROR){ return true; }else{ double min = Math.min(expected * (1.0 - MAX_DOUBLE_ERROR), expected * (1.0 + MAX_DOUBLE_ERROR)); double max = Math.max(expected * (1.0 - MAX_DOUBLE_ERROR), expected * (1.0 + MAX_DOUBLE_ERROR)); return result > min && result < max; } }");
79 | code.add(" static double relativeError(double expected, double result) { if (Double.isNaN(expected) || Double.isInfinite(expected) || Double.isNaN(result) || Double.isInfinite(result) || expected == 0) return 0; return Math.abs(result-expected) / Math.abs(expected); }");
80 | if (returnType.getDimension() > 0) {
81 | code.add(" static boolean compareOutput(double[] expected, double[] result) { if (expected.length != result.length) return false; for (int i=0; i 0) {
87 | code.add(" static boolean compareOutput(String[] expected, String[] result) { if (expected.length != result.length) return false; for (int i=0; i 0) {
94 | code.add(" static boolean compareOutput("+type+"[] expected, "+type+"[] result) { if (expected.length != result.length) return false; for (int i=0; i code) {
102 | DataType returnType = m_problem.getReturnType();
103 |
104 | Map typeFormatMap = new HashMap();
105 | typeFormatMap.put("int", "%d");
106 | typeFormatMap.put("float", "%.10g");
107 | typeFormatMap.put("char", "'%c'");
108 | typeFormatMap.put("byte", "%d");
109 | typeFormatMap.put("short", "%d");
110 | typeFormatMap.put("long", "%d");
111 | typeFormatMap.put("double", "%.10g");
112 | typeFormatMap.put("String", "\\\"%s\\\"");
113 | typeFormatMap.put("boolean", "%b");
114 | String formatString = typeFormatMap.get(returnType.getBaseName());
115 |
116 | code.add(" static String formatResult(" + returnType.getDescriptor(m_lang) + " res) {");
117 | if (returnType.getDimension() > 0) {
118 | code.add(" String ret = \"\";");
119 | code.add(" ret += \"{\";");
120 | code.add(" for (int i=0; i 0) ret += \",\";");
122 | code.add(" ret += String.format(\" " + formatString + "\", res[i]);");
123 | code.add(" }");
124 | code.add(" ret += \" }\";");
125 | code.add(" return ret;");
126 | } else {
127 | code.add(" return String.format(\"" + formatString + "\", res);");
128 | }
129 |
130 | code.add(" }");
131 | code.add(" ");
132 | }
133 |
134 | void generateVerifyCase(ArrayList code) {
135 | DataType returnType = m_problem.getReturnType();
136 | String typeName = returnType.getDescriptor(m_lang);
137 |
138 | code.add(" static int verifyCase(int casenum, " + typeName + " expected, " + typeName + " received) { ");
139 | code.add(" System.err.print(\"Example \" + casenum + \"... \");");
140 |
141 | // Print "PASSED" or "FAILED" based on the result
142 | if (returnType.getBaseName().equals("double")) {
143 | code.add(" if (compareOutput(expected, received)) {");
144 | code.add(" System.err.print(\"PASSED\");");
145 | code.add(" double rerr = relativeError(expected, received);");
146 | code.add(" if (rerr > 0) System.err.printf(\" (relative error %g)\", rerr);");
147 | code.add(" System.err.println();");
148 | code.add(" return 1;");
149 | } else {
150 | code.add(" if (compareOutput(expected, received)) {");
151 | code.add(" System.err.println(\"PASSED\");");
152 | code.add(" return 1;");
153 | }
154 | code.add(" } else {");
155 | code.add(" System.err.println(\"FAILED\");");
156 |
157 | code.add(" System.err.println(\" Expected: \" + formatResult(expected)); ");
158 | code.add(" System.err.println(\" Received: \" + formatResult(received)); ");
159 |
160 | code.add(" return 0;");
161 | code.add(" }");
162 | code.add(" }");
163 | code.add("");
164 | }
165 |
166 | void generateParameter(ArrayList code, DataType paramType, String name, String contents, boolean isPlaceholder) {
167 | if (isPlaceholder)
168 | contents = "";
169 |
170 | String baseName = paramType.getBaseName();
171 | boolean isLong = baseName.equals("long");
172 | String typeName = paramType.getDescriptor(m_lang) + " " + name;
173 | if (isLong) {
174 | if (paramType.getDimension() == 0) {
175 | contents = ConstantFormatting.formatLongForJava(contents);
176 | } else {
177 | contents = ConstantFormatting.formatLongArrayForJava(contents);
178 | }
179 | }
180 |
181 | while (typeName.length() < 25)
182 | typeName = typeName + " ";
183 |
184 | if (!baseName.equals("String")) {
185 | // Compress spaces in non-strings
186 | contents = contents.replaceAll("\\s+", " ");
187 | }
188 |
189 | code.add(" " + typeName + " = " + contents + ";");
190 | }
191 |
192 | void generateTestCase(ArrayList code, int index, TestCase testCase, boolean isPlaceholder) {
193 | DataType[] paramTypes = m_problem.getParamTypes();
194 | String[] paramNames = m_problem.getParamNames();
195 | DataType returnType = m_problem.getReturnType();
196 |
197 | String[] inputs = testCase.getInput();
198 | String output = testCase.getOutput();
199 |
200 | /*
201 | * Generate code for setting up individual test cases
202 | * and calling the method with these parameters.
203 | */
204 | // Generate each input variable separately
205 | for (int i = 0; i < inputs.length; ++i) {
206 | generateParameter(code, paramTypes[i], paramNames[i], inputs[i], isPlaceholder);
207 | }
208 |
209 | // Generate the output variable as the last variable
210 | generateParameter(code, returnType, "expected__", output, isPlaceholder);
211 |
212 | code.add("");
213 |
214 | StringBuffer line = new StringBuffer();
215 | line.append(" return verifyCase(casenum__, expected__, new " + m_problem.getClassName() + "()." + m_problem.getMethodName() + "(");
216 |
217 | // Generate the function call list
218 | for (int i = 0; i < inputs.length; ++i) {
219 | line.append(paramNames[i]);
220 | if (i < (inputs.length - 1))
221 | line.append(", ");
222 | }
223 |
224 | line.append("));");
225 | code.add(line.toString());
226 | }
227 |
228 | void generateRunTestCase(ArrayList code) {
229 | TestCase[] testCases = m_problem.getTestCases();
230 |
231 | code.add(" static int runTestCase(int casenum__) {");
232 | code.add(" switch(casenum__) {");
233 | // Generate the individual test cases
234 | for (int i = 0; i < testCases.length+m_pref.getNumPlaceholders(); ++i) {
235 | if (i == testCases.length) {
236 | code.add("");
237 | code.add(" // custom cases");
238 | code.add("");
239 | }
240 | code.add((i >= testCases.length ? "/*" : "") + " case " + i + ": {");
241 | generateTestCase(code, i, testCases[i < testCases.length ? i : 0], i >= testCases.length);
242 | code.add(" }" + (i >= testCases.length ? "*/" : ""));
243 | }
244 |
245 | // next
246 | code.add(" default:");
247 | code.add(" return -1;");
248 | code.add(" }");
249 | code.add(" }");
250 | }
251 |
252 | public String generateTestCode() {
253 | ArrayList code = new ArrayList();
254 |
255 | generateNamespaceStart(code);
256 | generateRunTest(code);
257 |
258 | generateOutputComparison(code);
259 | generateFormatResult(code);
260 | generateVerifyCase(code);
261 | generateRunTestCase(code);
262 | code.add("}");
263 |
264 | StringBuffer sb = new StringBuffer();
265 | for (String s : code) {
266 | sb.append(s);
267 | sb.append('\n');
268 | }
269 | String ret = sb.toString();
270 | ret = Pattern.compile("^ ", Pattern.MULTILINE).matcher(ret).replaceAll("\t\t\t\t\t");
271 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t\t\t\t");
272 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t\t\t");
273 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t\t");
274 | ret = Pattern.compile("^ " , Pattern.MULTILINE).matcher(ret).replaceAll("\t");
275 | return ret;
276 | }
277 | }
278 |
279 |
--------------------------------------------------------------------------------
/src/moj/LanguageAutoDetection.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | import java.util.*;
4 | import java.util.regex.Pattern;
5 |
6 | public class LanguageAutoDetection {
7 | final static String[] CPLUSPLUS_MARKERS = {
8 | "#\\s*include", "#\\s*define",
9 | "private:", "public:", "protected:",
10 | "struct\\s",
11 | "using\\s+namespace",
12 | "template\\s*<",
13 | "inline\\s*",
14 | "vector\\s*<",
15 | "::",
16 | };
17 | final static String[] JAVA_MARKERS = {
18 | "import\\s+java\\.",
19 | "public \\w",
20 | "String\\s*\\[\\]", "int\\s*\\[\\]", "long\\s*\\[\\]",
21 | "HashMap", "TreeMap"
22 | };
23 | final static String[] CSHARP_MARKERS = {
24 | "using\\s+System",
25 | "string\\s*\\[\\]",
26 | "\\[,\\]", "\\[,,\\]", "\\[,,,\\]", "\\[,,,,\\]",
27 | };
28 |
29 |
30 | static String filterComments(String source) {
31 | StringBuilder sb = new StringBuilder();
32 | for (int i=0; i markers = new TreeMap();
58 |
59 | markers.put("C++", CPLUSPLUS_MARKERS);
60 | markers.put("Java", JAVA_MARKERS);
61 | markers.put("C#", CSHARP_MARKERS);
62 |
63 | System.err.printf("moj language auto detection:");
64 |
65 | int best = 0;
66 | String ret = "C++";
67 | boolean first = true;
68 |
69 | for (String language : markers.keySet()) {
70 | int matched = 0, total = 0;
71 |
72 | for (String m : markers.get(language)) {
73 | if (Pattern.compile(m, Pattern.MULTILINE).matcher(source).find())
74 | ++matched;
75 | ++total;
76 | }
77 |
78 | if (matched > best ||
79 | matched == best && language.equals(candidate)) {
80 | best = matched;
81 | ret = language;
82 | }
83 |
84 | if (!first) System.err.printf(",");
85 | first = false;
86 | System.err.printf(" %s %d markers", language, matched);
87 | }
88 |
89 | System.err.printf("\n");
90 |
91 | return ret.equals(candidate);
92 |
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/moj/Preferences.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Preferences.java
3 | *
4 | * Description: Preferences class for FileEdit
5 | * @author Tim "Pops" Roberts
6 | * @version 3.0
7 | */
8 |
9 | package moj;
10 | import com.topcoder.client.contestApplet.common.LocalPreferences;
11 |
12 | public class Preferences {
13 |
14 | private static LocalPreferences pref = LocalPreferences.getInstance();
15 |
16 | public final static String KEY_TARGETCOMPILER = "moj.config.targetcompiler";
17 | public final static String KEY_NUMPLACEHOLDERS = "moj.config.numplaceholders";
18 | public final static String KEY_LANGUAGESWITCHWORKAROUND = "moj.config.languageswitchworkaround";
19 | public final static String KEY_ENABLEJAVASUPPORT = "moj.config.enablejavasupport";
20 |
21 | public final static String TARGETCOMPILER_GCC11 = "GCC -std=c++11";
22 | public final static String TARGETCOMPILER_GCC98 = "GCC (deprecated)";
23 | public final static String TARGETCOMPILER_VC = "Visual C++";
24 | public final static String TARGETCOMPILER_DEFAULT = TARGETCOMPILER_GCC11;
25 |
26 | public Preferences() {
27 | }
28 |
29 | public String getTargetCompiler() {
30 | String compiler = getStringProperty(KEY_TARGETCOMPILER, TARGETCOMPILER_DEFAULT);
31 | if (!compiler.equals(TARGETCOMPILER_GCC11) &&
32 | !compiler.equals(TARGETCOMPILER_GCC98) &&
33 | !compiler.equals(TARGETCOMPILER_VC)) {
34 | // Force any no-longer-supported settings to the current default
35 | compiler = TARGETCOMPILER_DEFAULT;
36 | }
37 | return compiler;
38 | }
39 |
40 | public void setTargetCompiler(String compiler) {
41 | pref.setProperty(KEY_TARGETCOMPILER, compiler);
42 | }
43 |
44 | public int getNumPlaceholders() {
45 | return getIntegerProperty(KEY_NUMPLACEHOLDERS, 3);
46 | }
47 |
48 | public void setNumPlaceholders(int numPlaceholders) {
49 | pref.setProperty(KEY_NUMPLACEHOLDERS, "" + numPlaceholders);
50 | }
51 |
52 | public boolean getEnableJavaSupport() {
53 | return getBooleanProperty(KEY_ENABLEJAVASUPPORT, true);
54 | }
55 |
56 | public void setEnableJavaSupport(boolean enabled) {
57 | pref.setProperty(KEY_ENABLEJAVASUPPORT, enabled ? "true" : "false");
58 | }
59 |
60 | public boolean getLanguageSwitchWorkaround() {
61 | return getBooleanProperty(KEY_LANGUAGESWITCHWORKAROUND, true);
62 | }
63 |
64 | public void setLanguageSwitchWorkaround(boolean enabled) {
65 | pref.setProperty(KEY_LANGUAGESWITCHWORKAROUND, enabled ? "true" : "false");
66 | }
67 |
68 | protected String getStringProperty(String key, String defaultValue) {
69 | String value = pref.getProperty(key);
70 | return value==null || value.equals("") ? defaultValue : value;
71 | }
72 |
73 | protected boolean getBooleanProperty(String key, boolean defaultValue) {
74 | String value = pref.getProperty(key);
75 | return value==null || value.equals("") ? defaultValue : value.equals("true");
76 | }
77 |
78 | protected int getIntegerProperty(String key, int defaultValue) {
79 | String value = pref.getProperty(key);
80 | if(value==null || value.equals("")) return defaultValue;
81 |
82 | try {
83 | return Integer.parseInt(value);
84 | } catch (NumberFormatException e) {
85 | return defaultValue;
86 | }
87 | }
88 |
89 | public void save() throws java.io.IOException {
90 | pref.savePreferences();
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/moj/moj.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import com.topcoder.client.contestant.ProblemComponentModel;
7 | import com.topcoder.shared.language.Language;
8 | import com.topcoder.shared.problem.*;
9 |
10 | public class moj {
11 | // Map used to store my tags
12 | private Map m_Tags = new HashMap();
13 |
14 | // Constants
15 | private static final String k_TESTCODE = "$TESTCODE$";
16 | private static final String k_DEFAULTMAIN = "$DEFAULTMAIN$";
17 | private static final String k_RUNTEST = "$RUNTEST$";
18 | private static final String k_VERSION = "\n// Powered by moj 4.18 [modified TZTester]";
19 |
20 | // Preferences
21 | private Preferences pref = new Preferences();
22 |
23 | public String preProcess(String source, ProblemComponentModel problem, Language lang, Renderer renderer) {
24 | // Set defaults for the tags in case we exit out early
25 | m_Tags.put(k_TESTCODE, "");
26 | m_Tags.put(k_RUNTEST, "");
27 | m_Tags.put(k_DEFAULTMAIN, "");
28 |
29 | // If there is source and the language matches that in the actual code, return it
30 | if (source.length() > 0 &&
31 | (!pref.getLanguageSwitchWorkaround() || LanguageAutoDetection.isMostLikely(source, lang.getName()))) {
32 | return source;
33 | }
34 |
35 | // See if we are needed at all and select the appropriate generator
36 | HarnessGenerator generator = null;
37 | if (lang.getName().equals("C++")) {
38 | generator = new CPPHarnessGenerator(problem, lang, pref);
39 | } else if (lang.getName().equals("Java")) {
40 | if (!pref.getEnableJavaSupport()) return "";
41 | generator = new JavaHarnessGenerator(problem, lang, pref);
42 | } else {
43 | return "";
44 | }
45 |
46 | // Re-initialize the tags
47 | m_Tags.clear();
48 |
49 | // Get the test cases
50 | TestCase[] testCases = problem.getTestCases();
51 |
52 | // Check to see if test cases are defined
53 | if ((testCases == null) || (testCases.length == 0)) {
54 | m_Tags.put(k_TESTCODE, "// *** moj WARNING *** No test cases defined for this problem");
55 | return "";
56 | }
57 |
58 | m_Tags.put(k_TESTCODE, generator.generateTestCode());
59 | m_Tags.put(k_DEFAULTMAIN, generator.generateDefaultMain());
60 | m_Tags.put(k_RUNTEST, generator.generateRunTest());
61 | return "";
62 | }
63 |
64 | public String postProcess(String source, Language lang) {
65 | return source + k_VERSION;
66 | }
67 |
68 | public Map getUserDefinedTags() {
69 | return m_Tags;
70 | }
71 |
72 | public void configure() {
73 | new ConfigurationDialog(pref).setVisible(true);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/test/moj/CPPParameterTest.java:
--------------------------------------------------------------------------------
1 | package moj;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | import moj.CPPHarnessGenerator.TestCodeGenerationState;
9 | import moj.mocks.*;
10 |
11 | import org.junit.*;
12 | import org.junit.runner.RunWith;
13 | import org.junit.runners.Parameterized;
14 | import org.junit.runners.Parameterized.Parameters;
15 |
16 | import com.topcoder.shared.language.CPPLanguage;
17 | import com.topcoder.shared.problem.DataType;
18 | import com.topcoder.shared.problem.InvalidTypeException;
19 |
20 | @RunWith(Parameterized.class)
21 | public class CPPParameterTest {
22 | private String typename, value, expected;
23 |
24 | public CPPParameterTest(String typename, String varval, String expected) {
25 | this.typename = typename;
26 | this.value = varval;
27 | this.expected = expected;
28 | }
29 |
30 | CPPHarnessGenerator generator;
31 | TestCodeGenerationState code;
32 |
33 | @Before public void setUp() {
34 | generator = new CPPHarnessGenerator(
35 | new ProblemComponentModelMock(),
36 | CPPLanguage.CPP_LANGUAGE,
37 | new PreferencesMock()
38 | );
39 | code = new TestCodeGenerationState();
40 | }
41 |
42 | static String compressSpaceBeforeEquals(String str) {
43 | return str.replaceFirst("\\s*=", " =").trim();
44 | }
45 |
46 | @Parameters
47 | public static List