/*
 * Copyright (c) 2020, Peter Abeles. All Rights Reserved.
 *
 * This file is part of Efficient Java Matrix Library (EJML).
 *
 * 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.
 */

package org.ejml.dense.fixed;

import org.ejml.UtilEjml;
import org.ejml.dense.row.misc.GenerateDeterminantFromMinor;
import org.ejml.dense.row.misc.GenerateUnrolledInverseFromMinor_DDRM;

import java.io.FileNotFoundException;

/**
 * Automatic code generator for FixedOps
 *
 * @author Peter Abeles
 */
public class GenerateCommonOps_DDF extends GenerateFixed {

    public GenerateCommonOps_DDF() {
        super("CommonOps_DDF");
    }

    @Override
    public void generate() throws FileNotFoundException {
        for (int dimension = 2; dimension <= 6; dimension++) {
            printPreable(dimension);

            add(dimension);
            vector_add(dimension);
            addEquals(dimension);
            vector_addEquals(dimension);
            subtract(dimension);
            vector_subtract(dimension);
            subtractEquals(dimension);
            vector_subtractEquals(dimension);
            transpose_one(dimension);
            transpose_two(dimension);
            for (int i = 0; i < 2; i++) {
                boolean add = i == 1;
                mult(dimension, add);
                multScale(dimension, add);
                multTransA(dimension, add);
                multTransAScale(dimension, add);
                multTransAB(dimension, add);
                multTransABScale(dimension, add);
                multTransB(dimension, add);
                multTransBScale(dimension, add);
            }
            multAddOuter(dimension);
            mult_m_v_v(dimension);
            mult_v_m_v(dimension);
            dot(dimension);
            setIdentity(dimension);
            if (dimension <= UtilEjml.maxInverseSize) {
                invert(dimension);
                det(dimension);
            }
            cholL(dimension);
            cholU(dimension);
            trace(dimension);
            diag(dimension);
            elementMax(dimension);
            elementMax_vector(dimension);
            elementMaxAbs(dimension);
            elementMaxAbs_vector(dimension);
            elementMin(dimension);
            elementMin_vector(dimension);
            elementMinAbs(dimension);
            elementMinAbs_vector(dimension);
            elementMult_two(dimension);
            elementMult_vector_two(dimension);
            elementMult_three(dimension);
            elementMult_vector_three(dimension);
            elementDiv_two(dimension);
            elementDiv_vector_two(dimension);
            elementDiv_three(dimension);
            elementDiv_vector_three(dimension);
            scale_two(dimension);
            scale_vector_two(dimension);
            scale_three(dimension);
            scale_vector_three(dimension);
            divide_two(dimension);
            divide_vector_two(dimension);
            divide_three(dimension);
            divide_vector_three(dimension);
            changeSign(dimension);
            changeSign_vector(dimension);
            fill(dimension);
            fill_vector(dimension);
            extract(dimension);

            out.println("}\n");
        }
    }

    public void printPreable( int dimen ) throws FileNotFoundException {

        setClassNames(dimen);

        out.print(
                "import org.ejml.UtilEjml;\n" +
                        "import org.ejml.data." + nameVector + ";\n" +
                        "import org.ejml.data." + nameMatrix + ";\n" +
                        "\n" +
                        "/**\n" +
                        " * <p>Common matrix operations for fixed sized matrices which are " + dimen + " x " + dimen + " or " + dimen + " element vectors.</p>\n" +
                        standardClassDocClosing("Peter Abeles") +
                        "public class " + className + " {\n");
    }

