#######################################################
# Copyright (c) 2015, ArrayFire
# All rights reserved.
#
# This file is distributed under 3-clause BSD license.
# The complete license agreement can be obtained at:
# http://arrayfire.com/licenses/BSD-3-Clause
########################################################

"""
dense linear algebra functions for arrayfire.
"""

from .library import *
from .array import *

def lu(A):
    """
    LU decomposition.

    Parameters
    ----------
    A: af.Array
       A 2 dimensional arrayfire array.

    Returns
    -------
    (L,U,P): tuple of af.Arrays
           - L - Lower triangular matrix.
           - U - Upper triangular matrix.
           - P - Permutation array.

    Note
    ----

    The original matrix `A` can be reconstructed using the outputs in the following manner.

    >>> A[P, :] = af.matmul(L, U)

    """
    L = Array()
    U = Array()
    P = Array()
    safe_call(backend.get().af_lu(ct.pointer(L.arr), ct.pointer(U.arr), ct.pointer(P.arr), A.arr))
    return L,U,P

def lu_inplace(A, pivot="lapack"):
    """
    In place LU decomposition.

    Parameters
    ----------
    A: af.Array
       - a 2 dimensional arrayfire array on entry.
       - Contains L in the lower triangle on exit.
       - Contains U in the upper triangle on exit.

    Returns
    -------
    P: af.Array
       - Permutation array.

    Note
    ----

    This function is primarily used with `af.solve_lu` to reduce computations.

    """
    P = Array()
    is_pivot_lapack = False if (pivot == "full") else True
    safe_call(backend.get().af_lu_inplace(ct.pointer(P.arr), A.arr, is_pivot_lapack))
    return P

def qr(A):
    """
    QR decomposition.

    Parameters
    ----------
    A: af.Array
       A 2 dimensional arrayfire array.

    Returns
    -------
    (Q,R,T): tuple of af.Arrays
           - Q - Orthogonal matrix.
           - R - Upper triangular matrix.
           - T - Vector containing additional information to solve a least squares problem.

    Note
    ----

    The outputs of this funciton have the following properties.

    >>> A = af.matmul(Q, R)
    >>> I = af.matmulNT(Q, Q) # Identity matrix
    """
    Q = Array()
    R = Array()
    T = Array()
    safe_call(backend.get().af_lu(ct.pointer(Q.arr), ct.pointer(R.arr), ct.pointer(T.arr), A.arr))
    return Q,R,T

def qr_inplace(A):
    """
    In place QR decomposition.

    Parameters
    ----------
    A: af.Array
       - a 2 dimensional arrayfire array on entry.
       - Packed Q and R matrices on exit.

    Returns
    -------
    T: af.Array
       - Vector containing additional information to solve a least squares problem.

    Note
    ----

    This function is used to save space only when `R` is required.
    """
    T = Array()
    safe_call(backend.get().af_qr_inplace(ct.pointer(T.arr), A.arr))
    return T

def cholesky(A, is_upper=True):
    """
    Cholesky decomposition

    Parameters
    ----------
    A: af.Array
       A 2 dimensional, symmetric, positive definite matrix.

    is_upper: optional: bool. default: True
       Specifies if output `R` is upper triangular (if True) or lower triangular (if False).

    Returns
    -------
    (R,info): tuple of af.Array, int.
           - R - triangular matrix.
           - info - 0 if decomposition sucessful.
    Note
    ----

    The original matrix `A` can be reconstructed using the outputs in the following manner.

    >>> A = af.matmulNT(R, R) #if R is upper triangular

    """
    R = Array()
    info = ct.c_int(0)
    safe_call(backend.get().af_cholesky(ct.pointer(R.arr), ct.pointer(info), A.arr, is_upper))
    return R, info.value

def cholesky_inplace(A, is_upper=True):
    """
    In place Cholesky decomposition.

    Parameters
    ----------
    A: af.Array
       - a 2 dimensional, symmetric, positive definite matrix.
       - Trinangular matrix on exit.

    is_upper: optional: bool. default: True.
       Specifies if output `R` is upper triangular (if True) or lower triangular (if False).

    Returns
    -------
    info : int.
           0 if decomposition sucessful.

    """
    info = ct.c_int(0)
    safe_call(backend.get().af_cholesky_inplace(ct.pointer(info), A.arr, is_upper))
    return info.value

