<commit_before>/*
 * Copyright (C) 2016 <NAME>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package jacobi.core.solver;

import jacobi.api.Matrix;
import java.util.stream.IntStream;

/**
 * Implementation of Backward and Forward Substitution.
 * 
 * For practical and performance reasons, this class would check if the input
 * matrix is truly upper or lower triangular. It is up to the caller to ensure
 * the data integrity of the input.
 * 
 * @author <NAME>
 */
public class Substitution {
    
    /**
     * Constructor.
     * @param mode  Forward/Backward substitution
     * @param tri  Lower/Upper triangular matrix
     */
    public Substitution(Mode mode, Matrix tri) {
        this.tri = tri;
        this.mode = mode;
    }
    
    /**
     * Compute substitution. This method is perturbative, i.e. it transforms 
     * the input into the output.
     * @param rhs  Right-hand side of the equation
     * @return  Object rhs after substitution
     */
    public Matrix compute(Matrix rhs) {
        return this.mode == Mode.BACKWARD ? this.backward(rhs) : this.forward(rhs);
    }
    
    /**
     * Forward substitution. This method is perturbative, i.e. it transforms 
     * the input into the output.
     * @param rhs  Right-hand side of the equation
     * @return  Object rhs after substitution
     */
    protected Matrix forward(Matrix rhs) {
        for(int i = 0; i < rhs.getRowCount(); i++){
            double[] sol = this.normalize(rhs, i);
            this.substitute(rhs, sol, i, i + 1, rhs.getRowCount());
            rhs.setRow(i, sol);
        }
        return rhs;
    }
    
    /**
     * Backward substitution. This method is perturbative, i.e. it transforms 
     * the input into the output.
     * @param rhs  Right-hand side of the equation
     * @return  Object rhs after substitution
     */
    protected Matrix backward(Matrix rhs) {
        for(int i = rhs.getRowCount() - 1; i >= 0; i--){
            double[] sol = this.normalize(rhs, i);
            this.substitute(rhs, sol, i, 0, i);
            rhs.setRow(i, sol);
        }
        return rhs;
    }
    
    /**
     * Substitute into a range of rows after a row solution found.
     * @param rhs  Right-hand side matrix
     * @param subs  Row solution found
     * @param subIndex  Row solution index
     * @param begin  Begin of row to be substituted
     * @param end   End of row to be substituted
     */
    protected void substitute(Matrix rhs, double[] subs, int subIndex, int begin, int end) {
        if( (end - begin) * (subs.length - subIndex) < DEFAULT_THRESHOLD ){<commit_msg>Support Forward/Backward substitution for non-square matrices, and checking<commit_after>/*
 * Copyright (C) 2016 <NAME>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package jacobi.core.solver;

import jacobi.api.Matrix;
import jacobi.core.util.Throw;
import java.util.stream.IntStream;

/**
 * Implementation of Backward and Forward Substitution.
 * 
 * For practical and performance reasons, this class would check if the input
 * matrix is truly upper or lower triangular. It is up to the caller to ensure
 * the data integrity of the input.
 * 
 * @author <NAME>
 */
public class Substitution {
    
    /**
     * Constructor.
     * @param mode  Forward/Backward substitution
     * @param tri  Lower/Upper triangular matrix
     */
    public Substitution(Mode mode, Matrix tri) {
        Throw.when()
            .isNull(() -> mode, () -> "No substitution mode.")
            .isNull(() -> tri, () -> "No triangular matrix.")
            .isTrue(
                () -> tri.getColCount() > tri.getRowCount(), 
                () -> "Triangular matrix is under-determined.");
        this.tri = tri;
        this.mode = mode;
    }
    
    /**
     * Compute substitution. This method is perturbative, i.e. it transforms 
     * the input into the output.
     * @param rhs  Right-hand side of the equation
     * @return  Object rhs after substitution
     */
    public Matrix compute(Matrix rhs) {
        Throw.when()
            .isNull(() -> rhs, () -> "No known values for substitution")
            .isTrue(
                () -> rhs.getRowCount() != this.tri.getRowCount(), 
                () -> "Dimension match. Expects " 
                        + this.tri.getRowCount() 
                        + ", got " 
                        + rhs.getRowCount() 
                        + " known values."
            );
        return this.mode == Mode.BACKWARD ? this.backward(rhs) : this.forward(rhs);
    }
    
    /**
     * Forward substitution. This method is perturbative, i.e. it transforms 
     * the input into the output.
     * @param rhs  Right-hand side of the equation
     * @return  Object rhs after substitution
     */
    protected Matrix forward(Matrix rhs) {
        for(int i = 0; i < rhs.getRowCount(); i++){
            double[] sol = this.normalize(rhs, i);
            this.substitute(rhs, sol, i, i + 1, rhs.getRowCount());
            rhs.setRow(i, sol);
        }
        return rhs;
    }
    
    /**
     * Backward substitution. This method is perturbative, i.e. it transforms 
     * the input into the output.
     * @param rhs  Right-hand side of the equation
     * @return  Object rhs after substitution
     */
    protected Matrix backward(Matrix rhs) {
        for(int i = rhs.getColCount() - 1; i >= 0; i--){
            double[] sol = this.normalize(rhs, i);
            this.substitute(rhs, sol, i, 0, i);
            rhs.setRow(i, sol);
        }
        return rhs;
    }
    
    /**
     * Substitute into a range of rows after a row solution found.
     * @param rhs  Right-hand side matrix
     * @param subs  Row solution found
     * @param subIndex  Row solution index
     * @param begin  Begin of row to be substituted
     * @param end   End of row to be substituted
     */
    protected void substitute(Matrix rhs, double[] subs, int subIndex, int begin, int end) {
        if( (end - begin) * (subs.length - subIndex) < DEFAULT_THRESHOLD ){
{'fraction_non_alphanumeric': 0.09800940005529445, 'fraction_numerical': 0.002764722145424385, 'mean_word_length': 2.2255907267053052, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2649618', 'n_tokens_mistral': 2057, 'n_tokens_neox': 1962, 'n_words': 782}