from math import exp, log

def fc_to_w_plus(f, c, k): return k*f*c/(1-c)
def fc_to_w(f, c, k): return k*c/(1-c)
def fc_to_w_minus(f, c, k): return k*(1-f)*c/(1-c)
def w_to_f(w_plus, w): return w_plus/w
def w_to_c(w, k): return w/(w+k)


class Truth:
    ts_update = 0
    def __init__(self, f, c, k=1.0, eternal=False) -> None:
        self.f: float = f
        self.c: float = c
        self.k: float = k
        if eternal:
            self.ts_update = None

    @property
    def e(self):
        return (self.c * (self.f - 0.5) + 0.5)

    @classmethod
    def from_w(w_plus, w, k):
        f, c = (w_to_f(w_plus, w), w_to_c(w, k)) if w != 0 else (0.5, 0.0)
        return Truth(f, c, k)

    def reset(self, f, c):
        self.f, self.c = f, c

    def to_w(self):
        f, c, k = self.f, self.c, self.k
        return fc_to_w_plus(f, c, k), fc_to_w_minus(f, c, k)    
    
    def revise_w(self, w_p, w, ts_now=None, duration=20):
        """
        Args:
            a_decay: decay factor. See methods, `Truth.get_decay_factor` and `Truth.decay`, for more information
        """
        # update ts
        if ts_now is not None:
            if self.ts_update is not None:
                if self.ts_update < ts_now:
                    dt = ts_now - self.ts_update
                    self.decay(self.get_decay_factor(duration), dt)
                self.ts_update = ts_now
            
        w_p1, w_m1 = self.to_w()
        w1 = w_p1 + w_m1
        # revision
        w = w1 + w
        w_p = w_p1 + w_p
        # calculate f, c
        self.f, self.c = w_to_f(w_p, w), w_to_c(w, self.k)
        


    def __str__(self) -> str:
        return f'%{self.f:.3f};{self.c:.3f}%'

    def __repr__(self) -> str:
        return str(self)
    
    def __iter__(self):
        return iter((self.f, self.c))
    

    @staticmethod
    def get_decay_factor(half_life_period: float):
        return -1/half_life_period*log(0.5)

    def decay(self, alpha: float, dt: float) -> None:
        """
        decay confidence by
        c' = c*exp(-alpha*dt)
        The smaller the alpha is, the slower the decay
        
        half-life period
        |  dt  |    alpha   |
        | ---- | ---------- |
        |    1 | 0.69314718 |
        |    2 | 0.34657359 |
        |    4 | 0.17328680 |
        |    8 | 0.08664340 |
        |   16 | 0.04332170 |
        |   32 | 0.02166085 |
        |   64 | 0.01083042 |
        |  128 | 0.00541521 |
        |  256 | 0.00270761 |
        |  512 | 0.00135380 |
        | 1024 | 0.00067690 |
        """
        self.c = self.c*exp(-alpha*dt)