def solve(A, B, options=MATPROP.NONE):
    """
    Solve a system of linear equations.

    Parameters
    ----------

    A: af.Array
       A 2 dimensional arrayfire array representing the coefficients of the system.

    B: af.Array
       A 1 or 2 dimensional arrayfire array representing the constants of the system.

    options: optional: af.MATPROP. default: af.MATPROP.NONE.
       - Additional options to speed up computations.
       - Currently needs to be one of `af.MATPROP.NONE`, `af.MATPROP.LOWER`, `af.MATPROP.UPPER`.

    Returns
    -------
    X: af.Array
       A 1 or 2 dimensional arrayfire array representing the unknowns in the system.

    """
    X = Array()
    safe_call(backend.get().af_solve(ct.pointer(X.arr), A.arr, B.arr, options.value))
    return X

def solve_lu(A, P, B, options=MATPROP.NONE):
    """
    Solve a system of linear equations, using LU decomposition.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array representing the coefficients of the system.
       - This matrix should be decomposed previously using `lu_inplace(A)`.

    P: af.Array
       - Permutation array.
       - This array is the output of an earlier call to `lu_inplace(A)`

    B: af.Array
       A 1 or 2 dimensional arrayfire array representing the constants of the system.

    Returns
    -------
    X: af.Array
       A 1 or 2 dimensional arrayfire array representing the unknowns in the system.

    """
    X = Array()
    safe_call(backend.get().af_solve_lu(ct.pointer(X.arr), A.arr, P.arr, B.arr, options.value))
    return X

def inverse(A, options=MATPROP.NONE):
    """
    Invert a matrix.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array

    options: optional: af.MATPROP. default: af.MATPROP.NONE.
       - Additional options to speed up computations.
       - Currently needs to be one of `af.MATPROP.NONE`.

    Returns
    -------

    AI: af.Array
       - A 2 dimensional array that is the inverse of `A`

    Note
    ----

    `A` needs to be a square matrix.

    """
    AI = Array()
    safe_call(backend.get().af_inverse(ct.pointer(AI.arr), A.arr, options.value))
    return AI

def rank(A, tol=1E-5):
    """
    Rank of a matrix.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array

    tol: optional: scalar. default: 1E-5.
       - Tolerance for calculating rank

    Returns
    -------

    r: int
       - Rank of `A` within the given tolerance
    """
    r = ct.c_uint(0)
    safe_call(backend.get().af_rank(ct.pointer(r), A.arr, ct.c_double(tol)))
    return r.value

def det(A):
    """
    Determinant of a matrix.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array

    Returns
    -------

    res: scalar
       - Determinant of the matrix.
    """
    re = ct.c_double(0)
    im = ct.c_double(0)
    safe_call(backend.get().af_det(ct.pointer(re), ct.pointer(im), A.arr))
    re = re.value
    im = im.value
    return re if (im == 0) else re + im * 1j

def norm(A, norm_type=NORM.EUCLID, p=1.0, q=1.0):
    """
    Norm of an array or a matrix.

    Parameters
    ----------

    A: af.Array
       - A 1 or 2 dimensional arrayfire array

    norm_type: optional: af.NORM. default: af.NORM.EUCLID.
       - Type of norm to be calculated.

    p: scalar. default 1.0.
       - Used only if `norm_type` is one of `af.NORM.VECTOR_P`, `af.NORM_MATRIX_L_PQ`

    q: scalar. default 1.0.
       - Used only if `norm_type` is `af.NORM_MATRIX_L_PQ`

    Returns
    -------

    res: scalar
       - norm of the input

    """
    res = ct.c_double(0)
    safe_call(backend.get().af_norm(ct.pointer(res), A.arr, norm_type.value,
                                    ct.c_double(p), ct.c_double(q)))
    return res.value

#######################################################
# Copyright (c) 2015, ArrayFire
# All rights reserved.
#
# This file is distributed under 3-clause BSD license.
# The complete license agreement can be obtained at:
# http://arrayfire.com/licenses/BSD-3-Clause
########################################################

"""
dense linear algebra functions for arrayfire.
"""

from .library import *
from .array import *

