# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/05_utils.ipynb.

# %% auto 0
__all__ = ['psyn', 'nsyn', 'zsyn', 'any2signint', 'num2signint', 'str2signint', 'bool2int', 'bool2signint', 'numstr2signint',
           'tosignint', 'matchsign', 'toint', 'tochr', 'tosym', 'tosyn', 'toset', 'topow', 'tosign', 'totuple',
           'prep_strs', 'make_fstr', 'make_bstr', 'make_estr', 'from_fstr', 'from_bstr', 'from_estr']

# %% ../nbs/05_utils.ipynb 3
from numbers import Number
from typing import Any, Tuple, Optional

# %% ../nbs/05_utils.ipynb 4
from atyp import BoolCall
from chck import isstr, isnum, isbool


from bflag import tern2x

# %% ../nbs/05_utils.ipynb 5
from .types import sint, NumStr, SignInt, SignChr, SignSym, ExpoChr, NumStrQ, SignIntQ, SignSymQ, SignChrQ, ExpoChrQ
from .guard import ispos, isneg, isnumstr, isexpochr
from .sign import sign, P, Z, N

from .cons import BASE
from .pttn import (ftmpl, btmpl, etmpl, fpttn, bpttn, epttn)

# %% ../nbs/05_utils.ipynb 7
def psyn(s: str) -> bool: return P.issyn(s)
def nsyn(s: str) -> bool: return N.issyn(s)
def zsyn(s: str) -> bool: return Z.issyn(s)

def any2signint(val: Any, posfn: Optional[BoolCall] = ispos, negfn: Optional[BoolCall] = isneg) -> SignInt:
    if isbool(val): val = 1 if val == True else -1
    return tern2x(val, posfn, negfn, P.int, N.int, Z.int)

def num2signint(s: Number) -> SignInt: 
    return any2signint(s, ispos, isneg)

def str2signint(s: str) -> SignInt:
    try: return num2signint(int(s))
    except: return any2signint(s, psyn, nsyn)

def bool2int(b, boolneg: bool = True):
    if not isbool(b): return b
    return -1 if b and boolneg else 1 if b and not boolneg else 1 if not b and boolneg else -1

def bool2signint(i: int | bool = True, boolneg: bool = True) -> SignInt:
    if isbool(i): i = bool2int(i, boolneg)
    return num2signint(int(i))

def numstr2signint(s: NumStrQ, boolneg: bool = True) -> SignInt: 
    if isbool(s): return bool2signint(s, boolneg)
    elif isstr(s): return str2signint(s)
    elif isnum(s): return num2signint(s)
    else: return Z.int
    return str2signint(s) if isstr(s) else num2signint(s) if isnum(s) else bool2signint(s) if isbool(s) else Z.int

def tosignint(s: Any, boolneg: bool = True) -> SignIntQ: 
    if isbool(s): return bool2signint(s, boolneg)
    elif isnumstr(s): return numstr2signint(s)    
    else: return None
    return numstr2signint(s) if isnumstr(s) else None

def matchsign(
    val: Any, pos: Any = P.int, neg: Any = N.int, nil: Any = Z.int, 
    default: Any = None, boolneg: bool = True
) -> Any:
    match tosignint(val, boolneg):
        case  1: return pos
        case -1: return neg
        case  0: return nil
        case  _: return default

def toint(s: NumStr, default: Any = None, boolneg: bool = True) -> SignInt: 
    return matchsign(s, P.int, N.int, Z.int, default, boolneg)

def tochr(s: NumStr, default: Any = None, boolneg: bool = True) -> SignChr: 
    return matchsign(s, P.chr, N.chr, Z.chr, default, boolneg)

def tosym(s: NumStr, default: Any = None, boolneg: bool = True) -> SignSym: 
    return matchsign(s, P.sym, N.sym, Z.sym, default, boolneg)

def tosyn(s: NumStr, default: Any = None, boolneg: bool = True) -> set: 
    return matchsign(s, P.syn, N.syn, Z.syn, default, boolneg)

def toset(s: NumStr, default: Any = None, boolneg: bool = True) -> set: 
    return matchsign(s, P.set, N.set, Z.set, default, boolneg)

def topow(base: int, expo: int, sign: NumStrQ = None, boolneg: bool = True) -> Number: 
    return base ** (expo * tosignint(sign or expo, boolneg))

def tosign(s: NumStr, force: bool = False, boolneg: bool = True) -> Optional[sign]: 
    return matchsign(s, P, N, Z, None if force else Z, boolneg)

def totuple(s: NumStr, force: bool = False, boolneg: bool = True) -> tuple: 
    return tuple(tosign(s, force, boolneg))

# %% ../nbs/05_utils.ipynb 9
def prep_strs(
    expo: Number = 0, sign: sint = None, base: Number = BASE, 
    echr: ExpoChr = '^', showsign: bool = True, showbase: bool = True,
    boolneg: bool = True
) -> Tuple[int, str, SignSymQ, SignChrQ, ExpoChrQ]:
    sign = tosign(sign or expo, force = True, boolneg = boolneg)
    bstr = str(base) if showbase else ''
    sstr = sign.sym if showsign else ''
    schr = sign.chr
    echr = echr if isexpochr(echr) else '^'
    estr = abs(expo)
    return bstr, sstr, schr, estr, echr

# %% ../nbs/05_utils.ipynb 11
def make_fstr(
    expo: Number = 0, sign: sint = None, base: Number = BASE, 
    echr: ExpoChr = '^', showsign: bool = True, showbase: bool = True, boolneg: bool = True
) -> str:
    bstr, sstr, schr, estr, echr = prep_strs(expo, sign, base, echr, showsign, showbase, boolneg=boolneg)
    return ftmpl.substitute(expo=estr, sign=schr)

def make_bstr(
    expo: Number = 0, sign: sint = None, base: Number = BASE, 
    echr: ExpoChr = '^', showsign: bool = True, showbase: bool = True, boolneg: bool = True
) -> str:
    bstr, sstr, schr, estr, echr = prep_strs(expo, sign, base, echr, showsign, showbase, boolneg=boolneg)
    return btmpl.substitute(base=bstr, sign=sstr, echr=echr, expo=estr)

def make_estr(
    expo: Number = 0, sign: sint = None, base: Number = BASE, 
    echr: ExpoChr = '^', showsign: bool = True, showbase: bool = True, boolneg: bool = True
) -> str:
    bstr, sstr, schr, estr, echr = prep_strs(expo, sign, base, echr, showsign, showbase, boolneg=boolneg)
    return etmpl.substitute(sign=sstr, expo=estr)


# %% ../nbs/05_utils.ipynb 14
def from_fstr(s: str, base: int = BASE) -> (Number, SignIntQ, Number):
    match = fpttn.match(s)
    if match:
        expo = int(match.group('expo'))
        sign = toint(match.group('sign'))
        return expo, sign, base
    raise ValueError(f"String '{s}' doesn't match factor format")

def from_bstr(s: str, base: int = BASE) -> (Number, SignIntQ, Number):
    match = bpttn.match(s)
    if match:
        base = int(match.group('base')) if match.group('base') else base
        expo = int(match.group('expo'))
        sign = toint(match.group('sign') + '1')
        return expo, sign, base
    raise ValueError(f"String '{s}' doesn't match base format")

def from_estr(s: str, base: int = BASE) -> (Number, Number, SignIntQ):
    match = epttn.match(s)
    if match:
        expo = int(match.group('expo'))
        sign = toint(match.group('sign') + '1')
        return expo, sign, base
    raise ValueError(f"String '{s}' doesn't match exponent format")