    private void add( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c = a + b <br>\n" +
                "     * c<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * <p>\n" +
                "     * Matrix C can be the same instance as Matrix A and/or B.\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Matrix. Not modified.\n" +
                "     * @param b A Matrix. Not modified.\n" +
                "     * @param c A Matrix where the results are stored. Modified.\n" +
                "     */\n" +
                "    public static void add( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                String n = y + "" + x;
                out.print("        c.a" + n + " = a.a" + n + " + b.a" + n + ";\n");
            }
        }
        out.print("    }\n\n");
    }

    private void vector_add( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c = a + b <br>\n" +
                "     * c<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * <p>\n" +
                "     * Vector C can be the same instance as Vector A and/or B.\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Vector. Not modified.\n" +
                "     * @param b A Vector. Not modified.\n" +
                "     * @param c A Vector where the results are stored. Modified.\n" +
                "     */\n" +
                "    public static void add( " + nameVector + " a , " + nameVector + " b , " + nameVector + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        c.a" + y + " = a.a" + y + " + b.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void addEquals( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * a = a + b <br>\n" +
                "     * a<sub>ij</sub> = a<sub>ij</sub> + b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Matrix. Modified.\n" +
                "     * @param b A Matrix. Not modified.\n" +
                "     */\n" +
                "    public static void addEquals( " + nameMatrix + " a , " + nameMatrix + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                String n = y + "" + x;
                out.print("        a.a" + n + " += b.a" + n + ";\n");
            }
        }
        out.print("    }\n\n");
    }

    private void vector_addEquals( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * a = a + b <br>\n" +
                "     * a<sub>i</sub> = a<sub>i</sub> + b<sub>i</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Vector. Modified.\n" +
                "     * @param b A Vector. Not modified.\n" +
                "     */\n" +
                "    public static void addEquals( " + nameVector + " a , " + nameVector + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        a.a" + y + " += b.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void subtract( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c = a - b <br>\n" +
                "     * c<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * <p>\n" +
                "     * Matrix C can be the same instance as Matrix A and/or B.\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Matrix. Not modified.\n" +
                "     * @param b A Matrix. Not modified.\n" +
                "     * @param c A Matrix where the results are stored. Modified.\n" +
                "     */\n" +
                "    public static void subtract( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                String n = y + "" + x;
                out.print("        c.a" + n + " = a.a" + n + " - b.a" + n + ";\n");
            }
        }
        out.print("    }\n\n");
    }

    private void vector_subtract( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c = a - b <br>\n" +
                "     * c<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * <p>\n" +
                "     * Vector C can be the same instance as Vector A and/or B.\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Vector. Not modified.\n" +
                "     * @param b A Vector. Not modified.\n" +
                "     * @param c A Vector where the results are stored. Modified.\n" +
                "     */\n" +
                "    public static void subtract( " + nameVector + " a , " + nameVector + " b , " + nameVector + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        c.a" + y + " = a.a" + y + " - b.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void subtractEquals( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * a = a - b <br>\n" +
                "     * a<sub>ij</sub> = a<sub>ij</sub> - b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Matrix. Modified.\n" +
                "     * @param b A Matrix. Not modified.\n" +
                "     */\n" +
                "    public static void subtractEquals( " + nameMatrix + " a , " + nameMatrix + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                String n = y + "" + x;
                out.print("        a.a" + n + " -= b.a" + n + ";\n");
            }
        }
        out.print("    }\n\n");
    }

    private void vector_subtractEquals( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * a = a - b <br>\n" +
                "     * a<sub>i</sub> = a<sub>i</sub> - b<sub>i</sub> <br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A Vector. Modified.\n" +
                "     * @param b A Vector. Not modified.\n" +
                "     */\n" +
                "    public static void subtractEquals( " + nameVector + " a , " + nameVector + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            String n = "" + y;
            out.print("        a.a" + n + " -= b.a" + n + ";\n");
        }
        out.print("    }\n\n");
    }

    private void transpose_one( int dimen ) {
        out.print("    /**\n" +
                "     * Performs an in-place transpose. This algorithm is only efficient for square\n" +
                "     * matrices.\n" +
                "     *\n" +
                "     * @param m The matrix that is to be transposed. Modified.\n" +
                "     */\n" +
                "    public static void transpose( " + nameMatrix + " m ) {\n" +
                "        double tmp;\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = y + 1; x <= dimen; x++) {
                String f = +y + "" + x;
                String t = +x + "" + y;

                out.print("        tmp = m.a" + f + "; m.a" + f + " = m.a" + t + "; m.a" + t + " = tmp;\n");
            }
        }
        out.print("    }\n\n");
    }

    private void transpose_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Transposes matrix 'a' and stores the results in 'b':<br>\n" +
                "     * <br>\n" +
                "     * b<sub>ij</sub> = a<sub>ji</sub><br>\n" +
                "     * where 'b' is the transpose of 'a'.\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param input The original matrix. Not modified.\n" +
                "     * @param output Where the transpose is stored. If null a new matrix is created. Modified.\n" +
                "     * @return The transposed matrix.\n" +
                "     */\n" +
                "    public static " + nameMatrix + " transpose( " + nameMatrix + " input , " + nameMatrix + " output ) {\n" +
                "        if( input == null )\n" +
                "            input = new " + nameMatrix + "();\n\n");
        out.print("        UtilEjml.checkSameInstance(input,output);\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                String f = +y + "" + x;
                String t = +x + "" + y;

                out.print("        output.a" + f + " = input.a" + t + ";\n");
            }
        }

        out.print("\n        return output;\n" +
                "    }\n\n");
    }

    private void mult( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAdd" : "mult";

        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= a * b <br>\n" +
                "     * <br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &sum;<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");

        out.print(
                "        UtilEjml.checkSameInstance(a,c);\n" +
                        "        UtilEjml.checkSameInstance(b,c);\n");

        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= ");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + y + "" + k + "*b.a" + k + "" + x);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(";\n");
                }
            }
        }
        out.print("    }\n\n");
    }

    private void multScale( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAdd" : "mult";

        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= &alpha; * a * b <br>\n" +
                "     * <br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &alpha; &sum;<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>kj</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha Scaling factor.\n" +
                "     * @param a (Input) The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b (Input) The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( double alpha , " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");

        out.print("        UtilEjml.checkSameInstance(a,c);\n" +
                "        UtilEjml.checkSameInstance(b,c);\n");

        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= alpha*(");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + y + "" + k + "*b.a" + k + "" + x);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(");\n");
                }
            }
        }
        out.print("    }\n\n");
    }

    private void multTransA( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAddTransA" : "multTransA";

        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= a<sup>T</sup> * b <br>\n" +
                "     * <br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &sum;<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a (Input) The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b (Input) The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
        out.print("        UtilEjml.checkSameInstance(a,c);\n" +
                "        UtilEjml.checkSameInstance(b,c);\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= ");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + k + "" + y + "*b.a" + k + "" + x);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(";\n");
                }
            }
        }
        out.printf("    }\n\n");
    }

    private void multTransAScale( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAddTransA" : "multTransA";

        out.print("    /**\n" +
                "     * <p>Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= &alpha; * a<sup>T</sup> * b <br>\n" +
                "     * <br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &alpha; * &sum;<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>kj</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha Scaling factor.\n" +
                "     * @param a (Input) The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b (Input) The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( double alpha , " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
        out.print("        UtilEjml.checkSameInstance(a,c);\n" +
                "        UtilEjml.checkSameInstance(b,c);\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= alpha*(");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + k + "" + y + "*b.a" + k + "" + x);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(");\n");
                }
            }
        }
        out.printf("    }\n\n");
    }

    private void multTransAB( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAddTransAB" : "multTransAB";

        out.printf("    /**\n" +
                "     * <p>\n" +
                "     * Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= a<sup>T</sup> * b<sup>T</sup><br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &sum;<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a (Input) The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b (Input) The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
        out.print("        UtilEjml.checkSameInstance(a,c);\n" +
                "        UtilEjml.checkSameInstance(b,c);\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= ");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + k + "" + y + "*b.a" + x + "" + k);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(";\n");
                }
            }
        }
        out.print("    }\n\n");
    }

    private void multTransABScale( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAddTransAB" : "multTransAB";

        out.printf("    /**\n" +
                "     * <p>\n" +
                "     * Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= &alpha;*a<sup>T</sup> * b<sup>T</sup><br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &alpha;*&sum;<sub>k=1:n</sub> { a<sub>ki</sub> * b<sub>jk</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha Scaling factor.\n" +
                "     * @param a (Input) The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b (Input) The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( double alpha , " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
        out.print("        UtilEjml.checkSameInstance(a,c);\n" +
                "        UtilEjml.checkSameInstance(b,c);\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= alpha*(");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + k + "" + y + "*b.a" + x + "" + k);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(");\n");
                }
            }
        }
        out.print("    }\n\n");
    }

    private void multTransB( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAddTransB" : "multTransB";

        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= a * b<sup>T</sup> <br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &sum;<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a (Input) The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b (Input) The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
        out.print("        UtilEjml.checkSameInstance(a,c);\n" +
                "        UtilEjml.checkSameInstance(b,c);\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= ");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + y + "" + k + "*b.a" + x + "" + k);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(";\n");
                }
            }
        }
        out.print("    }\n\n");
    }

    private void multTransBScale( int dimen, boolean add ) {
        String plus = add ? "+" : "";
        String name = add ? "multAddTransB" : "multTransB";

        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs the following operation:<br>\n" +
                "     * <br>\n" +
                "     * c " + plus + "= &alpha; * a * b<sup>T</sup> <br>\n" +
                "     * c<sub>ij</sub> " + plus + "= &alpha;*&sum;<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>jk</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha Scaling factor.\n" +
                "     * @param a (Input) The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b (Input) The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c (Output) Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void " + name + "( double alpha , " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c) {\n");
        out.print("        UtilEjml.checkSameInstance(a,c);\n" +
                "        UtilEjml.checkSameInstance(b,c);\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                out.print("        c.a" + y + "" + x + " " + plus + "= alpha*(");
                for (int k = 1; k <= dimen; k++) {
                    out.print("a.a" + y + "" + k + "*b.a" + x + "" + k);
                    if (k < dimen)
                        out.print(" + ");
                    else
                        out.print(");\n");
                }
            }
        }
        out.print("    }\n\n");
    }

    private void mult_m_v_v( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs matrix to vector multiplication:<br>\n" +
                "     * <br>\n" +
                "     * c = a * b <br>\n" +
                "     * <br>\n" +
                "     * c<sub>i</sub> = &sum;<sub>k=1:n</sub> { a<sub>ik</sub> * b<sub>k</sub>}\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b The right vector in the multiplication operation. Not modified.\n" +
                "     * @param c Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void mult( " + nameMatrix + " a , " + nameVector + " b , " + nameVector + " c) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        c.a" + y + " = ");
            for (int x = 1; x <= dimen; x++) {
                out.print("a.a" + y + "" + x + "*b.a" + x);
                if (x < dimen)
                    out.print(" + ");
                else
                    out.print(";\n");
            }
        }
        out.printf("    }\n\n");
    }

    private void multAddOuter( int dimen ) {
        out.print("    /**\n" +
                "     * C = &alpha;A + &beta;u*v<sup>T</sup>\n" +
                "     * \n" +
                "     * @param alpha scale factor applied to A\n" +
                "     * @param A matrix\n" +
                "     * @param beta scale factor applies to outer product\n" +
                "     * @param u vector\n" +
                "     * @param v vector\n" +
                "     * @param C Storage for solution. Can be same instance as A.\n" +
                "     */\n" +
                "    public static void multAddOuter( double alpha , " + nameMatrix + " A , double beta , " + nameVector + " u , " + nameVector + " v , " + nameMatrix + " C ) {\n");
        for (int i = 1; i <= dimen; i++) {
            for (int j = 1; j <= dimen; j++) {
                String m = i + "" + j;
                out.println("        C.a" + m + " = alpha*A.a" + m + " + beta*u.a" + i + "*v.a" + j + ";");
            }
        }
        out.printf("    }\n\n");
    }

    private void mult_v_m_v( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs vector to matrix multiplication:<br>\n" +
                "     * <br>\n" +
                "     * c = a * b <br>\n" +
                "     * <br>\n" +
                "     * c<sub>j</sub> = &sum;<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>kj</sub> }\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The left vector in the multiplication operation. Not modified.\n" +
                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void mult( " + nameVector + " a , " + nameMatrix + " b , " + nameVector + " c) {\n");

        for (int y = 1; y <= dimen; y++) {
            out.print("        c.a" + y + " = ");
            for (int x = 1; x <= dimen; x++) {
                out.print("a.a" + x + "*b.a" + x + "" + y);
                if (x < dimen)
                    out.print(" + ");
                else
                    out.print(";\n");
            }
        }
        out.print("    }\n\n");
    }

    private void dot( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs the vector dot product:<br>\n" +
                "     * <br>\n" +
                "     * c = a * b <br>\n" +
                "     * <br>\n" +
                "     * c &ge; &sum;<sub>k=1:n</sub> { b<sub>k</sub> * a<sub>k</sub> }\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The left vector in the multiplication operation. Not modified.\n" +
                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
                "     * @return The dot product\n" +
                "     */\n" +
                "    public static double dot( " + nameVector + " a , " + nameVector + " b ) {\n");
        out.print("        return ");
        for (int i = 1; i <= dimen; i++) {
            out.print("a.a" + i + "*b.a" + i);
            if (i < dimen)
                out.print(" + ");
            else
                out.print(";\n");
        }
        out.print("    }\n\n");
    }

    private void setIdentity( int dimen ) {
        out.print("    /**\n" +
                "     * Sets all the diagonal elements equal to one and everything else equal to zero.\n" +
                "     * If this is a square matrix then it will be an identity matrix.\n" +
                "     *\n" +
                "     * @param a A matrix.\n" +
                "     */\n" +
                "    public static void setIdentity( " + nameMatrix + " a ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                int val = x == y ? 1 : 0;
                out.print("a.a" + x + "" + y + " = " + val + ";");
                if (x < dimen)
                    out.print(" ");
                else
                    out.print("\n");
            }
        }
        out.print("    }\n\n");
    }

    private void invert( int dimen ) {
        out.print("    /**\n" +
                "     * Inverts matrix 'a' using minor matrices and stores the results in 'inv'. Scaling is applied to improve\n" +
                "     * stability against overflow and underflow.\n" +
                "     *\n" +
                "     * WARNING: Potentially less stable than using LU decomposition.\n" +
                "     *\n" +
                "     * @param a (Input) Matrix.\n" +
                "     * @param inv (Output) Inverted matrix. Can be the same as 'a'.\n" +
                "     * @return true if it was successful or false if it failed. Not reliable.\n" +
                "     */\n" +
                "    public static boolean invert( " + nameMatrix + " a , " + nameMatrix + " inv ) {\n" +
                "\n" +
                "        double scale = 1.0/elementMaxAbs(a);\n" +
                "\n");

        int[] matrix = new int[dimen*dimen];
        int index = 0;
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++, index++) {
                matrix[index] = index;
                String coor = y + "" + x;
                out.print("        double a" + coor + " = a.a" + coor + "*scale;\n");
            }
        }
        out.println();

        GenerateUnrolledInverseFromMinor_DDRM.printMinors(matrix, dimen, out);
        out.println();

        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                String coor0 = y + "" + x;
                String coor1 = x + "" + y;
                out.print("        inv.a" + coor0 + " = m" + coor1 + "/det;\n");
            }
        }
        out.println();
        out.print("        return !Double.isNaN(det) && !Double.isInfinite(det);\n");
        out.print("    }\n\n");
    }

    private void cholL( int N ) {
        out.print("    /**\n" +
                "     * Performs a lower Cholesky decomposition of matrix 'A' and stores result in A.\n" +
                "     *\n" +
                "     * @param A (Input) SPD Matrix. (Output) lower cholesky.\n" +
                "     * @return true if it was successful or false if it failed. Not always reliable.\n" +
                "     */\n" +
                "    public static boolean cholL( " + nameMatrix + " A ) {\n" +
                "\n");

        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= N; j++) {
                if (j > i) {
                    out.print("        A." + el(i, j) + " = 0;\n");
                } else if (i == j) {
                    out.print("        A." + el(i, i) + " = Math.sqrt(A." + el(i, i));
                    for (int k = 1; k < j; k++) {
                        out.print("-A." + el(i, k) + "*A." + el(i, k));
                    }
                    out.println(");");
                } else {
                    out.print("        A." + el(i, j) + " = (A." + el(i, j));
                    for (int k = 1; k < j; k++) {
                        out.print("-A." + el(i, k) + "*A." + el(j, k));
                    }
                    out.println(")/A." + el(j, j) + ";");
                }
            }
        }
        out.println("        return !UtilEjml.isUncountable(A." + el(N, N) + ");");
        out.print("    }\n\n");
    }

    private void cholU( int N ) {
        out.print("    /**\n" +
                "     * Performs an upper Cholesky decomposition of matrix 'A' and stores result in A.\n" +
                "     *\n" +
                "     * @param A (Input) SPD Matrix. (Output) upper cholesky.\n" +
                "     * @return true if it was successful or false if it failed. Not always reliable.\n" +
                "     */\n" +
                "    public static boolean cholU( " + nameMatrix + " A ) {\n" +
                "\n");

        for (int j = 1; j <= N; j++) {
            for (int i = 1; i <= N; i++) {
                if (j < i) {
                    out.println("        A." + el(i, j) + " = 0;");
                } else if (i == j) {
                    out.print("        A." + el(i, i) + " = Math.sqrt(A." + el(i, i));
                    for (int k = 1; k < i; k++) {
                        out.print("-A." + el(k, i) + "*A." + el(k, i));
                    }
                    out.println(");");
                } else {
                    out.print("        A." + el(i, j) + " = (A." + el(i, j));
                    for (int k = 1; k < i; k++) {
                        out.print("-A." + el(k, i) + "*A." + el(k, j));
                    }
                    out.println(")/A." + el(i, i) + ";");
                }
            }
        }
        out.println("        return !UtilEjml.isUncountable(A." + el(N, N) + ");");
        out.print("    }\n\n");
    }

    private static String el( int row, int col ) {
        return "a" + row + "" + col;
    }

    private void trace( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * This computes the trace of the matrix:<br>\n" +
                "     * <br>\n" +
                "     * trace = &sum;<sub>i=1:n</sub> { a<sub>ii</sub> }\n" +
                "     * </p>\n" +
                "     * <p>\n" +
                "     * The trace is only defined for square matrices.\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A square matrix. Not modified.\n" +
                "     */\n" +
                "    public static double trace( " + nameMatrix + " a ) {\n");
        out.print("        return ");
        for (int i = 1; i <= dimen; i++) {
            out.print("a.a" + i + "" + i);
            if (i < dimen)
                out.print(" + ");
            else
                out.println(";");
        }
        out.print("    }\n\n");
    }

    private void det( int dimen ) {
        out.print("    /**\n" +
                "     * Computes the determinant using minor matrices.<br>\n" +
                "     * WARNING: Potentially less stable than using LU decomposition.\n" +
                "     *\n" +
                "     * @param mat Input matrix. Not modified.\n" +
                "     * @return The determinant.\n" +
                "     */\n" +
                "    public static double det( " + nameMatrix + " mat ) {\n" +
                "\n");
        if (dimen == 2) {
            out.print("        return mat.a11*mat.a22 - mat.a12*mat.a21;\n");
        } else if (dimen == 3) {
            out.print("        double a = mat.a11*(mat.a22*mat.a33 - mat.a23*mat.a32);\n" +
                    "        double b = mat.a12*(mat.a21*mat.a33 - mat.a23*mat.a31);\n" +
                    "        double c = mat.a13*(mat.a21*mat.a32 - mat.a31*mat.a22);\n" +
                    "\n" +
                    "        return a-b+c;\n");
        } else {
            GenerateDeterminantFromMinor helper = new GenerateDeterminantFromMinor(out) {
                @Override
                protected String getInputValue( int element ) {
                    int row = element/(N + 1) + 1;
                    int col = element%(N + 1) + 1;
                    return "mat.a" + row + "" + col;
                }
            };
            helper.printFunctionInner(dimen);
            out.print("\n        return ret;\n");
        }

        out.print("    }\n\n");
    }

    private void diag( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Extracts all diagonal elements from 'input' and places them inside the 'out' vector. Elements\n" +
                "     * are in sequential order.\n" +
                "     * </p>\n" +
                "     *\n" +
                "     *\n" +
                "     * @param input Matrix. Not modified.\n" +
                "     * @param out Vector containing diagonal elements. Modified.\n" +
                "     */\n" +
                "    public static void diag( " + nameMatrix + " input , " + nameVector + " out ) {\n");
        for (int i = 1; i <= dimen; i++) {
            out.print("        out.a" + i + " = input.a" + i + "" + i + ";\n");
        }
        out.print("    }\n\n");
    }

    private void elementMax( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the value of the element in the matrix that has the largest value.<br>\n" +
                "     * <br>\n" +
                "     * Max{ a<sub>ij</sub> } for all i and j<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Not modified.\n" +
                "     * @return The max element value of the matrix.\n" +
                "     */\n" +
                "    public static double elementMax( " + nameMatrix + " a ) {\n");

        out.print("        double max = a.a11;\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                if (y == 1 && x == 1)
                    continue;
                String e = "a.a" + y + "" + x;
                out.print("        if( " + e + " > max ) max = " + e + ";\n");
            }
        }
        out.print("\n" +
                "        return max;\n" +
                "    }\n\n");
    }

    private void elementMax_vector( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the value of the element in the vector that has the largest value.<br>\n" +
                "     * <br>\n" +
                "     * Max{ a<sub>i</sub> } for all i<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A vector. Not modified.\n" +
                "     * @return The max element value of the matrix.\n" +
                "     */\n" +
                "    public static double elementMax( " + nameVector + " a ) {\n");

        out.print("        double max = a.a1;\n");
        for (int y = 2; y <= dimen; y++) {
            String e = "a.a" + y;
            out.print("        if( " + e + " > max ) max = " + e + ";\n");
        }
        out.print("\n" +
                "        return max;\n" +
                "    }\n\n");
    }

    private void elementMaxAbs( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the absolute value of the element in the matrix that has the largest absolute value.<br>\n" +
                "     * <br>\n" +
                "     * Max{ |a<sub>ij</sub>| } for all i and j<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Not modified.\n" +
                "     * @return The max abs element value of the matrix.\n" +
                "     */\n" +
                "    public static double elementMaxAbs( " + nameMatrix + " a ) {\n");

        out.print("        double max = Math.abs(a.a11);\n");
        out.print("        double tmp = Math.abs(a.a12); if( tmp > max ) max = tmp;\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                if (y == 1 && x <= 2)
                    continue;
                out.print("        tmp = Math.abs(a.a" + y + "" + x + "); if( tmp > max ) max = tmp;\n");
            }
        }
        out.print("\n" +
                "        return max;\n" +
                "    }\n\n");
    }

    private void elementMaxAbs_vector( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the absolute value of the element in the vector that has the largest absolute value.<br>\n" +
                "     * <br>\n" +
                "     * Max{ |a<sub>i</sub>| } for all i<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Not modified.\n" +
                "     * @return The max abs element value of the vector.\n" +
                "     */\n" +
                "    public static double elementMaxAbs( " + nameVector + " a ) {\n");

        out.print("        double max = Math.abs(a.a1);\n");
        out.print("        double tmp = Math.abs(a.a2); if( tmp > max ) max = tmp;\n");
        for (int y = 2; y <= dimen; y++) {
            out.print("        tmp = Math.abs(a.a" + y + "); if( tmp > max ) max = tmp;\n");
        }
        out.print("\n" +
                "        return max;\n" +
                "    }\n\n");
    }

    private void elementMin( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the value of the element in the matrix that has the minimum value.<br>\n" +
                "     * <br>\n" +
                "     * Min{ a<sub>ij</sub> } for all i and j<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Not modified.\n" +
                "     * @return The value of element in the matrix with the minimum value.\n" +
                "     */\n" +
                "    public static double elementMin( " + nameMatrix + " a ) {\n");

        out.print("        double min = a.a11;\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                if (y == 1 && x == 1)
                    continue;
                String e = "a.a" + y + "" + x;
                out.print("        if( " + e + " < min ) min = " + e + ";\n");
            }
        }
        out.print("\n" +
                "        return min;\n" +
                "    }\n\n");
    }

    private void elementMin_vector( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the value of the element in the vector that has the minimum value.<br>\n" +
                "     * <br>\n" +
                "     * Min{ a<sub>i</sub> } for all<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Not modified.\n" +
                "     * @return The value of element in the vector with the minimum value.\n" +
                "     */\n" +
                "    public static double elementMin( " + nameVector + " a ) {\n");

        out.print("        double min = a.a1;\n");
        for (int y = 2; y <= dimen; y++) {
            String e = "a.a" + y;
            out.print("        if( " + e + " < min ) min = " + e + ";\n");
        }
        out.print("\n" +
                "        return min;\n" +
                "    }\n\n");
    }

    private void elementMinAbs( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the absolute value of the element in the matrix that has the smallest absolute value.<br>\n" +
                "     * <br>\n" +
                "     * Min{ |a<sub>ij</sub>| } for all i and j<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Not modified.\n" +
                "     * @return The max element value of the matrix.\n" +
                "     */\n" +
                "    public static double elementMinAbs( " + nameMatrix + " a ) {\n");

        out.print("        double min = Math.abs(a.a11);\n");
        out.print("        double tmp = Math.abs(a.a12); if( tmp < min ) min = tmp;\n");
        for (int y = 1; y <= dimen; y++) {
            for (int x = 1; x <= dimen; x++) {
                if (y == 1 && x <= 2)
                    continue;
                out.print("        tmp = Math.abs(a.a" + y + "" + x + "); if( tmp < min ) min = tmp;\n");
            }
        }
        out.print("\n" +
                "        return min;\n" +
                "    }\n\n");
    }

    private void elementMinAbs_vector( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Returns the absolute value of the element in the vector that has the smallest absolute value.<br>\n" +
                "     * <br>\n" +
                "     * Min{ |a<sub>i</sub>| } for all i<br>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Not modified.\n" +
                "     * @return The max element value of the vector.\n" +
                "     */\n" +
                "    public static double elementMinAbs( " + nameVector + " a ) {\n");

        out.print("        double min = Math.abs(a.a1);\n");
        out.print("        double tmp = Math.abs(a.a1); if( tmp < min ) min = tmp;\n");
        for (int y = 2; y <= dimen; y++) {
            out.print("        tmp = Math.abs(a.a" + y + "); if( tmp < min ) min = tmp;\n");
        }
        out.print("\n" +
                "        return min;\n" +
                "    }\n\n");
    }

    private void elementMult_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element multiplication operation:<br>\n" +
                "     * <br>\n" +
                "     * a<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left matrix in the multiplication operation. Modified.\n" +
                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
                "     */\n" +
                "    public static void elementMult( " + nameMatrix + " a , " + nameMatrix + " b) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("a." + w + " *= b." + w + ";");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void elementMult_vector_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element multiplication operation:<br>\n" +
                "     * <br>\n" +
                "     * a<sub>i</sub> = a<sub>i</sub> * b<sub>i</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left vector in the multiplication operation. Modified.\n" +
                "     * @param b The right vector in the multiplication operation. Not modified.\n" +
                "     */\n" +
                "    public static void elementMult( " + nameVector + " a , " + nameVector + " b) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        a.a" + y + " *= b.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void elementMult_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element multiplication operation:<br>\n" +
                "     * <br>\n" +
                "     * c<sub>ij</sub> = a<sub>ij</sub> * b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left matrix in the multiplication operation. Not modified.\n" +
                "     * @param b The right matrix in the multiplication operation. Not modified.\n" +
                "     * @param c Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void elementMult( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("c." + w + " = a." + w + "*b." + w + ";");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void elementMult_vector_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element multiplication operation:<br>\n" +
                "     * <br>\n" +
                "     * c<sub>i</sub> = a<sub>i</sub> * b<sub>j</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left vector in the multiplication operation. Not modified.\n" +
                "     * @param b The right vector in the multiplication operation. Not modified.\n" +
                "     * @param c Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void elementMult( " + nameVector + " a , " + nameVector + " b , " + nameVector + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        c.a" + y + " = a.a" + y + "*b.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void elementDiv_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element division operation:<br>\n" +
                "     * <br>\n" +
                "     * a<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left matrix in the division operation. Modified.\n" +
                "     * @param b The right matrix in the division operation. Not modified.\n" +
                "     */\n" +
                "    public static void elementDiv( " + nameMatrix + " a , " + nameMatrix + " b) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("a." + w + " /= b." + w + ";");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void elementDiv_vector_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element division operation:<br>\n" +
                "     * <br>\n" +
                "     * a<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left vector in the division operation. Modified.\n" +
                "     * @param b The right vector in the division operation. Not modified.\n" +
                "     */\n" +
                "    public static void elementDiv( " + nameVector + " a , " + nameVector + " b) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        a.a" + y + " /= b.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void elementDiv_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element division operation:<br>\n" +
                "     * <br>\n" +
                "     * c<sub>ij</sub> = a<sub>ij</sub> / b<sub>ij</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left matrix in the division operation. Not modified.\n" +
                "     * @param b The right matrix in the division operation. Not modified.\n" +
                "     * @param c Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void elementDiv( " + nameMatrix + " a , " + nameMatrix + " b , " + nameMatrix + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("c." + w + " = a." + w + "/b." + w + ";");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void elementDiv_vector_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>Performs an element by element division operation:<br>\n" +
                "     * <br>\n" +
                "     * c<sub>i</sub> = a<sub>i</sub> / b<sub>i</sub> <br>\n" +
                "     * </p>\n" +
                "     * @param a The left vector in the division operation. Not modified.\n" +
                "     * @param b The right vector in the division operation. Not modified.\n" +
                "     * @param c Where the results of the operation are stored. Modified.\n" +
                "     */\n" +
                "    public static void elementDiv( " + nameVector + " a , " + nameVector + " b , " + nameVector + " c ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        c.a" + y + " = a.a" + y + "/b.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void scale_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an in-place element by element scalar multiplication.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>ij</sub> = &alpha;*a<sub>ij</sub>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The matrix that is to be scaled. Modified.\n" +
                "     * @param alpha the amount each element is multiplied by.\n" +
                "     */\n" +
                "    public static void scale( double alpha , " + nameMatrix + " a ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("a." + w + " *= alpha;");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void scale_vector_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an in-place element by element scalar multiplication.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>ij</sub> = &alpha;*a<sub>ij</sub>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The vector that is to be scaled. Modified.\n" +
                "     * @param alpha the amount each element is multiplied by.\n" +
                "     */\n" +
                "    public static void scale( double alpha , " + nameVector + " a ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        a.a" + y + " *= alpha;\n");
        }
        out.print("    }\n\n");
    }

    private void scale_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an element by element scalar multiplication.<br>\n" +
                "     * <br>\n" +
                "     * b<sub>ij</sub> = &alpha;*a<sub>ij</sub>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha the amount each element is multiplied by.\n" +
                "     * @param a The matrix that is to be scaled. Not modified.\n" +
                "     * @param b Where the scaled matrix is stored. Modified.\n" +
                "     */\n" +
                "    public static void scale( double alpha , " + nameMatrix + " a , " + nameMatrix + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("b." + w + " = a." + w + "*alpha;");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void scale_vector_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an element by element scalar multiplication.<br>\n" +
                "     * <br>\n" +
                "     * b<sub>i</sub> = &alpha;*a<sub>i</sub>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha the amount each element is multiplied by.\n" +
                "     * @param a The vector that is to be scaled. Not modified.\n" +
                "     * @param b Where the scaled matrix is stored. Modified.\n" +
                "     */\n" +
                "    public static void scale( double alpha , " + nameVector + " a , " + nameVector + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        b.a" + y + " = a.a" + y + "*alpha;\n");
        }
        out.print("    }\n\n");
    }

    private void divide_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an in-place element by element scalar division. Scalar denominator.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>ij</sub> = a<sub>ij</sub>/&alpha;\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The matrix whose elements are to be divided. Modified.\n" +
                "     * @param alpha the amount each element is divided by.\n" +
                "     */\n" +
                "    public static void divide( " + nameMatrix + " a , double alpha ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("a." + w + " /= alpha;");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void divide_vector_two( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an in-place element by element scalar division. Scalar denominator.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>i</sub> = a<sub>i</sub>/&alpha;\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a The vector whose elements are to be divided. Modified.\n" +
                "     * @param alpha the amount each element is divided by.\n" +
                "     */\n" +
                "    public static void divide( " + nameVector + " a , double alpha ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        a.a" + y + " /= alpha;\n");
        }
        out.print("    }\n\n");
    }

    private void divide_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an element by element scalar division. Scalar denominator.<br>\n" +
                "     * <br>\n" +
                "     * b<sub>ij</sub> = a<sub>ij</sub> /&alpha;\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha the amount each element is divided by.\n" +
                "     * @param a The matrix whose elements are to be divided. Not modified.\n" +
                "     * @param b Where the results are stored. Modified.\n" +
                "     */\n" +
                "    public static void divide( " + nameMatrix + " a , double alpha , " + nameMatrix + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("b." + w + " = a." + w + "/alpha;");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void divide_vector_three( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Performs an element by element scalar division. Scalar denominator.<br>\n" +
                "     * <br>\n" +
                "     * b<sub>i</sub> = a<sub>i</sub> /&alpha;\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param alpha the amount each element is divided by.\n" +
                "     * @param a The vector whose elements are to be divided. Not modified.\n" +
                "     * @param b Where the results are stored. Modified.\n" +
                "     */\n" +
                "    public static void divide( " + nameVector + " a , double alpha , " + nameVector + " b ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        b.a" + y + " = a.a" + y + "/alpha;\n");
        }
        out.print("    }\n\n");
    }

    private void changeSign( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Changes the sign of every element in the matrix.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>ij</sub> = -a<sub>ij</sub>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A matrix. Modified.\n" +
                "     */\n" +
                "    public static void changeSign( " + nameMatrix + " a )\n" +
                "    {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("a." + w + " = -a." + w + ";");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void changeSign_vector( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Changes the sign of every element in the vector.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>i</sub> = -a<sub>i</sub>\n" +
                "     * </p>\n" +
                "     *\n" +
                "     * @param a A vector. Modified.\n" +
                "     */\n" +
                "    public static void changeSign( " + nameVector + " a )\n" +
                "    {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        a.a" + y + " = -a.a" + y + ";\n");
        }
        out.print("    }\n\n");
    }

    private void fill( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Sets every element in the matrix to the specified value.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>ij</sub> = value\n" +
                "     * <p>\n" +
                "     *\n" +
                "     * @param a A matrix whose elements are about to be set. Modified.\n" +
                "     * @param v The value each element will have.\n" +
                "     */\n" +
                "    public static void fill( " + nameMatrix + " a , double v  ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        ");
            for (int x = 1; x <= dimen; x++) {
                String w = "a" + y + "" + x;
                out.print("a." + w + " = v;");
                if (x < dimen)
                    out.print(" ");
                else
                    out.println();
            }
        }
        out.print("    }\n\n");
    }

    private void fill_vector( int dimen ) {
        out.print("    /**\n" +
                "     * <p>\n" +
                "     * Sets every element in the vector to the specified value.<br>\n" +
                "     * <br>\n" +
                "     * a<sub>i</sub> = value\n" +
                "     * <p>\n" +
                "     *\n" +
                "     * @param a A vector whose elements are about to be set. Modified.\n" +
                "     * @param v The value each element will have.\n" +
                "     */\n" +
                "    public static void fill( " + nameVector + " a , double v  ) {\n");
        for (int y = 1; y <= dimen; y++) {
            out.print("        a.a" + y + " = v;\n");
        }
        out.print("    }\n\n");
    }

    private void extract( int dimen ) {
        out.print("    /**\n" +
                "     * Extracts the row from the matrix a.\n" +
                "     * @param a Input matrix\n" +
                "     * @param row Which row is to be extracted\n" +
                "     * @param out output. Storage for the extracted row. If null then a new vector will be returned.\n" +
                "     * @return The extracted row.\n" +
                "     */\n" +
                "    public static " + nameVector + " extractRow( " + nameMatrix + " a , int row , " + nameVector + " out ) {\n" +
                "        if( out == null) out = new " + nameVector + "();\n" +
                "        switch( row ) {\n");
        for (int i = 0; i < dimen; i++) {
            out.print("            case " + i + ":\n");
            for (int j = 0; j < dimen; j++) {
                int n = j + 1;
                out.print("                out.a" + n + " = a.a" + (i + 1) + "" + n + ";\n");
            }
            out.print("            break;\n");
        }
        out.print("            default:\n" +
                "                throw new IllegalArgumentException(\"Out of bounds row. row = \"+row);\n" +
                "        }\n" +
                "        return out;\n" +
                "    }\n" +
                "\n" +
                "    /**\n" +
                "     * Extracts the column from the matrix a.\n" +
                "     * @param a Input matrix\n" +
                "     * @param column Which column is to be extracted\n" +
                "     * @param out output. Storage for the extracted column. If null then a new vector will be returned.\n" +
                "     * @return The extracted column.\n" +
                "     */\n" +
                "    public static " + nameVector + " extractColumn( " + nameMatrix + " a , int column , " + nameVector + " out ) {\n" +
                "        if( out == null) out = new " + nameVector + "();\n" +
                "        switch( column ) {\n");
        for (int i = 0; i < dimen; i++) {
            out.print("            case " + i + ":\n");
            for (int j = 0; j < dimen; j++) {
                int n = j + 1;
                out.print("                out.a" + n + " = a.a" + n + "" + (i + 1) + ";\n");
            }
            out.print("            break;\n");
        }
        out.print("            default:\n" +
                "                throw new IllegalArgumentException(\"Out of bounds column. column = \"+column);\n" +
                "        }\n" +
                "        return out;\n" +
                "    }\n\n");
    }

    public static void main( String[] args ) throws FileNotFoundException {
        GenerateCommonOps_DDF app = new GenerateCommonOps_DDF();

        app.generate();
    }
}