def lu(A):
    """
    LU decomposition.

    Parameters
    ----------
    A: af.Array
       A 2 dimensional arrayfire array.

    Returns
    -------
    (L,U,P): tuple of af.Arrays
           - L - Lower triangular matrix.
           - U - Upper triangular matrix.
           - P - Permutation array.

    Note
    ----

    The original matrix `A` can be reconstructed using the outputs in the following manner.

    >>> A[P, :] = af.matmul(L, U)

    """
    L = Array()
    U = Array()
    P = Array()
    safe_call(backend.get().af_lu(ct.pointer(L.arr), ct.pointer(U.arr), ct.pointer(P.arr), A.arr))
    return L,U,P

def lu_inplace(A, pivot="lapack"):
    """
    In place LU decomposition.

    Parameters
    ----------
    A: af.Array
       - a 2 dimensional arrayfire array on entry.
       - Contains L in the lower triangle on exit.
       - Contains U in the upper triangle on exit.

    Returns
    -------
    P: af.Array
       - Permutation array.

    Note
    ----

    This function is primarily used with `af.solve_lu` to reduce computations.

    """
    P = Array()
    is_pivot_lapack = False if (pivot == "full") else True
    safe_call(backend.get().af_lu_inplace(ct.pointer(P.arr), A.arr, is_pivot_lapack))
    return P

def qr(A):
    """
    QR decomposition.

    Parameters
    ----------
    A: af.Array
       A 2 dimensional arrayfire array.

    Returns
    -------
    (Q,R,T): tuple of af.Arrays
           - Q - Orthogonal matrix.
           - R - Upper triangular matrix.
           - T - Vector containing additional information to solve a least squares problem.

    Note
    ----

    The outputs of this funciton have the following properties.

    >>> A = af.matmul(Q, R)
    >>> I = af.matmulNT(Q, Q) # Identity matrix
    """
    Q = Array()
    R = Array()
    T = Array()
    safe_call(backend.get().af_lu(ct.pointer(Q.arr), ct.pointer(R.arr), ct.pointer(T.arr), A.arr))
    return Q,R,T

def qr_inplace(A):
    """
    In place QR decomposition.

    Parameters
    ----------
    A: af.Array
       - a 2 dimensional arrayfire array on entry.
       - Packed Q and R matrices on exit.

    Returns
    -------
    T: af.Array
       - Vector containing additional information to solve a least squares problem.

    Note
    ----

    This function is used to save space only when `R` is required.
    """
    T = Array()
    safe_call(backend.get().af_qr_inplace(ct.pointer(T.arr), A.arr))
    return T

def cholesky(A, is_upper=True):
    """
    Cholesky decomposition

    Parameters
    ----------
    A: af.Array
       A 2 dimensional, symmetric, positive definite matrix.

    is_upper: optional: bool. default: True
       Specifies if output `R` is upper triangular (if True) or lower triangular (if False).

    Returns
    -------
    (R,info): tuple of af.Array, int.
           - R - triangular matrix.
           - info - 0 if decomposition sucessful.
    Note
    ----

    The original matrix `A` can be reconstructed using the outputs in the following manner.

    >>> A = af.matmulNT(R, R) #if R is upper triangular

    """
    R = Array()
    info = ct.c_int(0)
    safe_call(backend.get().af_cholesky(ct.pointer(R.arr), ct.pointer(info), A.arr, is_upper))
    return R, info.value

def cholesky_inplace(A, is_upper=True):
    """
    In place Cholesky decomposition.

    Parameters
    ----------
    A: af.Array
       - a 2 dimensional, symmetric, positive definite matrix.
       - Trinangular matrix on exit.

    is_upper: optional: bool. default: True.
       Specifies if output `R` is upper triangular (if True) or lower triangular (if False).

    Returns
    -------
    info : int.
           0 if decomposition sucessful.

    """
    info = ct.c_int(0)
    safe_call(backend.get().af_cholesky_inplace(ct.pointer(info), A.arr, is_upper))
    return info.value

