from wxPython.wx import *
import math
import LuDecomposition
import  TriDiagonal

DATASET = [-1.0, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1.0]

def givenFunction(x):
        return 1 / (1 + 100 * (x**2))

class NormalFunction:
        def perform(self, x):
                return givenFunction(x)

class ErrorLagrange:
        def __init__(self, aListX):
                self.lagrange = Lagrange(aListX)
                self.normalFunc = NormalFunction()

        def perform(self, x):
                return self.normalFunc.perform(x) - self.lagrange.perform(x)

class ErrorPiecewiseLagrange:
        def __init__(self, aControlPointListX, aPieceSize):
                self.piecewiseLagrange = PiecewiseLagrange(aControlPointListX, aPieceSize)
                self.normalFunc = NormalFunction()

        def perform(self, x):
                return self.normalFunc.perform(x) - self.piecewiseLagrange.perform(x)

class ErrorSpline:
        def __init__(self, aControlPointListX):
                self.spline = Spline(aControlPointListX)
                self.normalFunc = NormalFunction()

        def perform(self, x):
                return self.normalFunc.perform(x) - self.spline.perform(x)


class Lagrange:
        def __init__(self, aListX):
                self.controlPointListX = aListX
                self.controlPointListY = self._makeControlPointListY()

        def _makeControlPointListY(self):
                controlPointListY = []
                for x in self.controlPointListX:
                        controlPointListY.append(givenFunction(x))
                return controlPointListY

        def getControlPointListX(self):
                return self.controlPointListX

        def getControlPointListY(self):
                return self.controlPointListY

        def _basedFunction(self, x, i):
                bf = 1
                for j in range(0, len(self.controlPointListX)):
                        bf *= self._subBasedFunction(x, i, j)
                return bf

        def _subBasedFunction(self, x, i, j):
                if i == j:
                        return 1
                else:
                        return (x - self._getX(j)) / (self._getX(i) - self._getX(j))

        def perform(self, x):
                lg = 0.0
                for i in range(0, len(self.controlPointListX)):
                        lg += self._basedFunction(x, i) * self._getY(i)

                return lg

        def _getX(self, i):
                return self.controlPointListX[i]

        def _getY(self, i):
                return self.controlPointListY[i]

class PiecewiseLagrange:
        def __init__(self, aControlPointListX, aPieceSize):
                self.controlPointListX = aControlPointListX
                self.pieceSize = aPieceSize

        def getControlPointListX(self):
                return self.controlPointListX

        def perform(self, x):
                if (x >= self.controlPointListX[0]) and (x < self.controlPointListX[3]):
                        return Lagrange(self.getPiece(1)).perform(x)
                elif (x >= self.controlPointListX[3]) and (x < self.controlPointListX[6]):
                        return Lagrange(self.getPiece(2)).perform(x)
                elif (x >= self.controlPointListX[6]) and (x < self.controlPointListX[9]):
                        return Lagrange(self.getPiece(3)).perform(x)
                elif (x >= self.controlPointListX[9]) and (x <= self.controlPointListX[10]):
                        return Lagrange(self.getPiece(4)).perform(x)

        def getPiece(self, i):
                if ((self.pieceSize-1)*(i-1)+self.pieceSize) > self.getCountControlPoints():
                        return self.controlPointListX[self.getCountControlPoints()-self.pieceSize : self.getCountControlPoints()]
                return self.controlPointListX[self.getFirstPiecePoint(i) : self.getEndPiecePoint(i)]

        def getCountControlPoints(self):
                return len(self.controlPointListX)

        def getFirstPiecePoint(self, i):
                return (self.pieceSize-1)*(i-1)

        def getEndPiecePoint(self, i):
                return (self.pieceSize-1)*(i-1)+self.pieceSize

        def getCountPieces(self):
                return int(math.ceil( (self.getCountControlPoints()+1) / (self.pieceSize-1) ))

class Spline:
        def __init__(self, aControlPointListX):
                self.controlPointListX = aControlPointListX

                self.controlPointListY = self._makeControlPointListY()
                self.doublePrimeListY = self._makeDoublePrimeY()

        def getAi(self, i):
                return (self.getDoublePrimeY(i+1) - self.getDoublePrimeY(i)) / (6 * self.deltaX(i))

        def getBi(self, i):
                return (self.getDoublePrimeY(i) / 2)

        def getCi(self, i):
                return (self.deltaY(i) / self.deltaX(i) ) - (self.deltaX(i) / 6) * (self.getDoublePrimeY(i+1) + (2*self.getDoublePrimeY(i)) )

        def getDi(self, i):
                return self.getYi(i)

        def getXi(self, i):
                return self.controlPointListX[i]

        def getYi(self, i):
                return self.controlPointListY[i]

        def getDoublePrimeY(self, i):
                return self.doublePrimeListY[i][0]

        def getControlPointListX(self):
                return self.controlPointListX

        def getControlPointListY(self):
                return self.controlPointListY

        def perform(self, x):
                for i in range(0, len(self.controlPointListX)-2):
                        if (x >= self.getXi(i)) and (x < self.getXi(i+1)):
                                return self._subCubicSpline(x, i)

                if x >= self.getXi(len(self.controlPointListX)-2):
                        return self._subCubicSpline(x, len(self.controlPointListX)-2)

        def _subCubicSpline(self, x, i):
                return self.getAi(i) * ((x - self.getXi(i)) ** 3) + self.getBi(i) * ((x - self.getXi(i)) ** 2) + self.getCi(i) * ((x - self.getXi(i))) + self.getDi(i)

        def deltaX(self, i):
                return self.getXi(i+1) - self.getXi(i)

        def deltaY(self, i):
                return self.getYi(i+1) - self.getYi(i)

        def _makeControlPointListY(self):
                cplY = []
                for x in self.controlPointListX:
                        cplY.append (givenFunction(x))
                return cplY

        def _makeMatrixA(self):
                matrixA = self._makeEmptyMatrix()
                for i in range(0,9):
                        matrixA[i][i] = 2 * (self.deltaX(i)+self.deltaX(i+1))
                        if i>0:
                                matrixA[i-1][i] = self.deltaX(i)
                                matrixA[i][i-1] = self.deltaX(i)

                return matrixA

        def _makeMatrixB(self):
                matrixB = []
                for i in range(1,10):
                        matrixB.append([6 * ( self.deltaY(i)/self.deltaX(i) - self.deltaY(i-1)/self.deltaX(i-1) )])

                return matrixB

        def _makeDoublePrimeY(self):
                a = self._makeMatrixA()
                b = self._makeMatrixB()

                l, u = LuDecomposition.LuDecomposition(a).perform()
                matrixY = TriDiagonal.getMatrixY(l, b)
                tempY = TriDiagonal.getMatrixX(u, matrixY)
                tempY.insert(0, [0.0])
                tempY.append([0.0])

                return tempY

        def _makeEmptyMatrix(self):
                n = 9
                matrix = []
                for i in range(0,n):
                        row = []
                        for j in range(0,n):
                                row.append(0.0)
                        matrix.append(row)
                return matrix