Enable custom math verification.

For ilogb(0.0f), we want to allow two possible values.  This does
not match the current pattern where the CoreMathVerifier returns
the expected outputs given specified inputs.

This introduces a "custom" test mode where the Test* method calls the
CoreMathVerifier, passing it both the actual inputs and outputs, and
asking these to be verified.

Change-Id: I2e4b0201c93b206e3eb48a6832519f8b827f969f
diff --git a/api/gen_runtime.cpp b/api/gen_runtime.cpp
index 86bba10..d0b113e 100644
--- a/api/gen_runtime.cpp
+++ b/api/gen_runtime.cpp
@@ -211,7 +211,18 @@
      * convert.
      */
     string mCleanName;
-    string mTest;  // How to test.  One of "scalar", "vector", "noverify", "limited", and "none".
+    /* How to test.  One of:
+     * "scalar": Generate test code that checks entries of each vector indepently.  E.g. for
+     *           sin(float3), the test code will call the CoreMathVerfier.computeSin 3 times.
+     * "vector": Generate test code that calls the CoreMathVerifier only once for each vector.
+     *           This is useful for APIs like dot() or length().
+     * "noverify": Generate test code that calls the API but don't verify the returned value.
+     * "limited": Like "scalar" but tests a limited range of input values.
+     * "custom": Like "scalar" but instead of calling CoreMathVerifier.computeXXX() to compute
+     *           the expected value, we call instead CoreMathVerifier.verifyXXX().  This method
+     *           returns a string that contains the error message, null if there's no error.
+     */
+    string mTest;
     string mPrecisionLimit;  // Maximum precision required when checking output of this function.
 
     vector<vector<string> > mReplaceables;
@@ -296,6 +307,7 @@
     string mJavaArgumentsClassName;
     string mJavaArgumentsNClassName;
     string mJavaVerifierComputeMethodName;
+    string mJavaVerifierVerifyMethodName;
     string mJavaCheckMethodName;
     string mJavaVerifyMethodName;
 
@@ -305,8 +317,8 @@
 
     void writeJavaSection(ofstream& file) const;
     void writeJavaArgumentClass(ofstream& file, bool scalar) const;
-    void writeJavaCheckMethod(ofstream& file, bool generateCallToVerify) const;
-    void writeJavaVerifyScalarMethod(ofstream& file) const;
+    void writeJavaCheckMethod(ofstream& file, bool generateCallToVerifier) const;
+    void writeJavaVerifyScalarMethod(ofstream& file, bool verifierValidates) const;
     void writeJavaVerifyVectorMethod(ofstream& file) const;
     void writeJavaVerifyFunctionHeader(ofstream& file) const;
     void writeJavaInputAllocationDefinition(ofstream& file, const string& indent,
@@ -322,14 +334,15 @@
                                                     const string& seed, char vectorSize,
                                                     const Type& compatibleType,
                                                     const Type& generatedType) const;
-    void writeJavaCallToRs(ofstream& file, bool relaxed, bool generateCallToVerify) const;
+    void writeJavaCallToRs(ofstream& file, bool relaxed, bool generateCallToVerifier) const;
 
     void writeJavaTestAndSetValid(ofstream& file, int indent, const ParameterDefinition& p,
                                   const string& argsIndex, const string& actualIndex) const;
     void writeJavaTestOneValue(ofstream& file, int indent, const ParameterDefinition& p,
                                const string& argsIndex, const string& actualIndex) const;
     void writeJavaAppendOutputToMessage(ofstream& file, int indent, const ParameterDefinition& p,
-                                        const string& argsIndex, const string& actualIndex) const;
+                                        const string& argsIndex, const string& actualIndex,
+                                        bool verifierValidates) const;
     void writeJavaAppendInputToMessage(ofstream& file, int indent, const ParameterDefinition& p,
                                        const string& actual) const;
     void writeJavaAppendNewLineToMessage(ofstream& file, int indent) const;
