# frac.py - 0依赖分数类
from .smath import gcd as _gcd, lcm as _lcm

def is_int(floater):
    return int(floater) == floater
    
class frac:
    """轻量分数类 - 0依赖"""
    
    def __init__(self, numerator, denominator=None):
        if denominator is None:
             numerator, denominator = int(self._to_frac(numerator).n), int(self._to_frac(numerator).d)
        if denominator == 0:
            raise ValueError("分母不能为0")
        
        # 处理负号
        if denominator < 0:
            numerator = -numerator
            denominator = -denominator
        
        # 自己约分，不用 math
        g = _gcd(numerator, denominator)
        self.n = numerator // g
        self.d = denominator // g

    def as_percent(self, precision=0):
        """真正的分数百分数显示"""
        # 用整数运算避免浮点误差
        percent_numerator = self.n * 100
        whole = percent_numerator // self.d
        remainder = percent_numerator % self.d
        
        if precision == 0:
            # 四舍五入
            if remainder * 2 >= self.d:
                whole += 1
            return f"{whole}%"
        
        # 小数部分
        result = f"{whole}."
        for i in range(precision):
            remainder *= 10
            digit = remainder // self.d
            result += str(digit)
            remainder = remainder % self.d
        
        return f"{result}%"
        
    def __pow__(self, other):
        other = self._to_frac(other)
        
        # 如果指数是整数，可以保持分数
        if other.d == 1:
            # 整数指数： (n/d)**m = n**m / d**m
            exp = other.n  # 可能是负数
            if exp >= 0:
                return frac(self.n ** exp, self.d ** exp)
            else:
                # 负指数：倒数
                return frac(self.d ** (-exp), self.n ** (-exp))

            
        # 指数是分数：开方，可能无法保持精确分数
        # 比如 (1/3)**(1/2) = 1/√3，无理数
        # 这种情况只能返回近似分数
        return frac((self.n ** other.n) ** float(frac(1, other.d)), (self.d ** other.n) ** float(frac(1, other.d)))

    def __rpow__(self, other):
        """右幂运算：other ** self"""
        return other ** self
    
    def __add__(self, other):
        other = self._to_frac(other)
        return frac(
            self.n * other.d + other.n * self.d,
            self.d * other.d
        )
    
    def __sub__(self, other):
        other = self._to_frac(other)
        return frac(
            self.n * other.d - other.n * self.d,
            self.d * other.d
        )
    
    def __mul__(self, other):
        other = self._to_frac(other)
        return frac(self.n * other.n, self.d * other.d)
    
    def __truediv__(self, other):
        other = self._to_frac(other)
        if float(other) == 0:
            raise ZeroDivisionError("除数不能为0")
        return frac(self.n * other.d, self.d * other.n)
    
    def __eq__(self, other):
        other = self._to_frac(other)
        return self.n == other.n and self.d == other.d

    def __lt__(self, other):
        other = self._to_frac(other)
        return self._to_d(_lcm(self.d, other.d)) < other._to_d(_lcm(self.d, other.d))

    def __le__(self, other):
        other = self._to_frac(other)
        return (self._to_d(_lcm(self.d, other.d)) < other._to_d(_lcm(self.d, other.d))) or self.__eq__(other)
    
    def __gt__(self, other):
        other = self._to_frac(other)
        return (self._to_d(_lcm(self.d, other.d)) > other._to_d(_lcm(self.d, other.d)))

    def __ge__(self, other):
        other = self._to_frac(other)
        return (self._to_d(_lcm(self.d, other.d)) > other._to_d(_lcm(self.d, other.d))) or self.__eq__(other)

    def _to_d(self, d):
        n = d / self.d * self.n
        if n != int(n):
            raise ValueError("分子不能为小数")
        
        return n
    
    def __radd__(self, other):
        """右加法：other + self"""
        return self + other  # 直接复用 __add__
    
    def __rsub__(self, other):
        """右减法：other - self"""
        # 注意顺序：other - self
        return -self + other
    
    def __rmul__(self, other):
        """右乘法：other * self"""
        return self * other
    
    def __rtruediv__(self, other):
        """右除法：other / self"""
        # other / self = other * (1/self)
        return self._to_frac(other) * ~self
    
    
    def __neg__(self):
        """负数：-self"""
        return frac(-self.n, self.d)
    
    def __pos__(self):
        """正数：+self"""
        return self
    
    def __abs__(self):
        """绝对值：abs(self)"""
        return frac(abs(self.n), self.d)

    def __invert__(self):
        """~frac 返回倒数"""
        return frac(self.d, self.n)
    
    def __float__(self):
        return self.n / self.d
    
    def __str__(self):
        return str(float(self))
    
    def __repr__(self):
        return f"{self.n}/{self.d}" if self.d != 1 else str(self.n)
    
    def _to_frac(self, other):
        if isinstance(other, frac):
            return other
        if isinstance(other, str) and not "e" in other.lower():
            other = float(other)
        if isinstance(other, float) and is_int(other) and not "e" in str(other).lower():
            return frac(other, 1)
        if isinstance(other, (float, int, str)):
            if isinstance(other, (float, str)):
                # 转成整数处理 (1.23 -> 123/100)
                s = str(other)
                
                if 'e' not in s.lower():  # 暂不考虑科学计数法
                    parts = s.split('.')
                    if len(parts) == 2:
                        whole, dec = parts
                        n = int(whole + dec)
                        d = 10 ** len(dec)
                        return frac(n, d)
                else:
                    # 直接解析
                    coeff, exp = s.lower().split('e')
                    exp = int(exp)
                    
                    # 把系数转成整数分子分母
                    if '.' in coeff:
                        whole, dec = coeff.split('.')
                        n = int(whole + dec)
                        d = 10 ** len(dec)
                    else:
                        n = int(coeff)
                        d = 1
                    
                    # 根据指数调整
                    if exp >= 0:
                        n *= 10 ** exp
                    else:
                        d *= 10 ** (-exp)
                    
                    return frac(n, d)
                return frac(other, 1)
        raise TypeError(f"不支持的类型: {type(other)}")
