import { assert } from "../../assert.js";
import { lu_factor_linear_system } from "./lu_factor_linear_system.js";
import { lu_solve_linear_system } from "./lu_solve_linear_system.js";
import { solve_linear_system_GEPP_2x2 } from "./solve_linear_system_GEPP_2x2.js";

const SCRATCH_SIZE = 16;
const scratch = new Uint32Array(SCRATCH_SIZE);

/**
 * Solve linear equations Ax = b using Crout's method. Input is square matrix A and load vector b.
 * Solution x is written over load vector.
 * The dimension of the matrix is specified in size. If error is found, method returns a 0.
 *
 * @see https://github.com/Kitware/VTK/blob/af928016f4041ad8a6a193d032355b909721fc95/Common/Core/vtkMath.cxx
 *
 * @param {number[]|Float32Array|Float64Array} A Matrix of coefficients, column-major flat layout (A[j*size + i] = element at row i, col j)
 * @param {number[]|Float32Array|Float64Array} x Vector of right-hand constants for each equation, resulting solution for variables will also be written here
 * @param {number} size size of the system. Size 1 means a single variable and a single equation, 2 means a 2 variable equation system etc
 * @returns {boolean} true if solution found, false if factorization fails
 */
export function solve_linear_system(A, x, size) {
    assert.isNonNegativeInteger(size, 'size');
    assert.defined(A, 'A');
    assert.defined(x, 'x');

    if (size === 1) {
        if (A[0] === 0) {
            // Unable to solve linear system
            return false;
        }

        x[0] /= A[0];

        return true;

    } else if (size === 2) {

        // special fast path for 2 variable systems

        return solve_linear_system_GEPP_2x2(A, x, x);

    }

    const index = size <= SCRATCH_SIZE ? scratch : new Uint32Array(size);

    if (lu_factor_linear_system(A, index, size) === false) {

        // can not factorize system

        return false;
    }

    lu_solve_linear_system(A, index, x, size);

    return true;
}

