'''
Created on Feb 10, 2021

@author: vladislavkargin
'''

import rgs.mndrpy.Pairing as pr
import rgs.mndrpy.Meander as mr

import matplotlib.pyplot as plt


class ElementTL(object):
    '''
    This class represents an element of TL algebra. 
    It can be interpreted in two different ways:
     1) Currently, we think about it as an element of
    the left ideal which generated by the basis elements 
    which have an arbitrary pairing on the left  and a standard pairing
    on the right.
    2) it can also represent an arbitrary element of TL algebra
    if we fold every pairing appropriately.
    
    We realize this object as a dictionary with pairings as keys and 
    its coefficients as values.
    '''


    def __init__(self, S, q = 2):
        '''
        Constructor
        takes a dictionary S that associate a coefficient to a pairing.
        For example, {prng1:1, pring2:3}. This corresponds to element
        prng1 + 2 prng2.
        Another parameter is q. By definition it is set to 2. 
        '''
        self.S = dict.copy(S)
        self.q = q
        
    def __str__(self):
        return ' '.join([str(x) + ":" + str(self.S[x]) for x in self.S.keys()])
    
     
    def __add__(self, other):
        '''adds this element to the other'''
        #make a copy of self
        e = ElementTL(self.S)
        for x in other.S.keys():
            if x in e.S.keys():
                e.S[x] = e.S[x] + other.S[x]
            else:
                e.S[x] = other.S[x]
        return e  
    
    def __sub__(self, other):
        '''subtracts the other element'''
        #make a copy of self
        e = ElementTL(self.S)
        for x in other.S.keys():
            if x in e.S.keys():
                e.S[x] = e.S[x] - other.S[x]
            else:
                e.S[x] = -other.S[x]
        return e
    
    def __mul__(self, other): 
        '''multiplication by a scalar'''
        #make a copy of self
        e = ElementTL(self.S)
        for x in e.S.keys():
            e.S[x] = e.S[x] * other
        return e
    
    def __rmul__(self, other):
        '''multiplication by a scalar. Makes sure that it is 
        OK to multiply on both sides'''
        return self * other

    def __or__(self, other):
        '''This overloading for 'bitwise or' (|) operator is going
        to realize the scalar product of two TL elements.
        arguments: the other TL element
        
        returns the scalar product with the other TL element '''
        #TODO: need to make sure that it uses q-defined scalar product
        if self.q != other.q:
            print("Cannot calculate scalar products of ", str(self), " and ",
                  str(other), ". Parameters q are different in these elements.")
        c = 0
        for x in self.S.keys():
            for y in other.S.keys():
                mndr = mr.Meander(x,y)
                cycles, _ = mndr.findCycles()
                c = c + pow(self.q, len(cycles)) * self.S[x] * other.S[y]
        return c
        


'''
For testing methods
'''
def main():
    
    
    prng1 = pr.Pairing(path = None, prngArray = [3, 2, 1, 0, 5, 4])
    prng2 = pr.Pairing(path = None, prngArray = [1, 0, 3, 2, 5, 4])
    prng3 = pr.Pairing(path = None, prngArray = [5, 2, 1, 4, 3, 0])
    
    mndr = mr.Meander(prng1, prng2)
    print(mndr)
    mndr.draw()
    
    x = ElementTL({prng1:1, prng2:(3)})
    y = ElementTL({prng2:(-1), prng3:0})
    print(x)
    print(y)
    
    print(x + y)
    print(x - y)
    print(x * 3)
    print(3 * x)
    
    print(x|y)
    
    plt.show()
    
if __name__ == '__main__':
    main()    
    