def solve(A, B, options=MATPROP.NONE):
    """
    Solve a system of linear equations.

    Parameters
    ----------

    A: af.Array
       A 2 dimensional arrayfire array representing the coefficients of the system.

    B: af.Array
       A 1 or 2 dimensional arrayfire array representing the constants of the system.

    options: optional: af.MATPROP. default: af.MATPROP.NONE.
       - Additional options to speed up computations.
       - Currently needs to be one of `af.MATPROP.NONE`, `af.MATPROP.LOWER`, `af.MATPROP.UPPER`.

    Returns
    -------
    X: af.Array
       A 1 or 2 dimensional arrayfire array representing the unknowns in the system.

    """
    X = Array()
    safe_call(backend.get().af_solve(ct.pointer(X.arr), A.arr, B.arr, options.value))
    return X

def solve_lu(A, P, B, options=MATPROP.NONE):
    """
    Solve a system of linear equations, using LU decomposition.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array representing the coefficients of the system.
       - This matrix should be decomposed previously using `lu_inplace(A)`.

    P: af.Array
       - Permutation array.
       - This array is the output of an earlier call to `lu_inplace(A)`

    B: af.Array
       A 1 or 2 dimensional arrayfire array representing the constants of the system.

    Returns
    -------
    X: af.Array
       A 1 or 2 dimensional arrayfire array representing the unknowns in the system.

    """
    X = Array()
    safe_call(backend.get().af_solve_lu(ct.pointer(X.arr), A.arr, P.arr, B.arr, options.value))
    return X

def inverse(A, options=MATPROP.NONE):
    """
    Invert a matrix.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array

    options: optional: af.MATPROP. default: af.MATPROP.NONE.
       - Additional options to speed up computations.
       - Currently needs to be one of `af.MATPROP.NONE`.

    Returns
    -------

    AI: af.Array
       - A 2 dimensional array that is the inverse of `A`

    Note
    ----

    `A` needs to be a square matrix.

    """
    AI = Array()
    safe_call(backend.get().af_inverse(ct.pointer(AI.arr), A.arr, options.value))
    return AI

def rank(A, tol=1E-5):
    """
    Rank of a matrix.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array

    tol: optional: scalar. default: 1E-5.
       - Tolerance for calculating rank

    Returns
    -------

    r: int
       - Rank of `A` within the given tolerance
    """
    r = ct.c_uint(0)
    safe_call(backend.get().af_rank(ct.pointer(r), A.arr, ct.c_double(tol)))
    return r.value

def det(A):
    """
    Determinant of a matrix.

    Parameters
    ----------

    A: af.Array
       - A 2 dimensional arrayfire array

    Returns
    -------

    res: scalar
       - Determinant of the matrix.
    """
    re = ct.c_double(0)
    im = ct.c_double(0)
    safe_call(backend.get().af_det(ct.pointer(re), ct.pointer(im), A.arr))
    re = re.value
    im = im.value
    return re if (im == 0) else re + im * 1j

def norm(A, norm_type=NORM.EUCLID, p=1.0, q=1.0):
    """
    Norm of an array or a matrix.

    Parameters
    ----------

    A: af.Array
       - A 1 or 2 dimensional arrayfire array

    norm_type: optional: af.NORM. default: af.NORM.EUCLID.
       - Type of norm to be calculated.

    p: scalar. default 1.0.
       - Used only if `norm_type` is one of `af.NORM.VECTOR_P`, `af.NORM_MATRIX_L_PQ`

    q: scalar. default 1.0.
       - Used only if `norm_type` is `af.NORM_MATRIX_L_PQ`

    Returns
    -------

    res: scalar
       - norm of the input

    """
    res = ct.c_double(0)
    safe_call(backend.get().af_norm(ct.pointer(res), A.arr, norm_type.value,
                                    ct.c_double(p), ct.c_double(q)))
    return res.value

