Generate all APIs.

This CL expands the generator to create all the .rsh files, not just
the core_math one.  To do so, processing of types (simple, struct, enums)
and constants was added.  .spec files corresponding to each .rsh file was
created.  Documentation was added.

This CL also generates HTML documentation files.  This generation will soon
be upgraded.

To make the code easier to expand, I've done fairly extensive refactoring.

In a subsequent CL, the APIs will be regrouped in different header files to
simplify learning the APIs.  In an other, the documentation generation will
be futher improved and incorporated in the actual online help.

Also removes rs_path & related functions.

Change-Id: I2c88554c9c6a8625233772b89e055fc6c4ad5da5
diff --git a/api/GenerateTestFiles.cpp b/api/GenerateTestFiles.cpp
new file mode 100644
index 0000000..7cf1099
--- /dev/null
+++ b/api/GenerateTestFiles.cpp
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iomanip>
+#include <iostream>
+#include <cmath>
+#include <sstream>
+
+#include "Generator.h"
+#include "Specification.h"
+#include "Utilities.h"
+
+using namespace std;
+
+// Converts float2 to FLOAT_32 and 2, etc.
+static void convertToRsType(const string& name, string* dataType, char* vectorSize) {
+    string s = name;
+    int last = s.size() - 1;
+    char lastChar = s[last];
+    if (lastChar >= '1' && lastChar <= '4') {
+        s.erase(last);
+        *vectorSize = lastChar;
+    } else {
+        *vectorSize = '1';
+    }
+    dataType->clear();
+    for (int i = 0; i < NUM_TYPES; i++) {
+        if (s == TYPES[i].cType) {
+            *dataType = TYPES[i].rsDataType;
+            break;
+        }
+    }
+}
+
+// Returns true if any permutation of the function have tests to b
+static bool needTestFiles(const Function& function, int versionOfTestFiles) {
+    for (auto spec : function.getSpecifications()) {
+        if (spec->hasTests(versionOfTestFiles)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/* One instance of this class is generated for each permutation of a function for which
+ * we are generating test code.  This instance will generate both the script and the Java
+ * section of the test files for this permutation.  The class is mostly used to keep track
+ * of the various names shared between script and Java files.
+ * WARNING: Because the constructor keeps a reference to the FunctionPermutation, PermutationWriter
+ * should not exceed the lifetime of FunctionPermutation.
+ */
+class PermutationWriter {
+private:
+    FunctionPermutation& mPermutation;
+
+    string mRsKernelName;
+    string mJavaArgumentsClassName;
+    string mJavaArgumentsNClassName;
+    string mJavaVerifierComputeMethodName;
+    string mJavaVerifierVerifyMethodName;
+    string mJavaCheckMethodName;
+    string mJavaVerifyMethodName;
+
+    // Pointer to the files we are generating.  Handy to avoid always passing them in the calls.
+    GeneratedFile* mRs;
+    GeneratedFile* mJava;
+
+    /* Shortcuts to the return parameter and the first input parameter of the function
+     * specification.
+     */
+    const ParameterDefinition* mReturnParam;      // Can be nullptr.  NOT OWNED.
+    const ParameterDefinition* mFirstInputParam;  // Can be nullptr.  NOT OWNED.
+
+    /* All the parameters plus the return param, if present.  Collecting them together
+     * simplifies code generation.  NOT OWNED.
+     */
+    vector<const ParameterDefinition*> mAllInputsAndOutputs;
+
+    /* We use a class to pass the arguments between the generated code and the CoreVerifier.  This
+     * method generates this class.  The set keeps track if we've generated this class already
+     * for this test file, as more than one permutation may use the same argument class.
+     */
+    void writeJavaArgumentClass(bool scalar, set<string>* javaGeneratedArgumentClasses) const;
+
+    // Generate the Check* method that invokes the script and calls the verifier.
+    void writeJavaCheckMethod(bool generateCallToVerifier) const;
+
+    // Generate code to define and randomly initialize the input allocation.
+    void writeJavaInputAllocationDefinition(const ParameterDefinition& param) const;
+
+    /* Generate code that instantiate an allocation of floats or integers and fills it with
+     * random data. This random data must be compatible with the specified type.  This is
+     * used for the convert_* tests, as converting values that don't fit yield undefined results.
+     */
+    void writeJavaRandomCompatibleFloatAllocation(const string& dataType, const string& seed,
+                                                  char vectorSize,
+                                                  const NumericalType& compatibleType,
+                                                  const NumericalType& generatedType) const;
+    void writeJavaRandomCompatibleIntegerAllocation(const string& dataType, const string& seed,
+                                                    char vectorSize,
+                                                    const NumericalType& compatibleType,
+                                                    const NumericalType& generatedType) const;
+
+    // Generate code that defines an output allocation.
+    void writeJavaOutputAllocationDefinition(const ParameterDefinition& param) const;
+
+    /* Generate the code that verifies the results for RenderScript functions where each entry
+     * of a vector is evaluated independently.  If verifierValidates is true, CoreMathVerifier
+     * does the actual validation instead of more commonly returning the range of acceptable values.
+     */
+    void writeJavaVerifyScalarMethod(bool verifierValidates) const;
+
+    /* Generate the code that verify the results for a RenderScript function where a vector
+     * is a point in n-dimensional space.
+     */
+    void writeJavaVerifyVectorMethod() const;
+
+    // Generate the method header of the verify function.
+    void writeJavaVerifyMethodHeader() const;
+
+    // Generate codes that copies the content of an allocation to an array.
+    void writeJavaArrayInitialization(const ParameterDefinition& p) const;
+
+    // Generate code that tests one value returned from the script.
+    void writeJavaTestAndSetValid(const ParameterDefinition& p, const string& argsIndex,
+                                  const string& actualIndex) const;
+    void writeJavaTestOneValue(const ParameterDefinition& p, const string& argsIndex,
+                               const string& actualIndex) const;
+    // For test:vector cases, generate code that compares returned vector vs. expected value.
+    void writeJavaVectorComparison(const ParameterDefinition& p) const;
+
+    // Muliple functions that generates code to build the error message if an error is found.
+    void writeJavaAppendOutputToMessage(const ParameterDefinition& p, const string& argsIndex,
+                                        const string& actualIndex, bool verifierValidates) const;
+    void writeJavaAppendInputToMessage(const ParameterDefinition& p, const string& actual) const;
+    void writeJavaAppendNewLineToMessage() const;
+    void writeJavaAppendVariableToMessage(const ParameterDefinition& p, const string& value) const;
+    void writeJavaAppendFloatVariableToMessage(const string& value, bool regularFloat) const;
+    void writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const;
+    void writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const;
+
+    // Generate the set of instructions to call the script.
+    void writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const;
+
+    // Write an allocation definition if not already emitted in the .rs file.
+    void writeRsAllocationDefinition(const ParameterDefinition& param,
+                                     set<string>* rsAllocationsGenerated) const;
+
+public:
+    /* NOTE: We keep pointers to the permutation and the files.  This object should not
+     * outlive the arguments.
+     */
+    PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
+                      GeneratedFile* javaFile);
+    string getJavaCheckMethodName() const { return mJavaCheckMethodName; }
+
+    // Write the script test function for this permutation.
+    void writeRsSection(set<string>* rsAllocationsGenerated) const;
+    // Write the section of the Java code that calls the script and validates the results
+    void writeJavaSection(set<string>* javaGeneratedArgumentClasses) const;
+};
+
+PermutationWriter::PermutationWriter(FunctionPermutation& permutation, GeneratedFile* rsFile,
+                                     GeneratedFile* javaFile)
+    : mPermutation(permutation),
+      mRs(rsFile),
+      mJava(javaFile),
+      mReturnParam(nullptr),
+      mFirstInputParam(nullptr) {
+    mRsKernelName = "test" + capitalize(permutation.getName());
+
+    mJavaArgumentsClassName = "Arguments";
+    mJavaArgumentsNClassName = "Arguments";
+    const string trunk = capitalize(permutation.getNameTrunk());
+    mJavaCheckMethodName = "check" + trunk;
+    mJavaVerifyMethodName = "verifyResults" + trunk;
+
+    for (auto p : permutation.getParams()) {
+        mAllInputsAndOutputs.push_back(p);
+        if (mFirstInputParam == nullptr && !p->isOutParameter) {
+            mFirstInputParam = p;
+        }
+    }
+    mReturnParam = permutation.getReturn();
+    if (mReturnParam) {
+        mAllInputsAndOutputs.push_back(mReturnParam);
+    }
+
+    for (auto p : mAllInputsAndOutputs) {
+        const string capitalizedRsType = capitalize(p->rsType);
+        const string capitalizedBaseType = capitalize(p->rsBaseType);
+        mRsKernelName += capitalizedRsType;
+        mJavaArgumentsClassName += capitalizedBaseType;
+        mJavaArgumentsNClassName += capitalizedBaseType;
+        if (p->mVectorSize != "1") {
+            mJavaArgumentsNClassName += "N";
+        }
+        mJavaCheckMethodName += capitalizedRsType;
+        mJavaVerifyMethodName += capitalizedRsType;
+    }
+    mJavaVerifierComputeMethodName = "compute" + trunk;
+    mJavaVerifierVerifyMethodName = "verify" + trunk;
+}
+
+void PermutationWriter::writeJavaSection(set<string>* javaGeneratedArgumentClasses) const {
+    // By default, we test the results using item by item comparison.
+    const string test = mPermutation.getTest();
+    if (test == "scalar" || test == "limited") {
+        writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
+        writeJavaCheckMethod(true);
+        writeJavaVerifyScalarMethod(false);
+    } else if (test == "custom") {
+        writeJavaArgumentClass(true, javaGeneratedArgumentClasses);
+        writeJavaCheckMethod(true);
+        writeJavaVerifyScalarMethod(true);
+    } else if (test == "vector") {
+        writeJavaArgumentClass(false, javaGeneratedArgumentClasses);
+        writeJavaCheckMethod(true);
+        writeJavaVerifyVectorMethod();
+    } else if (test == "noverify") {
+        writeJavaCheckMethod(false);
+    }
+}
+
+void PermutationWriter::writeJavaArgumentClass(bool scalar,
+                                               set<string>* javaGeneratedArgumentClasses) const {
+    string name;
+    if (scalar) {
+        name = mJavaArgumentsClassName;
+    } else {
+        name = mJavaArgumentsNClassName;
+    }
+
+    // Make sure we have not generated the argument class already.
+    if (!testAndSet(name, javaGeneratedArgumentClasses)) {
+        mJava->indent() << "public class " << name;
+        mJava->startBlock();
+
+        for (auto p : mAllInputsAndOutputs) {
+            mJava->indent() << "public ";
+            if (p->isOutParameter && p->isFloatType && mPermutation.getTest() != "custom") {
+                *mJava << "Target.Floaty";
+            } else {
+                *mJava << p->javaBaseType;
+            }
+            if (!scalar && p->mVectorSize != "1") {
+                *mJava << "[]";
+            }
+            *mJava << " " << p->variableName << ";\n";
+        }
+        mJava->endBlock();
+        *mJava << "\n";
+    }
+}
+
+void PermutationWriter::writeJavaCheckMethod(bool generateCallToVerifier) const {
+    mJava->indent() << "private void " << mJavaCheckMethodName << "()";
+    mJava->startBlock();
+
+    // Generate the input allocations and initialization.
+    for (auto p : mAllInputsAndOutputs) {
+        if (!p->isOutParameter) {
+            writeJavaInputAllocationDefinition(*p);
+        }
+    }
+    // Generate code to enforce ordering between two allocations if needed.
+    for (auto p : mAllInputsAndOutputs) {
+        if (!p->isOutParameter && !p->smallerParameter.empty()) {
+            string smallerAlloc = "in" + capitalize(p->smallerParameter);
+            mJava->indent() << "enforceOrdering(" << smallerAlloc << ", " << p->javaAllocName
+                            << ");\n";
+        }
+    }
+
+    // Generate code to check the full and relaxed scripts.
+    writeJavaCallToRs(false, generateCallToVerifier);
+    writeJavaCallToRs(true, generateCallToVerifier);
+
+    mJava->endBlock();
+    *mJava << "\n";
+}
+
+void PermutationWriter::writeJavaInputAllocationDefinition(const ParameterDefinition& param) const {
+    string dataType;
+    char vectorSize;
+    convertToRsType(param.rsType, &dataType, &vectorSize);
+
+    const string seed = hashString(mJavaCheckMethodName + param.javaAllocName);
+    mJava->indent() << "Allocation " << param.javaAllocName << " = ";
+    if (param.compatibleTypeIndex >= 0) {
+        if (TYPES[param.typeIndex].kind == FLOATING_POINT) {
+            writeJavaRandomCompatibleFloatAllocation(dataType, seed, vectorSize,
+                                                     TYPES[param.compatibleTypeIndex],
+                                                     TYPES[param.typeIndex]);
+        } else {
+            writeJavaRandomCompatibleIntegerAllocation(dataType, seed, vectorSize,
+                                                       TYPES[param.compatibleTypeIndex],
+                                                       TYPES[param.typeIndex]);
+        }
+    } else if (!param.minValue.empty()) {
+        *mJava << "createRandomFloatAllocation(mRS, Element.DataType." << dataType << ", "
+               << vectorSize << ", " << seed << ", " << param.minValue << ", " << param.maxValue
+               << ")";
+    } else {
+        /* TODO Instead of passing always false, check whether we are doing a limited test.
+         * Use instead: (mPermutation.getTest() == "limited" ? "false" : "true")
+         */
+        *mJava << "createRandomAllocation(mRS, Element.DataType." << dataType << ", " << vectorSize
+               << ", " << seed << ", false)";
+    }
+    *mJava << ";\n";
+}
+
+void PermutationWriter::writeJavaRandomCompatibleFloatAllocation(
+            const string& dataType, const string& seed, char vectorSize,
+            const NumericalType& compatibleType, const NumericalType& generatedType) const {
+    *mJava << "createRandomFloatAllocation"
+           << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
+    double minValue = 0.0;
+    double maxValue = 0.0;
+    switch (compatibleType.kind) {
+        case FLOATING_POINT: {
+            // We're generating floating point values.  We just worry about the exponent.
+            // Subtract 1 for the exponent sign.
+            int bits = min(compatibleType.exponentBits, generatedType.exponentBits) - 1;
+            maxValue = ldexp(0.95, (1 << bits) - 1);
+            minValue = -maxValue;
+            break;
+        }
+        case UNSIGNED_INTEGER:
+            maxValue = maxDoubleForInteger(compatibleType.significantBits,
+                                           generatedType.significantBits);
+            minValue = 0.0;
+            break;
+        case SIGNED_INTEGER:
+            maxValue = maxDoubleForInteger(compatibleType.significantBits,
+                                           generatedType.significantBits);
+            minValue = -maxValue - 1.0;
+            break;
+    }
+    *mJava << scientific << std::setprecision(19);
+    *mJava << minValue << ", " << maxValue << ")";
+    mJava->unsetf(ios_base::floatfield);
+}
+
+void PermutationWriter::writeJavaRandomCompatibleIntegerAllocation(
+            const string& dataType, const string& seed, char vectorSize,
+            const NumericalType& compatibleType, const NumericalType& generatedType) const {
+    *mJava << "createRandomIntegerAllocation"
+           << "(mRS, Element.DataType." << dataType << ", " << vectorSize << ", " << seed << ", ";
+
+    if (compatibleType.kind == FLOATING_POINT) {
+        // Currently, all floating points can take any number we generate.
+        bool isSigned = generatedType.kind == SIGNED_INTEGER;
+        *mJava << (isSigned ? "true" : "false") << ", " << generatedType.significantBits;
+    } else {
+        bool isSigned =
+                    compatibleType.kind == SIGNED_INTEGER && generatedType.kind == SIGNED_INTEGER;
+        *mJava << (isSigned ? "true" : "false") << ", "
+               << min(compatibleType.significantBits, generatedType.significantBits);
+    }
+    *mJava << ")";
+}
+
+void PermutationWriter::writeJavaOutputAllocationDefinition(
+            const ParameterDefinition& param) const {
+    string dataType;
+    char vectorSize;
+    convertToRsType(param.rsType, &dataType, &vectorSize);
+    mJava->indent() << "Allocation " << param.javaAllocName << " = Allocation.createSized(mRS, "
+                    << "getElement(mRS, Element.DataType." << dataType << ", " << vectorSize
+                    << "), INPUTSIZE);\n";
+}
+
+void PermutationWriter::writeJavaVerifyScalarMethod(bool verifierValidates) const {
+    writeJavaVerifyMethodHeader();
+    mJava->startBlock();
+
+    string vectorSize = "1";
+    for (auto p : mAllInputsAndOutputs) {
+        writeJavaArrayInitialization(*p);
+        if (p->mVectorSize != "1" && p->mVectorSize != vectorSize) {
+            if (vectorSize == "1") {
+                vectorSize = p->mVectorSize;
+            } else {
+                cerr << "Error.  Had vector " << vectorSize << " and " << p->mVectorSize << "\n";
+            }
+        }
+    }
+
+    mJava->indent() << "for (int i = 0; i < INPUTSIZE; i++)";
+    mJava->startBlock();
+
+    mJava->indent() << "for (int j = 0; j < " << vectorSize << " ; j++)";
+    mJava->startBlock();
+
+    mJava->indent() << "// Extract the inputs.\n";
+    mJava->indent() << mJavaArgumentsClassName << " args = new " << mJavaArgumentsClassName
+                    << "();\n";
+    for (auto p : mAllInputsAndOutputs) {
+        if (!p->isOutParameter) {
+            mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName << "[i";
+            if (p->vectorWidth != "1") {
+                *mJava << " * " << p->vectorWidth << " + j";
+            }
+            *mJava << "];\n";
+        }
+    }
+    const bool hasFloat = mPermutation.hasFloatAnswers();
+    if (verifierValidates) {
+        mJava->indent() << "// Extract the outputs.\n";
+        for (auto p : mAllInputsAndOutputs) {
+            if (p->isOutParameter) {
+                mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName
+                                << "[i * " + p->vectorWidth + " + j];\n";
+            }
+        }
+        mJava->indent() << "// Ask the CoreMathVerifier to validate.\n";
+        if (hasFloat) {
+            mJava->indent() << "Target target = new Target(relaxed);\n";
+        }
+        mJava->indent() << "String errorMessage = CoreMathVerifier."
+                        << mJavaVerifierVerifyMethodName << "(args";
+        if (hasFloat) {
+            *mJava << ", target";
+        }
+        *mJava << ");\n";
+        mJava->indent() << "boolean valid = errorMessage == null;\n";
+    } else {
+        mJava->indent() << "// Figure out what the outputs should have been.\n";
+        if (hasFloat) {
+            mJava->indent() << "Target target = new Target(relaxed);\n";
+        }
+        mJava->indent() << "CoreMathVerifier." << mJavaVerifierComputeMethodName << "(args";
+        if (hasFloat) {
+            *mJava << ", target";
+        }
+        *mJava << ");\n";
+        mJava->indent() << "// Validate the outputs.\n";
+        mJava->indent() << "boolean valid = true;\n";
+        for (auto p : mAllInputsAndOutputs) {
+            if (p->isOutParameter) {
+                writeJavaTestAndSetValid(*p, "", "[i * " + p->vectorWidth + " + j]");
+            }
+        }
+    }
+
+    mJava->indent() << "if (!valid)";
+    mJava->startBlock();
+
+    mJava->indent() << "StringBuilder message = new StringBuilder();\n";
+    for (auto p : mAllInputsAndOutputs) {
+        if (p->isOutParameter) {
+            writeJavaAppendOutputToMessage(*p, "", "[i * " + p->vectorWidth + " + j]",
+                                           verifierValidates);
+        } else {
+            writeJavaAppendInputToMessage(*p, "args." + p->variableName);
+        }
+    }
+    if (verifierValidates) {
+        mJava->indent() << "message.append(errorMessage);\n";
+    }
+
+    mJava->indent() << "assertTrue(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
+    mJava->indentPlus()
+                << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), valid);\n";
+
+    mJava->endBlock();
+    mJava->endBlock();
+    mJava->endBlock();
+    mJava->endBlock();
+    *mJava << "\n";
+}
+
+void PermutationWriter::writeJavaVerifyVectorMethod() const {
+    writeJavaVerifyMethodHeader();
+    mJava->startBlock();
+
+    for (auto p : mAllInputsAndOutputs) {
+        writeJavaArrayInitialization(*p);
+    }
+    mJava->indent() << "for (int i = 0; i < INPUTSIZE; i++)";
+    mJava->startBlock();
+
+    mJava->indent() << mJavaArgumentsNClassName << " args = new " << mJavaArgumentsNClassName
+                    << "();\n";
+
+    mJava->indent() << "// Create the appropriate sized arrays in args\n";
+    for (auto p : mAllInputsAndOutputs) {
+        if (p->mVectorSize != "1") {
+            string type = p->javaBaseType;
+            if (p->isOutParameter && p->isFloatType) {
+                type = "Target.Floaty";
+            }
+            mJava->indent() << "args." << p->variableName << " = new " << type << "["
+                            << p->mVectorSize << "];\n";
+        }
+    }
+
+    mJava->indent() << "// Fill args with the input values\n";
+    for (auto p : mAllInputsAndOutputs) {
+        if (!p->isOutParameter) {
+            if (p->mVectorSize == "1") {
+                mJava->indent() << "args." << p->variableName << " = " << p->javaArrayName + "[i]"
+                                << ";\n";
+            } else {
+                mJava->indent() << "for (int j = 0; j < " << p->mVectorSize << " ; j++)";
+                mJava->startBlock();
+                mJava->indent() << "args." << p->variableName + "[j] = "
+                                << p->javaArrayName + "[i * " + p->vectorWidth + " + j]"
+                                << ";\n";
+                mJava->endBlock();
+            }
+        }
+    }
+    mJava->indent() << "Target target = new Target(relaxed);\n";
+    mJava->indent() << "CoreMathVerifier." << mJavaVerifierComputeMethodName
+                    << "(args, target);\n\n";
+
+    mJava->indent() << "// Compare the expected outputs to the actual values returned by RS.\n";
+    mJava->indent() << "boolean valid = true;\n";
+    for (auto p : mAllInputsAndOutputs) {
+        if (p->isOutParameter) {
+            writeJavaVectorComparison(*p);
+        }
+    }
+
+    mJava->indent() << "if (!valid)";
+    mJava->startBlock();
+
+    mJava->indent() << "StringBuilder message = new StringBuilder();\n";
+    for (auto p : mAllInputsAndOutputs) {
+        if (p->isOutParameter) {
+            writeJavaAppendVectorOutputToMessage(*p);
+        } else {
+            writeJavaAppendVectorInputToMessage(*p);
+        }
+    }
+
+    mJava->indent() << "assertTrue(\"Incorrect output for " << mJavaCheckMethodName << "\" +\n";
+    mJava->indentPlus()
+                << "(relaxed ? \"_relaxed\" : \"\") + \":\\n\" + message.toString(), valid);\n";
+
+    mJava->endBlock();
+    mJava->endBlock();
+    mJava->endBlock();
+    *mJava << "\n";
+}
+
+void PermutationWriter::writeJavaVerifyMethodHeader() const {
+    mJava->indent() << "private void " << mJavaVerifyMethodName << "(";
+    for (auto p : mAllInputsAndOutputs) {
+        *mJava << "Allocation " << p->javaAllocName << ", ";
+    }
+    *mJava << "boolean relaxed)";
+}
+
+void PermutationWriter::writeJavaArrayInitialization(const ParameterDefinition& p) const {
+    mJava->indent() << p.javaBaseType << "[] " << p.javaArrayName << " = new " << p.javaBaseType
+                    << "[INPUTSIZE * " << p.vectorWidth << "];\n";
+    mJava->indent() << p.javaAllocName << ".copyTo(" << p.javaArrayName << ");\n";
+}
+
+void PermutationWriter::writeJavaTestAndSetValid(const ParameterDefinition& p,
+                                                 const string& argsIndex,
+                                                 const string& actualIndex) const {
+    writeJavaTestOneValue(p, argsIndex, actualIndex);
+    mJava->startBlock();
+    mJava->indent() << "valid = false;\n";
+    mJava->endBlock();
+}
+
+void PermutationWriter::writeJavaTestOneValue(const ParameterDefinition& p, const string& argsIndex,
+                                              const string& actualIndex) const {
+    mJava->indent() << "if (";
+    if (p.isFloatType) {
+        *mJava << "!args." << p.variableName << argsIndex << ".couldBe(" << p.javaArrayName
+               << actualIndex;
+        const string s = mPermutation.getPrecisionLimit();
+        if (!s.empty()) {
+            *mJava << ", " << s;
+        }
+        *mJava << ")";
+    } else {
+        *mJava << "args." << p.variableName << argsIndex << " != " << p.javaArrayName
+               << actualIndex;
+    }
+
+    if (p.undefinedIfOutIsNan && mReturnParam) {
+        *mJava << " && !args." << mReturnParam->variableName << argsIndex << ".isNaN()";
+    }
+    *mJava << ")";
+}
+
+void PermutationWriter::writeJavaVectorComparison(const ParameterDefinition& p) const {
+    if (p.mVectorSize == "1") {
+        writeJavaTestAndSetValid(p, "", "[i]");
+    } else {
+        mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
+        mJava->startBlock();
+        writeJavaTestAndSetValid(p, "[j]", "[i * " + p.vectorWidth + " + j]");
+        mJava->endBlock();
+    }
+}
+
+void PermutationWriter::writeJavaAppendOutputToMessage(const ParameterDefinition& p,
+                                                       const string& argsIndex,
+                                                       const string& actualIndex,
+                                                       bool verifierValidates) const {
+    if (verifierValidates) {
+        const string actual = "args." + p.variableName + argsIndex;
+        mJava->indent() << "message.append(\"Output " + p.variableName + ": \");\n";
+        if (p.isFloatType) {
+            writeJavaAppendFloatVariableToMessage(actual, true);
+        } else {
+            writeJavaAppendVariableToMessage(p, actual);
+        }
+        writeJavaAppendNewLineToMessage();
+    } else {
+        const string expected = "args." + p.variableName + argsIndex;
+        const string actual = p.javaArrayName + actualIndex;
+        mJava->indent() << "message.append(\"Expected output " + p.variableName + ": \");\n";
+        if (p.isFloatType) {
+            writeJavaAppendFloatVariableToMessage(expected, false);
+        } else {
+            writeJavaAppendVariableToMessage(p, expected);
+        }
+        writeJavaAppendNewLineToMessage();
+        mJava->indent() << "message.append(\"Actual   output " + p.variableName + ": \");\n";
+        writeJavaAppendVariableToMessage(p, actual);
+
+        writeJavaTestOneValue(p, argsIndex, actualIndex);
+        mJava->startBlock();
+        mJava->indent() << "message.append(\" FAIL\");\n";
+        mJava->endBlock();
+        writeJavaAppendNewLineToMessage();
+    }
+}
+
+void PermutationWriter::writeJavaAppendInputToMessage(const ParameterDefinition& p,
+                                                      const string& actual) const {
+    mJava->indent() << "message.append(\"Input " + p.variableName + ": \");\n";
+    writeJavaAppendVariableToMessage(p, actual);
+    writeJavaAppendNewLineToMessage();
+}
+
+void PermutationWriter::writeJavaAppendNewLineToMessage() const {
+    mJava->indent() << "message.append(\"\\n\");\n";
+}
+
+void PermutationWriter::writeJavaAppendVariableToMessage(const ParameterDefinition& p,
+                                                         const string& value) const {
+    if (p.specType == "f16" || p.specType == "f32") {
+        mJava->indent() << "message.append(String.format(\"%14.8g {%8x} %15a\",\n";
+        mJava->indentPlus() << value << ", "
+                            << "Float.floatToRawIntBits(" << value << "), " << value << "));\n";
+    } else if (p.specType == "f64") {
+        mJava->indent() << "message.append(String.format(\"%24.8g {%16x} %31a\",\n";
+        mJava->indentPlus() << value << ", "
+                            << "Double.doubleToRawLongBits(" << value << "), " << value << "));\n";
+    } else if (p.specType[0] == 'u') {
+        mJava->indent() << "message.append(String.format(\"0x%x\", " << value << "));\n";
+    } else {
+        mJava->indent() << "message.append(String.format(\"%d\", " << value << "));\n";
+    }
+}
+
+void PermutationWriter::writeJavaAppendFloatVariableToMessage(const string& value,
+                                                              bool regularFloat) const {
+    mJava->indent() << "message.append(";
+    if (regularFloat) {
+        *mJava << "Float.toString(" << value << ")";
+    } else {
+        *mJava << value << ".toString()";
+    }
+    *mJava << ");\n";
+}
+
+void PermutationWriter::writeJavaAppendVectorInputToMessage(const ParameterDefinition& p) const {
+    if (p.mVectorSize == "1") {
+        writeJavaAppendInputToMessage(p, p.javaArrayName + "[i]");
+    } else {
+        mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
+        mJava->startBlock();
+        writeJavaAppendInputToMessage(p, p.javaArrayName + "[i * " + p.vectorWidth + " + j]");
+        mJava->endBlock();
+    }
+}
+
+void PermutationWriter::writeJavaAppendVectorOutputToMessage(const ParameterDefinition& p) const {
+    if (p.mVectorSize == "1") {
+        writeJavaAppendOutputToMessage(p, "", "[i]", false);
+    } else {
+        mJava->indent() << "for (int j = 0; j < " << p.mVectorSize << " ; j++)";
+        mJava->startBlock();
+        writeJavaAppendOutputToMessage(p, "[j]", "[i * " + p.vectorWidth + " + j]", false);
+        mJava->endBlock();
+    }
+}
+
+void PermutationWriter::writeJavaCallToRs(bool relaxed, bool generateCallToVerifier) const {
+    string script = "script";
+    if (relaxed) {
+        script += "Relaxed";
+    }
+
+    mJava->indent() << "try";
+    mJava->startBlock();
+
+    for (auto p : mAllInputsAndOutputs) {
+        if (p->isOutParameter) {
+            writeJavaOutputAllocationDefinition(*p);
+        }
+    }
+
+    for (auto p : mPermutation.getParams()) {
+        if (p != mFirstInputParam) {
+            mJava->indent() << script << ".set_" << p->rsAllocName << "(" << p->javaAllocName
+                            << ");\n";
+        }
+    }
+
+    mJava->indent() << script << ".forEach_" << mRsKernelName << "(";
+    bool needComma = false;
+    if (mFirstInputParam) {
+        *mJava << mFirstInputParam->javaAllocName;
+        needComma = true;
+    }
+    if (mReturnParam) {
+        if (needComma) {
+            *mJava << ", ";
+        }
+        *mJava << mReturnParam->variableName << ");\n";
+    }
+
+    if (generateCallToVerifier) {
+        mJava->indent() << mJavaVerifyMethodName << "(";
+        for (auto p : mAllInputsAndOutputs) {
+            *mJava << p->variableName << ", ";
+        }
+
+        if (relaxed) {
+            *mJava << "true";
+        } else {
+            *mJava << "false";
+        }
+        *mJava << ");\n";
+    }
+    mJava->decreaseIndent();
+    mJava->indent() << "} catch (Exception e) {\n";
+    mJava->increaseIndent();
+    mJava->indent() << "throw new RSRuntimeException(\"RenderScript. Can't invoke forEach_"
+                    << mRsKernelName << ": \" + e.toString());\n";
+    mJava->endBlock();
+}
+
+/* Write the section of the .rs file for this permutation.
+ *
+ * We communicate the extra input and output parameters via global allocations.
+ * For example, if we have a function that takes three arguments, two for input
+ * and one for output:
+ *
+ * start:
+ * name: gamn
+ * ret: float3
+ * arg: float3 a
+ * arg: int b
+ * arg: float3 *c
+ * end:
+ *
+ * We'll produce:
+ *
+ * rs_allocation gAllocInB;
+ * rs_allocation gAllocOutC;
+ *
+ * float3 __attribute__((kernel)) test_gamn_float3_int_float3(float3 inA, unsigned int x) {
+ *    int inB;
+ *    float3 outC;
+ *    float2 out;
+ *    inB = rsGetElementAt_int(gAllocInB, x);
+ *    out = gamn(a, in_b, &outC);
+ *    rsSetElementAt_float4(gAllocOutC, &outC, x);
+ *    return out;
+ * }
+ *
+ * We avoid re-using x and y from the definition because these have reserved
+ * meanings in a .rs file.
+ */
+void PermutationWriter::writeRsSection(set<string>* rsAllocationsGenerated) const {
+    // Write the allocation declarations we'll need.
+    for (auto p : mPermutation.getParams()) {
+        // Don't need allocation for one input and one return value.
+        if (p != mFirstInputParam) {
+            writeRsAllocationDefinition(*p, rsAllocationsGenerated);
+        }
+    }
+    *mRs << "\n";
+
+    // Write the function header.
+    if (mReturnParam) {
+        *mRs << mReturnParam->rsType;
+    } else {
+        *mRs << "void";
+    }
+    *mRs << " __attribute__((kernel)) " << mRsKernelName;
+    *mRs << "(";
+    bool needComma = false;
+    if (mFirstInputParam) {
+        *mRs << mFirstInputParam->rsType << " " << mFirstInputParam->variableName;
+        needComma = true;
+    }
+    if (mPermutation.getOutputCount() > 1 || mPermutation.getInputCount() > 1) {
+        if (needComma) {
+            *mRs << ", ";
+        }
+        *mRs << "unsigned int x";
+    }
+    *mRs << ")";
+    mRs->startBlock();
+
+    // Write the local variable declarations and initializations.
+    for (auto p : mPermutation.getParams()) {
+        if (p == mFirstInputParam) {
+            continue;
+        }
+        mRs->indent() << p->rsType << " " << p->variableName;
+        if (p->isOutParameter) {
+            *mRs << " = 0;\n";
+        } else {
+            *mRs << " = rsGetElementAt_" << p->rsType << "(" << p->rsAllocName << ", x);\n";
+        }
+    }
+
+    // Write the function call.
+    if (mReturnParam) {
+        if (mPermutation.getOutputCount() > 1) {
+            mRs->indent() << mReturnParam->rsType << " " << mReturnParam->variableName << " = ";
+        } else {
+            mRs->indent() << "return ";
+        }
+    }
+    *mRs << mPermutation.getName() << "(";
+    needComma = false;
+    for (auto p : mPermutation.getParams()) {
+        if (needComma) {
+            *mRs << ", ";
+        }
+        if (p->isOutParameter) {
+            *mRs << "&";
+        }
+        *mRs << p->variableName;
+        needComma = true;
+    }
+    *mRs << ");\n";
+
+    if (mPermutation.getOutputCount() > 1) {
+        // Write setting the extra out parameters into the allocations.
+        for (auto p : mPermutation.getParams()) {
+            if (p->isOutParameter) {
+                mRs->indent() << "rsSetElementAt_" << p->rsType << "(" << p->rsAllocName << ", ";
+                // Check if we need to use '&' for this type of argument.
+                char lastChar = p->variableName.back();
+                if (lastChar >= '0' && lastChar <= '9') {
+                    *mRs << "&";
+                }
+                *mRs << p->variableName << ", x);\n";
+            }
+        }
+        if (mReturnParam) {
+            mRs->indent() << "return " << mReturnParam->variableName << ";\n";
+        }
+    }
+    mRs->endBlock();
+}
+
+void PermutationWriter::writeRsAllocationDefinition(const ParameterDefinition& param,
+                                                    set<string>* rsAllocationsGenerated) const {
+    if (!testAndSet(param.rsAllocName, rsAllocationsGenerated)) {
+        *mRs << "rs_allocation " << param.rsAllocName << ";\n";
+    }
+}
+
+// Open the mJavaFile and writes the header.
+static bool startJavaFile(GeneratedFile* file, const Function& function, const string& testName,
+                          const string& relaxedTestName) {
+    const string fileName = testName + ".java";
+    if (!file->start(fileName)) {
+        return false;
+    }
+    file->writeNotices();
+
+    *file << "package android.renderscript.cts;\n\n";
+
+    *file << "import android.renderscript.Allocation;\n";
+    *file << "import android.renderscript.RSRuntimeException;\n";
+    *file << "import android.renderscript.Element;\n\n";
+
+    *file << "public class " << testName << " extends RSBaseCompute";
+    file->startBlock();  // The corresponding endBlock() is in finishJavaFile()
+    *file << "\n";
+
+    file->indent() << "private ScriptC_" << testName << " script;\n";
+    file->indent() << "private ScriptC_" << relaxedTestName << " scriptRelaxed;\n\n";
+
+    file->indent() << "@Override\n";
+    file->indent() << "protected void setUp() throws Exception";
+    file->startBlock();
+
+    file->indent() << "super.setUp();\n";
+    file->indent() << "script = new ScriptC_" << testName << "(mRS);\n";
+    file->indent() << "scriptRelaxed = new ScriptC_" << relaxedTestName << "(mRS);\n";
+
+    file->endBlock();
+    *file << "\n";
+    return true;
+}
+
+// Write the test method that calls all the generated Check methods.
+static void finishJavaFile(GeneratedFile* file, const Function& function,
+                           const vector<string>& javaCheckMethods) {
+    file->indent() << "public void test" << function.getCapitalizedName() << "()";
+    file->startBlock();
+    for (auto m : javaCheckMethods) {
+        file->indent() << m << "();\n";
+    }
+    file->endBlock();
+
+    file->endBlock();
+}
+
+// Open the script file and write its header.
+static bool startRsFile(GeneratedFile* file, const Function& function, const string& testName) {
+    string fileName = testName + ".rs";
+    if (!file->start(fileName)) {
+        return false;
+    }
+    file->writeNotices();
+
+    *file << "#pragma version(1)\n";
+    *file << "#pragma rs java_package_name(android.renderscript.cts)\n\n";
+    return true;
+}
+
+// Write the entire *Relaxed.rs test file, as it only depends on the name.
+static bool writeRelaxedRsFile(const Function& function, const string& testName,
+                               const string& relaxedTestName) {
+    string name = relaxedTestName + ".rs";
+
+    GeneratedFile file;
+    if (!file.start(name)) {
+        return false;
+    }
+    file.writeNotices();
+
+    file << "#include \"" << testName << ".rs\"\n";
+    file << "#pragma rs_fp_relaxed\n";
+    file.close();
+    return true;
+}
+
+/* Write the .java and the two .rs test files.  versionOfTestFiles is used to restrict which API
+ * to test.
+ */
+static bool writeTestFilesForFunction(const Function& function, int versionOfTestFiles) {
+    // Avoid creating empty files if we're not testing this function.
+    if (!needTestFiles(function, versionOfTestFiles)) {
+        return true;
+    }
+
+    const string testName = "GeneratedTest" + function.getCapitalizedName();
+    const string relaxedTestName = testName + "Relaxed";
+
+    if (!writeRelaxedRsFile(function, testName, relaxedTestName)) {
+        return false;
+    }
+
+    GeneratedFile rsFile;    // The Renderscript test file we're generating.
+    GeneratedFile javaFile;  // The Jave test file we're generating.
+    if (!startRsFile(&rsFile, function, testName)) {
+        return false;
+    }
+
+    if (!startJavaFile(&javaFile, function, testName, relaxedTestName)) {
+        return false;
+    }
+
+    /* We keep track of the allocations generated in the .rs file and the argument classes defined
+     * in the Java file, as we share these between the functions created for each specification.
+     */
+    set<string> rsAllocationsGenerated;
+    set<string> javaGeneratedArgumentClasses;
+    // Lines of Java code to invoke the check methods.
+    vector<string> javaCheckMethods;
+
+    for (auto spec : function.getSpecifications()) {
+        if (spec->hasTests(versionOfTestFiles)) {
+            for (auto permutation : spec->getPermutations()) {
+                PermutationWriter w(*permutation, &rsFile, &javaFile);
+                w.writeRsSection(&rsAllocationsGenerated);
+                w.writeJavaSection(&javaGeneratedArgumentClasses);
+
+                // Store the check method to be called.
+                javaCheckMethods.push_back(w.getJavaCheckMethodName());
+            }
+        }
+    }
+
+    finishJavaFile(&javaFile, function, javaCheckMethods);
+    // There's no work to wrap-up in the .rs file.
+
+    rsFile.close();
+    javaFile.close();
+    return true;
+}
+
+bool GenerateTestFiles(int versionOfTestFiles) {
+    bool success = true;
+    for (auto specFile : systemSpecification.getSpecFiles()) {
+        for (auto f : specFile->getFunctionsMap()) {
+            if (!writeTestFilesForFunction(*f.second, versionOfTestFiles)) {
+                success = false;
+            }
+        }
+    }
+    return success;
+}