#!/usr/bin/env python
# encoding: utf-8

from fraction import Fraction
from generic import add_in_dict, remove_in_dict, convolution_dict
import re

class FormalExp(object):
    """A formal expression (similare to Symbol in Sympy"""

    def __init__(self, coef = {}, letter = ""):
        """Initiat the formal expression

        :param coef: the dictionary representing the expression
        :param letter:  minimum expression, a letter 

        """

        if coef != {} and letter != "":
            raise ValueError("A FormalExp can't be initiate with dict_exp and a letter")
        elif letter != "":
            self._letter = letter
            self._coef = {letter: 1}
        elif coef != {}:
            self._coef = coef
        else:
            raise ValueError("FormalExp needs a letter or dictionary of coeficients")

        if len(self) != 1:
            self.mainOp = "+"

    def master_coef(self):
        """Return the master coefficient
        /!\ may not work pretty well if there is more than one indeterminate
        :returns: a_n

        """
        pattern = "\w\^(\d*)"
        finder = re.compile(pattern)
        power = {}
        for (k,v) in self._coef.items():
            if k=="":
                power[0] = v
            else:
                p = finder.findall(k)
                if p == []:
                    power[1] = v
                else:
                    power[int(p[0])] =  v

        m_power = max(power)
        return power[m_power]

    def check_calculous(self, other):
        """Check if other is a constant and then transform it into a dictionary compatible with FormalExp

        :param other: The thing to compute with the expression
        :returns: dictionary of this thing

        """
        if type(other) in [int, Fraction]:
            return {"":other}
        elif type(other) == FormalExp:
            return other._coef.copy()
        else:
            raise ValueError("Can't add {type} with FormalExp".format(type=type(other)))

    def const_or_formal(self, d):
        """Return a constant if there is nothing else, FormalExp otherwise

        :param d: dictionary descripting the expression
        :returns: a constant or a FormalExp

        """
        if list(d.keys()) == ['']:
            return d['']
        else:
            return FormalExp(d)

    def __add__(self, other):
        d = self.check_calculous(other)

        d = add_in_dict(self._coef, d)
        d = remove_in_dict(d)

        return [self.const_or_formal(d)]

    def __radd__(self, other):
        return self + other

    def __sub__(self, other):
        o_tmp = -other
        return self + o_tmp

    def __neg__(self):
        d = {}
        for k,v in self._coef.items():
            d[k] = -v
        return FormalExp(d)    

    def __mul__(self, other):
        d = self.check_calculous(other)

        d = convolution_dict(self._coef, d, op_key = self.op_key)
        d = remove_in_dict(d)

        return [self.const_or_formal(d)]

    def op_key(self, x,y):
        """Operation on keys for convolution_dict"""
        if x == "" or y == "":
            return x+y
        else:
            return x + "*" + y

    
    def __rmul__(self, other):
        d = self.check_calculous(other)

        d = convolution_dict(d, self._coef, op_key = self.op_key)
        d = remove_in_dict(d)

        return [self.const_or_formal(d)]

    def __div__(self, other):
        # Will never be done :D
        pass
    
    def __pow__(self, other):
        # Will never be done :D quoique
        pass

    def __len__(self):
        return len(list(self._coef.keys()))

    def __str__(self):
        return " + ".join([str(v) + str(k) for k,v in self._coef.items()])

if __name__ == '__main__':
    fe1 = FormalExp({"x": 1, "":2})    
    print(fe1.get_postfix())
    fe2 = FormalExp({"x^12": 5, "":2})    
    print(fe2.get_postfix())
    fe3 = fe1 * fe2
    for s in fe3:
        print(s)
    fe4 = fe1 * 2
    for s in fe4:
        print(s)

    fe = FormalExp(letter = "a")
    fe_ = -2 * fe
    print(fe_[0])

    fe = FormalExp(letter = "a")
    fe_ = fe * (-2)
    print(fe_[0])




# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del 

@@ -3667,33 +3667,261 @@
     
-return %22
+ans = %22%22%0A        for k,v in self._coef.items():%0A            if v %3C 0:%0A                ans += %22-%22%0A            else:%0A                ans
  +
+=
  %22
-.join(%5Bstr
++%22%0A%0A            if abs(v) == 1:%0A                ans += str(k)%0A            else:%0A                ans += str(abs
 (v)
+)
  + s
@@ -3929,40 +3929,98 @@
 r(k)
- for k,v in self._coef.items()%5D)
+%0A        if ans%5B0%5D == %22+%22:%0A            return ans%5B1:%5D%0A        else:%0A            return ans
 %0A%0Aif
@@ -4074,14 +4074,16 @@
 x%22: 
+-
 1, %22%22:
+-
 2%7D) 
@@ -4099,30 +4099,16 @@
 rint(fe1
-.get_postfix()
 )%0A    fe
@@ -4161,22 +4161,8 @@
 (fe2
-.get_postfix()
 )%0A  

improve printing ofr FormalExp