####################################################### # Copyright (c) 2015, ArrayFire # All rights reserved. # # This file is distributed under 3-clause BSD license. # The complete license agreement can be obtained at: # http://arrayfire.com/licenses/BSD-3-Clause ######################################################## dense linear algebra functions for arrayfire. LU decomposition.      Parameters     ----------     A: af.Array        A 2 dimensional arrayfire array.      Returns     -------     (L,U,P): tuple of af.Arrays            - L - Lower triangular matrix.            - U - Upper triangular matrix.            - P - Permutation array.      Note     ----      The original matrix `A` can be reconstructed using the outputs in the following manner.      >>> A[P, :] = af.matmul(L, U) In place LU decomposition.      Parameters     ----------     A: af.Array        - a 2 dimensional arrayfire array on entry.        - Contains L in the lower triangle on exit.        - Contains U in the upper triangle on exit.      Returns     -------     P: af.Array        - Permutation array.      Note     ----      This function is primarily used with `af.solve_lu` to reduce computations. QR decomposition.      Parameters     ----------     A: af.Array        A 2 dimensional arrayfire array.      Returns     -------     (Q,R,T): tuple of af.Arrays            - Q - Orthogonal matrix.            - R - Upper triangular matrix.            - T - Vector containing additional information to solve a least squares problem.      Note     ----      The outputs of this funciton have the following properties.      >>> A = af.matmul(Q, R)     >>> I = af.matmulNT(Q, Q) # Identity matrix In place QR decomposition.      Parameters     ----------     A: af.Array        - a 2 dimensional arrayfire array on entry.        - Packed Q and R matrices on exit.      Returns     -------     T: af.Array        - Vector containing additional information to solve a least squares problem.      Note     ----      This function is used to save space only when `R` is required. Cholesky decomposition      Parameters     ----------     A: af.Array        A 2 dimensional, symmetric, positive definite matrix.      is_upper: optional: bool. default: True        Specifies if output `R` is upper triangular (if True) or lower triangular (if False).      Returns     -------     (R,info): tuple of af.Array, int.            - R - triangular matrix.            - info - 0 if decomposition sucessful.     Note     ----      The original matrix `A` can be reconstructed using the outputs in the following manner.      >>> A = af.matmulNT(R, R) #if R is upper triangular In place Cholesky decomposition.      Parameters     ----------     A: af.Array        - a 2 dimensional, symmetric, positive definite matrix.        - Trinangular matrix on exit.      is_upper: optional: bool. default: True.        Specifies if output `R` is upper triangular (if True) or lower triangular (if False).      Returns     -------     info : int.            0 if decomposition sucessful. Solve a system of linear equations.      Parameters     ----------      A: af.Array        A 2 dimensional arrayfire array representing the coefficients of the system.      B: af.Array        A 1 or 2 dimensional arrayfire array representing the constants of the system.      options: optional: af.MATPROP. default: af.MATPROP.NONE.        - Additional options to speed up computations.        - Currently needs to be one of `af.MATPROP.NONE`, `af.MATPROP.LOWER`, `af.MATPROP.UPPER`.      Returns     -------     X: af.Array        A 1 or 2 dimensional arrayfire array representing the unknowns in the system. Solve a system of linear equations, using LU decomposition.      Parameters     ----------      A: af.Array        - A 2 dimensional arrayfire array representing the coefficients of the system.        - This matrix should be decomposed previously using `lu_inplace(A)`.      P: af.Array        - Permutation array.        - This array is the output of an earlier call to `lu_inplace(A)`      B: af.Array        A 1 or 2 dimensional arrayfire array representing the constants of the system.      Returns     -------     X: af.Array        A 1 or 2 dimensional arrayfire array representing the unknowns in the system. Invert a matrix.      Parameters     ----------      A: af.Array        - A 2 dimensional arrayfire array      options: optional: af.MATPROP. default: af.MATPROP.NONE.        - Additional options to speed up computations.        - Currently needs to be one of `af.MATPROP.NONE`.      Returns     -------      AI: af.Array        - A 2 dimensional array that is the inverse of `A`      Note     ----      `A` needs to be a square matrix. Rank of a matrix.      Parameters     ----------      A: af.Array        - A 2 dimensional arrayfire array      tol: optional: scalar. default: 1E-5.        - Tolerance for calculating rank      Returns     -------      r: int        - Rank of `A` within the given tolerance Determinant of a matrix.      Parameters     ----------      A: af.Array        - A 2 dimensional arrayfire array      Returns     -------      res: scalar        - Determinant of the matrix. Norm of an array or a matrix.      Parameters     ----------      A: af.Array        - A 1 or 2 dimensional arrayfire array      norm_type: optional: af.NORM. default: af.NORM.EUCLID.        - Type of norm to be calculated.      p: scalar. default 1.0.        - Used only if `norm_type` is one of `af.NORM.VECTOR_P`, `af.NORM_MATRIX_L_PQ`      q: scalar. default 1.0.        - Used only if `norm_type` is `af.NORM_MATRIX_L_PQ`      Returns     -------      res: scalar        - norm of the input