/*=============================================================================

    This file is part of FLINT.

    FLINT 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 2 of the License, or
    (at your option) any later version.

    FLINT 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 FLINT; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

=============================================================================*/
/******************************************************************************

    Copyright (C) 2010 William Hart
    Copyright (C) 2010-2011 Andy Novocin
    Copyright (C) 2010-2011 Fredrik Johansson
    Copyright (C) 2014 Abhinav Baid

******************************************************************************/


*******************************************************************************

    Memory management

*******************************************************************************

void d_mat_init(d_mat_t mat, slong rows, slong cols)

    Initialises a matrix with the given number of rows and columns for use. 

void d_mat_clear(d_mat_t mat)

    Clears the given matrix.

*******************************************************************************

    Basic assignment and manipulation

*******************************************************************************

void d_mat_set(d_mat_t mat1, const d_mat_t mat2)

    Sets \code{mat1} to a copy of \code{mat2}. The dimensions of 
    \code{mat1} and \code{mat2} must be the same.

void d_mat_swap(d_mat_t mat1, d_mat_t mat2)

    Swaps two matrices. The dimensions of \code{mat1} and \code{mat2} 
    are allowed to be different.

double d_mat_entry(d_mat_t mat, slong i, slong j)

    Returns the entry of \code{mat} at row $i$ and column $j$.
    Both $i$ and $j$ must not exceed the dimensions of the matrix.
    This function is implemented as a macro.

double d_mat_get_entry(const d_mat_t mat, slong i, slong j)

    Returns the entry of \code{mat} at row $i$ and column $j$.
    Both $i$ and $j$ must not exceed the dimensions of the matrix.
    
double * d_mat_entry_ptr(const d_mat_t mat, slong i, slong j)

    Returns a pointer to the entry of \code{mat} at row $i$ and column
    $j$. Both $i$ and $j$ must not exceed the dimensions of the matrix.
    
void d_mat_zero(d_mat_t mat)

    Sets all entries of \code{mat} to 0.

void d_mat_one(d_mat_t mat)

    Sets \code{mat} to the unit matrix, having ones on the main diagonal
    and zeroes elsewhere. If \code{mat} is nonsquare, it is set to the
    truncation of a unit matrix.

*******************************************************************************

    Random matrix generation

*******************************************************************************

void d_mat_randtest(d_mat_t mat, flint_rand_t state, slong minexp,
                    slong maxexp)

    Sets the entries of \code{mat} to random signed numbers with exponents
    between \code{minexp} and \code{maxexp} or zero.

*******************************************************************************

    Input and output

*******************************************************************************

void d_mat_print(const d_mat_t mat)

    Prints the given matrix to the stream \code{stdout}.

*******************************************************************************

    Comparison

*******************************************************************************

int d_mat_equal(const d_mat_t mat1, const d_mat_t mat2)

    Returns a non-zero value if \code{mat1} and \code{mat2} have 
    the same dimensions and entries, and zero otherwise.
    
int d_mat_approx_equal(const d_mat_t mat1, const d_mat_t mat2, double eps)

    Returns a non-zero value if \code{mat1} and \code{mat2} have 
    the same dimensions and entries within \code{eps} of each other,
    and zero otherwise.

int d_mat_is_zero(const d_mat_t mat)

    Returns a non-zero value if all entries \code{mat} are zero, and
    otherwise returns zero.

int d_mat_is_approx_zero(const d_mat_t mat, double eps)

    Returns a non-zero value if all entries \code{mat} are zero to within
    \code{eps} and otherwise returns zero.

int d_mat_is_empty(const d_mat_t mat)

    Returns a non-zero value if the number of rows or the number of
    columns in \code{mat} is zero, and otherwise returns
    zero.

int d_mat_is_square(const d_mat_t mat)

    Returns a non-zero value if the number of rows is equal to the
    number of columns in \code{mat}, and otherwise returns zero.

*******************************************************************************

    Transpose

*******************************************************************************

void d_mat_transpose(d_mat_t B, const d_mat_t A)

    Sets $B$ to $A^T$, the transpose of $A$. Dimensions must be compatible.
    $A$ and $B$ are allowed to be the same object if $A$ is a square matrix.

*******************************************************************************

    Matrix multiplication

*******************************************************************************

void d_mat_mul_classical(d_mat_t C, const d_mat_t A, const d_mat_t B)

    Sets \code{C} to the matrix product $C = A B$. The matrices must have
    compatible dimensions for matrix multiplication (an exception is raised
    otherwise). Aliasing is allowed.

*******************************************************************************

    Gram-Schmidt Orthogonalisation and QR Decomposition

*******************************************************************************

void d_mat_gso(d_mat_t B, const d_mat_t A)

    Takes a subset of $R^m$ $S = {a_1, a_2, \ldots, a_n}$ (as the columns of
    a $m x n$ matrix \code{A}) and generates an orthonormal set
    $S^' = {b_1, b_2, \ldots, b_n}$ (as the columns of the $m x n$ matrix
    \code{B}) that spans the same subspace of $R^m$ as $S$.

    This uses an algorithm of Schwarz-Rutishauser. See pp. 9 of
    \url{http://www.inf.ethz.ch/personal/gander/papers/qrneu.pdf}
    
void d_mat_qr(d_mat_t Q, d_mat_t R, const d_mat_t A)

    Computes the $QR$ decomposition of a matrix \code{A} using the Gram-Schmidt
    process. (Sets \code{Q} and \code{R} such that $A = QR$ where \code{R} is
    an upper triangular matrix and \code{Q} is an orthogonal matrix.)

    This uses an algorithm of Schwarz-Rutishauser. See pp. 9 of
    \url{http://www.inf.ethz.ch/personal/gander/papers/qrneu.pdf}

