# Path: athlib/utils.py
# def normalize_gender(gender: Any) -> str:
#     """
#     Return M, F or raise a ValueError
# 
#     :param gender: M or F (case variations and suffixes accepted)
#     :returns: M or F
#     :raises ValueError: raises an exception
# 
#     """
#     if gender and isStr(gender):
#         g = gender.upper().strip()
#         if g:
#             g = g[0]
#             if g in ('M','F'):
#                 return g
#     raise ValueError('cannot normalize gender = %s' % repr(gender))
# 
# def parse_hms(t: Union[int, float, str]) -> Union[int, float]:
#     """
#     Parse a time duration with 0, 1 or 2 colons and return seconds.
# 
#     >>> from .utils import parse_hms
#     >>> parse_hms('10')
#     10
#     >>> parse_hms('1:10')
#     70
#     >>> parse_hms('1:1:10')
#     3670
#     >>> parse_hms('1:1:10.1')
#     3670.1
#     >>> parse_hms(3670.1)
#     3670.1
#     """
#     if isinstance(t, (float, int)):
#         return t
# 
#     # Try : and ; separators
#     for sep in ':;':
#         if sep not in t:
#             continue
# 
#         sec = 0
# 
#         for s in t.split(sep):
#             sec *= 60
# 
#             try:
#                 sec += str2num(s)
#             except ValueError:
#                 raise ValueError('cannot parse seconds from %s' % repr(t))
# 
#         return sec
#     try:
#         return str2num(t)
#     except ValueError:
#         raise ValueError('cannot parse seconds from %s' % repr(t))
# 
# def normalize_event_code(c : str) -> str:
#     c = c.strip()   #remove excess whitespace
#     m =  PAT_EVENT_CODE.match(c)
#     if not m:
#         raise ValueError('cannot normalize event code %r' % c)
#     rm = PAT_RELAYS.match(c)
#     if rm:
#         c = '%sx%s' % (rm.group(1),rm.group(2).upper())
#     else:
#         R = [].append
#         for k, v in m.groupdict().items():
#             if v is None or k not in _gnorms: continue
#             s = v.strip()
#             if not s: continue
#             R((m.span(k),_gnorms[k](s)))
#         R = R.__self__
#         c = c.upper()
#         for ((start,end),repl) in sorted(R, reverse=True):
#             c = c[:start] + repl + c[end:] 
#     return c.replace(' ','')
# 
# def is_hand_timing(perf: Any) -> bool:
#     if isStr(perf):
#         perf = nativeStr(perf)
#         dp = perf.rfind('.')
#         return dp<0 or len(perf)-dp<3
#     return False
# 
# Path: athlib/codes.py
# PAT_RUN = re.compile(_orjoin(PAT_TRACK, PAT_ROAD, PAT_RELAYS))


import typing
from athlib import parse_hms, normalize_gender, normalize_event_code, is_hand_timing
from athlib.codes import PAT_RUN
from typing import Union, Dict, Tuple
        base_perf = self.get_base_perf(age,yv)

        #perf is a distance
        v = perf
        if not isinstance(v,(int,float)):
            v = float(v.replace(',','.'))
        return max(0,int(1000 + 1e-8 + multiplier * (v - base_perf)*100))

    def stav_points(self, age, perf: Union[int, float, str]) -> int:
        '''this works for all the piecewise linear distance events'''
        multipliers, yvs = self.args

        levels = [self.get_base_perf(age,yv) for yv in yvs]
        v = perf
        if not isinstance(v,(int,float)):
            v = float(v.replace(',','.'))
        diffs = 100*(v - levels[0]), 100*(v - levels[1])
        return max(0, int(1000 + 1e-8 + (diffs[0]*multipliers[0] if diffs[0]>=0
                        else diffs[0]*multipliers[1] if diffs[1]>0 
                        else diffs[1]*multipliers[2]+levels[2]-1000)))
    throw_points = stav_points
    pv_points = stav_points

def tyrving_score(gender: str, age: int, event_code: str, perf: Union[int, float, str]) -> int:
    timing_kind='automatic'
    if PAT_RUN.match(event_code):
        if is_hand_timing(perf):
            timing_kind = 'manual'

    event_code = normalize_event_code(event_code)

