#!/usr/bin/python
# -*- coding: utf-8 -*-

"""  Ejemplo del Metodo de Diferencias Finitas para resolver la ecuacion diferencial parcial:
    Uxx=-Pi*Pi*cos(Pix)
        xi<=U<=xf
        U(xi)=vi y U(xf)=vf
 con solucion analitica:
     cos(pix)

 Para conocer mas del metodo ver:
     http://132.248.182.159/acl/MDF

 Autor: Antonio Carrillo Ledesma
     http://mmc.geofisica.unam.mx/acl
"""


# 2.X compatible
from __future__ import print_function
import sys
if sys.version[0] == '2': input = raw_input 
import math


def LadoDerecho(x):
        "Lado derecho de la ecuacion diferencial parcial"
        return -math.pi * math.pi * math.cos(math.pi * x)


def Jacobi(A, b, N, iter):
	"Resuelve Ax=b usando el metodo Jacobi"
	sum = 0
        x = [0] * N
	xt = [0] * N
	for m in range(iter):
		sum =  A[0][2] * x[1]
		xt[0] = (1.0 / A[0][1]) * (b[0] - sum)
		for i in range(1,N-1):
			sum = A[i][0] * x[i-1] +  A[i][2] * x[i+1]
			xt[i] = (1.0 / A[i][1]) * (b[i] - sum)
		sum = A[N-1][0] * x[N-2]
		xt[N-1] = (1.0 / A[N-1][1]) * (b[N-1] - sum)
		for i in range(N):
	 		x[i] = xt[i];
	return x


def GaussSeidel(A, b, N, iter):
        "Resuelve Ax=b usando el metodo GaussSeidel"
        sum = 0
        x = [0] * N
        for m in range(iter):
                sum =  A[0][2] * x[1] 
                x[0] = (1.0 / A[0][1]) * (b[0] - sum) 
                for i in range(1,N-1):
                        sum = A[i][0] * x[i-1] +  A[i][2] * x[i+1] 
                        x[i] = (1.0 / A[i][1]) * (b[i] - sum) 
                sum = A[N-1][0] * x[N-2]
                x[N-1] = (1.0 / A[N-1][1]) * (b[N-1] - sum)

        return x



# MDF1DDBAND
if __name__ == '__main__':
	xi = 0.0                  # Inicio del diminio
	xf = 1.0                  # Fin del dominio
	vi = 1.0                  # Valor en la frontera xi
	vf = -1.0                 # Valor en la frontera xf
	n = 11                    # Particion
	N = n-2                   # Incognitas
	h = (xf - xi) / (n - 1);  # Incremento en la malla

	# Declaracion de la matriz A y los vectores b y x
	A = []                   # Matriz A
	for i in range(N):
		A.append([0]*3)
	b = [0] * N              # Vector b
        x = [0] * N              # Vector x

	# Stencil
	R = 1 / (h * h)
	P = -2 / (h * h)
	Q = 1 / (h * h)

	# Primer renglon de la matriz A y vector b
	A[0][1] = P
	A[0][2] = Q
	b[0] = LadoDerecho(xi) - vi * R
	print(b)
	# Renglones intermedios de la matriz A y vector b
	for i in range(1,N-1):
		A[i][0] = R
		A[i][1] = P
		A[i][2] = Q
		b[i] = LadoDerecho(xi + h * (i + 1))
	# Renglon final de la matriz A y vector b
	A[N - 1][0] = R
	A[N - 1][1] = P
	b[N - 1] = LadoDerecho(xi + h * N - 2) - vf * Q

	# Resuelve por el metodo Jacobi
	for i in range(N):
		for j in range(3):
			print(' ', A[i][j],end='')
		print("")

	print(b)
	x = Jacobi(A, b, N, N*14)
	print(x)
	# Solucion
        print(xi, vi)
        for i in range(N):
                print(xi + h * (i + 1), x[i])
        print(xf, vf)


	# Resuelve por el metodo Gauss-Seidel
        for i in range(N):
                for j in range(3):
                        print(' ', A[i][j],end='')
                print("")

        print(b)
        x = GaussSeidel(A, b, N, N*7)
        print(x)
	# Solucion
	print(xi, vi)
	for i in range(N):
		print(xi + h * (i + 1), x[i])
	print(xf, vf)
