''' mbinary
#########################################################################
# File : solve-linear-by-iteration.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-10-02  21:14
# Description:
#########################################################################
'''

'''
#########################################################################
# File : solve-linear-by-iteration.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-05-04  07:42
# Description: 
#########################################################################
'''




import numpy as np
from operator import le, lt
def jacob(A, b, x, accuracy=None, times=6):
    ''' Ax=b,  arg x is the init val, times is the time of iterating'''
    A, b, x = np.matrix(A), np.matrix(b), np.matrix(x)
    n, m = A.shape
    if n != m:
        raise Exception("Not square matrix: {A}".format(A=A))
    if b.shape != (n, 1):
        raise Exception(
            'Error: {b} must be {n} x1 in dimension'.format(b=b, n=n))
    D = np.diag(np.diag(A))
    DI = np.zeros([n, n])
    for i in range(n):
        DI[i, i] = 1/D[i, i]
    R = np.eye(n) - DI * A
    g = DI * b
    print('R =\n{}'.format(R))
    print('g =\n{}'.format(g))
    last = -x
    if accuracy != None:
        ct = 0
        while 1:
            ct += 1
            tmp = x-last
            last = x
            mx = max(abs(i) for i in tmp)
            if mx < accuracy:
                return x
            x = R*x+g
            print('x{ct} =\n{x}'.format(ct=ct, x=x))
    else:
        for i in range(times):
            x = R*x+g
            print('x{ct} =  \n{x}'.format(ct=i+1, x=x))
    print('isLimitd: {}'.format(isLimited(A)))
    return x


def gauss_seidel(A, b, x, accuracy=None, times=6):
    ''' Ax=b,  arg x is the init val, times is the time of iterating'''
    A, b, x = np.matrix(A), np.matrix(b), np.matrix(x)
    n, m = A.shape
    if n != m:
        raise Exception("Not square matrix: {A}".format(A=A))
    if b.shape != (n, 1):
        raise Exception(
            'Error: {b} must be {n} x1 in dimension'.format(b=b, n=n))
    D = np. matrix(np.diag(np.diag(A)))
    L = np.tril(A) - D  # L = np.triu(D.T) - D
    U = np.triu(A) - D
    DLI = (D+L).I
    S = - (DLI) * U
    f = (DLI)*b
    print('S =\n{}'.format(S))
    print('f =\n{}'.format(f))
    last = -x
    if accuracy != None:
        ct = 0
        while 1:
            ct += 1
            tmp = x-last
            last = x
            mx = max(abs(i) for i in tmp)
            if mx < accuracy:
                return x
            x = S*x+f
            print('x{ct} =\n{x}'.format(ct=ct, x=x))
    else:
        for i in range(times):
            x = S*x+f
            print('x{ct} =  \n{x}'.format(ct=i+1, x=x))
    print('isLimitd: {}'.format(isLimited(A)))
    return x


def isLimited(A, strict=False):
    '''通过检查A是否是[严格]对角优来判断迭代是否收敛, 即对角线上的值是否都大于对应行(或者列)的值'''
    diag = np.diag(A)
    op = lt if strict else le
    if op(A.max(axis=0), diag).all():
        return True
    if op(A.max(axis=1), diag).all():
        return True
    return False


testcase = []


def test():
    for func, A, b, x, *args in testcase:
        acc = None
        times = 6
        if args != []:
            if isinstance(args[0], int):
                times = args[0]
            else:
                acc = args[0]
        return func(A, b, x, acc, times)


if __name__ == '__main__':
    A = [[2, -1, -1],
         [1, 5, -1],
         [1, 1, 10]
         ]
    b = [[-5], [8], [11]]
    x = [[1], [1], [1]]
    # testcase.append([gauss_seidel,A,b,x])

    A = [[2, -1, 1], [3, 3, 9], [3, 3, 5]]
    b = [[-1], [0], [4]]
    x = [[0], [0], [0]]
    # testcase.append([jacob,A,b,x])

    A = [[5, -1, -1],
         [3, 6, 2],
         [1, -1, 2]
         ]
    b = [[16], [11], [-2]]
    x = [[1], [1], [-1]]
    testcase.append([gauss_seidel, A, b, x, 0.001])
    test()