# Path: athlib/utils.py
# def normalize_gender(gender: Any) -> str:
#     """
#     Return M, F or raise a ValueError
# 
#     :param gender: M or F (case variations and suffixes accepted)
#     :returns: M or F
#     :raises ValueError: raises an exception
# 
#     """
#     if gender and isStr(gender):
#         g = gender.upper().strip()
#         if g:
#             g = g[0]
#             if g in ('M','F'):
#                 return g
#     raise ValueError('cannot normalize gender = %s' % repr(gender))
# 
# def parse_hms(t: Union[int, float, str]) -> Union[int, float]:
#     """
#     Parse a time duration with 0, 1 or 2 colons and return seconds.
# 
#     >>> from .utils import parse_hms
#     >>> parse_hms('10')
#     10
#     >>> parse_hms('1:10')
#     70
#     >>> parse_hms('1:1:10')
#     3670
#     >>> parse_hms('1:1:10.1')
#     3670.1
#     >>> parse_hms(3670.1)
#     3670.1
#     """
#     if isinstance(t, (float, int)):
#         return t
# 
#     # Try : and ; separators
#     for sep in ':;':
#         if sep not in t:
#             continue
# 
#         sec = 0
# 
#         for s in t.split(sep):
#             sec *= 60
# 
#             try:
#                 sec += str2num(s)
#             except ValueError:
#                 raise ValueError('cannot parse seconds from %s' % repr(t))
# 
#         return sec
#     try:
#         return str2num(t)
#     except ValueError:
#         raise ValueError('cannot parse seconds from %s' % repr(t))
# 
# def normalize_event_code(c : str) -> str:
#     c = c.strip()   #remove excess whitespace
#     m =  PAT_EVENT_CODE.match(c)
#     if not m:
#         raise ValueError('cannot normalize event code %r' % c)
#     rm = PAT_RELAYS.match(c)
#     if rm:
#         c = '%sx%s' % (rm.group(1),rm.group(2).upper())
#     else:
#         R = [].append
#         for k, v in m.groupdict().items():
#             if v is None or k not in _gnorms: continue
#             s = v.strip()
#             if not s: continue
#             R((m.span(k),_gnorms[k](s)))
#         R = R.__self__
#         c = c.upper()
#         for ((start,end),repl) in sorted(R, reverse=True):
#             c = c[:start] + repl + c[end:] 
#     return c.replace(' ','')
# 
# def is_hand_timing(perf: Any) -> bool:
#     if isStr(perf):
#         perf = nativeStr(perf)
#         dp = perf.rfind('.')
#         return dp<0 or len(perf)-dp<3
#     return False
# 
# Path: athlib/codes.py
# PAT_RUN = re.compile(_orjoin(PAT_TRACK, PAT_ROAD, PAT_RELAYS))


# Path: athlib/tyrving_score.py
import typing
from athlib import parse_hms, normalize_gender, normalize_event_code, is_hand_timing
from athlib.codes import PAT_RUN
from typing import Union, Dict, Tuple

        base_perf = self.get_base_perf(age,yv)

        #perf is a distance
        v = perf
        if not isinstance(v,(int,float)):
            v = float(v.replace(',','.'))
        return max(0,int(1000 + 1e-8 + multiplier * (v - base_perf)*100))

    def stav_points(self, age, perf: Union[int, float, str]) -> int:
        '''this works for all the piecewise linear distance events'''
        multipliers, yvs = self.args

        levels = [self.get_base_perf(age,yv) for yv in yvs]
        v = perf
        if not isinstance(v,(int,float)):
            v = float(v.replace(',','.'))
        diffs = 100*(v - levels[0]), 100*(v - levels[1])
        return max(0, int(1000 + 1e-8 + (diffs[0]*multipliers[0] if diffs[0]>=0
                        else diffs[0]*multipliers[1] if diffs[1]>0 
                        else diffs[1]*multipliers[2]+levels[2]-1000)))
    throw_points = stav_points
    pv_points = stav_points

def tyrving_score(gender: str, age: int, event_code: str, perf: Union[int, float, str]) -> int:
    timing_kind='automatic'
    if PAT_RUN.match(event_code):
        if is_hand_timing(perf):
            timing_kind = 'manual'

    event_code = normalize_event_code(event_code)

    gender = normalize_gender(gender)