@@ -946,7 +959,7 @@
 
         if (s.compare(0, 5, "test:") == 0) {
             trim(&s, 5);
-            if (s == "scalar" || s == "vector" || s == "noverify" || s == "none") {
+            if (s == "scalar" || s == "vector" || s == "noverify" || s == "custom" || s == "none") {
                 spec->mTest = s;
             } else if (s.compare(0, 7, "limited") == 0) {
                 spec->mTest = "limited";
@@ -1140,6 +1153,7 @@
         mJavaVerifyMethodName += capitalize(p.rsType);
     }
     mJavaVerifierComputeMethodName = "compute" + capitalize(mCleanName);
+    mJavaVerifierVerifyMethodName = "verify" + capitalize(mCleanName);
 }
 
 void Permutation::writeFiles(ofstream& headerFile, ofstream& rsFile, ofstream& javaFile,
@@ -1367,7 +1381,11 @@
     if (mTest == "scalar" || mTest == "limited") {
         writeJavaArgumentClass(file, true);
         writeJavaCheckMethod(file, true);
-        writeJavaVerifyScalarMethod(file);
+        writeJavaVerifyScalarMethod(file, false);
+    } else if (mTest == "custom") {
+        writeJavaArgumentClass(file, true);
+        writeJavaCheckMethod(file, true);
+        writeJavaVerifyScalarMethod(file, true);
     } else if (mTest == "vector") {
         writeJavaArgumentClass(file, false);
         writeJavaCheckMethod(file, true);
@@ -1407,7 +1425,7 @@
     mFunction->writeJavaArgumentClassDefinition(name, s);
 }
 
-void Permutation::writeJavaCheckMethod(ofstream& file, bool generateCallToVerify) const {
+void Permutation::writeJavaCheckMethod(ofstream& file, bool generateCallToVerifier) const {
     file << tab(1) << "private void " << mJavaCheckMethodName << "() {\n";
     // Generate the input allocations and initialization.
     for (size_t i = 0; i < mParams.size(); i++) {
@@ -1425,8 +1443,8 @@
                  << ");\n";
         }
     }
-    writeJavaCallToRs(file, false, generateCallToVerify);
-    writeJavaCallToRs(file, true, generateCallToVerify);
+    writeJavaCallToRs(file, false, generateCallToVerifier);
+    writeJavaCallToRs(file, true, generateCallToVerifier);
     file << tab(1) << "}\n\n";
 }
 
@@ -1546,7 +1564,7 @@
     }
 }
 
-void Permutation::writeJavaVerifyScalarMethod(ofstream& file) const {
+void Permutation::writeJavaVerifyScalarMethod(ofstream& file, bool verifierValidates) const {
     writeJavaVerifyFunctionHeader(file);
     string vectorSize = "1";
     for (size_t i = 0; i < mParams.size(); i++) {
@@ -1578,16 +1596,31 @@
         }
     }
 
-    file << tab(4) << "// Figure out what the outputs should have been.\n";
-    file << tab(4) << "Floaty.setRelaxed(relaxed);\n";
-    file << tab(4) << "CoreMathVerifier." << mJavaVerifierComputeMethodName << "(args);\n";
-
-    file << tab(4) << "// Figure out what the outputs should have been.\n";
-    file << tab(4) << "boolean valid = true;\n";
-    for (size_t i = 0; i < mParams.size(); i++) {
-        const ParameterDefinition& p = *mParams[i];
-        if (p.isOutParameter) {
-            writeJavaTestAndSetValid(file, 4, p, "", "[i * " + p.vectorWidth + " + j]");
+    if (verifierValidates) {
+        file << tab(4) << "// Extract the outputs.\n";
+        for (size_t i = 0; i < mParams.size(); i++) {
+            const ParameterDefinition& p = *mParams[i];
+            if (p.isOutParameter) {
+                file << tab(4) << "args." << p.variableName << " = " << p.javaArrayName
+                     << "[i * " + p.vectorWidth + " + j];\n";
+            }
+        }
+        file << tab(4) << "// Ask the CoreMathVerifier to validate.\n";
+        file << tab(4) << "Floaty.setRelaxed(relaxed);\n";
+        file << tab(4) << "String errorMessage = CoreMathVerifier." << mJavaVerifierVerifyMethodName
+             << "(args, relaxed);\n";
+        file << tab(4) << "boolean valid = errorMessage == null;\n";
+    } else {
+        file << tab(4) << "// Figure out what the outputs should have been.\n";
+        file << tab(4) << "Floaty.setRelaxed(relaxed);\n";
+        file << tab(4) << "CoreMathVerifier." << mJavaVerifierComputeMethodName << "(args);\n";
+        file << tab(4) << "// Validate the outputs.\n";
+        file << tab(4) << "boolean valid = true;\n";
+        for (size_t i = 0; i < mParams.size(); i++) {
+            const ParameterDefinition& p = *mParams[i];
+            if (p.isOutParameter) {
+                writeJavaTestAndSetValid(file, 4, p, "", "[i * " + p.vectorWidth + " + j]");
+            }
         }
     }
 
@@ -1596,11 +1629,15 @@
     for (size_t i = 0; i < mParams.size(); i++) {
         const ParameterDefinition& p = *mParams[i];
         if (p.isOutParameter) {
-            writeJavaAppendOutputToMessage(file, 5, p, "", "[i * " + p.vectorWidth + " + j]");
+            writeJavaAppendOutputToMessage(file, 5, p, "", "[i * " + p.vectorWidth + " + j]",
+                                           verifierValidates);
         } else {
             writeJavaAppendInputToMessage(file, 5, p, "args." + p.variableName);
         }
     }
+    if (verifierValidates) {
+        file << tab(5) << "message.append(errorMessage);\n";
+    }
 
     file << tab(5) << "assertTrue(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
     file << tab(7) << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), valid);\n";
@@ -1648,24 +1685,35 @@
 
 void Permutation::writeJavaAppendOutputToMessage(ofstream& file, int indent,
                                                  const ParameterDefinition& p,
-                                                 const string& argsIndex,
-                                                 const string& actualIndex) const {
-    const string expected = "args." + p.variableName + argsIndex;
-    const string actual = p.javaArrayName + actualIndex;
-    file << tab(indent) << "message.append(\"Expected output " + p.variableName + ": \");\n";
-    if (p.isFloatType) {
-        writeJavaAppendFloatyVariableToMessage(file, indent, expected);
+                                                 const string& argsIndex, const string& actualIndex,
+                                                 bool verifierValidates) const {
+    if (verifierValidates) {
+        const string actual = "args." + p.variableName + argsIndex;
+        file << tab(indent) << "message.append(\"Output " + p.variableName + ": \");\n";
+        if (p.isFloatType) {
+            writeJavaAppendFloatyVariableToMessage(file, indent, actual);
+        } else {
+            writeJavaAppendVariableToMessage(file, indent, p, actual);
+        }
+        writeJavaAppendNewLineToMessage(file, indent);
     } else {
-        writeJavaAppendVariableToMessage(file, indent, p, expected);
-    }
-    writeJavaAppendNewLineToMessage(file, indent);
-    file << tab(indent) << "message.append(\"Actual   output " + p.variableName + ": \");\n";
-    writeJavaAppendVariableToMessage(file, indent, p, actual);
+        const string expected = "args." + p.variableName + argsIndex;
+        const string actual = p.javaArrayName + actualIndex;
+        file << tab(indent) << "message.append(\"Expected output " + p.variableName + ": \");\n";
+        if (p.isFloatType) {
+            writeJavaAppendFloatyVariableToMessage(file, indent, expected);
+        } else {
+            writeJavaAppendVariableToMessage(file, indent, p, expected);
+        }
+        writeJavaAppendNewLineToMessage(file, indent);
+        file << tab(indent) << "message.append(\"Actual   output " + p.variableName + ": \");\n";
+        writeJavaAppendVariableToMessage(file, indent, p, actual);
 
-    writeJavaTestOneValue(file, indent, p, argsIndex, actualIndex);
-    file << tab(indent + 1) << "message.append(\" FAIL\");\n";
-    file << tab(indent) << "}\n";
-    writeJavaAppendNewLineToMessage(file, indent);
+        writeJavaTestOneValue(file, indent, p, argsIndex, actualIndex);
+        file << tab(indent + 1) << "message.append(\" FAIL\");\n";
+        file << tab(indent) << "}\n";
+        writeJavaAppendNewLineToMessage(file, indent);
+    }
 }
 
 void Permutation::writeJavaAppendInputToMessage(ofstream& file, int indent,
@@ -1730,12 +1778,12 @@
 void Permutation::writeJavaAppendVectorOutputToMessage(ofstream& file, int indent,
                                                        const ParameterDefinition& p) const {
     if (p.mVectorSize == "1") {
-        writeJavaAppendOutputToMessage(file, indent, p, "", "[i]");
+        writeJavaAppendOutputToMessage(file, indent, p, "", "[i]", false);
 
     } else {
         file << tab(indent) << "for (int j = 0; j < " << p.mVectorSize << " ; j++) {\n";
         writeJavaAppendOutputToMessage(file, indent + 1, p, "[j]",
-                                       "[i * " + p.vectorWidth + " + j]");
+                                       "[i * " + p.vectorWidth + " + j]", false);
         file << tab(indent) << "}\n";
     }
 }
@@ -1809,7 +1857,7 @@
     file << tab(1) << "}\n\n";
 }
 
-void Permutation::writeJavaCallToRs(ofstream& file, bool relaxed, bool generateCallToVerify) const {
+void Permutation::writeJavaCallToRs(ofstream& file, bool relaxed, bool generateCallToVerifier) const {
     string script = "script";
     if (relaxed) {
         script += "Relaxed";
@@ -1844,7 +1892,7 @@
         file << mParams[mReturnIndex]->variableName << ");\n";
     }
 
-    if (generateCallToVerify) {
+    if (generateCallToVerifier) {
         file << tab(3) << mJavaVerifyMethodName << "(";
         for (size_t i = 0; i < mParams.size(); i++) {
             const ParameterDefinition& p = *mParams[i];