
    gfg                    N   d Z ddlmZmZmZmZ ddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl!Z!ddl"Z"ddl#Z#ddl$Z$ddl%m&Z&m'Z' ddl%m(Z(m)Z)m*Z* ddl%m+Z+m,Z,m-Z- ddl%m.Z.m/Z/m0Z0m1Z1m2Z2 ddl%m3Z3 dd	l%m4Z4m5Z5 dd
l%m6Z6m7Z7m8Z8 dZ9dZ:dddddddZ;dZ<dZ= e>d      D  ci c]  \  } }|| dz    c}}  e>d      D  ci c]  \  } }|| dz     c}} ddiddidddddddZ?e?d   j                  e?d           e?d   j                  e?d!          e?d"   j                  e?d    j                         D  ci c]  \  }} || e=z   c} }       e?d#   j                  e?d!   j                         D  ci c]  \  }} || e=z   c} }       e?d$   j                  e?d"          e?d$   j                  e?d#           ej                  e?      ZC ej                  e?      ZDd%D ]2  ZEe?eE   d&   eDeE   d'<   e?eE   d&   eDeE   d(<   e?eE   d&   eCeE   d'<   eCeE   d&= 4 eFj                  d)d*      ZHeFj                  d)d+      ZId,ZJeJD ci c]  }|| c}ZKeKj                  eLd-eMd.eNd/eFd0i       eKj                  d-d1d2d3d4d.d5d6d7d8d9d/d:d0d;       d<d<d=ZOd>d?d@ZPh dAZQ ej                  eFj                  d      ZTdB ZUdC ZVdrdDZWdE ZXeX	 	 dsdG       ZYeX	 	 dtdH       ZZ G dI dJe[      Z\dK Z]dL Z^	 dudNZSdO Z_dP Z`dQe"j                  dRdfdSZb	 	 	 	 dvdTZc	 dudUZddV ZedW Zf	 drdXZgdY Zh	 	 dwdZZi	 dud[Zje\	 	 dxd\       Zk	 	 	 dyd]ZleFemenffd^ZodeFemenffd_ZpdeFemenffd`Zq	 dzdaZrej                  dMfdbZtej                  fdcZudd ZvdMdeweFemenffdeZxdMdewdMeFemenffdfZy	 	 d{dgZz	 drdhZ{deFemenffdiZ|	 	 d|djZ}	 d}dkZ~dl Z	 drdmZddeFemenffdnZ	 	 	 d~doZdeFemenffdpZdddddeFemenffdqZde
j
                  fdrZ	 	 dwdsZdddefdtZ	 	 dwduZddedFfdvZ	 	 dwdwZ	 	 dwdxZ	 	 	 ddyZddedFfdzZ	 	 dwd{Z	 	 dwd|Zd} Z	 	 dwd~Zd ZewdfdZ	 d}dZ	 	 	 	 	 ddZ	 	 	 	 ddZ	 	 d|dZ	 d}dZ	 	 	 ddZ	 	 ddZ	 drdZ	 drdZ	 	 dxdZ	 drdZ	 drdZ	 	 dxdZ	 	 dxdZ	 dudZ	 drdZddddZ	 drdZ	 	 ddZ	 drdZ	 	 	 ddZd dMfdZd Z	 	 dwdZ	 	 	 	 ddZ	 	 	 	 ddZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Z	 ddZ	 ddZ	 d}dZ	 d}dZd Zd Zd Zd Zd Zd Zd Zd Zd Z	 ddZd Zd Zd Z	 	 ddZ	 	 	 ddZ	 	 	 ddZ	 	 ddZddefdZd Zd ed  eddd      D              z   Ze eeҫ      fdZ	 drdÄZddedfdĄZddedfdńZ	 drdƄZedfdǄZdȄ Z	 ddʄZd˄ Z	 	 	 dd̄Z	 	 	 dd̈́Zdd΄ dFfdτZdЄ Zdф Zd҄ Zdӄ ZdԄ Z	 dudՄZddքZ	 ddׄZd؄ Z	 dudلZ	 dudڄZdۄ Zd܄ Zd݄ Zdބ Zd߄ Zd Z	 ddZ	 ddZ	 ddZ	 ddZ	 ddZ	 ddZ	 ddZ	 ddZd Zd Z	 dudZ	 dudZ	 dudZ	 	 	 	 ddZ	 	 ddZde dMfdZddddMedfdZddMedfdZd Z	 dudZ	 dudZd Zd Z	 ddZ		 ddZ
	 	 ddZ	 	 ddZ	 	 ddZ	 	 	 dd Z	 	 	 ddZ	 	 ddZ	 	 	 ddZ	 	 	 	 ddZ	 	 	 	 ddZ	 	 	 	 ddZ	 	 	 	 ddZ	 ddZ	 	 dd	Z	 	 dd
Z	 ddZd Zd Z	 	 ddZd Zd Z	 drdZ	 drdZ d Z!d Z"d Z#dFddZ$	 	 ddZ%	 	 ddZ&d Z'dddejP                  fdZ)dejP                  fdZ*	 	 	 	 ddZ+e jX                  e!jZ                  dɐdfd Z.d! e jX                  e!jZ                  dfd"Z/ddd#e"j`                  e"jb                  dFfd$Z2	 	 	 	 	 dd%Z3	 	 	 	 	 	 dd&Z4	 	 	 	 	 	 dd'Z5d( Z6dd)dd*ddFe)fd+Z7dd,ddde)fd-Z8	 dud.Z9ddMdMe)fd/Z:	 	 	 	 	 dd0Z;	 	 	 	 	 dd1Z<ejz                  j|                  fd2Z?	 	 	 dd3Z@	 dud4ZA	 dud5ZBd6 ZC	 	 	 dd7ZD	 	 	 dd8ZEd9e)fd:ZF	 	 	 dd;ZGd< ZH	 dd=ZIe<dz
  fd>ZJ	 	 	 	 	 	 	 	 	 dd?ZK	 	 dd@ZL	 	 ddAZM	 	 	 ddBZNddeMeNeOeNffdCZPdD ZQ	 	 ddFZR epeDd$         fdGZS epe?d$         fdHZT	 ddIZU	 ddJZV epeDd$         dEfdKZW epe?d$         dEfdLZX	 	 	 ddMZY	 	 	 ddNZZ	 ddOZ[	 	 ddPZ\	 	 ddQZ]	 drdRZ^dS Z_drdTZ`	 ddUZa	 	 	 	 ddVZb	 	 	 	 	 	 	 ddWZc	 	 	 	 	 	 	 ddXZd	 	 	 ddYZedZ ZfdFe)dFdFfd[Zg	 dud\Zh	 dud]Zid^ Zjd_ Zkd` Zle fdaZm	 	 ddbZn	 	 	 	 ddcZo	 dddZpeXdedfdgddhdeeej                  dMdMdidMe)fdj       Zrdd eΐdk  eѐdE      D              dl dm ddFdndodMe)fdpZs e+ejz                  j                  et             eudqk(  r	 e-e        yyc c}} w c c}} w c c} }w c c} }w c c}w (  z%
flyingcircus.base: Base subpackage.
    )divisionabsolute_importprint_functionunicode_literalsN)INFOPATH)VERB_LVL
D_VERB_LVLVERB_LVL_NAMES)elapsedreportrun_doctests)msgdbgfmtfmtmsafe_format_map)do_nothing_decorator)HAS_JITjit)valid_indexfind_allnested_delimiters	#gzbz2lzmaxzlz)gzipbzipbzip2r   r   lzip      )kMGTPEZY   )m   μnpfazy    )dah)dc)	base1000+	base1000-base1000base10base10+base10-rB   r@   rA   rD   rE   rC   )rA   rE   rB   rC   r1   u   µz0123456789.e+-=()nu2   ⁰¹²³⁴⁵⁶⁷⁸⁹·ᵉ⁺⁻⁼⁽⁾ⁿu4   ₀₁₂₃₄₅₆₇₈₉.ₑ₊₋₌₍₎ₙ)xr?   bB?r;   HiIlLqQr4   r>   sr3   r+   rK   rM   r4   rS   rI   rJ   r;   rL   rN   rO   rP   rQ   rR   r>   )boolcharucharshortushortintuintlongulongllongullongfloatdoublestr   )crc32adler32      )	shake_128	shake_256>   b16b32b64b85urlsafe_b64c                 ^    t         j                  j                  |       j                  d      S )a   
    Heuristic to determine hidden files.

    Args:
        filepath (str): the input filepath.

    Returns:
        is_hidden (bool): True if is hidden, False otherwise.

    Notes:
        Only works with UNIX-like files, relying on prepended '.'.
    .)ospathbasename
startswith)filepaths    i/var/dept/share/cheung/public_html/OutSchool/python/env/lib/python3.12/site-packages/flyingcircus/base.py
_is_hiddenrv      s$     77H%0055    c                     t        j                  |        xr. t        j                  |        xr t        j                  |        }|S )a  
    Heuristic to determine non-standard files.

    Args:
        stats_mode (mode): A mode as specified by the `stat` module.

    Returns:
        is_special (bool): True if is hidden, False otherwise.

    Notes:
        Its working relies on Python stat module implementation.
    )statS_ISREGS_ISDIRS_ISLNK)
stats_mode
is_specials     ru   _is_specialr      sH     LL$$ 	%LL$$	%LL$$  rw   c                     |t        |       }t        |      st        }n#|t        u rt        }n|t
        t        fv rt
        }	  || dd        |S # t        $ r
 t        }Y |S w xY w)aT  
    Guess container for combinatorial computations.

    Args:
        seq (Sequence): The input items.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Returns:
        container (callable): The container function.
    r   )typecallabletuplera   	_str_joinbytes	bytearray	TypeError)seq	containers     ru   _guess_containerr      sv     I	I		c			ui(	(	#a(   	s   A A$#A$c                 B     t        j                          fd       }|S )a  
    Create a decorator-factory from a decorator.

    This is for creating decorators with parameters.

    Note: contrarily to function parameters, decorator parameter's names
    should never be reassigned.

    Args:
        decorator (callable): The input decorator.

    Returns:
        decorator_factory (callable): The decorated decorator-factory.

    Examples:
        >>> def multiply(func):
        ...     @functools.wraps(func)
        ...     def wrapper(*args, **kws):
        ...         return 10 * func(*args, **kws)
        ...     return wrapper
        >>> @multiply
        ... def my_sum(values):
        ...     return sum(values)
        >>> my_sum(range(10))
        450

        >>> @parametric
        ... def multiply(func, factor=1):
        ...     @functools.wraps(func)
        ...     def wrapper(*args, **kws):
        ...         return factor * func(*args, **kws)
        ...     return wrapper
        >>> @multiply()
        ... def my_sum(values):
        ...     return sum(values)
        >>> my_sum(range(10))
        45
        >>> @multiply(10)
        ... def my_sum(values):
        ...     return sum(values)
        >>> my_sum(range(10))
        450
    c                        fd}|S )Nc                      | gi S N )func_args_kws	decorators    ru   _wrapperz0parametric.<locals>._decorator.<locals>._wrapper'  s    T2E2T22rw   r   )r   r   r   r   s   `` ru   
_decoratorzparametric.<locals>._decorator%  s    	3 rw   	functoolswraps)r   r   s   ` ru   
parametricr      s)    Z __Y   rw   Fc                 J     t        j                          fd       }|S )a~	  
    Decorate a function to accept starred arguments instead of an iterable.

    Args:
        func (callable): The input function.
        mode (str): The auto-star detection mode.
            This determines on what condition `func` is decorated.
            Allowed modes:
             - 'type': if the first parameter is iterable.
             - 'nargs': if `func` accepts a single parameter.
        allow_empty (bool): Allow `func` to be called without parameters.
            This is effective only if the first parameter of `func`
            does not have a default value.

    Returns:
        wrapper (callable): The decorated function.

    Raises:
        TypeError: If `allow_empty` is False, `func`'s first parameter
            does not have a default value and no value is specified when
            calling the decorated function.

    Examples:
        >>> @auto_star_magic('type', True)
        ... def my_prod(items, start=1):
        ...     for item in items:
        ...         start *= item
        ...     return start
        >>> print(my_prod(1, 2, 3))
        6
        >>> print(my_prod(range(1, 4)))
        6
        >>> print(my_prod())
        1

        >>> @auto_star_magic('type', False)
        ... def my_prod(items, start=1):
        ...     for item in items:
        ...         start *= item
        ...     return start
        >>> print(my_prod(1, 2, 3))
        6
        >>> print(my_prod(range(1, 4)))
        6
        >>> print(my_prod())
        Traceback (most recent call last):
            ...
        TypeError: my_prod() missing 1 required positional argument: 'items'

        >>> @auto_star_magic('nargs', True)
        ... def my_prod(items):
        ...     start = 1
        ...     for item in items:
        ...         start *= item
        ...     return start
        >>> print(my_prod(1, 2, 3))
        6
        >>> print(my_prod(range(1, 4)))
        6
        >>> print(my_prod())
        1

        >>> @auto_star_magic('nargs', False)
        ... def my_prod(items):
        ...     start = 1
        ...     for item in items:
        ...         start *= item
        ...     return start
        >>> print(my_prod(1, 2, 3))
        6
        >>> print(my_prod(range(1, 4)))
        6
        >>> print(my_prod())
        Traceback (most recent call last):
            ...
        TypeError: my_prod() missing 1 required positional argument: 'items'
    c                      t        j                        }t        t        |j                              }t        d      rj                  nd}|j                  |   j                  t         j                  j                  k7  }s$|s"t        |       dk(  rt        t        d            	dk(  r	 t        | d          d}n	dk(  rt        |       d	k(  }nd}|r | i |S  | fi |S # t        t        f$ r d}Y &w xY w)
N__name__z	<unnamed>r   zJ{func_name}() missing 1 required positional argument: '{first_param_name}'r   TFnargsr/   )inspect	signaturenextiter
parametershasattrr   default	Parameteremptylenr   r   
IndexError)
argskwssigfirst_param_name	func_namehas_defaultuse_starallow_emptyr   modes
          ru   wrapperz auto_star_magic.<locals>.wrapper  s   %S^^ 45%,T:%>DMMK	NN+,448I8I8O8OO 	;3t9>,-. .
 v~$aM  $Ht9> )14%%HtD7HC7HH ":. %$H%s   /C) )C=<C=r   )r   r   r   r   s   ``` ru   auto_star_magicr   0  s*    f __TI I2 Nrw   c                 J     t        j                          fd       }|S )a  
    Decorator for iterize a function in its positional arguments.

    Furthermore, it adds support for an additional `fill` parameter
    (the exact parameter name can be changed).
    This parameter controls how shorter iterables are extented.
    If `fill` is None, the iterable is extended using an infinite loop.
    Otherwise, uses the `fill` value specified.

    Args:
        func (callable): The input function.
        container (callable|None): The container for the result.
            If callable, must accept a `map` object, otherwise
            the `map` object itself will be used.
        fill_param (str): The name of the extra parameter used for filling.


    Returns:
        wrapper (callable): The iterized function.

    Examples:
        >>> @iterize(list)
        ... def isum(*x):
        ...     return sum(x)
        >>> isum(range(10), 100)
        [100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
        >>> isum(range(10), 100, fill=0)
        [100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> isum(range(10), range(5))
        [0, 2, 4, 6, 8, 5, 7, 9, 11, 13]
        >>> isum(range(10), range(3))
        [0, 2, 4, 3, 5, 7, 6, 8, 10, 9]
        >>> isum(range(10), range(3), fill=0)
        [0, 2, 4, 3, 4, 5, 6, 7, 8, 9]
        >>> isum(range(4), range(8), range(12), fill=0)
        [0, 3, 6, 9, 8, 10, 12, 14, 8, 9, 10, 11]
        >>> isum(range(4), range(8), range(12))
        [0, 3, 6, 9, 8, 11, 14, 17, 8, 11, 14, 17]
    c                     	|v r|j                  	      nd fdnt        j                  dg }| D ]5  }	 t        |       	 t	        t        |            |j                  |       7 fd|D        }t        
g|i |}t              r |      }|S # t        $ r Y Nw xY w# t        $ r |g}Y `w xY w)Nc              3   .   K   | D ]  }|  	  wr   r   )itemsitemfills     ru   extendz(iterize.<locals>.wrapper.<locals>.extend  s(     ! DJJ    r   c              3   N   K   | ]  }t        |      k(  r|n |        y wr   r   ).0iargr   max_lens     ru   	<genexpr>z+iterize.<locals>.wrapper.<locals>.<genexpr>  s,      N?CCI(DfTl:Ns   "%)
pop	itertoolscycler   maxr   r   appendmapr   )r   r   iargsargresultr   r   r   r   
fill_paramr   s        @@@ru   r   ziterize.<locals>.wrapper  s    &0C&7swwz"T __F 
	CS	!'3s84G LL
	NGLNT)E)S)Iv&F !   es#   B1
B""	B.-B.1C ?C r   )r   r   r   r   s   ``` ru   iterizer     s)    Z __T @ Nrw   c                       e Zd ZdZd Z G d d      Z G d d      Zd Zd Zd	 Z	exZ
xZxZxZxZxZxZxZxZxZxZxZZexZxZxZxZxZxZxZxZxZxZ xZ!xZ"Z#y
)Infixa+  
    Emulate an infix operator using an arbitrary variable.

    This can also be used as a decorator.

    Examples:
        >>> to = Infix(range)
        >>> to(1, 10)
        range(1, 10)
        >>> 1 | to | 10
        range(1, 10)
        >>> [x for x in 1 | to | 15]
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

        >>> @Infix
        ... def till(a, b):
        ...     return range(a, b)
        >>> (1 | to | 10) == (1 | till | 10)
        True

        >>> ((1 + to + 9) == (1 - to - 9) == (1 * to * 9) == (1 / to / 9)
        ...  == (1 // to // 9) == (1 % to % 9) == (1 ** to ** 9)
        ...  == (1 >> to >> 9) == (1 << to << 9)
        ...  == (1 | to | 9) == (1 & to & 9) == (1 ^ to ^ 9)
        ...  == (1 << to >> 9) == (1 + to ^ 9))  # etc. (all combos work)
        True
    c                     || _         y)z
        Args:
            func (callable): The function to emulate the binary operator.
                The function must support two positional arguments.
        Nr   )selfr   s     ru   __init__zInfix.__init__  s     	rw   c                   L    e Zd Zd Zd ZexZxZxZxZxZ	xZ
xZxZxZxZxZxZZy)Infix.RBindc                      || _         || _        y r   r   bindedr   r   r   s      ru   r   zInfix.RBind.__init__      DI DKrw   c                 :    | j                  || j                        S r   r   r   others     ru   __call__zInfix.RBind.__call__!  s    99UDKK00rw   N)r   
__module____qualname__r   r   __radd____rsub____rmul____rtruediv____rfloordiv____rmod____rpow____rmatmul____rlshift____rrshift____ror____rand____rxor__r   rw   ru   RBindr     sn    	!
	1 -5	5 	58 	5h 	5 	5 	5	5	5"-	50;	5>I	5	5rw   r   c                   L    e Zd Zd Zd ZexZxZxZxZxZ	xZ
xZxZxZxZxZxZZy)Infix.LBindc                      || _         || _        y r   r   r   s      ru   r   zInfix.LBind.__init__,  r   rw   c                 :    | j                  | j                  |      S r   r   r   s     ru   r   zInfix.LBind.__call__1  s    99T[[%00rw   N)r   r   r   r   r   __add____sub____mul____truediv____floordiv____mod____pow__
__matmul__
__lshift__
__rshift____or____and____xor__r   rw   ru   LBindr   *  sn    	!
	1 *2	2 	2' 	2G 	2k 	2L 	2	2	2 *	2-7	2:D	2	2wrw   r   c                 :    | j                  | j                  |      S r   )r   r   r   s     ru   rbindzInfix.rbind:      zz$))U++rw   c                 :    | j                  | j                  |      S r   )r   r   r   s     ru   lbindzInfix.lbind>  r  rw   c                 &    | j                  ||      S r   r   )r   value1value2s      ru   r   zInfix.__call__B  s    yy((rw   N)$r   r   r   __doc__r   r   r   r  r  r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rw   ru   r   r     s    :5 5 2 2 ,,) &++G +g + ++ + +++&+)3+6@++7 )..H .x .( .\ .M ...).,7.:E..Xrw   r   c                 F   	 t         j                  } ||       }t	        t        t        |j                        t        |j                                    }i }|j                  D ]  }||v r	||   ||<   ||v s||   ||<    |S # t        $ r t         j                  }Y w xY w)a  
    Set keyword parameters of a function to specific or default values.

    Args:
        func (callable): The function to be inspected.
        func_kws (Mappable|None): The (key, value) pairs to set.
            If a value is None, it will be replaced by the default value.
            To use the names defined locally, use: `locals()`.

    Results:
        result (dict): A dictionary of the keyword parameters to set.

    See Also:
        inspect, locals, globals.
    )	r   getfullargspecAttributeError
getargspecdictzipreversedr   defaults)r   func_kwsget_argspec	inspectedr  r   keys          ru   set_func_kwsr  O  s    $),, D!IHY^^$hy/A/A&BCEHF~~ ((?"3-F3KH_"3-F3K	(
 M  )(()s   B B B c                     	 t         j                  } ||       }i }i }|j	                         D ]  \  }}||j
                  v r|||<   |||<     ||fS # t        $ r t         j                  }Y \w xY w)a  
    Split a set of keywords into accepted and not accepted by some function.

    Args:
        func (callable): The function to be inspected.
        func_kws (Mappable|None): The (key, value) pairs to split.

    Results:
        result (tuple): The tuple
            contains:
             - in_func (dict): The keywords NOT accepted by `func`.
             - not_in_func (Mappable|None): The keywords accepted by `func`.

    See Also:
        inspect, locals, globals.
    )r   r
  r  r  r   r   )r   r  r  r  in_funcnot_in_funcr'   vs           ru   split_func_kwsr  r  s    &),, D!IGK  1	GAJKN	
 K  )(()s   A A/.A/Tc                    t        |       }t        |      }t        |      }|t        k(  rb|rJ|D ]C  }t	        |t
              r|j                         }nt	        |t              st        |      }||z  }E |S |dj                  |      z  }|S |t
        t        fv rh|rP|D ]I  }t	        |t              r|j                         }n!t	        |t
        t        f      st        |      }||z  }K |S |dj                  |      z  }|S t        |d      r2|r"|D ]  }|t	        ||      r|n ||      z  } |S |D ]  }||z  }	 |S t        |d      r2|r"|D ]  }|t	        ||      r|n ||      z   } |S |D ]  }||z   }	 |S t        |d      rJ|r.|D ]'  }|j                  t	        ||      r|n ||             ) |S |D ]  }|j                  |        |S t        t        d            )ae  
    Join together multiple containers.

    Args:
        operands (Iterable): The operands to join.
        coerce (bool): Cast all operands into the type of the first.

    Returns:
        result: The joined object.

    Examples:
        >>> join(([1], [2], [3]))
        [1, 2, 3]
        >>> join(((1, 2), (3, 4)))
        (1, 2, 3, 4)
        >>> join(({1: 2}, {2: 3}, {3: 4}))
        {1: 2, 2: 3, 3: 4}
        >>> join(({1}, {2, 3}, {3, 4}))
        {1, 2, 3, 4}
        >>> join(([1], [2], (3, 4)))
        [1, 2, 3, 4]
        >>> join(((1,), [2], (3, 4)))
        (1, 2, 3, 4)
        >>> join(((1, 2), (3, 4)), coerce=False)
        (1, 2, 3, 4)
        >>> join(((1,), [2], (3, 4)), coerce=False)
        Traceback (most recent call last):
            ...
        TypeError: can only concatenate tuple (not "list") to tuple

        # These are side effect of duck-typing:
        >>> join([1, 2.5, 3])
        6
        >>> join([1.0, 2.5, 3])
        6.5
        >>> join([[1], 2, 3.0])
        Traceback (most recent call last):
            ...
        TypeError: 'int' object is not iterable
        >>> join([1, [2], 3.0])
        Traceback (most recent call last):
            ...
        TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'

        >>> join(['aaa', 'bbb', 'ccc'])
        'aaabbbccc'
        >>> join([b'aaa', b'bbb', b'ccc'])
        b'aaabbbccc'
        >>> join(x for x in string.ascii_lowercase)
        'abcdefghijklmnopqrstuvwxyz'
        >>> join(x.encode() for x in string.ascii_lowercase)
        b'abcdefghijklmnopqrstuvwxyz'

        >>> join(['aaa', b'bbb', 'ccc'])
        'aaabbbccc'
        >>> join([b'aaa', 'bbb', b'ccc'])
        b'aaabbbccc'
        >>> join(['aaa', b'bbb', 'ccc'], False)
        Traceback (most recent call last):
            ...
        TypeError: sequence item 0: expected str instance, bytes found
        >>> join([b'aaa', 'bbb', b'ccc'], False)
        Traceback (most recent call last):
            ...
        TypeError: sequence item 0: expected a bytes-like object, str found

    See Also:
        - flyingcircus.join_()
    r8   rw   __iadd__r   updatez%Cannot apply `join()` on `{operands}`)r   r   r   ra   
isinstancer   decodejoinr   encoder   r  
ValueErrorr   )operandscoerceiter_operandsr   type_resultrH   s         ru   r   r     sQ   R NM- Fv,Kc" a'
A#As+AA!V MI bggm,,FH MG 
	*	*" a%
A#Ay'9:aA!B M5 chh}--F4 M3 
	$" Nz![9!{1~MN. M) # !( M% 
	#" I#A{3AQII  M # $!$ M 
	"" I#A{3AQII M	 # !a ! M EFGGrw   c                      t        | fi |S )a  
    Star magic version of `flyingcircus.join()`.

    Examples:
        >>> join_([1], [2], [3])
        [1, 2, 3]
        >>> join_((1, 2), (3, 4))
        (1, 2, 3, 4)
        >>> join_({1: 2}, {2: 3}, {3: 4})
        {1: 2, 2: 3, 3: 4}
        >>> join_({1}, {2, 3}, {3, 4})
        {1, 2, 3, 4}
        >>> join_([1], [2], (3, 4))
        [1, 2, 3, 4]
        >>> join_((1,), [2], (3, 4))
        (1, 2, 3, 4)
        >>> join_((1, 2), (3, 4), coerce=False)
        (1, 2, 3, 4)
        >>> join_((1,), [2], (3, 4), coerce=False)
        Traceback (most recent call last):
            ...
        TypeError: can only concatenate tuple (not "list") to tuple

    See Also:
        - flyingcircus.join()
    )r   )r#  r   s     ru   join_r(    s    6 !D!!rw   c                 ,    |D ]  }t        | |      }  | S )aF  
    Get the nested attributes of an object.

    Args:
        obj (Any): The input object.
        *names (tuple[str]): The attributes to get.

    Returns:
        Any: The specified attribute.

    Examples:
        >>> print(get_nested_attr(1, 'real'))
        1
        >>> print(get_nested_attr(1, 'real', 'imag'))
        0
        >>> print(get_nested_attr(1, '__class__'))
        <class 'int'>
        >>> print(get_nested_attr(1, '__class__', '__name__'))
        int
        >>> print(get_nested_attr(1, '__class__', '__name__', '__class__'))
        <class 'str'>
    )getattr)objnamesnames      ru   get_nested_attrr.  1  s$    2  !c4 !Jrw   sha256hexc                    t        |      r ||       } t        |t              r|t        j                  v r\|t
        v r/t
        |   } t        t        |      |       j                  |      } n t        t        |      |       j                         } nz|t        v rTt        |   } t        t        |      |       j                  |d      } t        j                  |       j                  d      } nt        d      t        |      r ||       } t        |t              rH|dk(  rt        j                  |       } n@|t        v r t        t         | d      |       } nt        d      t        |      r ||       } t        | t"              r	 | j                  d      } || d| } | S # t$        $ r' t        j                  |       j                  d      } Y 8w xY w)a  Compute a checksum of an object.

    Args:
        obj (Any): The input object.
        chksum_func (callable|str): The checksum computing function.
            If str, must be in `hashlib.algorithm_available`
            or one of {"crc32", "adler32"}.
        ser_func (callable|str|None): The serialization function.
        to_str_func (callable|str|None): The checksum-to-string conversion function.
            If str, must be one of:
             - "hex": uses `binascii.b2a_hex()`
             - "b16": uses `base64.b16encode()`
             - "b32": uses `base64.b32encode()`
             - "b64": uses `base64.b64encode()`
             - "b85": uses `base64.b85encode()`
             - "urlsafe_b64": uses `base64.urlsafe_b64encode()`
        max_len: Max length of the checksum.
            If the checksum exceed this value, it is coerced to the specific length.
            If None, uses the default length.

    Returns:
        The computed checksum as string.

    Examples:
        >>> chksum(1)
        '018f5c4626b56e8489da7abb6c8b62331933c42c35d1342037a5242b8ed148f6'
        >>> chksum("This is some text")
        '388ef7c8f5cb46a241f12dbb7eefb5bde22b4cf1db539a03a71cb00b94b790e1'
        >>> chksum(None)
        '9c298d589a2158eb513cb52191144518a2acab2cb0c04f1df14fca0f712fa4a1'
        >>> chksum({1: {2: 3, 4: {5: [6, {7: 8}], 9: 0}}})
        'c6e3c985351ce6e394d77f931ae65fe8c78beb1c7f3521affa46c00cdd997557'
    littleasciizUnsupported checksum method.r0  r!  zUnsupported conversion method.N)r   r  ra   hashlibalgorithms_available_VAR_LENGTH_HASHLIB_ALGORITHMSr*  digest_ZLIB_CHECKSUMSzlibto_bytesbinasciib2a_hexr  r"  _BASE64_ENCODINGSbase64r   UnicodeDecodeError)r+  chksum_funcser_functo_str_funcr   lengthchksum_sizes          ru   checksumrE  P  s   R sm+s#'666<<7D3gg{3C8??G3gg{3C8??AO+)+6K,'$,S1::;QC""3'..w7C;<<	+	#+s#%""3'C--9'&[M"89#>C=>>	+	##u	8**W%C (7mJ	 " 	8""3'..w7C	8s   F" "-GGc              #      K   t        |      s|fd}|s|syt        |       D ]6  } ||      rt        | |      }t        |      }|r|s|s*|r-|r||fn| 8 yw)a  
    Iteratively (and selectively) list the attributes of an object.

    Args:
        obj (Any): The input object.
        skip (str|callable): The skip criterion.
            If str, names starting with the specified string are skipped.
            If callable, skips when `skip(name)` evaluates to True.
        methods (bool): Include methods.
            If False, the callable attributes are excluded.
        attributes (bool): Include non-callable attributes.
            If False, the non-callable attributes are excluded.
        yield_attr (bool): Yield both the name and the attribute.
            If False, only the name is yielded (same behavior as `dir()`).

    Yields:
        str|tuple[str, Any]: The name or the name/attribute pair.
            The return type/mode depends on the value of `yield_attr`.

    Examples:
        >>> list(idir(1, '_', True, True))  # doctest:+ELLIPSIS
        [..., 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
        >>> list(idir(1, '_', False, True))
        ['denominator', 'imag', 'numerator', 'real']
        >>> list(idir(1, '_', True, False))
        ['bit_length', 'conjugate', 'from_bytes', 'to_bytes']
        >>> list(idir(1, '_', False, False))
        []
        >>> list(idir(1, '_', False, True, True))
        [('denominator', 1), ('imag', 0), ('numerator', 1), ('real', 1)]
        >>> list(idir(1, '_', True, False, True))  # doctest:+ELLIPSIS
        [('bit_length', <built-in method bit_length of int object at ...>), ('conjugate', <built-in method conjugate of int object at ...>), ('from_bytes', <built-in method from_bytes of type object at ...>), ('to_bytes', <built-in method to_bytes of int object at ...>)]
    c                 $    | j                  |      S r   rs   )trS   s     ru   skipzidir.<locals>.skip  s    <<?"rw   N)r   dirr*  )r+  rJ  methods
attributes
yield_attrr-  attris_callables           ru   idirrQ    sh     V D> 	# :C ;Dz3%D"4.KKZ&0tTld:;s   ,AAAAc                     | j                         D ci c]  \  }}||
 }}}|r"t        |       t        |      k7  rt        d      |S c c}}w )at  
    Reverse the (key, value) relationship of a mapping.

    Given a (key, value) sequence, returns a (value, key) sequence.
    The values in the input mapping must be allowed to be used as `dict` keys.

    Args:
        mapping (Mapping): The input mapping
        check (bool): Perform a validity check.
            The check succeeds if all values in the original mapping are
            unique.

    Returns:
        result (dict): The reversed mapping.

    Raises:
        KeyError: if `check == True` and a duplicate key is detected.

    Examples:
        >>> mapping = {i: str(i) for i in range(5)}
        >>> print(sorted(mapping.items()))
        [(0, '0'), (1, '1'), (2, '2'), (3, '3'), (4, '4')]
        >>> print(sorted(reverse_mapping(mapping).items()))
        [('0', 0), ('1', 1), ('2', 2), ('3', 3), ('4', 4)]
        >>> reverse_mapping({1: 2, 2: 2})
        Traceback (most recent call last):
            ...
        KeyError: 'Reversing a mapping detected duplicate key in the result!'
        >>> reverse_mapping({1: 2, 2: 2}, False)
        {2: 2}

    See Also:
        - flyingcircus.reverse_mapping_iter()
    z9Reversing a mapping detected duplicate key in the result!)r   r   KeyError)mappingcheckr'   r  r   s        ru   reverse_mappingrV    sV    J  '}}/tq!ad/F/WV,GI 	I  0s   A
c                 <    	 t        |       S # t        $ r i cY S w xY w)a  Transparently convert any object to dict.

    If the object cannot be converted to `dict`, returns an empty `dict`.

    Args:
        obj: The input object.

    Returns:
        The object converted to a dict.

    Examples:
        >>> as_dict({1: 2, 3: 4})
        {1: 2, 3: 4}
        >>> as_dict(((1, 2), (3, 4)))
        {1: 2, 3: 4}
        >>> as_dict([[1, 2], [3, 4]])
        {1: 2, 3: 4}
        >>> as_dict(())
        {}
        >>> as_dict(None)
        {}
    )r  r   )r+  s    ru   as_dictrX    s%    .Cy 	s   
 c                     i }| j                         D ]+  \  }}|D ]!  }||v r||   j                  |       |g||<   # - |S )a7  
    Reverse the (key, values) relationship of a mapping.

    Given a (key, values) sequence, returns a reversed (value, keys) sequence.
    The input values need to be iterable and their items need to be hashable.
    Each of the values either generates a new key or gets appended to the new
    values in the result.

    Args:
        mapping (Mapping): The input mapping with iterable values.

    Returns:
        result (dict): The reversed mapping.

    Examples:
        >>> mapping = {i: [str(i)] for i in range(5)}
        >>> print(sorted(mapping.items()))
        [(0, ['0']), (1, ['1']), (2, ['2']), (3, ['3']), (4, ['4'])]
        >>> print(sorted(reverse_mapping_iter(mapping).items()))
        [('0', [0]), ('1', [1]), ('2', [2]), ('3', [3]), ('4', [4])]
        >>> reverse_mapping_iter({1: [2], 2: [2]})
        {2: [1, 2]}
        >>> reverse_mapping_iter({1: [1, 2], 2: [2]})
        {1: [1], 2: [1, 2]}
        >>> reverse_mapping_iter({1: [1, 2, 1], 2: [2, 1]})
        {1: [1, 1, 2], 2: [1, 2]}

    See Also:
        - flyingcircus.reverse_mapping()
    )r   r   )rT  r   r  valuesvalues        ru   reverse_mapping_iterr\  $  s\    > F}} &V 	&Eu$$S)!$u		&& Mrw   c                     	 t        |       |t        |       }t        |      st        } t	        j
                  | |       }|t        k(  r|S  ||      S # t        $ r | |   cY S w xY w)a+  
    Extract selected items according to the specified indexes.

    Note that this is mostly equivalent to (but faster than)
    `flyingcircus.iter_at()`.

    Args:
        seq (Sequence): The input items.
        indexes (Iterable[int|slice]): The items to select.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Returns:
        result: The selected items.

    Examples:
        >>> items = [x ** 2 for x in range(20)]
        >>> print(multi_at(items, (0, 6, 7, 0, 1, 3)))
        [0, 36, 49, 0, 1, 9]
        >>> indexes = (0, 6, 7, slice(1, 3))
        >>> print(multi_at(items, (12, 4, 11, slice(1, 3))))
        [144, 16, 121, [1, 4]]
        >>> print(multi_at(items, 19))
        361
        >>> print(multi_at(items, slice(3, 9, 2)))
        [9, 25, 49]

        >>> items = string.ascii_letters
        >>> print(multi_at(items, (0, 6, 7, 0, 1, 3)))
        ('a', 'g', 'h', 'a', 'b', 'd')
        >>> indexes = (0, 6, 7, slice(1, 3))
        >>> print(multi_at(items, (12, 4, 11, slice(1, 3))))
        ('m', 'e', 'l', 'bc')
        >>> print(multi_at(items, 19))
        t
        >>> print(multi_at(items, slice(3, 9, 2)))
        dfh

    See Also:
        - flyingcircus.iter_at()
    )r   r   r   r   operator
itemgetterr   )r   indexesr   r   s       ru   multi_atra  N  sw    \
CW S	I	"I.$$g.s3"e+vB61BB  7|s   A A'&A'c              #      K   	 t        |       |D ]	  }| |     y# t        $ r7 	 t        | |          | |   D ]  }|  Y y# t        $ r | |    Y Y yw xY ww xY ww)a3  
    Iterate over selected items according to the specified indexes.

    Note that this is mostly equivalent to `flyingcircus.multi_at()`
    except that this yields a generator.

    Args:
        seq (Sequence): The input items.
        indexes (Iterable[int|slice]): The items to select.

    Yields:
        item: The selected item.

    Examples:
        >>> items = [x ** 2 for x in range(20)]
        >>> print(list(iter_at(items, (0, 6, 7, 0, 1, 3))))
        [0, 36, 49, 0, 1, 9]
        >>> indexes = (0, 6, 7, slice(1, 3))
        >>> print(list(iter_at(items, (12, 4, 11, slice(1, 3)))))
        [144, 16, 121, [1, 4]]
        >>> print(list(iter_at(items, 19)))
        [361]
        >>> print(list(iter_at(items, slice(3, 9, 2))))
        [9, 25, 49]

    See Also:
        - flyingcircus.multi_at()
    N)r   r   )r   r`  indexr   s       ru   iter_atrd    s     >W  	Ee*	  	W G 
  	g,	sG   A! A!	AAAA!AAA!AAA!c              #      K   t        |       }|dkD  r?t        ||      }t        ||      }|}	 	 | j                  ||      }||k  r
| |dz  }ny#y# t        $ r Y yw xY ww)aV  
    Find all occurrences of an item in an sequence.

    For dense inputs (item is present in more than ~20% of the sequence),
    a looping comprehension may be faster.

    For string, bytes or bytearray inputs, `flyingcircus.find_all()`
    is typically faster.

    Args:
        seq (Sequence): The input sequence.
        item (Any): The item to find.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Yields:
        position (int): The position of the next finding.

    Examples:
        >>> list(index_all([1, 2, 3, 5, 3, 4, 5, 7, 8], 3))
        [2, 4]
        >>> list(index_all((1, 2, 3, 5, 3, 4, 5, 7, 8), 3))
        [2, 4]
        >>> list(index_all((1, 2, 3, 5, 3, 4, 5, 7, 8), 0))
        []
        >>> list(index_all((1, 2, 3, 5, 3, 4, 5, 7, 8), [3, 5]))
        []
        >>> list(index_all((1, 2, 3, 5, 3, 4, 5, 7, None), None))
        [8]
        >>> list(index_all((1, 2, 3, 5, 3, 4, 5, 7, 8), ()))
        []
        >>> list(index_all('0010120123012340123450123456', '0'))
        [0, 1, 3, 6, 10, 15, 21]
        >>> list(index_all('  1 12 123 1234 12345 123456', '0'))
        []
        >>> list(index_all('  1 12 123 1234 12345 123456', '12'))
        [4, 7, 11, 16, 22]
        >>> list(index_all(b'  1 12 123 1234 12345 123456', b'12'))
        [4, 7, 11, 16, 22]
        >>> list(index_all('0123456789', ''))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> list(index_all('', ''))
        []
        >>> list(index_all('', '0123456789'))
        []
        >>> list(index_all(b'0123456789', b''))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> list(index_all(b'', b''))
        []
        >>> list(index_all(b'', b'0123456789'))
        []

    See Also:
        - flyingcircus.find_all()
    r   r/   N)r   r   rc  r"  )r   r   firstlastr2   rM   s         ru   	index_allrh    s     | 	CA1uE1%4#		IIdA&9GFA  	  		s4   +A""A A"A A"	AA"AA"c           
   #   R  K   t        |      x}}g }t        t        | |            }t        t        | |            }|j                  |      }	t	        |	      D ]  }
|
|v r|j                  |
dz          |
|v s!t        |      dkD  r'|j                         }||z
  |
|z   t        |      f Vt        t        dt        |      t        |      z
  ||
             t        |      dkD  r=t        t        dt        |      t        |      z
  ||j                         dz
              yw)a$  
    Find matching delimiters in a sequence.

    The delimiters are matched according to nesting level.
    For string, bytes or bytearray inputs,
    `flyingcircus.find_nested_delims()` is a better option, because it
    supports multi-char delimiters and it is typically faster.

    Args:
        seq (Sequence): The input sequence.
        l_item (Any): The left delimiter item.
        r_item (Any): The right delimiter item.
        including (bool): Include delimiters.

    yields:
        result (tuple[int]): The matching delimiters.

    Examples:
        >>> s = '{a} {b:{c}}'
        >>> list(nested_pairs(s, '{', '}'))
        [(0, 3, 0), (7, 10, 1), (4, 11, 0)]
        >>> [s[i:j] for i, j, depth in nested_pairs(s, '{', '}')]
        ['{a}', '{c}', '{b:{c}}']
        >>> [s[i:j] for i, j, d in nested_pairs(s, '{', '}') if d == 0]
        ['{a}', '{b:{c}}']
        >>> list(nested_pairs('{a} {b:{c}', '{', '}'))
        Traceback (most recent call last):
            ...
        ValueError: Found `1` unmatched left token(s) `{` (position: 4).
        >>> list(nested_pairs('{a}} {b:{c}}', '{', '}'))
        Traceback (most recent call last):
            ...
        ValueError: Found `1` unmatched right token(s) `}` (position: 3).

    See Also:
        - flyingcircus.nested_delimiters()
    r/   r   z8Found `{}` unmatched right token(s) `{}` (position: {}).z7Found `{}` unmatched left token(s) `{}` (position: {}).N)
rY   setrh  unionsortedr   r   r   r"  r   )r   l_itemr_item	includingl_offsetr_offsetstackl_itemsr_items	positionsposprevs               ru   nested_pairsrx    s&    T i.(HxE)C()G)C()Gg&Ii  ?'>LLq!G^5zA~yy{hhE
CC 'L3w</"> ? ?? 5zA~L3w<'qB C 	C s   A6D'9B.D'c                 f    |d| }}n| |}}|s	||k  rdnd}|||z
  |z  s|ndz   }t        |||      S )ap  
    Span consecutive numbers in a range.

    This is useful to produce 1-based ranges, which first from 1 (if `start`
    is not specified) and include the `stop` element (if the `step` parameter
    allows).

    Args:
        first (int): The first value of the span.
            If `second` is None, the `start` value is 1,
            and this is the `stop` value.
            It is included if `step` is a multiple of the sequence length.
            Otherwise, this is the `start` value and it is always included.
        second (int|None): The second value of the span.
            If None, the start value is 1 and this parameter is ignored.
            Otherwise, this is the `stop` value of the range.
            It is included if `step` is a multiple of the sequence length.
            If `first < second` the sequence is yielded backwards.
        step (int): The step of the rows range.
            If `start > stop`, the step parameter should be negative in order
            to obtain a non-empty range.
            If None, this is computed automatically based on `first` and
            `second`, such that a non-empty sequence is avoided, if possible,
            i.e. `step == 1` if `start <= stop` else `step == -1`.

    Returns:
        result (range): The spanned range.

    Examples:
        >>> print(list(span(10)))
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> print(list(span(-10)))
        [1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
        >>> print(list(span(1)))
        [1]
        >>> print(list(span(1, 10)))
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> print(list(span(-1, 10)))
        [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> print(list(span(-1, 9, 2)))
        [-1, 1, 3, 5, 7, 9]
        >>> print(list(span(-1, 10, 2)))
        [-1, 1, 3, 5, 7, 9]
        >>> print(list(span(10, 1)))
        [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
        >>> print(list(span(10, 1, -2)))
        [10, 8, 6, 4, 2]
        >>> print(list(span(-1, -10, -2)))
        [-1, -3, -5, -7, -9]
        >>> print(list(span(-1, -11, -2)))
        [-1, -3, -5, -7, -9, -11]

        >>> 1 - span - 10 == span(1, 10)
    r/   r<   r   range)rf  secondstepstartstops        ru   spanr  O  sR    v ~tVtTMqr 54A>Dd##rw   c              #      K   |r| |k  r||k  s
| |kD  r||kD  r| }||kD  rt         j                  }n||k  rt         j                  }nt        | } |||      r| ||z  } |||      ryyw)aG  
    Generate a range of objects.

    It is a generalization of `range()` built-in to non-integer arguments.
    The objects must be ordered and support addition and subtraction.

    Args:
        start (Any): The initial value (included).
        stop (Any): The final value (excluded).
        step (Any): The step value.
        zero (Any): The "zero" value (used to determine the sign of `step`).
        adapt_step_sign (bool): Swap the sign of `step`.
            This is used to ensure a non-empty range.

    Yields:
        The value(s) between `start` and `stop` (excluded) in `step` steps.

    Raises:
        ValueError: If the step is zero.
        
    Examples:
        >>> list(any_range(0, 4))
        [0, 1, 2, 3]
        >>> list(any_range(4, 0))
        [4, 3, 2, 1]
        >>> list(any_range(0, 4, 2))
        [0, 2]
        >>> list(any_range(4, 0, 2))
        [4, 2]
        >>> list(any_range(4, 0, adapt_step_sign=False))
        []
        >>> list(any_range(4, 0, -2))
        [4, 2]
        >>> list(any_range(4, 0, -2, adapt_step_sign=False))
        [4, 2]
    N)r^  ltgtr"  )r~  r  r}  zeroadapt_step_signcomparercurrs          ru   	any_ranger    s|     T dlD4Kud{;;	;;D
4

 4
s   A)A.,A.c                 @    t        | d      xr |xr t        | |       S )a  
    Determine if an object is deep, i.e. it can be iterated through.

    Args:
        obj (Any): The object to test.
        skip (tuple|None): Types to skip descending into.

    Returns:
        result (bool): If the object is deep or not.

    Examples:
        >>> is_deep(1)
        False
        >>> is_deep(())
        True
        >>> is_deep([1, 2, 3])
        True
        >>> is_deep(range(4))
        True
        >>> is_deep((i for i in range(4)))
        True
        >>> is_deep('ciao')
        False
        >>> is_deep('c')
        False
        >>> is_deep('')
        False

        >>> is_deep(1, skip=None)
        False
        >>> is_deep('c', skip=None)
        True
        >>> is_deep('', skip=None)
        True
    __iter__)r   r  )r+  rJ  s     ru   is_deepr    s&    L 3
#LT-Kjd6K(LLrw   c                      dk(  r S t         d      r$t         fd j                         D              S t         t              rt
        nt        } | fd D              S )ag  
    Recursively convert mutable containers to immutable counterparts.

    The following conversions are performed:
     - Iterable -> tuple
     - set -> frozenset
     - dict (any container with `items()` method) -> tuple

    This is useful to sanitize default parameters in functions.

    Args:
        items: The input items.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        skip (tuple|None): Types to skip descending into.

    Returns:
        result (tuple): The output items.

    Examples:
        >>> freeze([1, 2, 'ciao', [2, 3]])
        (1, 2, 'ciao', (2, 3))
        >>> freeze([1, 2, 'ciao', {2, 3}])
        (1, 2, 'ciao', frozenset({2, 3}))
        >>> freeze([1, 2, 'ciao', {2: 3}])
        (1, 2, 'ciao', ((2, 3),))
        >>> freeze([1, 2, 'ciao', {2: 3}, {4: 5}])
        (1, 2, 'ciao', ((2, 3),), ((4, 5),))
    r   r   c              3   d   K   | ]'  }t        |      r|k7  rt        |d z
        n| ) ywr/   Nr  freezer   r   r   	max_depthrJ  s     ru   r   zfreeze.<locals>.<genexpr>  sC      +  4&45= tY]D1>BC+   -0c              3   d   K   | ]'  }t        |      r|k7  rt        |d z
        n| ) ywr  r  r  s     ru   r   zfreeze.<locals>.<genexpr>%  sC      #  4&45= tY]D1>BC#r  )r   r   r   r  rj  	frozensetr   r  rJ  r   s   ``` ru   r  r    sl    @ A~5'" + "KKM+ + +
 &0s%;	I # "# # #rw   c                      dk(  r S 	 t         fd D              S # t        $ r2 t         t              rt        nt
        } | fd D              cY S w xY w)a  
    Recursively convert immutable containers to mutable counterparts.

    The following conversions are performed:
     - Iterable -> dict (if possible) or list
     - frozenset -> set

    Args:
        items: The input items.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        skip (tuple|None): Types to skip descending into.

    Returns:
        result (tuple): The output items.

    Examples:
        >>> unfreeze((1, 2, 'ciao', (2, 3)))
        [1, 2, 'ciao', [2, 3]]
        >>> unfreeze((1, 2, 'ciao', frozenset({2, 3})))
        [1, 2, 'ciao', {2, 3}]
        >>> unfreeze((1, 2, 'ciao', ((2, 3),)))
        [1, 2, 'ciao', {2: 3}]
        >>> unfreeze((1, 2, 'ciao', ((2, 3),), ((4, 5),)))
        [1, 2, 'ciao', {2: 3}, {4: 5}]
    r   c              3   d   K   | ]'  }t        |      r|k7  rt        |d z
        n| ) ywr  r  unfreezer  s     ru   r   zunfreeze.<locals>.<genexpr>M  sC      #  4&45= y1}d3>BC#r  c              3   d   K   | ]'  }t        |      r|k7  rt        |d z
        n| ) ywr  r  r  s     ru   r   zunfreeze.<locals>.<genexpr>S  sC      #  4&45= y1}d3>BC#r  )r  r   r  r  rj  listr  s   ``` ru   r  r  ,  so    : A~
	# # "# # #  	#)%;I # "# # #	#s   " 8AAc                     t         t        t        t        t        t
        t        t        f}t        | |      ryt        | t        t        t        t        f      r|dk7  r| D ]  }t        ||dz
        s y yyy)a  
    Recursively determine if an object is mutable.

    This is useful to inspect whether it is safe to use an object as default
    parameter value.

    Args:
        obj (Any): The object to inspect.
        max_depth (int): Maximum depth to reach. Negative for unlimited.

    Returns:
        result (bool): The result of the mutability check.

    Examples:
        >>> is_mutable(1)
        False
        >>> is_mutable([1])
        True
        >>> is_mutable((1, 2))
        False
        >>> is_mutable((1, 2, []))
        True
        >>> is_mutable((1, 2, []), max_depth=0)
        False
        >>> is_mutable(zip(([], []), ((), ())))
        True
        >>> is_mutable(zip(((), ()), ((), ())))
        False
        >>> is_mutable(map(lambda x: [], (1, 2)))
        True
        >>> is_mutable(map(lambda x: 'ciao', (1, 2)))
        False
        >>> is_mutable(filter(lambda x: len(x) >= 0, ((), (), [])))
        True
        >>> is_mutable(filter(lambda x: len(x) >= 0, ((), ())))
        False
    Fr   r/   T)rT   rY   r_   ra   r   slicer{  r  r  r   r  r   filter
is_mutable)r+  r  shallow_immutablesr   s       ru   r  r  Z  sq    R 	c5#ueUI?#)*	C%c62	3> dIM2 rw   c                     |rt        j                  |d      nt        j                  |d      } | fd|D              S )ah  
    Compute multiple comparisons.

    Args:
        grouper (callable): Determine how to group multiple comparisons.
            Must accept the following signature:
            multi_comparison(*Iterable[bool]): bool
            Can be either `all` or `any`, or any callable with the supported
            signature.
        items (Iterable): The input items.
        comparison (callable): Compute pair-wise comparison.
            Must accept the following signature:
            comparison(Any, Any): bool
        symmetric (bool): Assume that the comparison is symmetric.
            A comparison is symmetric if:
            comparison(a, b) == comparison(b, a).

    Returns:
        result (bool): The result of the multiple comparisons.

    Examples:
        >>> multi_compare(all, [1, 1, 1, 1, 1, 1, 1, 1, 1])
        True
        >>> multi_compare(any, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0])
        True
        >>> multi_compare(all, 'xxxxx')
        True
        >>> multi_compare(all, 'yxxxy')
        False
        >>> multi_compare(any, 'xxxxxyxxxy')
        True
        >>> all_equal({0, 1})
        False
    r9   c              3   6   K   | ]  \  }} ||        y wr   r   )r   r5   rI   
comparisons      ru   r   z multi_compare.<locals>.<genexpr>  s     91:a#9   )r   combinationspermutations)grouperr   r  	symmetricpairwises     `  ru   multi_comparer    sA    P  %%eQ/#00: 9999rw   c                 (    |D ]  } || |      s y y)a  
    Check if an object is equal to any of the items.

    This is useful if any among the object or the items does not implement
    `__eq__` to return a `bool` for any given input.
    Note that this operation is always of O(N) complexity
    (N being the `items` size).

    Args:
        obj (Any): The object to check.
        items (Iterable): The container to check.
        eq (callable): The function to check for equality.
            Must have the following signature: eq(Any, Any): bool

    Returns:
        bool: The result of the check.

    Examples:
        >>> in_(1, [1, 2, 3])
        True
        >>> in_(0, [1, 2, 3])
        False
        >>> in_(1, {1, 2, 3})
        True
        >>> in_(0, {1, 2, 3})
        False
    TFr   )r+  r   eqr   s       ru   in_r    s$    >  c4= rw   c                     t        | t        j                  j                        r| dd | dd k(  S t	        |       }	 t        |      }|D ]	  }||k7  s	 y y# t        $ r Y yw xY w)a(  
    Check if all items are equal.

    Args:
        items (Iterable): The input items.

    Returns:
        result (bool): The result of the equality test.

    Examples:
        >>> all_equal([1, 1, 1, 1, 1, 1, 1, 1, 1])
        True
        >>> all_equal([1, 1, 1, 1, 0, 1, 1, 1, 1])
        False
        >>> all_equal('xxxxx')
        True
        >>> all_equal('xxxxy')
        False
        >>> all_equal({0})
        True
        >>> all_equal({0, 1})
        False
        >>> all(all_equal(x) for x in ((), [], {}, set()))
        True
    r/   Nr<   TF)r  collectionsabcSequencer   r   StopIterationr   
iter_itemsrf  r   s       ru   	all_equalr    s{    4 %112QRyE#2J&&%[
	$E
  	D}	   		s   A 	A"!A"c                     t        |       syt        |       dk(  rydk(  ryr fd| D              }d|z   S t        | d   dz
        }d|z   S )a  
    Compute the nesting level of nested iterables.

    Args:
        obj (Any): The object to test.
        deep (bool): Evaluate all item.
            If True, all elements within `obj` are evaluated.
            If False, only the first element of each deep object is evaluated.
            An object is considered deep using `is_deep()`.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        combine (callable): Combine multiple depth at the same level.
            If `deep` is False, this parameter is ignored.
        skip (tuple|None): Types to skip descending into.

    Returns:
        result (int): The nesting level.

    Examples:
        >>> nesting_level([])
        1
        >>> nesting_level([], True)
        1
        >>> nesting_level([[]], False)
        2
        >>> nesting_level(
        ...     [[(1, 2, 3), (4, 5)], [(1, 2), (3,)], ['1,2', [6, 7]]], True)
        3
        >>> nesting_level(
        ...     [[1, (2, 3), (4, 5)], [(1, 2), (3,)], ['1,2', [6, 7]]], False)
        2
        >>> nesting_level(
        ...     [1, [[[[[[[[[[[[[[[[[[[[[5]]]]]]]]]]]]]]]]]]]]]], True)
        22
        >>> nesting_level(
        ...     [1, [[[[[[[[[[[[[[[[[[[[[5]]]]]]]]]]]]]]]]]]]]]], False)
        1
        >>> nesting_level(((1, 2), 1, (1, (2, 3))), True)
        3
        >>> nesting_level(((1, 2), 1, (1, (2, 3))), True, combine=min)
        1
    r   r/   c              3   B   K   | ]  }t        |d z
          ywr  )nesting_level)r   rH   combinedeepr  rJ  s     ru   r   z nesting_level.<locals>.<genexpr>I  s*      ! ay1}gtD!   )r  r   r  )r+  r  r  r  rJ  
next_levels    ```` ru   r  r    sz    ^ 3	SQ	a  !! J :~ 'Ai!mWd<J:~rw   c                 F   t        |       syrJt        fd| D              }rt        |      st        d      t	              st         |      }n%t        t        t        |             dz
        }t        |       ft        d |D              z   S )a  
    Compute the length of nested iterables.

    Args:
        obj (Any): The object to test.
        deep (bool): Evaluate all item.
            If True, all elements within `obj` are evaluated.
            If False, only the first element of each deep object is evaluated.
            An object is considered deep using `is_deep()`.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        combine (callable|None): Combine multiple depth at the same level.
            If None, the lengths do not get combined (using `combine=tuple`
            has the same effect).
            If `deep` is False, this parameter is ignored.
        check_same (bool): Check that same-level items have the same length.
        skip (tuple|None): Types to skip descending into.

    Returns:
        result (tuple[int]): The length of the nested iterables.

    Raises:
        ValueError: if `check_same` is True and items at the same nesting level
            have different length.

    Examples:
        >>> nested_len(((1, 1), (1, 1), (1, 1)))
        (3, 2)
        >>> nested_len(((1, 2), (1,), (1, (2, 3))), check_same=True)
        Traceback (most recent call last):
            ...
        ValueError: Same nesting level items with different length.
        >>> nested_len(((1, 2), (1,), (1, (2, 3))), check_same=False)
        (3, 2, 2)
        >>> nested_len(((1, 2), (1, 2), (1, 2)), check_same=False)
        (3, 2)
        >>> nested_len(((1,), (1,), (1,)))
        (3, 1)
        >>> nested_len((1, 2, 3))
        (3,)
        >>> nested_len(
        ...     ((1, 2), (1,), (1, (2, 3))), combine=None, check_same=False)
        (3, (2,), (1,), (2, (2,)))
        >>> nested_len(
        ...     ((1, 2), (1,), ((1,), (2, 3))), combine=None, check_same=False)
        (3, (2,), (1,), (2, (1,), (2,)))
        >>> nested_len(
        ...     ((1, 2), (1,), ((1, (2, 3)),)), combine=None, check_same=False)
        (3, (2,), (1,), (1, (2, (2,))))
    r   c           	   3   D   K   | ]  }t        |d z
          ywr  )
nested_len)r   rH   
check_samer  r  r  rJ  s     ru   r   znested_len.<locals>.<genexpr>  s2        tY]GZGs    z/Same nesting level items with different length.r/   c              3   &   K   | ]	  }|s|  y wr   r   r   rH   s     ru   r   znested_len.<locals>.<genexpr>  s     ">A1">   )	r  r   r  r"  r   r  r   r   r   )r+  r  r  r  r  rJ  r  s    ````` ru   r  r  S  s    p 3   J )J"7 EG GG$ ,J#T#Yy1}gD"J C{U">j">>>>rw   c                 p   |xs t        | d       }| }t        |t              r%|r| f|z  }|rt        |      |k7  rt	        d      |S t        | d      |k7  s|r,t        | |d   ||      }|ddd   D ]  }t        ||d|      } |r-t        |d      ||rt        | d      ndz   k7  rt	        d      |S )	ac  
    Automatically repeat the specified object n times.

    If the object is not Iterable, a tuple with the specified size is returned.
    If the object is Iterable, the object is repeated only if `n` is Iterable,
    in which case it is repeated accordingly.
    The resulting length depends on the value of `force`.

    Args:
        obj: The object to operate with.
        n (int|Sequence[int]): The length(s) of the output object.
            If Sequence, multiple nested tuples will be generated.
        force (bool): Force the repetition, even if the object is Iterable.
            If True, the nested length of the result is equal to `n` plus the
            length of the input, otherwise it is equal to `n` (but the last
            value) plus the length of the input.
        check (bool): Ensure that the object has length n.
            More precisely the `n` and the initial part of
            `nested_len(check_same=True)` must be identical.

    Returns:
        result (tuple): Returns obj repeated n times.

    Raises:
        AssertionError: If force is True and the object does not have length n.

    Examples:
        >>> auto_repeat(1, 3)
        (1, 1, 1)
        >>> auto_repeat([1], 3)
        [1]
        >>> auto_repeat([1, 3], 2)
        [1, 3]
        >>> auto_repeat([1, 3], 2, True)
        ([1, 3], [1, 3])
        >>> auto_repeat([1, 2, 3], 2, True, True)
        ([1, 2, 3], [1, 2, 3])
        >>> auto_repeat([1, 2, 3], 2, False, True)
        Traceback (most recent call last):
            ...
        ValueError: Incompatible input value length.
        >>> auto_repeat(1, (3,))
        (1, 1, 1)
        >>> auto_repeat(1, (3, 2))
        ((1, 1), (1, 1), (1, 1))
        >>> auto_repeat(1, (2, 3))
        ((1, 1, 1), (1, 1, 1))
        >>> auto_repeat([1], (3, 1), False, True)
        ([1], [1], [1])
        >>> auto_repeat([1], (3, 1), True, True)
        (([1],), ([1],), ([1],))
        >>> auto_repeat([1], (3, 1), False, False)
        ([1], [1], [1])
        >>> auto_repeat([1], (3, 1), True, False)
        (([1],), ([1],), ([1],))
        >>> auto_repeat([1], (3, 3), False, False)
        ([1], [1], [1])
        >>> auto_repeat([1], (3, 3), True, False)
        (([1], [1], [1]), ([1], [1], [1]), ([1], [1], [1]))
        >>> auto_repeat([1], (3, 3), True, True)
        (([1], [1], [1]), ([1], [1], [1]), ([1], [1], [1]))
        >>> auto_repeat([1], (3, 3), False, True)
        Traceback (most recent call last):
            ...
        ValueError: Incompatible input value length.
        >>> auto_repeat(((1, 1), (1, 1), (1, 1)), (3, 2), False, True)
        ((1, 1), (1, 1), (1, 1))
        >>> auto_repeat((1, 1), (3, 2), False, True)
        ((1, 1), (1, 1), (1, 1))
        >>> auto_repeat((1, 1, 1), (3, 2), False, True)
        Traceback (most recent call last):
            ...
        ValueError: Incompatible input value length.
        >>> auto_repeat((1, 1, 1), (3, 2), False, False)
        ((1, 1, 1), (1, 1, 1), (1, 1, 1))
        >>> auto_repeat((1, 1), (3, 2), True, True)
        (((1, 1), (1, 1)), ((1, 1), (1, 1)), ((1, 1), (1, 1)))

    See Also:
        - flyingcircus.stretch()
    r  z Incompatible input value length.Tr  r<   r=   Nr   )r   r  rY   r   r"  r  auto_repeat)r+  r2   forcerU  r   rM   s         ru   r  r    s    l 1j11EF!SVaZFS[A%?@@ M cd+q0E aeUE:Frv2vY =$VQe<=6d3E
348rJK?@@Mrw   c                    t        |       st        |       }|S t        | d      }t        |      dk(  r/t              dkD  r!d   |d   k(  rt	        fd| D              }|S |d   dk(  rt	        fd| D              d   z  }|S |k(  r	 t        | d       | }|S |d   d   k(  rt	        fd	| D              }|S t        d
j                  |             # t
        $ r t	        fd| D              }Y |S w xY w)a  
    Automatically stretch the values to the target shape.

    This is similar to `flyingcircus.auto_repeat()`, except that it
    can flexibly repeat values only when needed.
    This is similar to shape broadcasting of multi-dimensional arrays.

    Args:
        items (Any|Iterable): The input items.
        shape (Sequence[int]): The target shape (nested lengths).
        skip (tuple|None): Types to skip descending into.

    Returns:
        result (tuple): The values stretched to match the target shape.

    Raises:
        ValueError: If `items` and `shape` are incompatible.

    Examples:
        >>> stretch(1, 10)
        (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
        >>> stretch(1, (2, 3))
        ((1, 1, 1), (1, 1, 1))
        >>> stretch(1, (2, 3, 2))
        (((1, 1), (1, 1), (1, 1)), ((1, 1), (1, 1), (1, 1)))
        >>> stretch((1, (2, 3)), (2, 2))
        ((1, 1), (2, 3))
        >>> stretch((1, (2, 3, 4)), (2, 3))
        ((1, 1, 1), (2, 3, 4))
        >>> stretch((1, (2, 3,)), (2, 3))
        Traceback (most recent call last):
            ...
        ValueError: Cannot stretch `(2, 3)` to `(3,)`.
        >>> stretch((1, (2, 3, 4, 5)), (2, 3))
        Traceback (most recent call last):
            ...
        ValueError: Cannot stretch `(2, 3, 4, 5)` to `(3,)`.
        >>> stretch(((1, 2),), (4, 2))
        ((1, 2), (1, 2), (1, 2), (1, 2))
        >>> stretch((1, 2, 3, 4), (4, 2))
        ((1, 1), (2, 2), (3, 3), (4, 4))
        >>> items = [[[[1], [2], [3]]], [[[4], [5], [6]]]]
        >>> print(nested_len(items))
        (2, 1, 3, 1)
        >>> stretch(items, (2, 4, 3, 4))
        ((((1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3)), ((1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3)), ((1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3)), ((1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3))), (((4, 4, 4, 4), (5, 5, 5, 5), (6, 6, 6, 6)), ((4, 4, 4, 4), (5, 5, 5, 5), (6, 6, 6, 6)), ((4, 4, 4, 4), (5, 5, 5, 5), (6, 6, 6, 6)), ((4, 4, 4, 4), (5, 5, 5, 5), (6, 6, 6, 6))))

    See Also:
        - flyingcircus.auto_repeat()
    Fr  r/   r   c              3   @   K   | ]  }t        |d d dd        yw)r/   NT)r  r   r   shapes     ru   r   zstretch.<locals>.<genexpr>M  s*      # D%)T48#s   c              3   J   K   | ]  }d d rt        |d d       n|  ywr  )stretchr  s     ru   r   zstretch.<locals>.<genexpr>Q  s3      # -2!"IeABi(4?#    #Tc              3   r   K   | ].  }t        |      rt        |d d       nt        |d d        0 ywr  r  r  r  r   r   r  rJ  s     ru   r   zstretch.<locals>.<genexpr>X  sI      '  tT* D%),$T5956'   47c              3   r   K   | ].  }t        |      rt        |d d       nt        |d d        0 ywr  r  r  s     ru   r   zstretch.<locals>.<genexpr>`  sI      #  4& eABi( uQRy12#r  zCannot stretch `{}` to `{}`.)r  r  r  r   r   r"  format)r   r  rJ  r   	old_shapes    ``  ru   r  r    sW   z 5$UE*> M; u7	y>Q3u:>eAh)A,6N #!# #F6 M1 q\Q #!# #%*1X.F. M) %	5T2  M q\U1X% # "	# #F M .55eUCE E  ' ' !&	' '  M#'s   C D Dc              #      K   | D ]9  }|rt        ||      s
|| k(  s|dk(  r|  	 t        ||dz
  |      D ]  }|  ; y# t        $ r | Y Lw xY ww)a  
    Recursively flattens nested Iterables.

    The maximum depth is limited by Python's recursion limit.

    Args:
        items (Iterable): The input items.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        shallow (tuple|None): Data types to always consider shallow.
            Note that recursive and self-slicing objects are handled
            separately.

    Yields:
        item (any): The next non-Iterable item of the flattened items.

    Examples:
        >>> ll = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
        >>> list(flatten(ll))
        [1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> list(flatten(ll)) == list(itertools.chain.from_iterable(ll))
        True
        >>> ll = [ll, ll]
        >>> list(flatten(ll))
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> list(itertools.chain.from_iterable(ll))
        [[1, 2, 3], [4, 5, 6], [7], [8, 9], [1, 2, 3], [4, 5, 6], [7], [8, 9]]
        >>> list(flatten([1, 2, 3]))
        [1, 2, 3]
        >>> list(flatten(['best', ['function', 'ever']]))
        ['best', 'function', 'ever']
        >>> ll2 = [[(1, 2, 3), (4, 5)], [(1, 2), (3, 4, 5)], ['1, 2', [6, 7]]]
        >>> list(flatten(ll2))
        [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, '1, 2', 6, 7]
        >>> list(flatten(ll2, shallow=(tuple, str)))
        [(1, 2, 3), (4, 5), (1, 2), (3, 4, 5), '1, 2', 6, 7]
        >>> list(flatten(ll2, max_depth=1))
        [(1, 2, 3), (4, 5), (1, 2), (3, 4, 5), '1, 2', [6, 7]]
        >>> list(flatten(ll2, shallow=None))
        [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, '1', ',', ' ', '2', 6, 7]
        >>> list(
        ...     flatten([['best', 'func'], 'ever'], 1,
        ...     shallow=None))
        ['best', 'func', 'e', 'v', 'e', 'r']
        >>> list(
        ...     flatten([['best', 'func'], 'ever'],
        ...     shallow=None))
        ['b', 'e', 's', 't', 'f', 'u', 'n', 'c', 'e', 'v', 'e', 'r']
        >>> list(flatten(list(range(10))))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> list(flatten(range(10)))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> list(flatten([[0, 1], range(2, 5), (i for i in range(5, 10))]))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    r   r/   N)r  flattenr   )r   r  shallowr   subitems        ru   r  r  l  sq     t  	z$0aJ&tY]GD "G!M"	  
s'   #AAAAAAAc                 @    |rt        j                  | d|iS t        |  S )a.  
    Transpose an iterable of iterables.

    Args:
        items (Iterable[Iterable]): The input items.
        longest (bool): Align to longest inner iterable.
            If True, aligns to the longest inner iterable from the input.
            If False, cuts the inner iterables to the shortest.
        fill (Any): Fill value to use for aligning to longest.
            If `longest` is False, this parameter has no effect.

    Returns:
        result (zip|itertools.zip_longest): The transposed items.

    Examples:
        >>> tuple(transpose(((1, 2), (3, 4), (5, 6))))
        ((1, 3, 5), (2, 4, 6))
        >>> tuple(transpose(((1, 2), (3, 4), (5, 6, 7))))
        ((1, 3, 5), (2, 4, 6))
        >>> tuple(transpose(((1, 2), (3, 4), (5, 6, 7)), True))
        ((1, 3, 5), (2, 4, 6), (None, None, 7))
    	fillvalue)r   zip_longestr  )r   longestr   s      ru   	transposer    s'    4 $$e<t<<E{rw   c                    t        | d      r| j                  I|rGt        d | j                  | j                  fD              r| j                  | j                  kD  rd}nd}n| j                  }t        d | j                  | j                  |fD              r| } t        |       | j                  | j                  |      S  t        |       | j                  | j                        S )a  
    Flip a slice or range.

    This is achieved by swapping its `start` and `stop` attributes.
    It works for any object implementing `start` and `stop` attributes.

    If `step` is specified and not None, `-step` is used as new `step`.

    Args:
        obj (slice|range): The input slice/range.
        force_step (bool): Force producing a slice with an explicit step.
            If True, `1` is used as `step` in place of `None`.

    Returns:
        obj (slice|range): The output slice/range.

    Examples:
        >>> flip_slice(slice(10, 20))
        slice(20, 10, None)
        >>> flip_slice(slice(10, 20, 2))
        slice(20, 10, -2)
        >>> flip_slice(slice(20, 10, -2))
        slice(10, 20, 2)
        >>> flip_slice(slice(10, 20), True)
        slice(20, 10, -1)
        >>> flip_slice(slice(20, 10))
        slice(10, 20, None)
        >>> flip_slice(slice(20, 10), True)
        slice(10, 20, 1)
        >>> flip_slice(slice(None, 10))
        slice(10, None, None)
        >>> flip_slice(slice(None, 10), True)
        slice(10, None, 1)
        >>> flip_slice(slice(20, None))
        slice(None, 20, None)
        >>> flip_slice(slice(20, None), True)
        slice(None, 20, 1)
        >>> flip_slice(slice(None, None))
        slice(None, None, None)
        >>> flip_slice(slice(None, None), True)
        slice(None, None, 1)

        >>> flip_slice(range(10, 20))
        range(20, 10, -1)
        >>> flip_slice(range(10, 20, 2))
        range(20, 10, -2)
        >>> flip_slice(range(20, 10, -2))
        range(10, 20, 2)
        >>> flip_slice(range(10, 20), True)
        range(20, 10, -1)
        >>> flip_slice(range(20, 10))
        range(10, 20, -1)
        >>> flip_slice(range(20, 10), True)
        range(10, 20, -1)
    r}  c              3   $   K   | ]  }|d u 
 y wr   r   r  s     ru   r   zflip_slice.<locals>.<genexpr>	  s     @Q1D=@   r<   r/   c              3   $   K   | ]  }|d u 
 y wr   r   r  s     ru   r   zflip_slice.<locals>.<genexpr>	  s     Bq}Br  )r   r}  allr~  r  r   )r+  
force_stepr}  s      ru   
flip_slicer    s    t sF88
@399chh*?@@		CHH,88DBsyy#((D&ABB5DtCy399d33tCy399--rw   c              #   0  K   t        t        t        |             |         }|j                  r|j                  nd}|dkD  rt	        |       D ]  \  }}||vs|  yt        |       }t	        t        |             D ]  \  }}||z
  dz
  |vs|  yw)a
  
    Extract the elements not matching a given slice.

    Args:
        seq (Sequence): The input items.
        slice_ (slice): The slice to be complemented.

    Yields:
        item (Any): The next item not matching the slice pattern.

    Examples:
        >>> items = tuple(range(10))

        >>> s = slice(None)
        >>> print(items[s], tuple(complement(items, s)))
        (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) ()

        >>> s = slice(None, None, 2)
        >>> print(items[s], tuple(complement(items, s)))
        (0, 2, 4, 6, 8) (1, 3, 5, 7, 9)

        >>> s = slice(None, None, 3)
        >>> print(items[s], tuple(complement(items, s)))
        (0, 3, 6, 9) (1, 2, 4, 5, 7, 8)

        >>> s = slice(2, None, None)
        >>> print(items[s], tuple(complement(items, s)))
        (2, 3, 4, 5, 6, 7, 8, 9) (0, 1)

        >>> s = slice(None, 7, None)
        >>> print(items[s], tuple(complement(items, s)))
        (0, 1, 2, 3, 4, 5, 6) (7, 8, 9)

        >>> s = slice(2, None, 3)
        >>> print(items[s], tuple(complement(items, s)))
        (2, 5, 8) (0, 1, 3, 4, 6, 7, 9)

        >>> s = slice(None, 7, 3)
        >>> print(items[s], tuple(complement(items, s)))
        (0, 3, 6) (1, 2, 4, 5, 7, 8, 9)

        >>> s = slice(2, 7, 3)
        >>> print(items[s], tuple(complement(items, s)))
        (2, 5) (0, 1, 3, 4, 6, 7, 8, 9)

        >>> s = slice(None, None, -3)
        >>> print(items[s], tuple(complement(items, s)))
        (9, 6, 3, 0) (8, 7, 5, 4, 2, 1)

        >>> s = slice(2, None, -3)
        >>> print(items[s], tuple(complement(items, s)))
        (2,) (9, 8, 7, 6, 5, 4, 3, 1, 0)

        >>> s = slice(None, 7, -3)
        >>> print(items[s], tuple(complement(items, s)))
        (9,) (8, 7, 6, 5, 4, 3, 2, 1, 0)

        >>> s = slice(2, 7, -3)
        >>> print(items[s], tuple(complement(items, s)))
        () (9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

        >>> s = slice(7, 2, -3)
        >>> print(items[s], tuple(complement(items, s)))
        (7, 4) (9, 8, 6, 5, 3, 2, 1, 0)

        >>> items = tuple(1 for i in range(10))
        >>> s = slice(None, None, 3)
        >>> print(items[s], tuple(complement(items, s)))
        (1, 1, 1, 1) (1, 1, 1, 1, 1, 1)

        >>> items = tuple(i % 2 for i in range(10))
        >>> s = slice(None, None, 3)
        >>> print(items[s], tuple(complement(items, s)))
        (0, 1, 0, 1) (1, 0, 0, 1, 1, 0)

        >>> ll = list(range(1000))
        >>> vals = (3, 5, 7, 17, 101)
        >>> vals += tuple(-x for x in vals) + (None,)
        >>> print(vals)
        (3, 5, 7, 17, 101, -3, -5, -7, -17, -101, None)
        >>> sls = [slice(*x) for x in itertools.product(vals, vals, vals)]
        >>> all(
        ...     set(complement(ll, sl)).intersection(ll[sl]) == set()
        ...     for sl in sls)
        True
    r/   r   N)rj  r{  r   r}  	enumerater  )r   slice_
to_excluder}  rM   r   	num_itemss          ru   
complementr  	  s     r U3s8_V,-J ++6;;1Dax ~ 	GAt
"
	 H	 #/ 	GAtA!*4
	s   AB6BBc                 ,     t              r fdS  S )a  
    Modify a function so that it is applied only if a condition is satisfied.

    Args:
        func (callable): A function to apply to an object.
            Must have the following signature: func(Any): Any
        condition (callable|None): The condition function.
            If not None, the function `func` is applied to an object only
            if the condition on the object evaluates to True.
            Must have the following signature: condition(Any): bool

    Returns:
        result (callable): The conditional function.

    Examples:
        >>> conditional_apply(str)(1)
        '1'
        >>> list(map(conditional_apply(str), range(10)))
        ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
        >>> [conditional_apply(str, lambda x: x > 5)(i) for i in range(10)]
        [0, 1, 2, 3, 4, 5, '6', '7', '8', '9']
        >>> list(map(conditional_apply(str, lambda x: x > 5), range(10)))
        [0, 1, 2, 3, 4, 5, '6', '7', '8', '9']
        >>> print(conditional_apply(str) == str)
        True
    c                 (     |       r |       S | S r   r   )rH   	conditionr   s    ru   <lambda>z#conditional_apply.<locals>.<lambda>	  s    IaLa a rw   )r   )r   r  s   ``ru   conditional_applyr  	  s    : 	77rw   c           
          |t        |      n|}t        |      st        }g }|D ]G  }t        ||      r"|j	                  t        | |||dz
  |             1|j	                   | |             I  ||      S )aw  
    Compute a function on each element of a nested structure of iterables.

    The result preserves the nested structure of the input.

    Args:
        func (callable): The function to apply to the individual item.
            Must have the following signature: func(Any): Any.
        items (Iterable): The input items.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        skip (tuple|None): Types to skip descending into.

    Returns:
        new_items (Iterable): The mapped items.

    Examples:
        >>> items = [1, 2, [3, 4, 5], 2, (3, [4, 5])]
        >>> print(deep_map(str, items))
        ['1', '2', ['3', '4', '5'], '2', ('3', ['4', '5'])]
        >>> print(deep_map(float, items))
        [1.0, 2.0, [3.0, 4.0, 5.0], 2.0, (3.0, [4.0, 5.0])]
        >>> print(deep_map(lambda x: x, items))
        [1, 2, [3, 4, 5], 2, (3, [4, 5])]
        >>> print(deep_map(lambda x: x, items, tuple))
        (1, 2, (3, 4, 5), 2, (3, (4, 5)))
    r/   )r   r   r   r  r   deep_map)r   r   r   r  rJ  final_container	new_itemsr   s           ru   r  r  	  s    F &/%6d5kIOO$$ I )4tY	AtDF T$Z() 9%%rw   c           
         |i n
t        |      }|t        |      n|}t        |      st        }g }|D ]J  }t	        |fi |r"|j                  t        | |||dz
  |             1 | |      s:|j                  |       L  ||      S )aN  
    Filter the elements from a nested structure of iterables.

    The result preserves the nested structure of the input.

    Args:
        func (callable): The condition function to include the individual item.
            Must have the following signature: func(Any): bool
        items (Iterable): The input items.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        is_deep_kws (Mapping|None): Keyword parameters for `is_deep()`.
            These are passed to `flyingcircus.is_deep()`.

    Returns:
        new_items (Iterable): The filtered items.

    Examples:
        >>> items = [1, 2, [3, 4, 5], 2, (3, [4, 5])]
        >>> print(deep_filter(lambda x: x > 1, items))
        [2, [3, 4, 5], 2, (3, [4, 5])]
        >>> print(deep_filter(lambda x: x > 2, items))
        [[3, 4, 5], (3, [4, 5])]
        >>> print(deep_filter(lambda _: True, items))
        [1, 2, [3, 4, 5], 2, (3, [4, 5])]
    r/   )r  r   r   r   r  r   deep_filter)r   r   r   r  is_deep_kwsr  r  r   s           ru   r  r  	  s    D $+"k1BK%.%6d5kIOO$& I '4';'D$	9q=+NP Dz  &' 9%%rw   c           	          | Yg }|D ]J  }|dk(  st        ||      r||k(  r|j                  |       +|j                  t        | ||dz
  |             L  | |      S |S )a/  
    Convert the containers from a nested structure of iterables.

    Args:
        container (callable|None): The container to apply.
            Must have the following signature:
            Must have the following signature:
            container(Iterable): container.
            If None, no conversion is performed.
        items (Iterable): The input items.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        skip (tuple|None): Types to skip descending into.

    Returns:
        new_items (container): The converted nested structure of iterables.

    Examples:
        >>> items = [1, 2, [3, 4, 5], 2, (3, [4, 5], 'ciao')]
        >>> deep_convert(list, items)
        [1, 2, [3, 4, 5], 2, [3, [4, 5], 'ciao']]
        >>> deep_convert(tuple, items)
        (1, 2, (3, 4, 5), 2, (3, (4, 5), 'ciao'))
        >>> deep_convert(tuple, items, skip=None)
        (1, 2, (3, 4, 5), 2, (3, (4, 5), (('c',), ('i',), ('a',), ('o',))))
        >>> deep_convert(None, items)
        [1, 2, [3, 4, 5], 2, (3, [4, 5], 'ciao')]
        >>> print(
        ...     deep_map(lambda x: x, items, tuple)
        ...     == deep_convert(tuple, items))
        True
        >>> print(
        ...     deep_filter(lambda x: True, items, tuple)
        ...     == deep_convert(tuple, items))
        True
    r   r/   )r  r   deep_convert)r   r   r  rJ  r  r   s         ru   r  r  0
  sx    R 	 	HDA~WT4%8DEM  &   D)a-FH		H ##rw   c                 J   |t        |       n|}t        |      st        }|d }|d }|d }g }| D ]f  }	|dk(  st        |	|      r|	| k(  r+ ||	      s"|j	                   ||	      r ||	      n|	       D|j	                  t        |	|||||dz
  |             h  ||      S )a  
    Apply conditional mapping, filtering and conversion on nested structures.

    The behavior of this function can be obtained by combining the following:
     - flyingcircus.conditional_apply()
     - flyingcircus.deep_map()
     - flyingcircus.deep_filter()
     - flyingcircus.deep_convert()

    In particular:

    deep_filter_map(
        items, func, map_condition, filter_condition, container,
        avoid, max_depth)

    is equivalent to:

    deep_convert(
        container,
        deep_map(
            conditional_apply(func, map_condition),
            deep_filter(filter_condition, items, avoid, max_depth),
            avoid, max_depth),
        avoid, max_depth)

    If some of the parameters of `deep_filter_map()` can be set to None, the
    equivalent expression can be simplified.
    However, if the call to `deep_filter_map()` would require both
    `deep_map()` and `deep_filter()`, then `deep_filter_map()` is generally
    (and typically also substantially) more performing.

    Args:
        items (Iterable): The input items.
        func (callable): The function to apply to the individual item.
            Must have the following signature: func(Any): Any.
        map_condition (callable|None): The map condition function.
            Only items matching the condition are mapped.
            Must have the following signature: map_condition(Any): bool.
        filter_condition (callable|None): The filter condition function.
            Only items matching the condition are included.
            Must have the following signature: filter_condition(Any): bool.
                container (callable|None): The container to apply.
            Must have the following signature:
            container(Iterable): container.
            If None, the original container is retained.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        skip (tuple|None): Types to skip descending into.

    Returns:
        new_items (Iterable): The mapped and filtered items.

    Examples:
        >>> items = [1, 2, [3, 4, 5], 2, (3, [4, 5])]
        >>> print(
        ...     deep_filter_map(items, str, lambda x: x > 2, lambda x: x > 1))
        [2, ['3', '4', '5'], 2, ('3', ['4', '5'])]
        >>> print(deep_map(
        ...     conditional_apply(str, lambda x: x > 2),
        ...     deep_filter(lambda x: x > 1, items)))
        [2, ['3', '4', '5'], 2, ('3', ['4', '5'])]
        >>> print(deep_filter_map(items, str, None, lambda x: x > 1))
        ['2', ['3', '4', '5'], '2', ('3', ['4', '5'])]
        >>> print(deep_filter_map(items, str, lambda x: x > 2, None))
        [1, 2, ['3', '4', '5'], 2, ('3', ['4', '5'])]
        >>> print(deep_filter_map(items, None, None, lambda x: x > 1))
        [2, [3, 4, 5], 2, (3, [4, 5])]
        >>> deep_filter_map(items, str, None, None)
        ['1', '2', ['3', '4', '5'], '2', ('3', ['4', '5'])]
        >>> deep_filter_map(items, str, None, None, tuple)
        ('1', '2', ('3', '4', '5'), '2', ('3', ('4', '5')))
        >>> def c1(x): return x > 1
        >>> def c2(x): return x > 2
        >>> print(
        ...     deep_filter_map(items, str, c2, c1, tuple)
        ...     == deep_convert(tuple, deep_map(conditional_apply(str, c2),
        ...         deep_filter(c1, items))))
        True
        >>> print(
        ...     deep_filter_map(items, str, c2, c1)
        ...     == deep_map(conditional_apply(str, c2),
        ...         deep_filter(c1, items)))
        True
        >>> print(
        ...    deep_filter_map(items, str, None, c1)
        ...    == deep_map(str, deep_filter(c1, items)))
        True
        >>> print(
        ...    deep_filter_map(items, str, c2, None)
        ...    == deep_map(conditional_apply(str, c2), items))
        True
        >>> print(
        ...    deep_filter_map(items, None, None, c1)
        ...    == deep_filter(c1, items))
        True
        >>> print(
        ...    deep_filter_map(items, str, None, None)
        ...    == deep_map(str, items))
        True
        >>> print(
        ...    deep_filter_map(items, None, None, None, tuple)
        ...    == deep_convert(tuple, items))
        True
    c                     | S r   r   rH   s    ru   r   zdeep_filter_map.<locals>.func
  s    AXrw   c                      yNTr   _s    ru   map_conditionz&deep_filter_map.<locals>.map_condition
  s    Trw   c                      yr  r   r   s    ru   filter_conditionz)deep_filter_map.<locals>.filter_condition
  s    rw   r   r/   )r   r   r   r  r   deep_filter_map)
r   r   r  r  r   r  rJ  r  r  r   s
             ru   r  r  g
  s    f &/%6d5kIOO$|),I *>t!4%  }T/BdM$/?M4)** 9%%rw   c                     || d}}n|| z
  | }}|dkD  r7|r ||j                               |z  |z   S  ||j                               |z  S |dk(  r|S t        d      )a  
    Pick a random integer in the specified range.

    Note that this is roughly equivalent to (but faster than)
    `random.randrange()` or `random.randint()`.

    Args:
        first (int): The first value of the range.
            If `second` is None, the `start` value is 0 and this is the `stop`
            value (not included in the range).
            Otherwise, this is the `start` value and it is always included.
        second (int|None): The second value of the range.
            If None, the start value is 1 and the stop value is `first`.
            Otherwise, this is the stop value and it is not included.
        rand_bits (callable): Function to generate random bits as int.
            Must accept the following signature: rand_bits(n: int): int
            The `n` parameter is the number of `bits`.

    Returns:
        result (int): The random value within the specified range.

    Raises:
        ValueError: if `first` and `second` would produce an empty range.
            This happens if `second` is None and `first` is not positive, or
            if `second <= first`.

    Examples:
        >>> random.seed(0); n = 16; [random_int(n) for _ in range(n)]
        [11, 12, 8, 12, 13, 1, 8, 14, 0, 15, 12, 13, 9, 10, 9, 14]
        >>> random.seed(0); n = 16; [random_int(10, 20) for _ in range(n)]
        [13, 16, 12, 14, 16, 10, 14, 15, 18, 17, 16, 14, 12, 13, 14, 15]
        >>> random.seed(0); n = 16; [random_int(-10, 10) for _ in range(n)]
        [-3, 2, -6, -2, 3, -9, -2, 0, 6, 5, 2, -1, -5, -4, -1, 0]
        >>> random.seed(0); n = 16; [random_int(-10, -5) for _ in range(n)]
        [-9, -7, -9, -8, -7, -10, -8, -8, -6, -7, -7, -8, -9, -9, -8, -8]
        >>> random_int(0)
        0
        >>> random_int(10, 10)
        10
        >>> random_int(10, 5)
        Traceback (most recent call last):
            ...
        ValueError: The range size must be greater than 0
    r   z%The range size must be greater than 0)
bit_lengthr"  )rf  r|  	rand_bitssizeoffsets        ru   
random_intr  
  su    ` ~af~ufaxT__./$6??T__./$66	@AArw   c                    t        |       }t        ||      }t        ||      }|dk(  r&||dz
  k(  rt        | d      r| j                          | S ||z   }t	        ||dz   dz        D ]  }||z
  }| |   | |   c| |<   | |<    | S )a  
    Reverse or flip in-place a sequence.

    Warning! This function modifies its `seq` parameter.

    This supports also partial reversing / flipping.

    Note that this is roughly equivalent to:

    seq[first:last + 1] = reversed(seq[first:last + 1])

    but it does not require additional memory.

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Returns:
        result (int): The reversed sequence.

    Examples:
        >>> seq = list(range(10))
        >>> print(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> print(flip(seq))
        [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> print(seq)
        [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

        >>> print(flip(seq, 3, 7))
        [9, 8, 7, 2, 3, 4, 5, 6, 1, 0]
        >>> print(flip(seq, 3, 7))
        [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

        >>> print(flip(seq, 3, 6))
        [9, 8, 7, 3, 4, 5, 6, 2, 1, 0]
        >>> print(flip(seq, 3, 6))
        [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    r   r/   reverser9   )r   r   r   r  r{  )r   rf  rg  r2   r0   rM   js          ru   flipr  3  s    \ 	CAq!EtQDzda!emY(? J	 5Luq1ul+ 	,AAA VSVNCFCF	, Jrw   c                    t        |       }t        ||      }t        ||      }|dk  rd}|dkD  r1t        |||      D ]  } |||dz         }| |   | |   c| |<   | |<   ! | S t        |||       D ]  } ||      }| |   | |   c| |<   | |<    | S )a  
    Shuffle in-place a sequence.

    Warning! This function modifies its `seq` parameter.

    This use Fisher-Yates shuffle (Durstenfeld method).

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.
        k (int): Swap reduction factor.
            Larger values result in more speed and less randomness.
            Must be 1 or more, otherwise it is ignored
        rand_int (callable): The random integer generator.
            Must accept the signature: rand_int(a: int, b: int|None): int
            If `b` is None, must produce a random value in the [0, a) range
            (0 is included, `a` is excluded), otherwise must produce a random
            value in the [a, b) range (`a` is included, `b` is excluded)

    Returns:
        result (int): The shuffled sequence.

    Examples:
        >>> seq = list(range(16))
        >>> print(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

        >>> seq = list(range(16)); random.seed(0)
        >>> print(shuffle(seq))
        [15, 8, 1, 10, 5, 11, 3, 9, 7, 4, 0, 14, 2, 12, 6, 13]

        >>> seq = list(range(16)); random.seed(0)
        >>> print(shuffle(seq, k=2))
        [7, 9, 2, 11, 4, 0, 15, 3, 8, 5, 10, 1, 12, 6, 14, 13]

        >>> seq = list(range(16)); random.seed(0)
        >>> print(shuffle(seq, 6))
        [0, 1, 2, 3, 4, 5, 9, 13, 12, 6, 7, 11, 14, 10, 8, 15]

        >>> seq = list(range(16)); random.seed(0)
        >>> print(shuffle(seq, 3, 13))
        [0, 1, 2, 5, 10, 8, 12, 4, 3, 11, 13, 7, 9, 6, 14, 15]

        >>> seq = list(range(16)); random.seed(0)
        >>> print(shuffle(seq, 0, 13))
        [7, 8, 5, 9, 3, 10, 11, 2, 13, 12, 4, 1, 6, 0, 14, 15]

    See Also:
        - random.shuffle()
        - random.randrange()
        - flyingcircus.random_int()
        - https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
    r/   r   r   r   r{  )r   rf  rg  r'   rand_intr2   rM   r  s           ru   shuffler  x  s    | 	CAq!EtQD1uqyudA& 	,AD1H%A VSVNCFCF	, J tUQB' 	,AA VSVNCFCF	, Jrw   c                     t        |       }t        ||      }t        ||      }||k  rdnd}t        |||z   |      D ]$  } || |         s| |   | |   c| |<   | |<   ||z  }& |dkD  r|S |dz   S )a  
    Partition in-place a sequence according to a specified condition.

    Warning! This function modifies its `seq` parameter.

    Args:
        seq (MutableSequence): The input sequence.
        condition (callable): The partitioning condition.
            Must have the following signature: condition(Any): bool.
            If the condition is met,
        first (int): The first index.
            The index is forced within boundaries.
            If first < last, the partitioning is performed forward,
            otherwise the partitioning is performed backward.
        last (int): The last index (included).
            The index is forced within boundaries.
            If first < last, the partitioning is performed forward,
            otherwise the partitioning is performed backward.

    Returns:
        result (int): The index that delimits the partitioning.

    Examples:
        >>> seq = list(range(10))
        >>> k = partition(seq, lambda x: x % 2 == 0)
        >>> print(seq[:k], seq[k:])
        [0, 2, 4, 6, 8] [5, 3, 7, 1, 9]

        >>> k = partition(seq, lambda x: x >= 5)
        >>> print(seq[:k], seq[k:])
        [6, 8, 5, 7, 9] [4, 3, 0, 1, 2]

        >>> k = partition(seq, lambda x: x < 5)
        >>> print(seq[:k], seq[k:])
        [4, 3, 0, 1, 2] [6, 8, 5, 7, 9]

        >>> seq = list(range(10))
        >>> k = partition(seq, lambda x: x % 5, 2, 8)
        >>> print(seq[2:k], seq[k:9])
        [2, 3, 4, 6, 7, 8] [5]

        >>> seq = list(range(10))
        >>> k = partition(seq, lambda x: x % 2 == 0, -1, 0)
        >>> print(seq[:k], seq[k:])
        [5, 1, 9, 3, 7] [0, 2, 4, 6, 8]

    See Also:
        - flyingcircus.selection()
        - flyingcircus.quick_sort()
    r/   r<   r   r  )r   r  rf  rg  r2   r}  rM   s          ru   	partitionr    s    n 	CAq!EtQD1"D5$+t, SV!$QUCJATME axqyrw   c                 ~   t        |       }t        ||      }t        ||      }t        ||      }|rt        | |||       ||k  rw |||dz         }| |   }|}	|}
	 | |	   |k  r|	dz  }	| |	   |k  r| |
   |kD  r|
dz  }
| |
   |kD  r|	|
k\  rn| |
   | |	   c| |	<   | |
<   |	dz  }	|
dz  }
N||
k  r|
}n|
dz   }||k  rw| S )a	  
    Rearrange in-place a sequence so that the k-th element is at position k.

    Warning! This function modifies its `seq` parameter.

    This implements quick-select using an iterative approach with
    Hoare partitioning scheme.

    Essentially, this ensures that `seq[k]` is the k-th largest element.
    The order of the elements below or above `k` is ignored.
    The problem is also known as selection or k-th statistics.

    Args:
        seq (MutableSequence): The input sequence.
        k (int): The input index.
            This is 0-based and supports negative indexing.
            The index is forced within boundaries.
            If k is not in the (first, last) interval, the result is undefined.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.
        pivot (callable): The function for choosing the pivot index.
            Must accept the following signature:
            pivot(first: int, last: int): int
            The arguments are the first and last indices of the subsequence
            to be partitioned.
        randomize (bool|int): Pre-shuffle the input.
            If int, this is passed as `k` parameter
            (shuffling reduction factor) to `flyingcircus.shuffle()`.
            This render the worst case **very** unlikely.
            This is useful for deterministic pivoting.

    Returns:
        seq (MutableSequence): The partially sorted sequence.

    Examples:
        >>> seq = [2 * x for x in range(10)]
        >>> print(seq)
        [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

        >>> random.seed(0); seq.sort(); print(shuffle(seq))
        [4, 18, 10, 14, 0, 6, 2, 16, 12, 8]
        >>> k = 2
        >>> print(selection(seq, k)[k])
        4

        >>> random.seed(0); seq.sort(); print(shuffle(seq))
        [4, 18, 10, 14, 0, 6, 2, 16, 12, 8]
        >>> k = 5
        >>> print(selection(seq, k)[k])
        10

        >>> random.seed(0); seq.sort(); print(shuffle(seq))
        [4, 18, 10, 14, 0, 6, 2, 16, 12, 8]
        >>> k = -1
        >>> print(selection(seq, k)[k])
        18

        >>> random.seed(0); seq.sort(); print(shuffle(seq))
        [4, 18, 10, 14, 0, 6, 2, 16, 12, 8]
        >>> k = 7
        >>> print(selection(seq, k, 1, 7)[k])
        18

    See Also:
        - flyingcircus.partition()
        - flyingcircus.quick_sort()
    )r'   r/   )r   r   r  )r   r'   rf  rg  pivot	randomizer2   r3   rH   rM   r  s              ru   	selectionr    s   X 	CAAqAq!EtQDUDI.
$,& %"Fa&1*Q a&1*a&1*Q a&1*Av!$QQAAQQ  6DEEK $,L Jrw   c                 
   t        |       }t        ||      }t        ||      }t        |dz   |dz         D ]J  }|}||kD  s| |dz
     | |   kD  s| |dz
     | |   c| |<   | |dz
  <   |dz  }||kD  s<| |dz
     | |   kD  r1L | S )u  
    Sort in-place a sequence using insertion sort.

    Warning! This function modifies its `seq` parameter.

    This is slower than `sorted()` or `list.sort()`, but uses less memory.

    The algorithm is:

     - best-case: O(n)
     - average-case: O(n²)
     - worst-case: O(n²)
     - memory: O(1)
     - stable

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Returns:
        seq (MutableSequence): The sorted sequence.

    Examples:
        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> insertion_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> insertion_sort(seq, 3, 7)
        [1, 0, 3, 2, 4, 5, 7, 9, 6, 8]

        >>> seq = [9, 0, 2, 6, 3, 5, 1, 7, 8, 4, 1, 0, 3, 2, 4, 5, 7, 9, 6, 8]
        >>> insertion_sort(seq)
        [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

        >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> insertion_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        >>> insertion_sort(seq)
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    See Also:
        - flyingcircus.selection_sort()
        - flyingcircus.step_sort()
        - flyingcircus.quick_sort()
        - flyingcircus.merge_sort()
        - flyingcirucs.nat_merge_sort()
    r/   r  )r   rf  rg  r2   rM   r  s         ru   insertion_sortr    s    r 	CAq!EtQD519dQh' %iCAJQ/!$QUSVCFCAJFA %iCAJQ/
 Jrw   c                 R   t        |       }t        ||      }t        ||      }||k  r~| |   }|}| |   }|}t        ||dz         D ]  }| |   }	|	|k  r|	}|}|	|kD  s|	}|} ||k7  r| |   | |   c| |<   | |<   ||k(  r|}||k7  r| |   | |   c| |<   | |<   |dz  }|dz  }||k  r~| S )u  
    Sort in-place using (double-edged) selection sort.
    Warning! This function modifies its `seq` parameter.

    This is slower than `sorted()` or `list.sort()`, but uses less memory.

    The algorithm is:

     - best-case: O(n²)
     - average-case: O(n²)
     - worst-case: O(n²)
     - memory: O(1)
     - stable

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Returns:
        seq (MutableSequence): The sorted sequence.

    Examples:
        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> selection_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> selection_sort(seq, 3, 7)
        [1, 0, 3, 2, 4, 5, 7, 9, 6, 8]

        >>> seq = [9, 0, 2, 6, 3, 5, 1, 7, 8, 4, 1, 0, 3, 2, 4, 5, 7, 9, 6, 8]
        >>> selection_sort(seq)
        [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

        >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> selection_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        >>> selection_sort(seq)
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    See Also:
        - flyingcircus.insertion_sort()
        - flyingcircus.step_sort()
        - flyingcircus.quick_sort()
        - flyingcircus.merge_sort()
        - flyingcirucs.nat_merge_sort()
    r/   r  )
r   rf  rg  r2   min_valrM   max_valr  r'   rH   s
             ru   selection_sortr    s   p 	CAq!EtQD
$, e*d)udQh' 	AAA7{W	 A:!$QUCJAA:A19 #AD	CIs1v
	7 $,8 Jrw   c                    t        |       }t        ||      }t        ||      }|>dg}d\  }}}||k  r|j                  |       ||z  |z  }||k  r|j                          nt	        |      rt         ||      d      }|dz   }|D ]Y  }	t        ||	z   |      D ]E  }
| |
   }|
}|||	z   k\  r/| ||	z
     |kD  r$| ||	z
     | |<   ||	z  }|||	z   k\  r| ||	z
     |kD  r$|| |<   G [ | S )u  
    Sort in-place a sequence using step insertion (Shell) sort.

    Warning! This function modifies its `seq` parameter.

    This is slower than `sorted()` or `list.sort()`, but uses less memory.

    The algorithm is:

     - best-case: O(n log n)
     - average-case: O(n log n) to O(n √n) -- depending on the steps
     - worst-case: O(n log² n) to O(n²) -- depending on the steps
     - memory: O(1)
     - unstable

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.
        steps (Iterable[int]|None): The steps to use.
            If None, uses pseudo-optimal values following the sequence:
            x(k) = 9 * x(k-1) // 4; with x(0) = 1, x(1) = 4
            in decreasing order not exceeding the sequence length.
            This is empirically fast, but theoretical performance is unknown.

    Returns:
        seq (MutableSequence): The sorted sequence.

    Examples:
        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> step_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> step_sort(seq, 3, 7)
        [1, 0, 3, 2, 4, 5, 7, 9, 6, 8]

        >>> seq = [9, 0, 2, 6, 3, 5, 1, 7, 8, 4, 1, 0, 3, 2, 4, 5, 7, 9, 6, 8]
        >>> step_sort(seq)
        [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

        >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> step_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        >>> step_sort(seq)
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    See Also:
        - flyingcircus.insertion_sort()
        - flyingcircus.selection_sort()
        - flyingcircus.quick_sort()
        - flyingcircus.merge_sort()
        - flyingcirucs.nat_merge_sort()
    r/   )rb   	   rb   Tr  )r   r   r   r  r   rl  r{  )r   rf  rg  stepsr2   rH   r0   r>   r  r}  rM   tempr  s                ru   	step_sortr%  *  s+   ~ 	CAq!EtQD}1a!eLLOA
A !e 		%uQx.!8D ut|T* 	Aq6DAut|#AH(<QXAT	 ut|#AH(< CF	 Jrw   c                    t        |       }t        ||      }t        ||      }|rt        | |||       ||fg}|r|j                         \  }} |||dz         }| |   }|}	|}
	 | |	   |k  r|	dz  }	| |	   |k  r| |
   |kD  r|
dz  }
| |
   |kD  r|	|
k\  rn| |
   | |	   c| |	<   | |
<   |	dz  }	|
dz  }
N|
|kD  r|
|z
  dkD  r|j	                  ||
g       ||
dz   kD  r||
z
  dkD  r|j	                  |
dz   |g       |r| S )u  
    Sort in-place a sequence using quick (partition-exchange) sort.

    Warning! This function modifies its `seq` parameter.

    This is slower than `sorted()` or `list.sort()`, but uses less memory.

    Uses an iterative approach with Hoare partitioning scheme.

    The algorithm is:
     - best-case: O(n log n)
     - average-case: O(n log n)
     - worst-case: O(n²)
     - memory: O(log n) to O(n)
     - unstable

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.
        pivot (callable): The function for choosing the pivot index.
            Must accept the following signature:
            pivot(first: int, last: int): int
            The arguments are the first and last indices of the subsequence
            to be partitioned.
        randomize (bool): Pre-shuffle the input.
            If int, this is passed as `k` parameter
            (shuffling reduction factor) to `flyingcircus.shuffle()`.
            This render the worst case **very** unlikely.
            This is useful for deterministic pivoting.

    Returns:
        seq (MutableSequence): The sorted sequence.

    Examples:
        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> quick_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> quick_sort(seq, 3, 7)
        [1, 0, 3, 2, 4, 5, 7, 9, 6, 8]

        >>> seq = [9, 0, 2, 6, 3, 5, 1, 7, 8, 4, 1, 0, 3, 2, 4, 5, 7, 9, 6, 8]
        >>> quick_sort(seq)
        [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

        >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> quick_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        >>> quick_sort(seq)
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    See Also:
        - flyingcircus.partition()
        - flyingcircus.selection()
        - flyingcircus.insertion_sort()
        - flyingcircus.selection_sort()
        - flyingcircus.step_sort()
        - flyingcircus.merge_sort()
        - flyingcirucs.nat_merge_sort()
    r/   r   )r   r   r  r   r   )r   rf  rg  r  r  r2   indicesr3   rH   rM   r  s              ru   
quick_sortr(    sR   P 	CAq!EtQDUD),t}oG* kkmt%"Fa&1*Q a&1*a&1*Q a&1*Av!$QQAAQQ  u9UQNNE1:&!a%<D1HqLNNAE4=)- . Jrw   c                    t        |       }t        ||      }t        ||      }||z
  dz   }|dz   }dg|z  }d}||k  rt        ||d|z        D ]h  }||z   }	|	|kD  r|}	|d|z  z   }
|
|kD  r|}
||	}}t        ||z
  |
|z
        D ]2  }||	k  r||
k\  s| |   | |   k  r| |   ||<   |dz  }&| |   ||<   |dz  }4 j || || |dz  }||k  r| S )ay  
    Sort in-place a sequence using merge sort.

    Warning! This function modifies its `seq` parameter.

    This is slower than `sorted()` or `list.sort()`.

    Uses a bottom-up approach.
    Note that the merge step is not in-place in the sense that it still
    requires a memory buffer the size of the sequence.

    The algorithm is:
     - best-case: O(n log n)
     - average-case: O(n log n)
     - worst-case: O(n log n)
     - memory: O(n)
     - stable

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Returns:
        seq (MutableSequence): The sorted sequence.

    Examples:
        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> merge_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> merge_sort(seq, 3, 7)
        [1, 0, 3, 2, 4, 5, 7, 9, 6, 8]

        >>> seq = [9, 0, 2, 6, 3, 5, 1, 7, 8, 4, 1, 0, 3, 2, 4, 5, 7, 9, 6, 8]
        >>> merge_sort(seq)
        [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

        >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> merge_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        >>> merge_sort(seq)
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    See Also:
        - flyingcircus.insertion_sort()
        - flyingcircus.selection_sort()
        - flyingcircus.step_sort()
        - flyingcircus.quick_sort()
        - flyingcirucs.nat_merge_sort()
    r/   Nr9   r  )r   rf  rg  r2   r	  r  r$  widthrO   r0   r;   rM   r  r'   s                 ru   
merge_sortr+    s3   x 	CAq!EtQD%<!D!8D6D=DE
1*udAI. 	AE	A4xAIA4xaqA1u9a%i0 q5a1fA#a&(8!!fDGFA!!fDGFA	  E$
% 1*& Jrw   c                 j   t        |       }t        ||      }t        ||      }||z
  dz   }dg|z  }|}	 |}|dz   |k  r*| |dz      | |   k\  r|dz  }|dz   |k  r| |dz      | |   k\  r|}||k  rI|dz  }|dz   |k  r*| |dz      | |   k  r|dz  }|dz   |k  r| |dz      | |   k  r||k  rt        | ||       ||k  r|dz   }nn|}	 |}|dz   |k  r*| |dz      | |   k\  r|dz  }|dz   |k  r| |dz      | |   k\  r|dz  }|}|dz   |k  r*| |dz      | |   k\  r|dz  }|dz   |k  r| |dz      | |   k\  r||k(  r||kD  r	 | S ||kD  r|}||kD  r|}||}
}	t        ||z
  ||z
  dz         D ]2  }|	|k  r|
|kD  s| |	   | |
   k  r| |	   ||<   |	dz  }	&| |
   ||<   |
dz  }
4 ||k  r|dz   }n
|| ||dz    |})a  
    Sort in-place a sequence using natural merge sort.

    Warning! This function modifies its `seq` parameter.

    This is slower than `sorted()` or `list.sort()`.

    Uses a natural approach (exploiting partially sorted sub-sequences).
    The best-case is for sorted or reversed inputs.
    Note that the merge step is not in-place in the sense that it still
    requires a memory buffer the size of the sequence.

    The algorithm is:
     - best-case: O(n)
     - average-case: O(n log n)
     - worst-case: O(n log n)
     - memory: O(n)
     - stable

    Args:
        seq (MutableSequence): The input sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Returns:
        seq (MutableSequence): The sorted sequence.

    Examples:
        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> nat_merge_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [1, 0, 3, 5, 7, 9, 2, 4, 6, 8]
        >>> nat_merge_sort(seq, 3, 7)
        [1, 0, 3, 2, 4, 5, 7, 9, 6, 8]

        >>> seq = [9, 0, 2, 6, 3, 5, 1, 7, 8, 4, 1, 0, 3, 2, 4, 5, 7, 9, 6, 8]
        >>> nat_merge_sort(seq)
        [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

        >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> nat_merge_sort(seq)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        >>> seq = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        >>> nat_merge_sort(seq)
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    See Also:
        - flyingcircus.insertion_sort()
        - flyingcircus.selection_sort()
        - flyingcircus.step_sort()
        - flyingcircus.quick_sort()
        - flyingcirucs.merge_sort()
    r/   N)r   r   r  r{  )r   rf  rg  r2   r	  r$  rO   r;   r0   rM   r  r'   s               ru   nat_merge_sortr-  Y  s   z 	CAq!EtQD%<!D6D=DA
!etmAE
c!f 4FA !etmAE
c!f 4t8FAa%4-CAJQ$7Q a%4-CAJQ$71uS!Qt8AA   	A
!etmAE
c!f 4FA !etmAE
c!f 4	Q!etmAE
c!f 4FA !etmAE
c!f 4:!d(& J% t8At8A!1q5y!e)a-0 	A1u!a%3q6SV#3a&QQa&QQ	 t8AA"&CdQhA; rw   c                 T    t        t        t        |             | j                        S )a'  
    Sort the indexes of a sequence.

    This is useful to sort a sequence using the ordering from another sequence.

    Args:
        seq (Sequence): The input sequence.

    Returns:
        indexes (list): The sorted indexes.

    Examples:
        >>> x = 'flyingcircus'
        >>> i = argsort(x)
        >>> print(list(iter_at(x, i)))
        ['c', 'c', 'f', 'g', 'i', 'i', 'l', 'n', 'r', 's', 'u', 'y']
        >>> j = argsort(i)
        >>> print(list(iter_at(list(iter_at(x, i)), j)))
        ['f', 'l', 'y', 'i', 'n', 'g', 'c', 'i', 'r', 'c', 'u', 's']

        >>> y = 'abracadabras'
        >>> print(list(iter_at(y, i)))
        ['d', 'r', 'a', 'a', 'a', 'a', 'b', 'c', 'b', 's', 'a', 'r']
        >>> print(list(iter_at(list(iter_at(y, i)), j)))
        ['a', 'b', 'r', 'a', 'c', 'a', 'd', 'a', 'b', 'r', 'a', 's']

        >>> print(sorted(x) == list(iter_at(x, i)))
        True
        >>> print(list(x) == list(iter_at(list(iter_at(x, i)), j)))
        True
        >>> print(list(y) == list(iter_at(list(iter_at(y, i)), j)))
        True
    r  )rl  r{  r   __getitem__r   s    ru   argsortr2    s    D %C/s77rw   c                 l    t        |       }| |cxk  r|k  rn yt        t        |       |||      |   S y)aB  
    Find the smallest k-th element in a sequence.

    This is roughly equivalent to, but asymptotically more efficient than:
    `sorted(seq)[k]`.
    The problem is also known as selection or k-th statistics.
    This is the not-in-place version of `flyingcircus.selection()`.
    This is similar to `flyingcircus.medoid()` and `flyingcircus.quantiloid()`,
    except that the arguments are different and they are potentially faster.

    Args:
        seq (Sequence): The input sequence.
        k (int): The input index.
            This is 0-based and supports negative indexing.
            If k > len(seq) or k < -len(seq), None is returned.
            If k is not in the (first, last) interval, the result is undefined.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Returns:
        result: The smallest k-th element.

    Examples:
        >>> seq = [2 * x for x in range(10)]
        >>> print(seq)
        [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
        >>> random.seed(0); print(shuffle(seq))
        [4, 18, 10, 14, 0, 6, 2, 16, 12, 8]
        >>> seq = tuple(seq)  # seq is now immutable
        >>> print(select_ordinal(seq, 0))
        0
        >>> print(select_ordinal(seq, 9))
        18
        >>> print(select_ordinal(seq, 4))
        8
        >>> print(select_ordinal(seq, -10))
        0
        >>> print(select_ordinal(seq, -1))
        18
        >>> print(select_ordinal(seq, -6))
        8
        >>> print(select_ordinal(seq, 10))
        None
        >>> print(select_ordinal(seq, -11))
        None
        >>> print(select_ordinal(seq, 7, 1, 7))
        18

    See Also:
        - flyingcircus.partition()
        - flyingcircus.medoid()
        - flyingcircus.quantiloid()
    N)r   r  r  )r   r'   rf  rg  r2   s        ru   select_ordinalr4    s?    x 	CA	rQ{{  cAud3A66rw   c              #   d   K   t               }| D ]  }||vs|j                  |      r|  yw)a*  
    Get unique items (keeping order of appearance).

    If the order of appearance is not important, use `set()`.

    Args:
        items (Iterable): The input items.

    Yields:
        item: Unique items.

    Examples:
        >>> items = (5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 3, 2, 4, 2, 4, 1, 1)
        >>> tuple(uniques(items))
        (5, 4, 3, 2, 1)
        >>> tuple(set(items))
        (1, 2, 3, 4, 5)
        >>> sorted(set(items)) == sorted(uniques(items))
        True
        >>> tuple(uniques('abcde'))
        ('a', 'b', 'c', 'd', 'e')
    N)rj  add)r   seenr   s      ru   uniquesr8  :  s5     . 5D tDHHTNJs   000c                     d}| D ]  }|}	 t        |      }||} |||      }  |S # t        $ r Y w xY w# ||}w  |||      }w xY w)a  
    Combine the length of each item within items.

    For each item within items, determine if the item has a length and then
    use a given combination function to combine the multiple extracted length.
    If an item is not a sequence, its length is assumed to be 0.

    A useful application is to determine the longest item.

    Args:
        items (Iterable): The collection of items to inspect.
        combine (callable): The combination method.
            Must have the following signature: combine(int, int): int.
            The lengths are combined incrementally.
        non_seq_len (int): The length of non-sequence items.
            Typical choices are `0` or `1` depending on the application.

    Returns:
        num (int): The combined length of the collection.

    Examples:
        >>> a = list(range(10))
        >>> b = tuple(range(5))
        >>> c = set(range(20))
        >>> combine_iter_len((a, b, c))
        20
        >>> combine_iter_len((a, b, c), min)
        5
        >>> combine_iter_len((1, a))
        10
    N)r   r   )r   r  non_seq_lennumvalnew_nums         ru   combine_iter_lenr>  X  sx    F C 
,	,#hG {gs+
, J  		 {gs+s   (	4747Ac              #      K   t        |      }	 t        |      }|r|D ]  } | ||       |} y|D ]  } | ||       |} y# t        $ r Y yw xY ww)a  
    Apply a binary function to consecutive elements in an iterable.

    The same can be obtained combining `map()` and `flyingcircus.slide()`,
    but this is faster.

    Args:
        func (callable): The pairwise operator to apply.
        items (Iterable): The input items.
        reverse (bool): Reverse the order of the arguments in `func()`.

    Yields:
        value: The result of func applied to the next pair.

    Examples:
        >>> list(pairwise_map(lambda x, y: (x, y), range(8)))
        [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
        >>> list(pairwise_map(lambda x, y: (x, y), range(8), True))
        [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6)]
        >>> list(pairwise_map(lambda x, y: x < y, range(10)))
        [True, True, True, True, True, True, True, True, True]
        >>> list(pairwise_map(lambda x, y: x < y, []))
        []

        >>> def sub_args(x): return x[0] - x[1]
        >>> items = range(1000)
        >>> all(x == y for x, y in zip(
        ...     pairwise_map(operator.sub, items),
        ...     map(sub_args, slide(items, 2))))
        True

    See Also:
        - flyingcircus.slide()
    Nr   r   r  )r   r   r  r  	last_itemr   s         ru   pairwise_maprB    s}     L eJ!$	
 " !4++ 	! # !9d++ 	!  s'   AA ,A	AAAAc           
      p   t        j                  t        |       |      }|dkD  r4t        |      D cg c]  \  }}t        j                  ||d|       }}}n'd }	t        |      D cg c]  \  }} |	||       }}}|rt        |      }|rt        | S t        j                  |d|iS c c}}w c c}}w )a	  
    Generate a sliding grouping / window across the items.

    The number of elements for each yield is fixed.

    This can be used to compute sliding/running/moving/rolling statistics
    for the general case, but rolling computations that can be expressed
    in terms of the previous iteration may be computed more efficiently
    with `flyingcircus.rolling()`.

    Args:
        items (Iterable): The input items.
        size (int): The windowing size.
        step (int|None): The windowing step.
            If int, must be larger than 0.
        truncate (bool): Determine how to handle uneven splits.
            If True, last groups are skipped if smaller than `size`.
        fill (Any): Value to use for filling the last group.
            This is only used when `truncate` is False.
        reverse (bool): Reverse the order within the window.

    Returns:
        result (zip|itertools.zip_longest): Iterable of items within window.

    Examples:
        >>> tuple(slide(range(8), 2))
        ((0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7))
        >>> tuple(slide(range(8), 3))
        ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7))

        >>> tuple(slide(range(8), 3, 2))
        ((0, 1, 2), (2, 3, 4), (4, 5, 6))
        >>> tuple(slide(range(8), 3, 2, False))
        ((0, 1, 2), (2, 3, 4), (4, 5, 6), (6, 7, None))
        >>> tuple(slide(range(8), 3, 2, False, -1))
        ((0, 1, 2), (2, 3, 4), (4, 5, 6), (6, 7, -1))

        >>> tuple(slide(range(5), 3, 1))
        ((0, 1, 2), (1, 2, 3), (2, 3, 4))
        >>> tuple(slide(range(5), 3, 1, False))
        ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, None), (4, None, None))

        >>> tuple(slide(range(8), 2, 1))
        ((0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7))
        >>> tuple(slide(range(8), 1, 1))
        ((0,), (1,), (2,), (3,), (4,), (5,), (6,), (7,))
        >>> tuple(slide(range(8), 1, 2))
        ((0,), (2,), (4,), (6,))
        >>> tuple(slide(range(8), 2, 2))
        ((0, 1), (2, 3), (4, 5), (6, 7))

        >>> tuple(slide(range(8), 2, reverse=True))
        ((1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6))

        >>> def irange(*args):
        ...    for x in range(*args):
        ...        yield x
        >>> tuple(slide(irange(8), 2))
        ((0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7))

    See Also:
        - flyingcircus.sliding()
        - flyingcircus.separate()
        - flyingcircus.split()
        - flyingcircus.chunks()
        - flyingcircus.rolling()
    r/   Nc                 H    t        t        j                  | ||      d        | S r   )r   r   islice)iteratorr2   s     ru   consumedzslide.<locals>.consumed  s     !!(Aq148Orw   r  )r   teer   r  rE  r  r  r  )
r   r	  r}  truncater   r  itersrM   iteringrG  s
             ru   sliderL    s    T MM$u+t,Eax (.07 Wat40 0
	
 4=U3CE%/QHWa E EE{$$e<t<<!0Es   "B,*B2c              #      K   |rt        |||||      D ]	  } | |   yt        |||||      D ]  } | |        yw)a  
    Apply a function to a sliding grouping / window of the items.

    Args:
        func (callable): The function to apply.
        items (Iterable): The input items.
        size (int): The windowing size.
        step (int|None): The windowing step.
            If int, must be larger than 0.
        truncate (bool): Determine how to handle uneven splits.
            If True, last groups are skipped if smaller than `size`.
        fill (Any): Value to use for filling the last group.
            This is only used when `truncate` is False.
        star(bool): Pass arguments to func using star magic.

    Yields:
        result: The function applied to the slided items.

    Examples:
        >>> items = list(range(10))
        >>> print(items)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> print(list(sliding(sum, items, 2)))
        [1, 3, 5, 7, 9, 11, 13, 15, 17]
        >>> print(list(sliding(sum, items, 3)))
        [3, 6, 9, 12, 15, 18, 21, 24]
        >>> print(list(sliding(sum, items, 3, 2)))
        [3, 9, 15, 21]

        >>> print(list(sliding(sum, items, 3, 2, False, 0)))
        [3, 9, 15, 21, 17]
        >>> print(list(sliding(sum, items, 3, 2, False)))
        Traceback (most recent call last):
            ...
        TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

        >>> print(list(sliding(operator.add, items, 2, star=True)))
        [1, 3, 5, 7, 9, 11, 13, 15, 17]
        >>> print(list(sliding(operator.add, items, 2)))
        Traceback (most recent call last):
            ...
        TypeError: add expected 2 arguments, got 1

    See Also:
        - flyingcircus.slide()
        - flyingcircus.separate()
        - flyingcircus.split()
        - flyingcircus.chunks()
        - flyingcircus.rolling()
    N)rL  )r   r   r	  r}  rI  r   starbatchs           ru   slidingrP  #  sZ     t 5$h= 	E,	 5$h= 	Eu+	s   >A c                 ^    t        |       g|z  }|rt        | S t        j                  |d|iS )af  
    Separate items into groups with fixed size.

    The number of elements for each yield is fixed.

    For different handling of the last group for uneven splits, and for
    splitting into groups of varying size, see `flyingcircus.split()`.

    Args:
        items (Iterable): The input items.
        size (int): Number of elements to group together.
        truncate (bool): Determine how to handle uneven splits.
            If True, last group is skipped if smaller than `size`.
        fill (Any): Value to use for filling the last group.
            This is only used when `truncate` is False.

    Returns:
        result (zip|itertools.zip_longest): Iterable of grouped items.
            Each group is a tuple regardless of the original container.

    Examples:
        >>> l = list(range(10))
        >>> tuple(separate(l, 4))
        ((0, 1, 2, 3), (4, 5, 6, 7), (8, 9, None, None))
        >>> tuple(separate(tuple(l), 2))
        ((0, 1), (2, 3), (4, 5), (6, 7), (8, 9))
        >>> tuple(separate(l, 4, True))
        ((0, 1, 2, 3), (4, 5, 6, 7))
        >>> tuple(separate(l, 4, False, 0))
        ((0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 0, 0))

        >>> tuple(separate(l, 2)) == tuple(slide(l, 2, 2))
        True

    See Also:
        - flyingcircus.slide()
        - flyingcircus.sliding()
        - flyingcircus.split()
        - flyingcircus.chunks()
    r  )r   r  r   r  )r   r	  rI  r   	iteratorss        ru   separaterS  f  s8    ` e$II$$i@4@@rw   c              #     K   t        |t              rt        |t        |       |z        }t        |       }t	        |      |k\  r|dd }dt        t        j                  |            z   |fz   }t        |      dz
  }|r(t        |      D ]  }t        ||   ||dz              yt        |      D ]  }| ||   ||dz         yw)a  
    Split items into groups according to size(s).

    The number of elements for each group can vary.
    Note that if the values in `sizes` are the same, `separate()` can be a
    faster alternative.
    All and only the elements in `items` are ever yielded.

    Args:
        seq (Sequence): The input items.
        sizes (int|Sequence[int]): The size(s) of each group.
            If Sequence, each group has the number of elements specified.
            If int, all groups have the same number of elements.
            The last group will have the remaining items (if any).
        slices (bool): Yield the slices.
            If True, yield the slices that would split the input.
            Otherwise, yield the splitted input.

    Yields:
        Sequence: The items/slices from the grouping.
            Its container matches the one of `items`.

    Examples:
        >>> l = list(range(10))
        >>> tuple(split(l, 4))
        ([0, 1, 2, 3], [4, 5, 6, 7], [8, 9])
        >>> tuple(split(l, (2, 3)))
        ([0, 1], [2, 3, 4], [5, 6, 7, 8, 9])
        >>> tuple(split(l, (2, 4, 1)))
        ([0, 1], [2, 3, 4, 5], [6], [7, 8, 9])
        >>> tuple(split(l, (2, 4, 1, 20)))
        ([0, 1], [2, 3, 4, 5], [6], [7, 8, 9])
        >>> tuple(split(tuple(l), 4))
        ((0, 1, 2, 3), (4, 5, 6, 7), (8, 9))
        >>> tuple(split(tuple(l), 4, True))
        (slice(0, 4, None), slice(4, 8, None), slice(8, 10, None))
        >>> tuple(split(tuple(l), 2)) == tuple(separate(l, 2))
        True

    See Also:
        - flyingcircus.slide()
        - flyingcircus.sliding()
        - flyingcircus.separate()
        - flyingcircus.chunks()
        - flyingcircus.split_by()
        - flyingcircus.group_by()
        - flyingcircus.regroup_by()
    Nr<   r   r/   )
r  rY   r  r   sumr   r   
accumulater{  r  )r   sizesslicesr  rc  r;  rM   s          ru   splitrZ    s     h %E3s8u#45CI
5zYcr
5--e455DE
e*q.Cs 	0Aa%A,//	0 s 	-AeAhuQU|,,	-s   CCc              #     K   t         j                  g dt         j                  g dt        g di}t	        |      }||v r	||   d   }nt        dj                  |            t        d|      }t         |t        |       |z              }|rPdt        |       |z  cxk  r|dz  k  r6n n3t        |       |z  dz   }	t        |        |z  }
|f|	|
z
  z  |dz
  f|
z  z   }t        | ||      D ]  }|  y	w)
a^
  
    Split items into groups according to the number desired.

    If the number of items does not allow groups (chunks) of the same size,
    the chunks are determined depending on the values of `balanced`.
    All and only the elements in `items` are ever yielded.

    Args:
        seq (Sequence): The input items.
        n (int): Approximate number of chunks.
            The exact number depends on the value of `mode`.
        mode (str|int): Determine which approximation to use.
            If str, valid inputs are:
             - 'upper', '+': at most `n` chunks are generated.
             - 'lower', '-': at least `n` chunks are generated.
             - 'closest', '~': the number of chunks is `n` or `n + 1`
               depending on which gives the most evenly distributed chunks
               sizes.
            If int, valid inputs are `+1`, `0` and `-1`, mapping to 'upper',
            'closest' and 'lower' respectively.
        balanced (bool): Produce balanced chunks.
            If True, the size of any two chunks is not larger than one.
            Otherwise, the first chunks except the last have the same size.
            This has no effect if the number of items is a multiple of `n`.
        slices (bool): Yield the slices.
            If True, yield the slices that would split the input.
            Otherwise, yield the splitted input.

    Yields:
        Sequence: The items/slices from the grouping.
            Its container matches the one of `items`.

    Examples:
        >>> l = list(range(10))
        >>> tuple(chunks(l, 5))
        ([0, 1], [2, 3], [4, 5], [6, 7], [8, 9])
        >>> tuple(chunks(l, 2))
        ([0, 1, 2, 3, 4], [5, 6, 7, 8, 9])
        >>> tuple(chunks(l, 3))
        ([0, 1, 2, 3], [4, 5, 6], [7, 8, 9])
        >>> tuple(chunks(l, 4))
        ([0, 1, 2], [3, 4, 5], [6, 7], [8, 9])
        >>> tuple(chunks(l, -1))
        ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],)
        >>> tuple(chunks(l, 3, balanced=False))
        ([0, 1, 2, 3], [4, 5, 6, 7], [8, 9])
        >>> tuple(chunks(l, 3, '-'))
        ([0, 1, 2], [3, 4, 5], [6, 7], [8, 9])
        >>> tuple(chunks(l, 3, '-', False))
        ([0, 1, 2], [3, 4, 5], [6, 7, 8], [9])
        >>> tuple(chunks(list(range(10)), 3, '~'))
        ([0, 1, 2], [3, 4, 5], [6, 7], [8, 9])
        >>> tuple(chunks(list(range(10)), 3, '~', False))
        ([0, 1, 2], [3, 4, 5], [6, 7, 8], [9])

    See Also:
        - flyingcircus.slide()
        - flyingcircus.sliding()
        - flyingcircus.separate()
        - flyingcircus.split()
        - flyingcircus.split_by()
        - flyingcircus.group_by()
        - flyingcircus.regroup_by()
    )upper+r/   )lower-r<   )closest~r   r   Invalid mode `{mode}`r   r/   r9   N)mathceilfloorroundr\  r"  r  r   rY   r   rZ  )r   r2   r   balancedrY  reversed_modesmodesapproxr	  r'   rQ   groups               ru   chunksrm    s     N 			$

&"$N !0Eu}tQ077T7BCCAq	Avc#hl#$DAC444194Hq XIw!a% D1H;?2sD&) s   C4C6c              #   N  K   t        |       }	 t        |      }t        |      }|r ||      n|}dx}}t        |d      D ]0  \  }}|r ||      n|}	||	k7  s|rt	        ||      n| ||  |	}|}2 ||k\  r|rt	        ||dz         n| ||dz     yy# t
        $ r Y yw xY ww)a  
    Split consecutive items into groups by some criterion.

    Args:
        seq (Sequence): The input items.
        key (callable|None): Splitting criterion.
            Must have the following signature: key(Any): Any
            Each element of the sequence is given as input.
            If None, the element itself is used.
        slices (bool): Yield the slices.
            If True, yield the slices that would split the input.
            Otherwise, yield the splitted input.

    Yields:
        Sequence: The items/slices from the grouping.
            Its container matches the one of `items`.

    Examples:
        >>> items = [1, 1, 1, 2, 2, 2, 3, 3, 3]
        >>> print(list(split_by(items)))
        [[1, 1, 1], [2, 2, 2], [3, 3, 3]]

        >>> items = [0, 0, 2, 2, 2, 1, 1, 3, 3, 3]
        >>> print(list(split_by(items)))
        [[0, 0], [2, 2, 2], [1, 1], [3, 3, 3]]

        >>> items = [0, 0, 2, 2, 2, 1, 1, 3, 3, 3]
        >>> print(list(split_by(items, lambda x: x % 2)))
        [[0, 0, 2, 2, 2], [1, 1, 3, 3, 3]]

    See Also:
        - flyingcircus.split()
        - flyingcircus.chunks()
        - flyingcircus.group_by()
        - flyingcircus.regroup_by()
    r   r/   N)r   r   r   r  r  r  )
r   r  rY  r   r   callable_keyrg  rM   r  currents
             ru   split_byrq  =  s     P IE>E{  }(s4yd	A * 	GAt#/c$iTGw%+eAqkQq9	 6%+%1q5/Qq1u=   s-   B%B >B%=B%	B"B%!B""B%c              #     K   t        |       } 	 t        |       }t        |      }|r ||      n|}|g}| D ]0  }|r ||      n|}||k(  r|j                  |       &||f |}|g}2 |r||f yy# t        $ r Y yw xY ww)a-  
    Split consecutive items into groups by some criterion.

    This similar to `flyingcircus.split_by()` except that it also
    yields the value that determines the splitting.

    Args:
        items (Iterable): The input items.
        key (callable|None): Splitting criterion.
            Must have the following signature: key(Any): Any
            Each element of the sequence is given as input.
            If None, the element itself is used.

    Yields:
        tuple[Any, list]: The grouping item and the groups.

    Examples:
        >>> items = [1, 1, 1, 2, 2, 2, 3, 3, 3]
        >>> print(list(group_by(items)))
        [(1, [1, 1, 1]), (2, [2, 2, 2]), (3, [3, 3, 3])]

        >>> items = [0, 0, 2, 2, 2, 1, 1, 3, 3, 3]
        >>> print(list(group_by(items)))
        [(0, [0, 0]), (2, [2, 2, 2]), (1, [1, 1]), (3, [3, 3, 3])]

        >>> items = [0, 0, 2, 2, 2, 1, 1, 3, 3, 3]
        >>> print(list(group_by(items, lambda x: x % 2)))
        [(0, [0, 0, 2, 2, 2]), (1, [1, 1, 3, 3, 3])]

        >>> items = (0, 0, 2, 2, 2, 1, 1, 3, 3, 3)
        >>> print(list(group_by(items, lambda x: x % 2)))
        [(0, [0, 0, 2, 2, 2]), (1, [1, 1, 3, 3, 3])]

    See Also:
        - flyingcircus.split()
        - flyingcircus.chunks()
        - flyingcircus.split_by()
        - flyingcircus.regroup_by()
    N)r   r   r   r   r  )r   r  r   ro  rg  rl  rp  s          ru   group_byrs  ~  s     T KEE{  }(s4yd 	D#/c$iTGwT"Ek!	 +   s(   BA3 AB3	A?<B>A??Bc                     i }t        |      }| D ]+  }|r ||      n|}||vrg ||<   ||   j                  |       - |S )a  
    Split items into groups by some criterion.

    The items are not required to be consecutive.
    For splitting consecutive items use `flyingcircus.split_by()`
    or `flyingcircus.group_by()`.

    Args:
        items (Iterable): The input items.
        key (callable|None): Splitting criterion.
            Must have the following signature: key(Any): Any
            Each element of the sequence is given as input.
            If None, the element itself is used.

    Returns:
        dict: Contains the criterion and a list of the grouped items.

    Examples:
        >>> items = [1, 2, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3]
        >>> print(regroup_by(items))
        {1: [1, 1, 1, 1], 2: [2, 2, 2, 2], 3: [3, 3, 3, 3]}

        >>> items = [1, 2, 3, 0, 3, 1, 2, 0, 0, 2, 1, 3]
        >>> print(regroup_by(items, lambda x: x % 2))
        {1: [1, 3, 3, 1, 1, 3], 0: [2, 0, 2, 0, 0, 2]}

        >>> items = range(16)
        >>> print(regroup_by(items, lambda x: x % 2))
        {0: [0, 2, 4, 6, 8, 10, 12, 14], 1: [1, 3, 5, 7, 9, 11, 13, 15]}

    See Also:
        - flyingcircus.split()
        - flyingcircus.chunks()
        - flyingcircus.split_by()
        - flyingcircus.group_by()
    )r   r   )r   r  r   ro  r   	key_values         ru   
regroup_byrv    s[    l FC=L '!-CI4	F" "F9y  &	'
 Mrw   c              #     K   t        |       }||k  ry||4 ||g|z        }| t        |dz
        D ]  } ||| |   |      }|   || d|       }| t        d||z
  dz         D ]   } ||| ||z   dz
     | |dz
     |      }| " |)t        ||z
  |      D ]  } |||| |dz
           }|  yy|8 t        |       |g|z        }t        |      D ]  } |||d | d| z           t        ||z
  dz         D ]  } || |||z            |.t        d|dz         D ]  } || ||z
  |z   d d| z           yyw)a  
    Compute a rolling function on a sequence.

    The function is expressed in terms of an initialization and an update.

    The following pairs can be used for notable rolling computations:
     - rolling mean
       - func: mean
       - update: lambda x, a, b, n: x + a / n - b / n

    Args:
        seq (Sequence[Any]): The input sequence.
        size (int): The rolling window.
        func (callable): The function to compute.
            Must have the following signature: func(Sequence): Any
            If `update` is a callable provided, this serves as initialization.
        update (callable|None): The updating function.
            Must have the following signature:
            func(last_value, new_item, old_item, size): Any
        fill (Any|None): The filling value.
            If None, the rolling starts and stops at the edges of the sequence.
            Otherwise, the fill value is used to compute partial windows
            at the edges.

    Yields:
        value: The next rolling value.

    Examples:
        >>> print(list(rolling(range(16), 2, sum)))
        [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
        >>> print(list(rolling(range(16), 3, sum)))
        [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42]
        >>> print(list(rolling(range(16), 4, sum)))
        [6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54]

        >>> print(list(rolling([1] * 16, 4, sum)))
        [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
        >>> print(list(rolling([1] * 16, 4, sum, fill=0)))
        [0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0]
        >>> print(list(rolling([1] * 16, 4, sum, fill=1)))
        [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
        >>> print(list(rolling([1] * 8, 8, sum, fill=0)))
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        >>> print(list(rolling([1] * 8, 8, sum, fill=1)))
        [8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]

        >>> print(list(rolling([1] * 8, 8, sum)))
        [8]
        >>> print(list(rolling([1] * 8, 9, sum)))
        []
        >>> print(list(rolling([1] * 8, 9, sum, fill=0)))
        []

        >>> def sum_update(x, a, b, n): return x + a - b
        >>> print(list(rolling(range(16), 2, sum)))
        [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
        >>> print(list(rolling(range(16), 2, sum, sum_update)))
        [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]

        >>> def mean_update(x, a, b, n): return x + a / n - b / n
        >>> print(list(rolling(range(12), 2, mean)))
        [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5]
        >>> print(list(rolling(range(12), 2, mean, mean_update)))
        [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5]

        >>> def prod_update(x, a, b, n): return x * a / b
        >>> print(list(rolling(range(1, 13), 3, prod)))
        [6, 24, 60, 120, 210, 336, 504, 720, 990, 1320]
        >>> print(list(rolling(range(1, 13), 3, prod, prod_update)))
        [6, 24.0, 60.0, 120.0, 210.0, 336.0, 504.0, 720.0, 990.0, 1320.0]

        >>> def hash_func(seq): return sum(hash(x) for x in seq)
        >>> def hash_update(x, a, b, n): return x + hash(a) - hash(b)
        >>> print(list(rolling(range(12), 3, hash_func)))
        [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
        >>> print(list(rolling(range(12), 3, hash_func, hash_update)))
        [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

    See Also:
        - flyingcircus.slide()
        - flyingcircus.sliding()
    Nr/   )r   r{  r   )	r   r	  r   r  r   r2   r[  rM   buffers	            ru   rollingry    s    p 	CA4x$$'EK4!8_ uc!fd3 S$Z q!d(Q,' 	A5#a$hl"3SQZFEK	 1t8Q' udCAJ7 
 T#Yv}-F4[ 16!":BQ/001q4x!|$ 	(As1QX''	(1dQh' <3q4x!|}-r
:;;< s   EEc              #   v   K   t         |      }t               }|r
||kD  s|dk  ryt        t        |            } | fd|D               	 t        |dz
  dd      D ]  }||   ||z   |z
  k7  s n y||xx   dz  cc<   t        |dz   |      D ]  }||dz
     dz   ||<     | fd|D               iw)a  
    Generate all possible k-combinations of given items.

    This is similar to `itertools.combinations()`.
    The `itertools` version should be preferred to this function.

    Args:
        seq (Sequence): The input items.
        k (int): The number of items to select.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        result (Sequence): The next output elements.

    Examples:
        >>> list(combinations(range(3), 2))
        [(0, 1), (0, 2), (1, 2)]

        >>> list(combinations(range(4), 0))
        []
        >>> list(combinations(range(4), 1))
        [(0,), (1,), (2,), (3,)]
        >>> list(combinations(range(4), 2))
        [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
        >>> list(combinations(range(4), 3))
        [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]
        >>> list(combinations(range(4), 4))
        [(0, 1, 2, 3)]
        >>> list(combinations(range(4), 5))
        []

        >>> list(combinations([], 0))
        []
        >>> list(combinations([], 1))
        []

        >>> list(combinations(range(2), 2))
        [(0, 1)]

        >>> list(combinations(tuple(range(3)), 2))
        [(0, 1), (0, 2), (1, 2)]
        >>> list(combinations(list(range(3)), 2))
        [[0, 1], [0, 2], [1, 2]]
        >>> list(combinations(range(3), 2))
        [(0, 1), (0, 2), (1, 2)]
        >>> list(combinations('abc', 2))
        ['ab', 'ac', 'bc']
        >>> list(combinations(b'abc', 2))
        [b'ab', b'ac', b'bc']

        >>> p1 = sorted(combinations(range(10), 3))
        >>> p2 = sorted(itertools.combinations(range(10), 3))
        >>> p1 == p2
        True

    See Also:
        - flyingcircus.multi_combinations()
        - flyingcircus.permutations()
        - flyingcircus.multi_permutations()
        - flyingcircus.cyclic_permutations()
        - flyingcircus.unique_permutations()
        - flyingcircus.cartesian_product()
    r/   Nc              3   (   K   | ]	  }|     y wr   r   r   rM   r   s     ru   r   zcombinations.<locals>.<genexpr>       ,qCF,   r<   c              3   (   K   | ]	  }|     y wr   r   r|  s     ru   r   zcombinations.<locals>.<genexpr>       01A0r~  r   r   r  r{  r   r'   r   r;  r'  rM   r  s   `      ru   r  r  w  s     J !i0I
c(C!c'QU58nG
,G,
,,
q1ub"% 	AqzQWq[(	 
a
q1ua 	,A Q!+GAJ	,0000 s   A.B92AB9c              #   
   K   t         |      }t               }|r|sydg|z  } | fd|D               	 t        |dz
  dd      D ]  }||   |dz
  k7  s n y||   dz   g||z
  z  ||d  | fd|D               Iw)aQ  
    Generate all possible k-multi-combinations of given items.

    These are also called combinations with repetitions.

    This is similar to `itertools.combinations_with_replacement()`.
    The `itertools` version should be preferred to this function.

    Args:
        seq (Sequence): The input items.
        k (int): The number of items to select.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        result (Sequence): The next output elements.

    Examples:
        >>> list(multi_combinations(range(2), 0))
        []
        >>> list(multi_combinations(range(2), 1))
        [(0,), (1,)]
        >>> list(multi_combinations(range(2), 2))
        [(0, 0), (0, 1), (1, 1)]
        >>> list(multi_combinations(range(2), 3))
        [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)]
        >>> list(multi_combinations(range(2), 4))
        [(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 1), (0, 1, 1, 1), (1, 1, 1, 1)]

        >>> list(multi_combinations([], 0))
        []
        >>> list(multi_combinations([], 1))
        []

        >>> list(multi_combinations(tuple(range(3)), 2))
        [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]
        >>> list(multi_combinations(list(range(3)), 2))
        [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
        >>> list(multi_combinations(range(3), 2))
        [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]
        >>> list(multi_combinations('abc', 2))
        ['aa', 'ab', 'ac', 'bb', 'bc', 'cc']
        >>> list(multi_combinations(b'abc', 2))
        [b'aa', b'ab', b'ac', b'bb', b'bc', b'cc']

        >>> p1 = sorted(multi_combinations(range(10), 3))
        >>> p2 = sorted(itertools.combinations_with_replacement(range(10), 3))
        >>> p1 == p2
        True

    See Also:
        - flyingcircus.combinations()
        - flyingcircus.permutations()
        - flyingcircus.multi_permutations()
        - flyingcircus.cyclic_permutations()
        - flyingcircus.unique_permutations()
        - flyingcircus.cartesian_product()
    Nr   c              3   (   K   | ]	  }|     y wr   r   r|  s     ru   r   z%multi_combinations.<locals>.<genexpr>  r}  r~  r/   r<   c              3   (   K   | ]	  }|     y wr   r   r|  s     ru   r   z%multi_combinations.<locals>.<genexpr>  r  r~  r   r   r{  )r   r'   r   r;  r'  rM   s   `     ru   multi_combinationsr    s     ~ !i0I
c(CacAgG
,G,
,,
q1ub"% 	AqzS1W$	 qzA~&!a%00000 s   AB*Bc              #      K   t         |      }t               }||n|}||kD  s
|dk  s|dk  ryt        t        |            }t        t        |||z
  d            } | fd|d| D               |r{t        |dz
  dd      D ]c  }||xx   dz  cc<   ||   dk(  r||dz   d |||dz    z   ||d ||z
  ||<   5||   }||    ||   c||<   || <    | fd|d| D                n y|rzyyw)a/  
    Generate all possible k-permutations of given items.

    If k is smaller than the number of input items, generates partial
    permutations.

    This is similar to `itertools.permutations()`.
    The `itertools` version should be preferred to this function.

    Args:
        seq (Sequence): The input items.
        k (int|None): The number of items to select.
            If k is None, all permutations are generated.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        result (Sequence): The next output elements.

    Examples:
        >>> list(permutations(range(3), 0))
        []
        >>> list(permutations(range(3), 1))
        [(0,), (1,), (2,)]
        >>> list(permutations(range(3), 2))
        [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
        >>> list(permutations(range(3), 3))
        [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
        >>> list(permutations(range(3), 4))
        []

        >>> list(permutations([], 0))
        []
        >>> list(permutations([], 1))
        []

        >>> list(permutations(tuple(range(3)), 2))
        [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
        >>> list(permutations(list(range(3)), 2))
        [[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]]
        >>> list(permutations(range(3), 2))
        [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
        >>> list(permutations('abc', 2))
        ['ab', 'ac', 'ba', 'bc', 'ca', 'cb']
        >>> list(permutations(b'abc', 2))
        [b'ab', b'ac', b'ba', b'bc', b'ca', b'cb']

        >>> p1 = sorted(permutations(range(10), 3))
        >>> p2 = sorted(itertools.permutations(range(10), 3))
        >>> p1 == p2
        True

    See Also:
        - flyingcircus.combinations()
        - flyingcircus.multi_combinations()
        - flyingcircus.multi_permutations()
        - flyingcircus.cyclic_permutations()
        - flyingcircus.unique_permutations()
        - flyingcircus.cartesian_product()
    Nr/   r<   c              3   (   K   | ]	  }|     y wr   r   r|  s     ru   r   zpermutations.<locals>.<genexpr>g  s     0qCF0r~  r   c              3   (   K   | ]	  }|     y wr   r   r|  s     ru   r   zpermutations.<locals>.<genexpr>q  s     <1A<r~  r  )r   r'   r   r;  r'  cyclesrM   r  s   `       ru   r  r    s2    B !i0I
c(CyaA3w!a%375:G%S1Wb)*F
0GBQK0
00
q1ub"% 	A1INIayA~%a!efo!a%0@@!Gq	1I*11"+wqz'
GQBK<<<<	  s   C,C20C2c              #   2   K   t         |      }t               }|r|dk  rydg|z  } | fd|D               	 t        |dz
  dd      D ]%  }||   |dz
  k7  r nt        ||      D ]  }d||<   	 ' y||xx   dz  cc<    | fd|D               Zw)at  
    Generate all possible k-multi-permutations of given items.

    These are also called permutations with repetitions or k-cartesian product.

    This is similar to `itertools.product()`.
    The `itertools` version should be preferred to this function.

    Args:
        seq (Sequence): The input items.
        k (int|None): The number of items to select.
            If k is None, all permutations are generated.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        result (Sequence): The next output elements.

    Examples:
        >>> list(multi_permutations(range(2), 0))
        []
        >>> list(multi_permutations(range(2), 1))
        [(0,), (1,)]
        >>> list(multi_permutations(range(2), 2))
        [(0, 0), (0, 1), (1, 0), (1, 1)]
        >>> list(multi_permutations(range(2), 3))
        [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]

        >>> list(multi_permutations([], 0))
        []
        >>> list(multi_permutations([], 1))
        []

        >>> list(multi_permutations(tuple(range(3)), 2))
        [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
        >>> list(multi_permutations(list(range(3)), 2))
        [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
        >>> list(multi_permutations(range(4), 1))
        [(0,), (1,), (2,), (3,)]
        >>> list(multi_permutations('abc', 2))
        ['aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']
        >>> list(multi_permutations(b'abc', 2))
        [b'aa', b'ab', b'ac', b'ba', b'bb', b'bc', b'ca', b'cb', b'cc']

        >>> p1 = sorted(multi_permutations(range(10), 3))
        >>> p2 = sorted(itertools.product(range(10), repeat=3))
        >>> p1 == p2
        True

    See Also:
        - flyingcircus.combinations()
        - flyingcircus.multi_combinations()
        - flyingcircus.permutations()
        - flyingcircus.cyclic_permutations()
        - flyingcircus.unique_permutations()
        - flyingcircus.cartesian_product()
    r/   Nr   c              3   (   K   | ]	  }|     y wr   r   r|  s     ru   r   z%multi_permutations.<locals>.<genexpr>  r}  r~  r<   c              3   (   K   | ]	  }|     y wr   r   r|  s     ru   r   z%multi_permutations.<locals>.<genexpr>  r  r~  r  r  s   `      ru   multi_permutationsr  x  s     B !i0I
c(C!a%cAgG
,G,
,,
q1ub"% 	AqzS1W$q! #A!"GAJ#		 
a
0000 s   BBc              #   p   K   |rdnd}t        t        |             D ]  }| ||z  d | d||z   z     yw)a  
    Generate cyclic permutations of given items.

    Args:
        seq (Sequence): The input items.
        forward (bool): Determine how to advance through permutations.

    Yields:
        result (Sequence): The next output elements.

    See Also:
        - flyingcircus.combinations()
        - flyingcircus.multi_combinations()
        - flyingcircus.permutations()
        - flyingcircus.multi_permutations()
        - flyingcircus.unique_permutations()
        - flyingcircus.cartesian_product()

    Examples:
        >>> list(cyclic_permutations(tuple(range(4))))
        [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)]
        >>> list(cyclic_permutations(list(range(4))))
        [[0, 1, 2, 3], [1, 2, 3, 0], [2, 3, 0, 1], [3, 0, 1, 2]]

        >>> list(cyclic_permutations(list(range(3)), True))
        [[0, 1, 2], [1, 2, 0], [2, 0, 1]]
        >>> list(cyclic_permutations(list(range(3)), False))
        [[0, 1, 2], [2, 0, 1], [1, 2, 0]]

        >>> list(cyclic_permutations('abcdef', True))
        ['abcdef', 'bcdefa', 'cdefab', 'defabc', 'efabcd', 'fabcde']
        >>> list(cyclic_permutations('abcdef', False))
        ['abcdef', 'fabcde', 'efabcd', 'defabc', 'cdefab', 'bcdefa']
        >>> list(cyclic_permutations(b'abcdef', True))
        [b'abcdef', b'bcdefa', b'cdefab', b'defabc', b'efabcd', b'fabcde']
        >>> list(cyclic_permutations(b'abcdef', False))
        [b'abcdef', b'fabcde', b'efabcd', b'defabc', b'cdefab', b'bcdefa']
    r/   r<   N)r{  r   )r   forwardsignrM   s       ru   cyclic_permutationsr    sH     R 1RD3s8_ .$()ns9D1H~--.s   46c              #   &  K   t        | |      }t        t        |       dz
  dd      }t        |       } 	  ||        |dd D ]  }| |   | |dz      k  s n y| |   }|D ]  }|| |   k  s n d}| |   | |   c| |<   | |<   | d|d   | |dz   d ]w)a*  
    Generate unique permutations of items in an efficient way.

    If items does not contain repeating elements, this is equivalent to
    `flyingcircus.permutations()`.

    Args:
        seq (Sequence): The input items.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        result (Sequence): The next output elements.

    Examples:
        >>> list(unique_permutations([0, 0, 0]))
        [[0, 0, 0]]
        >>> list(itertools.permutations([0, 0, 0]))
        [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)]

        >>> list(unique_permutations([0, 0, 2]))
        [[0, 0, 2], [0, 2, 0], [2, 0, 0]]
        >>> list(itertools.permutations([0, 0, 2]))
        [(0, 0, 2), (0, 2, 0), (0, 0, 2), (0, 2, 0), (2, 0, 0), (2, 0, 0)]

        >>> list(unique_permutations([0, 1, 2]))
        [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]
        >>> list(permutations([0, 1, 2]))
        [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]
        >>> list(itertools.permutations([0, 1, 2]))
        [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]

        >>> list(unique_permutations([]))
        [[]]

        >>> list(unique_permutations('aabb'))
        ['aabb', 'abab', 'abba', 'baab', 'baba', 'bbaa']
        >>> list(unique_permutations(b'aabb'))
        [b'aabb', b'abab', b'abba', b'baab', b'baba', b'bbaa']

        >>> p1 = sorted(unique_permutations(tuple(range(8))))
        >>> p2 = sorted(itertools.permutations(tuple(range(8))))
        >>> p1 == p2
        True

    References:
        - Donald Knuth, The Art of Computer Programming, Volume 4, Fascicle
          2: Generating All Permutations.

    See Also:
        - flyingcircus.combinations()
        - flyingcircus.multi_combinations()
        - flyingcircus.permutations()
        - flyingcircus.multi_permutations()
        - flyingcircus.cyclic_permutations()
        - flyingcircus.cartesian_product()
    r/   r<   Nr   )r   r{  r   rl  )r   r   r`  r'   k_valrM   s         ru   unique_permutationsr    s     z !i0ICHqL"b)G
+C
n 	A1vAE
"	 A 	As1v~	 AQQAA"Qr'lAEF s   ABB,%B)r'   r   c              '     K   |D cg c]  }t        ||       }}t        |      st        n|d   }| dkD  r|| z  }t        |      }t	        t        |            }t	        t        |t	        t        t        |                        }d}dg|z  }	 |} t        |      D ]  \  }	\  }}
|| |
z     |||	z
  dz
  <   | |
z  }   | dkD  ry ||       |dz  }Dc c}w w)a!  
    Generate the cartesian product of the input sequences.

    This is similar to `itertools.product()`.
    The `itertools` version should be preferred to this function.

    Args:
        *seqs (Sequence[Sequence]): The input sequences.
        k (int): Repetition factor for the input sequences.
            The input sequences are repeated `k` times.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        result (Sequence): The next output elements.

    Examples:
        >>> sorted(cartesian_product([1, 2], [3, 4, 5]))
        [[1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5]]
        >>> sorted(cartesian_product((1, 2), (3, 4, 5)))
        [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)]
        >>> sorted(cartesian_product(range(2), range(3)))
        [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
        >>> sorted(cartesian_product('abc', 'ABC'))
        ['aA', 'aB', 'aC', 'bA', 'bB', 'bC', 'cA', 'cB', 'cC']
        >>> sorted(cartesian_product([1, 2], k=2))
        [[1, 1], [1, 2], [2, 1], [2, 2]]
        >>> sorted(cartesian_product('ab', k=3))
        ['aaa', 'aab', 'aba', 'abb', 'baa', 'bab', 'bba', 'bbb']
        >>> sorted(cartesian_product('ab', 'c', k=2))
        ['acac', 'acbc', 'bcac', 'bcbc']
        >>> sorted(cartesian_product('ab', 'cd', k=2))
        ['acac', 'acad', 'acbc', 'acbd', 'adac', 'adad', 'adbc', 'adbd', 'bcac', 'bcad', 'bcbc', 'bcbd', 'bdac', 'bdad', 'bdbc', 'bdbd']

    See Also:
        - flyingcircus.combinations()
        - flyingcircus.multi_combinations()
        - flyingcircus.permutations()
        - flyingcircus.multi_permutations()
        - flyingcircus.cyclic_permutations()
        - flyingcircus.unique_permutations()
    r   r/   N)	r   r  r   r   r  r  r  r   r  )r'   r   seqsr   
containersr2   r_seqsrM   r   r  r0   s              ru   cartesian_productr  N  s     ` ?CCs"3	2CJC&z2
1I1uax` 	D	A(4.!FFDS&!1234D	AVaZF
$T? 	KAxQ #AE
F1q519!GA	 q5F##FA q Ds   CCB?Cc           	   #   x   K   |t               }t        |      r|t        t        t        fv rt
        }	  |        t               t        fdt        j                  t        d      |dz
        D              }|D ]   | fdt        |      D               ! y# t        $ r	 t
        }Y vw xY ww)aH  
    Generate all k-partitions for the items.

    Args:
        seq (Sequence): The input items.
        k (int): The number of splitting partitions.
            Each group has exactly `k` elements.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        partition (Sequence[Sequence]]): The grouped items.
            Each partition contains `k` grouped items from the source.

    Examples:
        >>> items = tuple(range(3))
        >>> tuple(partitions(items, 2))
        (((0,), (1, 2)), ((0, 1), (2,)))
        >>> tuple(partitions(items, 3))
        (((0,), (1,), (2,)),)

        >>> items = 'abc'
        >>> tuple(partitions(items, 2))
        (('a', 'bc'), ('ab', 'c'))
        >>> tuple(partitions(items, 3))
        (('a', 'b', 'c'),)

        >>> items = range(3)
        >>> tuple(partitions(items, 2))
        ((range(0, 1), range(1, 3)), (range(0, 2), range(2, 3)))
        >>> tuple(partitions(items, 3))
        ((range(0, 1), range(1, 2), range(2, 3)),)

        >>> tuple(partitions(tuple(range(4)), 3))
        (((0,), (1,), (2, 3)), ((0,), (1, 2), (3,)), ((0, 1), (2,), (3,)))
        >>> tuple(partitions(list(range(4)), 3))
        ([[0], [1], [2, 3]], [[0], [1, 2], [3]], [[0, 1], [2], [3]])
    Nc              3   B   K   | ]  }d t        |      z   fz     yw)rU  Nr   )r   rc  r;  s     ru   r   zpartitions.<locals>.<genexpr>  s+      C 	uU|sf$Cr  r/   c              3   :   K   | ]  }|   |d z         ywr  r   )r   rM   rc  r   s     ru   r   zpartitions.<locals>.<genexpr>  s$     EqE!HU1q5\2E   )r   r   ra   r   r   r   r   r   r   r  r{  )r   r'   r   r`  rc  r;  s   `   @@ru   
partitionsr    s     V I	I)UI/F"F	# c(C C++E!SM1q5AC CG  FEE!HEEEF  	s)   2B:B%  A%B:%B74B:6B77B:c           	   #     K   |t        |       }t        |      st        }|rt        |       }t	        |      D ]  \  }}t        t        ||                 t        t        j                  t        j                  | |            }t        |       t        j                  ||      D ]  } ||        y| D 	cg c]  }	t        t        |	             }
}	t        |
      }	 t        j                  t        |      t        ||            D ]?  }g }|
D ]  }|j                  ||z         ||z  }  |d t!        ||       D               A yc c}	w # t"        $ r t%               }t        |      t        ||      k  r:|j'                  t        d |
D                     t        |      t        ||      k  r:t        |      }t        |       t        j                  ||      D ]  } |d t!        ||       D                Y yw xY ww)a  
    Obtain a number of random unique combinations of a sequence of sequences.

    Args:
        seq (Sequence[Sequence]): The input sequence of sequences.
        k (int): The number of random unique combinations to obtain.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.
        pseudo (bool): Generate random combinations somewhat less randomly.
            If True, the memory requirements for intermediate steps will
            be significantly lower (but still all `k` items are required to
            fit in memory).

    Yields:
        combination (Sequence): The next random unique combination.

    Examples:
        >>> import string
        >>> max_lens = list(range(2, 10))
        >>> items = tuple(
        ...     string.ascii_lowercase[:max_len] for max_len in max_lens)
        >>> random.seed(0)
        >>> num = 10
        >>> for i in random_unique_combinations_k(items, num):
        ...     print(i)
        ('b', 'a', 'd', 'a', 'd', 'a', 'a', 'f')
        ('a', 'a', 'c', 'c', 'b', 'f', 'd', 'f')
        ('b', 'b', 'b', 'e', 'c', 'b', 'e', 'a')
        ('a', 'b', 'a', 'b', 'd', 'g', 'c', 'd')
        ('b', 'c', 'd', 'd', 'b', 'b', 'f', 'g')
        ('a', 'a', 'b', 'a', 'f', 'd', 'c', 'g')
        ('a', 'c', 'd', 'a', 'f', 'a', 'c', 'f')
        ('b', 'c', 'd', 'a', 'f', 'd', 'h', 'd')
        ('a', 'c', 'b', 'b', 'a', 'e', 'b', 'g')
        ('a', 'c', 'c', 'b', 'e', 'b', 'f', 'e')
        >>> max_lens = list(range(2, 4))
        >>> items = tuple(
        ...     string.ascii_uppercase[:max_len] for max_len in max_lens)
        >>> random.seed(0)
        >>> num = 10
        >>> for i in random_unique_combinations_k(items, num):
        ...     print(i)
        ('B', 'B')
        ('B', 'C')
        ('A', 'A')
        ('B', 'A')
        ('A', 'B')
        ('A', 'C')
    Nc              3   ,   K   | ]  \  }}||     y wr   r   r   rM   r   s      ru   r   z/random_unique_combinations_k.<locals>.<genexpr>M  s     IGAtQI   c              3   2   K   | ]  }t        |        y wr   )r  )r   r   s     ru   r   z/random_unique_combinations_k.<locals>.<genexpr>V  s     F'*W-Fs   c              3   ,   K   | ]  \  }}||     y wr   r   r  s      ru   r   z/random_unique_combinations_k.<locals>.<genexpr>\  s     LGAtQLr  )r   r   r   r  r  r  r   rE  productr   prodrandomsampler{  minr   r  OverflowErrorrj  r6  )r   r'   r   pseudo	comb_gensr;  comb_gencombscombinationr   max_lensmax_kr`  r   index_combs
index_combs                   ru   random_unique_combinations_kr    s    n I	I	I	&y1 	*MCD3()	* Y%%i&7&7&CQGH$++E15 	)KK((	) 144CT
O44X	M}}U5\3q%=A J' )GNN3=1.C)  Is7C7HIIIJ 5  	M%K k"SE]2FXFFH k"SE]2
 {+KK '..{A> M
Ls:s7KLLLM	Ms?   B:H<EH%A+E HA$H
;AH
H	H

Hc              #      K   |t        |       }t        |      st        }t        |       D ]  } |t	         ||      |              yw)a  
    Generate all k-partitions for all unique permutations of the items.

    Args:
        seq (Sequence): The input items.
        k (int): The number of splitting partitions.
            Each group has exactly `k` elements.
        container (callable|None): The container for the result.
            If None, this is inferred from `seq` if possible, otherwise
            uses `tuple`.

    Yields:
        partitions (Sequence[Sequence[Sequence]]]): The items partitions.
            More precisely, all partitions of size `num` for each unique
            permutations of `items`.

    Examples:
        >>> list(unique_partitions([0, 1], 2))
        [[[[0], [1]]], [[[1], [0]]]]
        >>> tuple(unique_partitions((0, 1), 2))
        ((((0,), (1,)),), (((1,), (0,)),))
    N)r   r   r   r  r  )r   r'   r   permss       ru   unique_partitionsr  `  sO     4 I	I	$S) 9
9U#3Q7889s   A
Ac                 r   |rt        t        | |            }ng }|st        t        |             } t        |       D ]f  }d}t	        |      D ]@  \  }}|D 	cg c]  }	|	|   	 c}	|gz   }
t        t        |
            t        |
      k  s>d} n |sV|j                  |       h |rt        |       |S c c}	w )a	  
    Generate a latin square.

    Args:
        seq (Sequence): The input items.
        randomize (bool): Shuffle the output "rows".
        cyclic (bool): Generate cyclic permutations only.
        forward (bool): Determine how to advance through permutations.
            Note that for cyclic permutations (`cyclic == True`), this means
            that the cycling is moving forward (or not),
            whereas for non-cyclic permutations (`cyclic == False`), this is
            equivalent to reversing the items order prior to generating the
            permutations, i.e.
            `permutations(reversed(items)) == reversed(permutations(items))`.

    Returns:
        result (list[Sequence]): The latin square.

    Examples:
        >>> random.seed(0)
        >>> latin_square(list(range(4)))
        [[2, 3, 0, 1], [3, 0, 1, 2], [1, 2, 3, 0], [0, 1, 2, 3]]

        >>> latin_square(list(range(4)), False, False, False)
        [[3, 2, 1, 0], [2, 3, 0, 1], [1, 0, 3, 2], [0, 1, 2, 3]]
        >>> latin_square(list(range(4)), False, True, False)
        [[0, 1, 2, 3], [3, 0, 1, 2], [2, 3, 0, 1], [1, 2, 3, 0]]
        >>> latin_square(list(range(4)), False, False, True)
        [[0, 1, 2, 3], [1, 0, 3, 2], [2, 3, 0, 1], [3, 2, 1, 0]]
        >>> latin_square(list(range(4)), False, True, True)
        [[0, 1, 2, 3], [1, 2, 3, 0], [2, 3, 0, 1], [3, 0, 1, 2]]

        >>> random.seed(0)
        >>> latin_square(list(range(4)), True, False, False)
        [[1, 0, 3, 2], [0, 1, 2, 3], [2, 3, 0, 1], [3, 2, 1, 0]]
        >>> random.seed(0)
        >>> latin_square(list(range(4)), True, True, False)
        [[2, 3, 0, 1], [1, 2, 3, 0], [3, 0, 1, 2], [0, 1, 2, 3]]
        >>> random.seed(0)
        >>> latin_square(list(range(4)), True, False, True)
        [[2, 3, 0, 1], [3, 2, 1, 0], [1, 0, 3, 2], [0, 1, 2, 3]]
        >>> random.seed(0)
        >>> latin_square(list(range(4)), True, True, True)
        [[2, 3, 0, 1], [3, 0, 1, 2], [1, 2, 3, 0], [0, 1, 2, 3]]

        >>> random.seed(0)
        >>> latin_square(tuple(range(4)))
        [(2, 3, 0, 1), (3, 0, 1, 2), (1, 2, 3, 0), (0, 1, 2, 3)]

        >>> random.seed(0)
        >>> latin_square('abcde')
        ['eabcd', 'abcde', 'deabc', 'bcdea', 'cdeab']
        >>> print('\n'.join(latin_square('0123456789')))
        7890123456
        8901234567
        5678901234
        9012345678
        3456789012
        4567890123
        1234567890
        2345678901
        0123456789
        6789012345
    TF)	r  r  r  r  r  r   rj  r   r  )r   r  cyclicr  r   elemsvalidrM   elemrH   orthogonalss              ru   latin_squarer    s    J )#w78x}%C!#& 	%EE$U+ 4-34qt4v=s;'(3{+;;!E	
 e$	% M 5s   B4c                     | |k  S r   r   rH   r7   s     ru   r  r    s
    Q!V rw   c                 j    t        t        || d            }|r|xs t        t        || d            }|S )a  
    Determine if an iterable is sorted or not.

    Args:
        items (Iterable): The input items.
        compare (callable): The comparison function.
            Must have the signature: compare(Any, Any): bool.
        both (bool): Compare the items both forward and backward.

    Returns:
        result (bool): The result of the comparison for all consecutive pairs.

    Examples:
        >>> items = list(range(10))
        >>> print(items)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> is_sorted(items)
        True
        >>> is_sorted(items[::-1])
        True
        >>> is_sorted(items[::-1], both=False)
        False
        >>> is_sorted(items[::-1], lambda x, y: x > y, both=False)
        True

        >>> items = [1] * 10
        >>> print(items)
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        >>> is_sorted(items)
        True
        >>> is_sorted(items, lambda x, y: x < y)
        False

        >>> is_sorted([0, 1, 2, 4, 4, 5, 6, 7, 8, 9])
        True
        >>> is_sorted([0, 1, 2, 5, 4, 5, 6, 7, 8, 9])
        False
        >>> is_sorted([])
        True
        >>> is_sorted(iter([0, 1, 2, 4, 4, 5, 6, 7, 8, 9]))
        True
        >>> is_sorted(iter([0, 1, 2, 5, 4, 5, 6, 7, 8, 9]))
        False

    See Also:
        - flyingcircus.pairwise_map()
    FT)r  rB  )r   comparebothr   s       ru   	is_sortedr    s8    f gue45FB3|GUDABMrw   c                     d}t        |       dz
  }d}||k  r2|s0||z   dz  }| |   |k(  r|}d}n|| |   k  r|dz
  }n|dz   }||k  r|s0|S )aY  
    Search for an item in a sorted sequence.

    If the sequence is not sorted the results are unpredictable.
    Internally, it uses a binary search, which is the theoretical optimal
    algorithm for searching sorted sequences.

    Note that if the item is present in the sequence, for built-in sequences,
    the `.index()` method may be faster.

    Args:
        seq (Sequence): The input sequence.
        item (Any): The item to search for.

    Returns:
        result (int): The matching index.
            If `item` is in `seq`, this is the index at which `item` is found,
            i.e. `item == seq[result]`
            Otherwise, this is the index at which inserting `item` in `seq`
            would result in a sorted sequence.

    Examples:
        >>> search_sorted(list(range(1000)), 500)
        500
        >>> search_sorted([x ** 2 for x in range(100)], 500)
        23

        >>> items = [x ** 2 for x in range(10)]
        >>> print(items)
        [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

        >>> value = 9
        >>> i = search_sorted(items, value)
        >>> print(items[i] == value)
        True
        >>> new_items = items[:i] + [value] + items[i:]
        >>> print(new_items)
        [0, 1, 4, 9, 9, 16, 25, 36, 49, 64, 81]
        >>> print(is_sorted(new_items))
        True

        >>> value = 5
        >>> i = search_sorted(items, value)
        >>> print(items[i] == value)
        False
        >>> new_items = items[:i] + [value] + items[i:]
        >>> print(new_items)
        [0, 1, 4, 5, 9, 16, 25, 36, 49, 64, 81]
        >>> print(is_sorted(new_items))
        True

        >>> all(search_sorted(items, x) == items.index(x) for x in items)
        True

        >>> search_sorted([], 'ciao')
        0
        >>> search_sorted(string.ascii_lowercase, 'x')
        23

        These fails because the input is not sorted!
        >>> items = [1, 4, 6, 8, 2, 3, 5]
        >>> value = 5
        >>> search_sorted(items, value) == items.index(value)
        False
        >>> search_sorted(string.ascii_letters, 'X')
        0

    See Also:
        - flyingcircus.is_sorted()
    r   r/   Fr9   Tr   )r   r   rf  rg  foundmidpoints         ru   search_sortedr    sy    R Es8a<DE
4-DLQ&x=D EEc(m#!| 1 4- Lrw   c              #     K   t        |       }t        |      }|dkD  r|dkD  rt        ||      }t        ||      }dg|z  }d}d}||k  r9||   ||   k(  r|dz  }|||<   |dz  }n|dk7  r	||dz
     }n
d||<   |dz  }||k  r9|}	d}|	|k  rT| |	   ||   k(  r
|	dz  }	|dz  }||k(  r|	|z
   ||dz
     }n#|	|k  r| |	   ||   k7  r|dk7  r	||dz
     }n|	dz  }	|	|k  rSyyyw)a  
    Find occurrences of a sub-sequence in a sequence.

    Args:
        seq (Sequence): The input sequence.
        subseq (Sequence): The input sub-sequence.
        first (int): The first index.
            The index is forced within boundaries.
        last (int): The last index (included).
            The index is forced within boundaries.

    Yields:
        result (int): The index of the next match.

    Examples:
        >>> list(find_subseq(list(range(10)), list(range(5, 7))))
        [5]
        >>> list(find_subseq(list(range(10)), []))
        []
        >>> list(find_subseq([], list(range(5, 7))))
        []
        >>> list(find_subseq([], []))
        []
        >>> list(find_subseq(list(range(10)), list(range(5, 12))))
        []
        >>> list(find_subseq(list(range(10)), list(range(-2, 5))))
        []
        >>> list(find_subseq(list(range(10)), list(range(-5, -1))))
        []
        >>> list(find_subseq(list(range(10)), list(range(11, 12))))
        []
        >>> list(find_subseq(list(range(10)), list(range(3, 8))))
        [3]
        >>> list(find_subseq(list(range(10)), list(range(5))))
        [0]
        >>> list(find_subseq(list(range(10)), list(range(5, 10))))
        [5]
        >>> list(find_subseq(list(range(10)) * 10, list(range(3, 8))))
        [3, 13, 23, 33, 43, 53, 63, 73, 83, 93]
        >>> list(find_subseq(list(range(10)) * 10, list(range(3, 8)), 10, 90))
        [13, 23, 33, 43, 53, 63, 73, 83]

        >>> seq = '12431243123431212'
        >>> list(find_subseq(seq, '1234'))
        [8]
        >>> list(find_subseq(seq, '12'))
        [0, 4, 8, 13, 15]
        >>> list(find_subseq(seq, '12')) == list(find_all(seq, '12'))
        True

    See Also:
        - flyingcircus.find_all()
    r   r/   N)r   r   )
r   subseqrf  rg  r2   r0   offsetsr  r'   rM   s
             ru   find_subseqr  r  sK    t 	CAFA1uQE1%4#. #'!eayF1I%Q
Q6AA!"GAJFA !e 4i1v"QQAv!eAENds1v26AAFA 4i. 	s   B C"AC"C"c                 b    |t        t        t                           }t        |      st        }t               t              st
        |sEt        t        j                  d  D cg c]  }t        |j                                c}            } | fd|D              S c c}w )a+  
    Convert tabular data from a Sequence of Mappings to a Mapping of Sequences.

    Args:
        data (Sequence[Mapping]): The input tabular data.
        labels (Sequence|None): The labels of the tabular data.
            If Sequence, all elements should be present as keys of the dicts.
            If the dicts contain keys not specified in `labels` they will be
            ignored.
            If None, the `labels` are guessed from the data.
        d_val (Any): The default value to use for incomplete dicts.
            This will be inserted in the lists to keep track of missing data.
        mapping_container (callable|None): The mapping container.
            If None, this is inferred from `data` if possible, otherwise
            uses `dict`.
        sequence_container (callable|None): The sequence container.
            If None, this is inferred from `data` if possible, otherwise
            uses `list`.

    Returns:
        result (Mapping[Sequence]): The output tabular data.
            All list will have the same size.

    Examples:
        >>> data = [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}, {'a': 5, 'b': 6}]
        >>> tuple(seqmap2mapseq(data).items())
        (('a', [1, 3, 5]), ('b', [2, 4, 6]))
        >>> data == mapseq2seqmap((seqmap2mapseq(data)))
        True

        >>> data = [{'a': 1}, {'b': 4}, {'b': 6}]
        >>> tuple(seqmap2mapseq(data).items())
        (('a', [1, None, None]), ('b', [None, 4, 6]))
        >>> data == mapseq2seqmap((seqmap2mapseq(data)))
        True

    See Also:
        - flyingcircus.mapseq2seqmap()
    c                 $    | j                  |      S r   )rk  r  s     ru   r  zseqmap2mapseq.<locals>.<lambda>*  s     rw   c              3   H   K   | ]   fd D              f  yw)c              3   4   K   | ]  }|v r|   n  y wr   r   )r   r   d_vallabels     ru   r   z*seqmap2mapseq.<locals>.<genexpr>.<genexpr>-  s'      G9=ETMT%[u4Gs   Nr   )r   r  r  datasequence_containers    @ru   r   z seqmap2mapseq.<locals>.<genexpr>+  s9        
	 GAEG 
G	Hs   ")r   r   r   r   r  r  rl  r   reducerj  keys)r  labelsr  mapping_containerr  rH   s   ` ` ` ru   seqmap2mapseqr    s    Z   d4j!12%& !!$Z&'!	((#T%Bc!&&(m%BD E  	   &Cs   . B,c                 \    t               t              st        | t         t        t	                              }t        |      st
        }st         j                               t         t        t	                             } | fdt        |      D              S )a  
    Convert tabular data from a Mapping of Sequences to a Sequence of Mappings.

    Args:
        data (Mapping[Sequence]): The input tabular data.
            All lists must have the same length.
        labels (Iterable|None): The labels of the tabular data.
            If None, the `labels` are guessed from the data.
        d_val (Any): The default value to be used for reducing dicts.
            The values matching `d_val` are not included in the dicts.
        mapping_container (callable|None): The mapping container.
            If None, this is inferred from `data` if possible, otherwise
            uses `dict`.
        sequence_container (callable|None): The sequence container.
            If None, this is inferred from `data` if possible, otherwise
            uses `list`.

    Returns:
        result (dict[Any:list]): The tabular data as a dict of lists.
            All list will have the same size.

    Examples:
        >>> data = {'a': [1, 3, 5], 'b': [2, 4, 6]}
        >>> [sorted(d.items()) for d in mapseq2seqmap(data)]
        [[('a', 1), ('b', 2)], [('a', 3), ('b', 4)], [('a', 5), ('b', 6)]]
        >>> data == seqmap2mapseq((mapseq2seqmap(data)))
        True

        >>> data = {'a': [1, None, None], 'b': [None, 4, 6]}
        >>> [sorted(d.items()) for d in mapseq2seqmap(data)]
        [[('a', 1)], [('b', 4)], [('b', 6)]]
        >>> data == seqmap2mapseq((mapseq2seqmap(data)))
        True

    See Also:
        - flyingcircus.seqmap2mapseq()
    c              3   F   K   | ]   fd D                yw)c              3   F   K   | ]  }|      ur||      f  y wr   r   )r   r  r  r  rM   s     ru   r   z*mapseq2seqmap.<locals>.<genexpr>.<genexpr>k  s6      ,(-E{1~U* DKN#,   !Nr   )r   rM   r  r  r  r  s    @ru   r   z mapseq2seqmap.<locals>.<genexpr>j  s-      #  	 ,17, 	,#s   !)
r   r   r  r   r   r  r   r  r   r{  )r  r  r  r  r  	num_elemss   ````  ru   mapseq2seqmapr  3  s    V   J%& !!$tDJ'7"89&'!tyy{#Dd6l+,-I # y!	# # #rw   c              #   0   K   | r| dz   | dz  } | ryyw)z
    Get the bit values for a number (lower to higher significance).

    Args:
        value (int): The input value.

    Yields:
        result (int): The bits.

    Examples:
        >>> list(bits(100))
        [0, 0, 1, 0, 0, 1, 1]
    r/   Nr   )r[  s    ru   bitsr  r  s"       ai! s   c              #   j   K   | j                         }t        |dz
  dd      D ]  }| |z	  dz    yw)z
    Get the reversed bit values for a number (higher to lower significance).

    Args:
        value (int): The input value.

    Yields:
        result (int): The bits.

    Examples:
        >>> list(bits(100))
        [0, 0, 1, 0, 0, 1, 1]
    r/   r<   N)r  r{  )r[  rI   rM   s      ru   bits_rr    sA       	A1q5"b! zQs   13c                     | |z	  dz  S )aQ  
    Get the bit value for a number at a given position.

    Args:
        value (int): The input value.
        i (int): The bit position.

    Returns:
        result (int): The bit value.

    Examples:
        >>> get_bit(100, 2)
        1
        >>> [get_bit(100, i) for i in range(10)]
        [0, 0, 1, 0, 0, 1, 1, 0, 0, 0]
    r/   r   r[  rM   s     ru   get_bitr    s    & QJ!rw   c                 (    |r| d|z  z  S | d|z   z  S )a  
    Put a bit value on a number at a given position.

    Args:
        value (int): The input value.
        i (int): The bit position.
        x (int|bool): The bit value.

    Returns:
        result (int): The output value.

    Examples:
        >>> put_bit(100, 2, 1)
        100
        >>> put_bit(100, 2, 0)
        96
        >>> put_bit(100, 3, 1)
        108
        >>> put_bit(100, 3, 0)
        100
    r/   r   )r[  rM   rH   s      ru   put_bitr    s'    0 	Qay  rw   c                     | d|z  z  S )a%  
    Set the bit value for a number at a given position.

    Args:
        value (int): The input value.
        i (int): The bit position.

    Returns:
        result (int): The output value.

    Examples:
        >>> set_bit(100, 2)
        100
        >>> set_bit(96, 2)
        100
    r/   r   r  s     ru   set_bitr    s    " AFrw   c                     | d|z   z  S )a)  
    Unset the bit value for a number at a given position.

    Args:
        value (int): The input value.
        i (int): The bit position.

    Returns:
        result (int): The output value.

    Examples:
        >>> unset_bit(100, 2)
        96
        >>> unset_bit(96, 2)
        96
    r/   r   r  s     ru   	unset_bitr    s    " Q!V9rw   c                     | d|z  z  S )a  
    Flip (toggle) the bit value for a number at a given position.

    Args:
        value (int): The input value.
        i (int): The bit position.

    Returns:
        result (int): The output value.

    Examples:
        >>> flip_bit(100, 2)
        96
        >>> flip_bit(96, 2)
        100

        >>> all(
        ...      x == flip_bit(flip_bit(x, j), j)
        ...      for x in range(1024) for j in range(10))
        True
    r/   r   r  s     ru   flip_bitr    s    , AFrw   c              #   T   K   t        |t        |             D ]  \  }}|s	|  yw)aj  
    Decode the bits for a number according to the specified tokens.

    Args:
        value (int): The input value.
        tokens (Sequence): The tokens for decoding.

    Yields:
        token (Any): The codified bit as token.

    Examples:
        >>> ''.join(decode_bits(5, 'abc'))
        'ac'
        >>> ''.join(decode_bits(6, 'rwx'))
        'wx'
    N)r  r  )r[  tokenstokenbits       ru   decode_bitsr    s.     " &$u+. 
sKs   ((c                 N    d}| D ]  }t        ||j                  |            } |S )a  
    Encode the items into a number according to the specified tokens.

    Args:
        items (Iterable): The input items.
        tokens (Sequence): The tokens for encoding.

    Returns:
        result (int): The encoded number.

    Examples:
        >>> encode_bits('abc', 'abc')
        7
        >>> encode_bits('ac', 'abc')
        5
        >>> encode_bits('rw', 'rwx')
        3
        >>> encode_bits('rx', 'rwx')
        5
    r   )r  rc  )r   r  r[  r   s       ru   encode_bitsr  /  s2    * E 3v||D123Lrw   c              #   T   K   t        |      D ]  \  }}||t        | |      f  yw)aG  
    Unravel the bits for a number according to the specified tokens.

    This is similar to `decode_bits()` except that this yields additional info.

    Args:
        value (int): The input value.
        tokens (Sequence): The tokens for decoding.

    Yields:
        token (tuple): The position, token and bit value.
    N)r  r  )r[  r  rM   r  s       ru   unravel_bitsr  K  s4      f% *5q)))*s   &(c              #   .   K   | D ]  }||z   | } yw)a  
    Alternate the sign of arbitrary items.

    A similar result could be achieved with `itertools.cycle()` and `zip()`.

    Args:
        items (Iterable[Number]): The input items.
        start (Number): The initial value.
            In strict terms, this should be 1 or -1 but this is not enforced.

    Yields:
        item (Number): The item with alternating sign.

    Examples:
        >>> print(list(alternate_sign(range(16))))
        [0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15]
        >>> print(list(alternate_sign(range(16), -1)))
        [0, 1, -2, 3, -4, 5, -6, 7, -8, 9, -10, 11, -12, 13, -14, 15]
        >>> print(list(alternate_sign(range(16), 2)))
        [0, -2, 4, -6, 8, -10, 12, -14, 16, -18, 20, -22, 24, -26, 28, -30]
        >>> print(list(alternate_sign(itertools.repeat(1, 16))))
        [1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1]
    Nr   r   r~  r   s      ru   alternate_signr  ]  s(     4  dls   c                     | D ]  }||z  }	 |S )a  
    Compute the product of arbitrary items.

    This is similar to `sum`, but uses product instead of addition.

    Args:
        items (Iterable[Number]): The input items.
        start (Number): The initial value.

    Returns:
        result (Number): The cumulative product of `items`.

    Examples:
        >>> prod([2] * 10)
        1024
        >>> prod(range(1, 11))
        3628800

        >>> all(prod(range(1, n + 1)) == math.factorial(n) for n in range(10))
        True
    r   r  s      ru   r  r  }  s!    0  Lrw   c                 <    t        t        j                  | |       S )a   
    Compute the pairwise difference of arbitrary items.

    This is similar to `flyingcircus.div()`, but uses subtraction instead
    of division.

    Args:
        items (Iterable[Number]): The input items.
        reverse (bool): Reverse the order of the operands.

    Yields:
        value (Number): The next pairwise difference.


    Examples:
        >>> list(diff(range(10)))
        [1, 1, 1, 1, 1, 1, 1, 1, 1]
        >>> list(diff(range(10), True))
        [-1, -1, -1, -1, -1, -1, -1, -1, -1]
    r"  )rB  r^  subr   r  s     ru   diffr    s    2 e[AArw   c                 <    t        t        j                  | |       S )a  
    Compute the pairwise division of arbitrary items.

    This is similar to `flyingcircus.diff()`, but uses division instead
    of subtraction.

    Args:
        items (Iterable[Number]): The input items.
        reverse (bool): Reverse the order of the operands.

    Yields:
        value (Number): The next pairwise division.

    Examples:
        >>> items = [2 ** x for x in range(10)]
        >>> items
        [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
        >>> list(div(items))
        [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0]
        >>> list(div(items, True))
        [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
    r"  )rB  r^  truedivr  s     ru   divr    s    6 ((%WEErw   c                 P    t        |       }| dz  }||dkD  r|dkD  rdz   S dz   S dz   S )a  
    Round to the largest close integer.

    To round down, just use `int()`

    Args:
        x (Number): The input number.

    Returns:
        x (int): The rounded-up integer.

    Examples:
        >>> round_up(10.4)
        11
        >>> round_up(10.9)
        11
        >>> round_up(11.0)
        11
        >>> round_up(-10.4)
        -11
        >>> round_up(-10.9)
        -11
        >>> round_up(-11.0)
        -11
    r/   r   r<   rY   )rH   int_xfrac_xs      ru   round_upr
    s=    4 FEUF
%!)QBBBBBBrw   c                 2    t        | |      \  }}|r|dz  }|S )as  
    Compute integer ceil division.

    This is such that:

    q = div_ceil(a, b)
    r = mod_ceil(a, b)
    q * b + r == a

    Args:
        a (int): The dividend.
        b (int): The divisor.

    Returns:
        q (int): The quotient.

    Examples:
        >>> div_ceil(6, 3)
        2
        >>> div_ceil(6, -3)
        -2
        >>> div_ceil(-6, 3)
        -2
        >>> div_ceil(-6, -3)
        2
        >>> div_ceil(7, 3)
        3
        >>> div_ceil(7, -3)
        -2
        >>> div_ceil(-7, 3)
        -2
        >>> div_ceil(-7, -3)
        3
        >>> div_ceil(3, 7)
        1
        >>> div_ceil(3, -7)
        0
        >>> div_ceil(-3, 7)
        0
        >>> div_ceil(-3, -7)
        1
        >>> div_ceil(1, 1)
        1
        >>> div_ceil(1, -1)
        -1
        >>> div_ceil(-1, 1)
        -1
        >>> div_ceil(-1, -1)
        1

    See Also:
        - operator.div()
        - operator.mod()
        - divmod()
        - flyingcircus.mod_ceil()
        - flyingcircus.divmod_ceil()
        - flyingcircus.div_round()
        - flyingcircus.mod_round()
        - flyingcircus.divmod_round()
    r/   divmodr5   rI   rQ   rs       ru   div_ceilr    s%    z !Q<DAq	QHrw   c                 >    t        | |      \  }}|r|dz  }| ||z  z
  S )aq  
    Compute integer ceil modulus.

    This is such that:

    q = div_ceil(a, b)
    r = mod_ceil(a, b)
    q * b + r == a

    Args:
        a (int): The dividend.
        b (int): The divisor.

    Returns:
        r (int): The remainder.

    Examples:
        >>> mod_ceil(6, 3)
        0
        >>> mod_ceil(6, -3)
        0
        >>> mod_ceil(-6, 3)
        0
        >>> mod_ceil(-6, -3)
        0
        >>> mod_ceil(7, 3)
        -2
        >>> mod_ceil(7, -3)
        1
        >>> mod_ceil(-7, 3)
        -1
        >>> mod_ceil(-7, -3)
        2
        >>> mod_ceil(3, 7)
        -4
        >>> mod_ceil(3, -7)
        3
        >>> mod_ceil(-3, 7)
        -3
        >>> mod_ceil(-3, -7)
        4
        >>> mod_ceil(1, 1)
        0
        >>> mod_ceil(1, -1)
        0
        >>> mod_ceil(-1, 1)
        0
        >>> mod_ceil(-1, -1)
        0

    See Also:
        - operator.div()
        - operator.mod()
        - divmod()
        - flyingcircus.div_ceil()
        - flyingcircus.divmod_ceil()
        - flyingcircus.div_round()
        - flyingcircus.mod_round()
        - flyingcircus.divmod_round()
    r/   r  r  s       ru   mod_ceilr  ;  s.    z !Q<DAq	Qq1u9rw   c                 B    t        | |      \  }}|r|dz  }|| ||z  z
  fS )a  
    Compute integer ceil division and modulus.

    This is such that:

    q, r = divmod_ceil(a, b)
    q * b + r == a

    Args:
        a (int): The dividend.
        b (int): The divisor.

    Returns:
        r (int): The remainder.

    Examples:
        >>> n = 100
        >>> l = list(range(-n, n + 1))
        >>> ll = [(a, b) for a, b in itertools.product(l, repeat=2) if b]
        >>> result = True
        >>> for a, b in ll:
        ...     q, r = divmod_ceil(a, b)
        ...     result = result and (q == div_ceil(a, b))
        ...     result = result and (r == mod_ceil(a, b))
        ...     result = result and (q * b + r == a)
        >>> print(result)
        True

    See Also:
        - operator.div()
        - operator.mod()
        - divmod()
        - flyingcircus.div_ceil()
        - flyingcircus.mod_ceil()
        - flyingcircus.div_round()
        - flyingcircus.mod_round()
        - flyingcircus.divmod_round()
    r/   r  r  s       ru   divmod_ceilr    s2    N !Q<DAq	Qa!a%i<rw   c                 <    | dk\  |dk\  k7  r| |z  r| |z  dz   S | |z  S )a  
    Compute integer round division.

    This behaves the same as C `/` operator on integers.

    This is such that:

    q = div_round(a, b)
    r = mod_round(a, b)
    q * b + r == a

    Args:
        a (int): The dividend.
        b (int): The divisor.

    Returns:
        q (int): The quotient.

    Examples:
        >>> div_round(6, 3)
        2
        >>> div_round(6, -3)
        -2
        >>> div_round(-6, 3)
        -2
        >>> div_round(-6, -3)
        2
        >>> div_round(7, 3)
        2
        >>> div_round(7, -3)
        -2
        >>> div_round(-7, 3)
        -2
        >>> div_round(-7, -3)
        2
        >>> div_round(3, 7)
        0
        >>> div_round(3, -7)
        0
        >>> div_round(-3, 7)
        0
        >>> div_round(-3, -7)
        0
        >>> div_round(1, 1)
        1
        >>> div_round(1, -1)
        -1
        >>> div_round(-1, 1)
        -1
        >>> div_round(-1, -1)
        1

    See Also:
        - operator.div()
        - operator.mod()
        - divmod()
        - flyingcircus.div_ceil()
        - flyingcircus.mod_ceil()
        - flyingcircus.divmod_ceil()
        - flyingcircus.mod_round()
        - flyingcircus.divmod_round()
    r   r/   r   r5   rI   s     ru   	div_roundr    s2    ~ 	
QAFAAvzAvrw   c                 N    | dk\  r|dk\  r| |z  S | | z  S |dk\  r|  |z   S | |z  S )a  
    Compute integer round modulus.

    This behaves the same as C `%` operator on integers.

    This is such that:

    q = div_round(a, b)
    r = mod_round(a, b)
    q * b + r == a

    Args:
        a (int): The dividend.
        b (int): The divisor.

    Returns:
        r (int): The remainder.

    Examples:
        >>> mod_round(6, 3)
        0
        >>> mod_round(6, -3)
        0
        >>> mod_round(-6, 3)
        0
        >>> mod_round(-6, -3)
        0
        >>> mod_round(7, 3)
        1
        >>> mod_round(7, -3)
        1
        >>> mod_round(-7, 3)
        -1
        >>> mod_round(-7, -3)
        -1
        >>> mod_round(3, 7)
        3
        >>> mod_round(3, -7)
        3
        >>> mod_round(-3, 7)
        -3
        >>> mod_round(-3, -7)
        -3
        >>> mod_round(1, 1)
        0
        >>> mod_round(1, -1)
        0
        >>> mod_round(-1, 1)
        0
        >>> mod_round(-1, -1)
        0

    See Also:
        - operator.div()
        - operator.mod()
        - divmod()
        - flyingcircus.div_ceil()
        - flyingcircus.mod_ceil()
        - flyingcircus.divmod_ceil()
        - flyingcircus.div_round()
        - flyingcircus.divmod_round()
    r   r   r  s     ru   	mod_roundr    sE    ~ 	Av6q5Lr6M6R!V9q5Lrw   c                 h    | dk\  r|dk\  r| |z  }n| | z  }n|dk\  r|  |z   }n| |z  }| |z
  |z  |fS )a  
    Compute integer round division and modulus.

    This behaves the same as C `/` and `%` operators on integers.

    This is such that:

    q, r = divmod_round(a, b)
    q * b + r == a

    Args:
        a (int): The dividend.
        b (int): The divisor.

    Returns:
        r (int): The remainder.

    Examples:
        >>> n = 100
        >>> l = list(range(-n, n + 1))
        >>> ll = [(a, b) for a, b in itertools.product(l, repeat=2) if b]
        >>> result = True
        >>> for a, b in ll:
        ...     q, r = divmod_round(a, b)
        ...     result = result and (q == div_round(a, b))
        ...     result = result and (r == mod_round(a, b))
        ...     result = result and (q * b + r == a)
        >>> print(result)
        True

    See Also:
        - operator.div()
        - operator.mod()
        - divmod()
        - flyingcircus.div_ceil()
        - flyingcircus.mod_ceil()
        - flyingcircus.divmod_ceil()
        - flyingcircus.div_round()
        - flyingcircus.mod_round()
    r   r   )r5   rI   r  s      ru   divmod_roundr  ?  sV    R 	Av6AAQBA6"q&	AAAEa<?rw   c                 (    | j                         dz
  S )a  
    Compute the integer base-2 logarithm of a number.

    This is defined as the largest integer whose power-2 value is smaller than
    the number, i.e. floor(log2(n))

    Args:
        num (int): The input number.

    Returns:
        result (int): The integer base-2 logarithm of the abs(num).
            If the input is zero, returns -1.

    Examples:
        >>> ilog2(1024)
        10
        >>> ilog2(1023)
        9
        >>> ilog2(1025)
        10
        >>> ilog2(2 ** 400)
        400
        >>> ilog2(-1024)
        10
        >>> ilog2(-1023)
        9
        >>> ilog2(-1025)
        10
        >>> ilog2(0)
        -1
        >>> all(ilog2(2 ** i) == i for i in range(1000))
        True
        >>> all(ilog2(-(2 ** i)) == i for i in range(1000))
        True
    r/   )r  r;  s    ru   ilog2r  v  s    X >>arw   c                     | dk  r|  } | | j                         dz  z	  dz   }|| |z  z   dz  }t        ||z
        dkD  r|}|| |z  z   dz  }t        ||z
        dkD  r||z  | kD  r|dz  }||z  | kD  r|S )ay  
    Compute the integer square root of a number.

    This is defined as the largest integer whose square is smaller than the
    number, i.e. floor(sqrt(num))

    Args:
        num (int): The input number.

    Returns:
        result (int): The integer square root of num.

    Examples:
        >>> isqrt(1024)
        32
        >>> isqrt(1023)
        31
        >>> isqrt(1025)
        32
        >>> isqrt(2 ** 400)
        1606938044258990275541962092341162602522202993782792835301376
        >>> isqrt(2 ** 400) == 2 ** 200
        True
        >>> all(isqrt(2 ** (2 * i)) == 2 ** i for i in range(1000))
        True
    r   r9   r/   r  abs)r;  guessr   s      ru   isqrtr#    s    6 QwdCNN$))Q.EcUl"q(F
fun

!#,&1, fun

! 6/C
! 6/C
Mrw   c                 N   | dk  r|  } |dkD  rd| j                         dz  z  }|}||z  }d|z  }t        || z
        |kD  r6|dkD  r1|dz  }|| k  r||z  }n||z  }||z  }t        || z
        |kD  r|dkD  r1||z  | k  r|dz  }||z  | k  r||z  | kD  r|dz  }||z  | kD  r|S | S )a  
    Compute the integer k-th root of a number.

    This is defined as the largest integer whose n-th power is smaller than
    the number, i.e. floor(num ** (1 / base))

    Args:
        num (int): The input number.
        k (int): The order of the root.
            Must be k >= 1. When k == 1, the result is `num`.

    Returns:
        result (int): The integer k-th root of num.

    Examples:
        >>> iroot(64, 3)
        4
        >>> iroot(63, 3)
        3
        >>> iroot(65, 3)
        4
        >>> iroot(2 ** 300, 3)
        1267650600228229401496703205376
        >>> iroot(2 ** 300, 3) == 2 ** 100
        True
        >>> all(
        ...     iroot(2 ** (k * i), k) == 2 ** i
        ...     for i in range(100) for k in range(2, 10))
        True
    r   r/   r9   r   )r;  r'   r   r  r"  limits         ru   irootr&    s    B Qwd1ucnn&!++!Q%#+&6A:qLFs{& & aKE %#+&6A: kCaKF kCkCaKF kC M 
rw   c                 v    d|cxk  r| k  st        d       t        d      t        t        d| dz               S )a  
    Compute the number of ways to permuting n items into groups of size k.

    This is equivalent to the number of ways to choose k items from n items
    without repetition and with order.

    This is often indicated as `n_P_k` or `P(n, k)`.

    n! / (n - k)!

    Args:
        n (int): The number of items.
            Must be non-negative.
        k (int): The group size.
            Must be non-negative and smaller than or equal to `n`.

    Returns:
        result (int): The number of k-permutation of n items.

    Raises:
        ValueError: if the inputs are invalid.

    Examples:
        >>> perm(10, 5)
        30240
        >>> [perm(8, i) for i in range(8 + 1)]
        [1, 8, 56, 336, 1680, 6720, 20160, 40320, 40320]
        >>> [perm(9, i) for i in range(9 + 1)]
        [1, 9, 72, 504, 3024, 15120, 60480, 181440, 362880, 362880]
        >>> perm(0, 0)
        1
        >>> perm(1, 0)
        1
        >>> perm(0, 1)
        Traceback (most recent call last):
            ...
        ValueError: Values must be non-negative and n >= k in perm(n, k)
        >>> all(perm(n, n) == math.factorial(n) for n in range(20))
        True

    References:
        - https://en.wikipedia.org/wiki/Permutation
    r   z4Values must be non-negative and n >= k in perm(n, k)r/   r"  r  r{  r2   r'   s     ru   permr*    sP    X ;Q;BD 	D BD 	D E!QUO$$rw   c                 R    | dk  rt        d      t        t        d| dz               S )a6  
    Compute the factorial of a number `n!`.

    n! = n * (n - 1) * ... * 1

    Args:
        n (int): The input number.
            Must be non-negative.

    Returns:
        result (int): The number of k-permutation of n items.

    Raises:
        ValueError: if the inputs are invalid.

    Examples:
        >>> factorial(5)
        120
        >>> [factorial(i) for i in range(12)]
        [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800]
        >>> factorial(0)
        1
        >>> factorial(1)
        1
        >>> factorial(-1)
        Traceback (most recent call last):
            ...
        ValueError: Value must be non-negative
        >>> all(factorial(n) == math.factorial(n) for n in range(20))
        True

    References:
        - https://en.wikipedia.org/wiki/Factorial
    r   zValue must be non-negativer/   r(  )r2   s    ru   	factorialr,  <  s-    F 	1u566E!QUO$$rw   c                     d|cxk  r| k  sn t        t        d            || |z
  k  r|n| |z
  }t        t        | |z
  dz   | dz               t	        j
                  |      z  S )a  
    Compute the number of ways to combining n items into groups of size k.

    This is essentially binomial coefficient of order n and position k.
    This is equivalent to the number of ways to choose k items from n items
    without repetition and without order.

    This is often indicated as `(n k)`, `n_C_k` or `C(n, k)`.

    If more than one binomial coefficient of the same order are needed, then
    `flyingcircus.get_binomial_coeffs()` may be more efficient.

    Args:
        n (int): The number of items.
            Must be non-negative.
        k (int): The group size.
            Must be non-negative and smaller than or equal to `n`.

    Returns:
        result (int): The number of k-combinations of n items.

    Raises:
        ValueError: if the inputs are invalid.

    Examples:
        >>> comb(10, 5)
        252
        >>> [comb(10, i) for i in range(10 + 1)]
        [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
        >>> [comb(11, i) for i in range(11 + 1)]
        [1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]

        >>> N = 10
        >>> for n in range(N):
        ...     print([comb(n, k) for k in range(n + 1)])
        [1]
        [1, 1]
        [1, 2, 1]
        [1, 3, 3, 1]
        [1, 4, 6, 4, 1]
        [1, 5, 10, 10, 5, 1]
        [1, 6, 15, 20, 15, 6, 1]
        [1, 7, 21, 35, 35, 21, 7, 1]
        [1, 8, 28, 56, 70, 56, 28, 8, 1]
        [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
        >>> comb(0, 0)
        1
        >>> comb(1, 1)
        1

        >>> comb(1, 0)
        1
        >>> comb(0, 1)
        Traceback (most recent call last):
            ...
        ValueError: Values must be non-negative and n=0 >= k=1
        >>> comb(1, 2)
        Traceback (most recent call last):
            ...
        ValueError: Values must be non-negative and n=1 >= k=2
        >>> comb(2, 1)
        2
        >>> all(comb(n, k) * math.factorial(k) == perm(n, k)
        ...     for n in range(20) for k in range(n))
        True

    See Also:
        - flyingcircus.get_binomial_coeffs()
        - flyingcircus.binomial_triangle_range()

    References:
        - https://en.wikipedia.org/wiki/Combination
        - https://en.wikipedia.org/wiki/Binomial_coefficient
        - https://en.wikipedia.org/wiki/Binomial_triangle
    r   z.Values must be non-negative and n={n} >= k={k}r/   )r"  r   r  r{  rd  r,  r)  s     ru   combr.  f  sm    X ;Q;ABD 	D QUAA E!a%!)QU+,q0AAArw   c              #      K   d}|r|s| dz   n| dz  dz   }|r=|r;t        t        | dd            }|D ]  }|  || dz  rdnddd   D ]  }|  yt        |      D ]  }| || |z
  z  |dz   z  } yw)a-
  
    Generate the numbers of a given row of the binomial triangle.

    This is also known as Pascal's triangle.

    These are the numbers in the `num`-th row (order) of the binomial triangle.
    If only a specific binomial coefficient is required, use
    `flyingcircus.comb()`.

    Args:
        num (int): The row index of the triangle.
            Indexing starts from 0.
        full (bool): Compute all numbers of the row.
            If True, all numbers are yielded.
            Otherwise, it only yields non-repeated numbers (exploiting the
            fact that they are palindromes).
        cached (bool): Cache the non-repeated numbers.
            If True and `full == True`, the non-repeated numbers are cached
            (thus allocating some additional temporary memory).
            Otherwise, if False or `full == False` this has no effect,
            i.e. the numbers are computed directly.

    Yields:
        value (int): The next binomial coefficient of a given order / row.

    Examples:
        >>> list(get_binomial_coeffs(12))
        [1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]
        >>> list(get_binomial_coeffs(13))
        [1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1]
        >>> list(get_binomial_coeffs(12, cached=True))
        [1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]
        >>> list(get_binomial_coeffs(13, cached=True))
        [1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1]
        >>> list(get_binomial_coeffs(12, full=False))
        [1, 12, 66, 220, 495, 792, 924]
        >>> list(get_binomial_coeffs(13, full=False))
        [1, 13, 78, 286, 715, 1287, 1716]
        >>> num = 10
        >>> for n in range(num):
        ...     print(list(get_binomial_coeffs(n)))
        [1]
        [1, 1]
        [1, 2, 1]
        [1, 3, 3, 1]
        [1, 4, 6, 4, 1]
        [1, 5, 10, 10, 5, 1]
        [1, 6, 15, 20, 15, 6, 1]
        [1, 7, 21, 35, 35, 21, 7, 1]
        [1, 8, 28, 56, 70, 56, 28, 8, 1]
        [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
        >>> all(list(get_binomial_coeffs(n))
        ...     == list(get_binomial_coeffs(n, cached=True))
        ...     for n in range(10))
        True
        >>> all(list(get_binomial_coeffs(n))
        ...     == [comb(n, k) for k in range(n + 1)]
        ...     for n in range(10))
        True

    See Also:
        - flyingcircus.comb()
        - flyingcircus.binomial_triangle_range()

    References:
        - https://en.wikipedia.org/wiki/Binomial_coefficient
        - https://en.wikipedia.org/wiki/Binomial_triangle
    r/   r9   F)fullcachedr<   r=   N)r  get_binomial_coeffsr{  )r;  r0  r1  r[  r  cacherM   s          ru   r2  r2    s     P EVC!G#(Q,D(5GH 	EK	#'Br6B67 	EK	 t 	1AKS1W%!a%0E	1s   A6A8c              #   H   K   d}|| k7  r| |||z   }}|dz  }|| k7  ryyw)aQ  
    Generate the first Fibonacci-like numbers.

    The next number is computed by adding the last two.

    This is useful for generating a sequence of Fibonacci numbers.
    To generate a specific Fibonacci number,
    use `flyingcircus.fibonacci()`.

    Args:
        max_count (int): The maximum number of values to yield.
            If `max_count == -1`, the generation proceeds indefinitely.
        first (int): The first number of the sequence.
        second (int): The second number of the sequence.

    Yields:
        value (int): The next Fibonacci number.

    Examples:
        >>> [x for x in get_fibonacci(16)]
        [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
        >>> [x for x in get_fibonacci(16, 0, 2)]
        [0, 2, 2, 4, 6, 10, 16, 26, 42, 68, 110, 178, 288, 466, 754, 1220]
        >>> [x for x in get_fibonacci(16, 3, 1)]
        [3, 1, 4, 5, 9, 14, 23, 37, 60, 97, 157, 254, 411, 665, 1076, 1741]

    See Also:
        - flyingcircus.get_gen_fibonacci()
        - flyingcircus.fibonacci()
        - https://en.wikipedia.org/wiki/Fibonacci_number
    r   r/   Nr   )	max_countrf  r|  rM   s       ru   get_fibonaccir6  !  s9     F 	
A
y.v	Q y.s   ""r   r/   c              #      K   t        ||f      }t        ||d      }t        ||d      }d}|| k7  r6|d    |dd t        d t        ||      D              fz   }|dz  }|| k7  r5yyw)a  
    Generate the first generalized Fibonacci-like numbers.

    The next number is computed as a linear combination of the last values
    multiplied by their respective weights.

    These can be used to compute n-step Fibonacci, Lucas numbers,
    Pell numbers, Perrin numbers, etc.

    Args:
        max_count (int): The maximum number of values to yield.
            If `max_count == -1`, the generation proceeds indefinitely.
        values (int|Sequence[int]): The initial numbers of the sequence.
            If int, the value is repeated for the number of `weights`, and
            `weights` must be a sequence.
        weights (int|Sequence[int]): The weights for the linear combination.
            If int, the value is repeated for the number of `values`, and
            `values` must be a sequence.

    Yields:
        value (int): The next number of the sequence.

    Examples:
        >>> [x for x in get_gen_fibonacci(16)]
        [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
        >>> [x for x in get_gen_fibonacci(16, (1, 1, 1))]
        [1, 1, 1, 3, 5, 9, 17, 31, 57, 105, 193, 355, 653, 1201, 2209, 4063]
        >>> [x for x in get_gen_fibonacci(16, (1, 0, 1))]
        [1, 0, 1, 2, 3, 6, 11, 20, 37, 68, 125, 230, 423, 778, 1431, 2632]
        >>> [x for x in get_gen_fibonacci(13, (0, 1), 2)]
        [0, 1, 2, 6, 16, 44, 120, 328, 896, 2448, 6688, 18272, 49920]
        >>> [x for x in get_gen_fibonacci(16, (1, 0, 1), (1, 2, 1))]
        [1, 0, 1, 2, 4, 9, 19, 41, 88, 189, 406, 872, 1873, 4023, 8641, 18560]

    See Also:
        - flyingcircus.get_fibonacci()
        - flyingcircus.fibonacci()
        - https://en.wikipedia.org/wiki/Fibonacci_number
    T)rU  r   r/   Nc              3   ,   K   | ]  \  }}||z    y wr   r   )r   wrH   s      ru   r   z$get_gen_fibonacci.<locals>.<genexpr>}  s     "JTQ1q5"Jr  )r>  r  rV  r  )r5  rZ  weightsr;  rM   s        ru   get_gen_fibonaccir<  L  s     V FG,
-CD1F'3d3G	A
y.Qis"JS&5I"JJLL	Q y.s   A%A*(A*c                 4    t        |       D ]	  }|||z   }} |S )a]  
    Generate the n-th Fibonacci number.

    This is useful for generating a specific Fibonacci number.
    For generating a sequence of Fibonacci numbers, use
    `flyingcircus.get_fibonacci()`.

    Args:
        num (int): The ordinal to generate.
            Starts counting from 0.
            If `num < 0`, the first number is returned.
        first (int): The first number of the sequence.
        second (int): The second number of the sequence.

    Returns:
        value (int): The Fibonacci number.

    Examples:
        >>> fibonacci(10)
        55
        >>> fibonacci(100)
        354224848179261915075
        >>> fibonacci(200)
        280571172992510140037611932413038677189525
        >>> fibonacci(300)
        222232244629420445529739893461909967206666939096499764990979600
        >>> fibonacci(0)
        0
        >>> fibonacci(1)
        1
        >>> fibonacci(15, 0, 2)
        1220
        >>> fibonacci(15, 3, 1)
        1741

    See Also:
        - flyingcircus.get_fibonacci()
        - flyingcircus.get_gen_fibonacci()
        - https://en.wikipedia.org/wiki/Fibonacci_number
    rz  )r;  rf  r|  r  s       ru   	fibonaccir>    s*    X 3Z /v/Lrw   c              #      K   |d| }}n| |}}|s	||k  rdnd}t        |||      D ]  } |t        |              yw)aU  
    Generate the binomial triangle rows in a given range.
    
    This is also known as Pascal's triangle.

    See `flyingcircus.get_binomial_coeffs()` for generating any given
    row of the triangle.

    Args:
        first (int): The first value of the range.
            Must be non-negative.
            If `second == None` this is the `stop` value, and is not included.
            Otherwise, this is the `start` value and is included.
            If `first < second` the sequence is yielded backwards.
        second (int|None): The second value of the range.
            If None, the start value is 0.
            Otherwise, this is the `stop` value and is not included.
            Must be non-negative.
            If `first < second` the sequence is yielded backwards.
        step (int): The step of the rows range.
            If the sequence is yielded backward, the step should be negative,
            otherwise an empty sequence is yielded.
            If None, this is computed automatically based on `first` and
            `second`, such that a non-empty sequence is avoided, if possible.
        container (callable): The row container.
            This should be a Sequence constructor.

    Yields:
        row (Sequence[int]): The rows of the binomial triangle.

    Examples:
        >>> tuple(binomial_triangle(5))
        ((1,), (1, 1), (1, 2, 1), (1, 3, 3, 1), (1, 4, 6, 4, 1))
        >>> tuple(binomial_triangle(5, 7))
        ((1, 5, 10, 10, 5, 1), (1, 6, 15, 20, 15, 6, 1))
        >>> tuple(binomial_triangle(7, 9))
        ((1, 7, 21, 35, 35, 21, 7, 1), (1, 8, 28, 56, 70, 56, 28, 8, 1))
        >>> tuple(binomial_triangle(5, 2))
        ((1, 5, 10, 10, 5, 1), (1, 4, 6, 4, 1), (1, 3, 3, 1))
        >>> tuple(binomial_triangle(0, 7, 2))
        ((1,), (1, 2, 1), (1, 4, 6, 4, 1), (1, 6, 15, 20, 15, 6, 1))
        >>> tuple(binomial_triangle(0, 6, 2))
        ((1,), (1, 2, 1), (1, 4, 6, 4, 1))
        >>> tuple(binomial_triangle(7, 1, -2))
        ((1, 7, 21, 35, 35, 21, 7, 1), (1, 5, 10, 10, 5, 1), (1, 3, 3, 1))
        >>> tuple(binomial_triangle(7, 1, 2))  # empty range!
        ()
        >>> list(binomial_triangle(3))
        [(1,), (1, 1), (1, 2, 1)]
        >>> list(binomial_triangle(5, container=list))
        [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]
        >>> for row in binomial_triangle(10):
        ...    print(row)
        (1,)
        (1, 1)
        (1, 2, 1)
        (1, 3, 3, 1)
        (1, 4, 6, 4, 1)
        (1, 5, 10, 10, 5, 1)
        (1, 6, 15, 20, 15, 6, 1)
        (1, 7, 21, 35, 35, 21, 7, 1)
        (1, 8, 28, 56, 70, 56, 28, 8, 1)
        (1, 9, 36, 84, 126, 126, 84, 36, 9, 1)

    See Also:
        - flyingcircus.comb()
        - flyingcircus.get_binomial_coeffs()

    References
        - https://en.wikipedia.org/wiki/Binomial_coefficient
        - https://en.wikipedia.org/wiki/Binomial_triangle
    Nr   r/   r<   )r{  r2  )rf  r|  r}  r   r~  r  rM   s          ru   binomial_triangler@    sY     Z ~tVtDLqb5$% 0+A.//0s   =?c                 z    | dz  s| dkD  s
| dz  s| dkD  ryd}||z  | k  r| |z  r| |dz   z  sy|dz  }||z  | k  ry)a  
    Determine if a number is prime.

    A prime number is only divisible by 1 and itself.
    0 and 1 are considered special cases; in this implementations they are
    considered primes.

    It is implemented by testing for possible factors using wheel increment.

    Args:
        num (int): The number to check for primality.
            Must be greater than 1.

    Returns:
        result (bool): The result of the primality.

    Examples:
        >>> _is_prime(100)
        False
        >>> _is_prime(101)
        True
        >>> _is_prime(0)
        True
        >>> all(is_prime(n) for n in (-2, -1, 0, 1, 2))
        True

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.primes_range()

    References:
        - https://en.wikipedia.org/wiki/Prime_number
        - https://en.wikipedia.org/wiki/Trial_division
        - https://en.wikipedia.org/wiki/Wheel_factorization
    r9   r&   F      Tr   )r;  rM   s     ru   	_is_primerD    s`    J 1W#'C!Gq	A
a%3,aC1q5MFA	 a%3,
 rw   )r9   c              #   8   K   | ]  }t        |      s|  y wr   )rD  r   r2   s     ru   r   r   =  s      9
9Q<A9s     c                    | dk  r|  } |sd}d}| |d   k  ry| |d   k  r|r| |v S | dk  rN|D ]  }| |z  s y||z  | kD  s y d|d   d	z
  d
z  d
z  z   }||z  | k  r| |z  r| |dz   z  sy|d
z  }||z  | k  ryd}| j                         dz  }t        |      D ]  \  }}|s	| |k  s|dz   } n t        t        |            }t	        | |      S )u  
    Determine if a number is prime.

    A prime number is only divisible by 1 and itself.
    0 and 1 are considered special cases; in this implementations they are
    considered primes.

    The implementation is using a certain number of precomputed primes,
    later switching to trial division with hard-coded (2, 3) wheel
    (up to 2 ** 20).
    After, it uses `flyingcircus.is_pseudo_prime()` with bases consisting
    of consecutive primes, and this is guaranteed to be deterministic up to
    3317044064679887385961981 > 2 ** 81.

    Args:
        num (int): The number to test for primality.
            Only works for numbers larger than 1.
        small_primes (Sequence|None): The first prime numbers (sorted).
            Must contain prime starting from 2 (included).
            Must be in increasing order.
            If None, uses a hard-coded sequence and skip fast hashed checks.
        hashed_small_primes (Container|None): The first prime numbers.
            Must contain prime starting from 2 (included).
            Should use a high performance container like `set` or `frozenset`.
            Must contain the same numbers included in `small_primes`.
            If None, skip fast hashed checks.

    Returns:
        result (bool): The result of the primality test.

    Examples:
        >>> is_prime(100)
        False
        >>> is_prime(101)
        True
        >>> is_prime(-100)
        False
        >>> is_prime(-101)
        True
        >>> is_prime(2 ** 17)
        False
        >>> is_prime(17 * 19)
        False
        >>> is_prime(2 ** 17 - 1)
        True
        >>> is_prime(2 ** 31 - 1)
        True
        >>> is_prime(2 ** 17 - 1, (2,))
        True
        >>> is_prime(2 ** 17 - 1, (2, 3))
        True
        >>> is_prime(2 ** 17 - 1, (2, 3, 5))
        True
        >>> all(is_prime(n) for n in (-2, -1, 0, 1, 2))
        True

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.is_pseudo_prime()
        - flyingcircus.primes_range()

    References:
        - https://en.wikipedia.org/wiki/Prime_number
        - https://en.wikipedia.org/wiki/Trial_division
        - https://en.wikipedia.org/wiki/Wheel_factorization
        - https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
    r   )r9   r&   rB                          %   +   NTr<      FrB  r/   rC  r9   )iql   }B l   ;n>l   pl   He%Z	 Nl   y5D( NNl   7y_@I7 l   %!HnfW r&   )r  r  r   
get_primesis_pseudo_prime)	r;  small_primeshashed_small_primesprimerM   limits	num_basesr%  basess	            ru   is_primer\  B  s;   N QwdE"
\!_	R 	 %8)))	! 	E;$		
 b!A%!+a//!esl7#Q-Q	 !esl
 A NN$)	!&) 	HAuuE		 j+,sE**rw   c                    | dk  r|  } | dk  ry| dz  sy|s*t        dt        | dz
  | j                         dz              }| dz
  | dz
   z  j                         dz
  }| |z	  }|D ]f  }|| k\  r|| z  }|dk\  st        |||       }|dk7  s&|| dz
  k7  s/d}t        d|      D ]#  }t        |d|       }|| dz
  k(  rd} n
|dk(  s"  y |rf y y)u  
    Determine if a number is pseudo-prime (using the Miller-Rabin test).

    Args:
        num (int): The number to test for primality.
        bases (Iterable[int]|None): The bases for the test.
            If None, uses the theoretical values for which the test is proven
            to be deterministic, if the extended Riemann hypothesis is True:
            bases = range(2, min(n-2, log2(n) ** 2)

    Returns:
        result (bool): The result of the pseudo-primality test.

    Examples:
        >>> is_pseudo_prime(100, (2,))
        False
        >>> is_pseudo_prime(101, (2,))
        True
        >>> is_pseudo_prime(-100, (2,))
        False
        >>> is_pseudo_prime(-101, (2,))
        True
        >>> is_pseudo_prime(2 ** 17, (2,))
        False
        >>> is_pseudo_prime(17 * 19, (2,))
        False
        >>> is_pseudo_prime(2 ** 17 - 1, (2,))
        True
        >>> is_pseudo_prime(2 ** 31 - 1, (2,))
        True
        >>> is_pseudo_prime(2 ** 17 - 1, (2,))
        True
        >>> is_pseudo_prime(2 ** 17 - 1, (2, 3))
        True
        >>> is_pseudo_prime(2 ** 17 - 1, (2, 3, 5))
        True
        >>> all(is_pseudo_prime(n) for n in (-2, -1, 0, 1, 2))
        True
        >>> is_pseudo_prime(2047, (2,))
        True
        >>> is_prime(2047)
        False

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.is_pseudo_prime()
        - flyingcircus.primes_range()

    References:
        - https://en.wikipedia.org/wiki/Prime_number
        - https://en.wikipedia.org/wiki/Trial_division
        - https://en.wikipedia.org/wiki/Wheel_factorization
        - https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
    r   r&   Tr9   Fr/   )r{  r  r  pow)r;  r[  r  r>   baserH   rJ  r  s           ru   rU  rU    s   r Qwd
Qw1WaS1Wcnn&6!&;<=
'sQwZ	++-1AqA !3;CKD19D!S!AAv!sQw,q! %AAq#AC!G|#Av$%  #!$ rw   c              #     K   d}|dk  rt        t        d            |sd}||d   k  r|D ]  }||k\  s	| |dz  }|| k(  r y|} dk  rOd|dz
  d	z  d	z  z   }	 t        |      r| |dz  }|| k(  ryt        |dz         r|dz    |dz  }|| k(  ry|d	z  }@t        |      kD  rt	        t        d            n|d t              }t	        fd
t        d|dz         D              }t	        t        ||d   |z   fz               }	t        |	      }
|d   |dz
  |z  |z  z   }d}|| k7  r2||k\  rt        |      r	| |dz  }||	|   z  }|dz  }||
z  }|| k7  r1yyw)aH  
    Generate the next prime numbers.

    New possible primes are obtained using a hard-coded (2, 3) wheel.

    Args:
        max_count (int): The maximum number of values to yield.
            If `max_count == -1`, the generation proceeds indefinitely.
        start (int): The initial value.
            This must be positive.
        small_primes (Sequence|None): The first prime numbers (sorted).
            Must contains prime starting from 2 (included).
            Must be in increasing order.
            If None, uses a hard-coded sequence.
        wheel (int): The number of primes to use as wheel.
            Must be > 1.
            The wheel is generated using `flyingcircus.get_primes()`.

    Yields:
        num (int): The next prime number.

    Examples:
        >>> n = 15
        >>> primes = get_primes()
        >>> [next(primes) for i in range(n)]
        [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
        >>> list(get_primes(n))
        [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
        >>> list(get_primes(10, 101))
        [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
        >>> list(get_primes(10, 1000))
        [1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061]

        >>> list(get_primes(n, 2, None))
        [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
        >>> list(get_primes(10, 101, None))
        [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
        >>> list(get_primes(10, 1000, None))
        [1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061]

        >>> list(get_primes(n, 2, None, 3))
        [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
        >>> list(get_primes(10, 101, None, 3))
        [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
        >>> list(get_primes(10, 1000, None, 3))
        [1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061]

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.primes_range()

    References:
        - https://en.wikipedia.org/wiki/Prime_number
        - https://en.wikipedia.org/wiki/Wheel_factorization
    r   :`get_primes()` must start at a positive value, not `{num}`r9   r&   rB  rI  rJ  rK  rL  rM  rN  rO  rP  rQ  )   rR  /   r<   r/   Nr9   rB  rC  c              3   L   K   | ]  t        fd D              r  yw)c              3   P   K   | ]  }t        j                  |      d k(    ywr  rd  gcdr   r'   r2   s     ru   r   z'get_primes.<locals>.<genexpr>.<genexpr>i        61488Aq>Q&6   #&Nr  r   r2   wheels    @ru   r   zget_primes.<locals>.<genexpr>g  '      8666 8    $)	r"  r   r\  r   r   rT  r  r{  r  )r5  r~  rV  rn  rM   rX  r;  
prod_wheelcoprimesdeltas
len_deltasr  s      `        ru   rT  rT    s    x 	
AqyMNP 	PM|B! 	"E~Q	>!E	" z519"Q&&}	Q	>a AgQ	>1HC  3|$$*UA./E %(E%[
 8Q
Q/8 8 tHj(@'BBCD[
qkUQY:5
BB9ne|	Q6!9CFAOA 9ns   2E'D.E'%E'c              #   ^  K   d}|dk  rt        t        d            |sd}dk  r`d|dz
  dz  dz  z   }||d   kD  r9t        |      r| |dz  }|| k(  ry	t        |d
z
        r|d
z
   |dz  }|| k(  ry	|dz  }||d   kD  rHnt        |      kD  rt	        t        d            n|d	 t              }t	        fdt        |dz   dd      D              }t	        t        ||d   |z
  fz               }t        |      }	|d   |dz
  |z  |z  z   }d}
||kD  r|||
   z  }|
dz  }
|
|	z  }
||kD  r||d   kD  r9|| k7  r4t        |      r	| |dz  }|||
   z  }|
dz  }
|
|	z  }
||d   kD  r|| k7  r4|| k7  r/t        |      D ]   }||k  s	||k  s| |dz  }|| k(  r y	|}" y	y	w)a	  
    Generate the previous prime numbers.

    New possible primes are obtained using a hard-coded (2, 3) wheel.

    Args:
        max_count (int): The maximum number of values to yield.
            If equal to -1 or larger than the number of primes smaller than or
            equal to `start`, the generation proceeds until the smallest
            proper prime number (2) is yielded.
        start (int): The initial value.
            This must be positive.
        small_primes (Sequence|None): The first prime numbers (sorted).
            Must contains prime starting from 2 (included).
            Must be in increasing order.
            If None, uses a hard-coded sequence.
        wheel (int): The number of primes to use as wheel.
            Must be > 1.
            The wheel is generated using `flyingcircus.get_primes()`.

    Yields:
        num (int): The next prime number.

    Examples:
        >>> n = 15
        >>> primes = get_primes_r(-1, 47)
        >>> [next(primes) for i in range(n)]
        [47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2]
        >>> list(get_primes_r(n, 47))
        [47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2]
        >>> list(get_primes_r(10, 150))
        [149, 139, 137, 131, 127, 113, 109, 107, 103, 101]
        >>> list(get_primes_r(10, 1062))
        [1061, 1051, 1049, 1039, 1033, 1031, 1021, 1019, 1013, 1009]
        >>> list(get_primes_r(10, 10))
        [7, 5, 3, 2]

        >>> list(get_primes_r(n, 47, None))
        [47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2]
        >>> list(get_primes_r(10, 150, None))
        [149, 139, 137, 131, 127, 113, 109, 107, 103, 101]
        >>> list(get_primes_r(10, 1062, None))
        [1061, 1051, 1049, 1039, 1033, 1031, 1021, 1019, 1013, 1009]
        >>> list(get_primes_r(10, 10, None))
        [7, 5, 3, 2]

        >>> list(get_primes_r(n, 47, None, 3))
        [47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2]
        >>> list(get_primes_r(10, 150, None, 3))
        [149, 139, 137, 131, 127, 113, 109, 107, 103, 101]
        >>> list(get_primes_r(10, 1062, None, 3))
        [1061, 1051, 1049, 1039, 1033, 1031, 1021, 1019, 1013, 1009]
        >>> list(get_primes_r(10, 10, None, 3))
        [7, 5, 3, 2]

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.primes_range()

    References:
        - https://en.wikipedia.org/wiki/Prime_number
        - https://en.wikipedia.org/wiki/Wheel_factorization
    r   ra  rb  r9   rB  r/   rC  r<   Nrb   c              3   L   K   | ]  t        fd D              r  yw)c              3   P   K   | ]  }t        j                  |      d k(    ywr  rg  ri  s     ru   r   z)get_primes_r.<locals>.<genexpr>.<genexpr>  rj  rk  Nrl  rm  s    @ru   r   zget_primes_r.<locals>.<genexpr>  ro  rp  )
r"  r   r\  r   r   rT  r  r{  r  r  )r5  r~  rV  rn  rM   r;  rq  rr  rs  rt  r  rX  s      `        ru   get_primes_rrx  x  sA    H 	
AqyMNP 	PMz519"Q&&L$$}	Q	>a AgQ	>1HC L$$ 3|$$*UA./E %(E%[
 8Z!^Q38 8 tHj(@'BBCD[
qkUQY:5
BBEk6!9CFAOA Ek L$$i}	Q6!9CFAOA L$$i 	I~l+ 	"E~%3,Q	>!E	" s&   BF-B/F-7A F-8F-F-F-c              #      K   |d| }}n| |}}||k  rt        d|      D ]  }||k  r|  y yt        d|      D ]  }||kD  r|  y yw)a?  
    Compute the prime numbers in the range.

    Args:
        first (int): The first value of the range.
            If `second == None` this is the `stop` value, and is not included.
            Otherwise, this is the start value and can be included
            (if it is a prime number).
            If `first < second` the sequence is yielded backwards.
        second (int|None): The second value of the range.
            If None, the start value is 2.
            If `first < second` the sequence is yielded backwards.

    Yields:
        num (int): The next prime number.

    Examples:
        >>> list(primes_range(50))
        [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
        >>> list(primes_range(51, 100))
        [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
        >>> list(primes_range(101, 150))
        [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
        >>> list(primes_range(151, 200))
        [151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199]
        >>> list(primes_range(1000, 1050))
        [1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049]
        >>> list(primes_range(1050, 1000))
        [1049, 1039, 1033, 1031, 1021, 1019, 1013, 1009]
        >>> list(primes_range(1, 50))
        [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
        >>> list(primes_range(50, 1))
        [47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2]

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.get_primes()
        - https://en.wikipedia.org/wiki/Prime_number
        - https://en.wikipedia.org/wiki/Wheel_factorization
    Nr9   r<   )rT  rx  )rf  r|  r~  r  rX  s        ru   primes_rangerz    ss     V ~tVt}E* 	Et|		 ""e, 	Et|		s   AAc              #     K   t        | t              st        t        |             } | dk(  rd y| dk  rd |  } n	| dk(  r|  |sd}|D ]  }| |z  r	| | |z  } | |z  s t        |      kD  rt	        t        d            n|d D ]  }| |z  r	| | |z  } | |z  s t              }t	        fdt        d|dz         D              }t	        t        ||d   |z   fz               }t        |      }d}|d   dz
  |z  |z  z   }	|	|	z  | k  r/| |	z  s|	 | |	z  } | |	z  s|	||   z  }	|dz  }||z  }|	|	z  | k  r/| dkD  r|  yyw)a  
    Find all factors of a number.

    It is implemented by testing for possible factors using wheel increment.

    This is not suitable for large numbers.

    Args:
        num (int|float): The number to factorize.
            If float, its nearest integer is factorized.
        small_primes (Sequence|None): The first prime numbers (sorted).
            Must contains prime starting from 2 (included).
            Must be in increasing order.
            If None, uses a hard-coded sequence.
        wheel (int): The number of primes to use as wheel.
            Must be > 1.
            The wheel is generated using `flyingcircus.get_primes()`.

    Yields:
        factor (int): The next factor of the number.
            Factors are yielded in increasing order.

    Examples:
        >>> list(get_factors(100))
        [2, 2, 5, 5]
        >>> list(get_factors(1234567890))
        [2, 3, 3, 5, 3607, 3803]
        >>> list(get_factors(-65536))
        [-1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
        >>> list(get_factors(0))
        [0]
        >>> list(get_factors(1))
        [1]
        >>> list(get_factors(-1))
        [-1]
        >>> list(get_factors(987654321.0))
        [3, 3, 17, 17, 379721]
        >>> all(n == prod(get_factors(n)) for n in range(1000))
        True

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.primes_range()
        - flyingcircus.get_primes()

    References:
        - https://en.wikipedia.org/wiki/Trial_division
        - https://en.wikipedia.org/wiki/Wheel_factorization
    r   Nr<   r/   rb  r9   c              3   L   K   | ]  t        fd D              r  yw)c              3   P   K   | ]  }t        j                  |      d k(    ywr  rg  ri  s     ru   r   z(get_factors.<locals>.<genexpr>.<genexpr>  s      2qtxx1~"2rk  Nrl  rm  s    @ru   r   zget_factors.<locals>.<genexpr>  s'      42E22 	
4rp  )	r  rY   rg  r   r   rT  r  r{  r  )
r;  rV  rn  rX  rq  rr  rs  rt  r  r'   s
     `       ru   get_factorsr~  2  s    l c3%*o
ax
Qwd		M ;KEMC ;
 s<  j*+Ve$ ;KEMC ; eJ 4JN+4 4H 4HQK*$<#>>?@FVJ	AuqyZ/*<<A
a%3,7GAIC 7 	
VAY	Q	Z a%3, Qw	 s+   AEE&5EE+BE1EEc                 >    t        j                  t        |             S )a  
    Find all factors of a number and collect them in an ordered dict.

    Args:
        num (int): The number to factorize.

    Returns:
        factors (collections.Counter): The factors of the number.

    Examples:
        >>> sorted(get_factors_as_dict(100).items())
        [(2, 2), (5, 2)]
        >>> sorted(get_factors_as_dict(1234567890).items())
        [(2, 1), (3, 2), (5, 1), (3607, 1), (3803, 1)]
        >>> sorted(get_factors_as_dict(65536).items())
        [(2, 16)]
    )r  Counterr~  r  s    ru   get_factors_as_dictr    s    $ {3/00rw   r3  c                    t        |t              r<|j                         }|dk(  rd\  }}n&|dk(  rd\  }}n|dk(  rd\  }}n|dk(  rd\  }}n|\  }}d	}d
}d}t        |       D ]c  }||k(  r|d
z  }|d
kD  r5r||t        |      z   z  }n!|t        |      j	                  t
              z  }|d
kD  r|z  }|t        |      z  }|}d
}e |d
kD  r6r||t        |      z   z  }|S |t        |      j	                  t
              z  }|S )u  
    Find all factors of a number and output a human-readable text.

    Args:
        num (int): The number to factorize.
        mode (str|Iterable[str|None]): The formatting mode.
            If str, available options:
             - `ascii`: uses `^` for power, and ` * ` for multiplication.
             - `py`: uses ` ** ` for power, and ` * ` for multiplication.
             - `sup`: uses superscript for power, and ` ` for multiplication.
             - `utf8`: uses superscript for power, and `×` for multiplication.
            If Iterable of str or None, must have length 2:
             - the first is used as exponent symbol if str, otherwise if None,
               uses superscript notation.
             - the second is used as multiplication symbol (requires str).

    Returns:
        text (str): The factors of the number.

    Examples:
        >>> get_factors_as_str(100, 'ascii')
        '2^2 * 5^2'
        >>> get_factors_as_str(1234567890, 'ascii')
        '2 * 3^2 * 5 * 3607 * 3803'
        >>> get_factors_as_str(65536, 'ascii')
        '2^16'

        >>> get_factors_as_str(100, 'py')
        '2 ** 2 * 5 ** 2'
        >>> get_factors_as_str(1234567890, 'py')
        '2 * 3 ** 2 * 5 * 3607 * 3803'
        >>> get_factors_as_str(65536, 'py')
        '2 ** 16'

        >>> get_factors_as_str(100, 'utf8')
        '2²×5²'
        >>> get_factors_as_str(1234567890, 'utf8')
        '2×3²×5×3607×3803'
        >>> get_factors_as_str(65536, 'utf8')
        '2¹⁶'

        >>> get_factors_as_str(100, 'sup')
        '2² 5²'
        >>> get_factors_as_str(1234567890, 'sup')
        '2 3² 5 3607 3803'
        >>> get_factors_as_str(65536, 'sup')
        '2¹⁶'

        >>> get_factors_as_str(100, tuple('^*'))
        '2^2*5^2'
        >>> get_factors_as_str(1234567890, tuple('^*'))
        '2*3^2*5*3607*3803'
        >>> get_factors_as_str(65536, tuple('^*'))
        '2^16'
    r3  )^ * py)z ** r  sup)N utf8)N   ×r8   r/   r   )r  ra   r^  r~  	translateSUPERSCRIPT_MAP)r;  r   exp_sepfact_septextlast_factorexpfactors           ru   get_factors_as_strr    sA   t $zz|7? *GXT\ -GXU] )GXV^ *GX DK
Cc" [ 1HCQwGc#h..DCH..??DQ CKD KC QwGc#h&&D K CH&&77DKrw   c           	   #   .  K   | dv r|  y| dk  rd |  } t        t        t        j                  t	        |             j                         d       \  }}t        j                  d |D         D ]   }t        d t        ||      D               " yw)	a  
    Find all divisors of a number.

    It is implemented by computing the possible unique combinations of the
    factors.

    This is not suitable for large numbers.

    Args:
        num (int|float): The number to factorize.
            If float, its nearest integer is factorized.

    Yields:
        factor (int): The next factor of the number.
            Factors are yielded in increasing order.

    Examples:
        >>> list(get_divisors(100))
        [1, 2, 4, 5, 10, 20, 25, 50, 100]
        >>> list(get_divisors(-100))
        [-1, 1, 2, 4, 5, 10, 20, 25, 50, 100]
        >>> list(get_divisors(2 ** 8))
        [1, 2, 4, 8, 16, 32, 64, 128, 256]
        >>> list(get_divisors(12345))
        [1, 3, 5, 15, 823, 2469, 4115, 12345]
        >>> list(get_divisors(0))
        [0]
        >>> list(get_divisors(1))
        [1]
        >>> list(get_divisors(2))
        [1, 2]
        >>> list(get_divisors(101))
        [1, 101]

    See Also:
        - flyingcircus.is_prime()
        - flyingcircus.primes_range()
        - flyingcircus.get_primes()
        - flyingcircus.get_factors()

    References:
        - https://en.wikipedia.org/wiki/Trial_division
        - https://en.wikipedia.org/wiki/Wheel_factorization
    r7  Nr   r<   Tr"  c              3   8   K   | ]  }t        |d z           ywr  rz  rF  s     ru   r   zget_divisors.<locals>.<genexpr>B   s     %JqeAEl%Js   c              3   ,   K   | ]  \  }}||z    y wr   r   )r   r  powers      ru   r   zget_divisors.<locals>.<genexpr>C   s      N -FeONr  )	r  rl  r  r  r~  r   r   r  r  )r;  unique_factorsunique_powerspowerss       ru   get_divisorsr     s     Z f}	
Qwd$'K,-335t*E %F!NM##%JM%JK N N14^V1LN N 	NNs   BBc                 l   t        t        |             }|d|t        |      z
  z  z  }t        ||      D cg c]  }|D ]  }|  }}}t	        t        |            }t        t        |            D ]  }t        d ||   D              ||<    t        t        t        |      ||            S c c}}w )a:  
    Find all possible factorizations with k factors.

    Ones are not present, unless because there are not enough factors.

    Args:
        num (int): The number of elements to arrange.
        k (int): The number of factors.
        sort (callable): The sorting function.
            This is passed to the `key` arguments of `sorted()`.
        reverse (bool): The sorting direction.
            This is passed to the `reverse` arguments of `sorted()`.
            If False, sorting is ascending.
            Otherwise, sorting is descending.

    Returns:
        factorizations (tuple[tuple[int]]): The possible factorizations.
            Each factorization has exactly `k` items.
            Eventually, `1`s are used to ensure the number of items.

    Examples:
        >>> nums = (32, 41, 46, 60)
        >>> for i in nums:
        ...     get_k_factors_all(i, 2)
        ((2, 16), (4, 8), (8, 4), (16, 2))
        ((1, 41), (41, 1))
        ((2, 23), (23, 2))
        ((2, 30), (3, 20), (4, 15), (5, 12), (6, 10), (10, 6), (12, 5), (15, 4), (20, 3), (30, 2))
        >>> for i in nums:
        ...     get_k_factors_all(i, 3)
        ((2, 2, 8), (2, 4, 4), (2, 8, 2), (4, 2, 4), (4, 4, 2), (8, 2, 2))
        ((1, 1, 41), (1, 41, 1), (41, 1, 1))
        ((1, 2, 23), (1, 23, 2), (2, 1, 23), (2, 23, 1), (23, 1, 2), (23, 2, 1))
        ((2, 2, 15), (2, 3, 10), (2, 5, 6), (2, 6, 5), (2, 10, 3), (2, 15, 2), (3, 2, 10), (3, 4, 5), (3, 5, 4), (3, 10, 2), (4, 3, 5), (4, 5, 3), (5, 2, 6), (5, 3, 4), (5, 4, 3), (5, 6, 2), (6, 2, 5), (6, 5, 2), (10, 2, 3), (10, 3, 2), (15, 2, 2))
    r/   c              3   J   K   | ]  }t        j                  d  |        yw)c                     | |z  S r   r   r  s     ru   r  z-get_k_factors_all.<locals>.<genexpr>.<lambda>~   
    !a% rw   Nr   r  r   r  s     ru   r   z$get_k_factors_all.<locals>.<genexpr>}   s%      "P89I/3"P   !#r  r  )r   r~  r   r  r  rj  r{  rl  )	r;  r'   sortr  factorssubitemsr   factorizationsrM   s	            ru   get_k_factors_allr  H   s    Z K$%Gtq3w<'((G *'15  	N  #n-.N3~&' P! "P=KA=N"P PqP N+wGHHs   B0c                    |dkD  rt        t        |             }t        |      |k  r!|j                  dg|t        |      z
  z         d}|dv rt	        |      }n(|dv rt	        |d      }n|dk(  rt        |       n|j                  d      r8t        |t        d      d       }t        j                  |       t        |       n|j                  d	      r<	 t        |t        d	      d       t        |      dz
  z  }||dd   ddd   ||dd<   nm|dv rit        j                  d|      D cg c]  }g  }}t	        |d      D ]$  }	t	        |d       }|d
   j                  |	       & t	        |t        d      }|st!        ||d|      }t#        d |D              }
|
S | f}
|
S # t        $ r d
}Y w xY wc c}w )a  
    Generate a factorization of a number with k factors.

    Each factor contains (approximately) the same number of prime factors.

    Args:
        num (int): The number of elements to arrange.
        k (int): The number of factors.
        mode (str): The generation mode.
            This determines the factors order before splitting.
            The splitting itself is obtained with `chunks()`.
            Accepted values are:
             - 'increasing', 'ascending', '+': factors are sorted increasingly
               before splitting;
             - 'decreasing', 'descending', '-': factors are sorted decreasingly
               before splitting;
             - 'random': factors are shuffled before splitting;
             - 'seedX' where 'X' is an int, str or bytes: same as random, but
               'X' is used to initialize the random seed;
             - 'altX' where 'X' is an int: starting from 'X', factors are
               alternated before splitting;
             - 'alt1': factors are alternated before splitting;
             - 'optimal', 'similar', '!', '=': factors have the similar sizes.
        balanced (bool): Balance the number of primes in each factor.
            See `flyingcircus.chunks()` for more info.

    Returns:
        tuple (int): A listing of `k` factors of `num`.

    Examples:
        >>> [get_k_factors(402653184, k) for k in range(3, 6)]
        [(1024, 768, 512), (192, 128, 128, 128), (64, 64, 64, 48, 32)]
        >>> [get_k_factors(402653184, k) for k in (2, 12)]
        [(24576, 16384), (8, 8, 8, 8, 6, 4, 4, 4, 4, 4, 4, 4)]
        >>> get_k_factors(6, 4)
        (3, 2, 1, 1)
        >>> get_k_factors(-12, 4)
        (3, 2, 2, -1)
        >>> get_k_factors(0, 4)
        (1, 1, 1, 0)
        >>> get_k_factors(720, 4)
        (6, 6, 5, 4)
        >>> get_k_factors(720, 4, '+')
        (4, 4, 9, 5)
        >>> get_k_factors(720, 3)
        (12, 10, 6)
        >>> get_k_factors(720, 3, '+')
        (8, 6, 15)
        >>> get_k_factors(720, 3, mode='-')
        (45, 4, 4)
        >>> get_k_factors(720, 3, mode='seed0')
        (18, 10, 4)
        >>> get_k_factors(720, 3, 'alt')
        (30, 4, 6)
        >>> get_k_factors(720, 3, 'alt1')
        (12, 6, 10)
        >>> get_k_factors(720, 3, '=')
        (12, 10, 6)
    r/   N)
increasing	ascendingr]  )
decreasing
descendingr_  Tr"  r  seedaltr   r9   r<   )optimalsimilar!=c                 8    t        |       dkD  rt        |       S dS )Nr   )r   r  r  s    ru   r  zget_k_factors.<locals>.<lambda>   s    SVaZ$q' Q rw   r/  r  r]  )r   rh  c              3   J   K   | ]  }t        j                  d  |        yw)c                     | |z  S r   r   r  s     ru   r  z)get_k_factors.<locals>.<genexpr>.<lambda>   r  rw   Nr  r  s     ru   r   z get_k_factors.<locals>.<genexpr>   s%      E89I/3Er  )r  r~  r   r   rl  r  rs   auto_convertr  r  rY   r"  r   repeatr   r  rm  r   )r;  r'   r   rh  r  groupsr  rM   r  r  factorizations              ru   get_k_factorsr     s   @ 	1u{3'(w<!NNA3!c'l"23433WoG66Wd3GXG__V$S[\ 23DKKG__U#SZ[)*c'lQ.>? $ADqDM$B$/GADqDM55"+"2"24";<Qb<F< $7 ) FHq	  () Fd;FGQS8DF E=CE E
  %   =s   &F/ 4	G /F=<F=c                 (    t        |       | d d d   fS )Nr<   )rV  r  s    ru   r  r     s    A$B$( rw   c                 <    t        | |      }t        |||      d   S )aA  
    Find the optimal shape for arranging n elements into a rank-k tensor.

    Args:
        num (int): The number of elements to arrange.
        dims (int): The rank of the tensor.
        sort (callable): The function defining optimality.
            The factorization that minimizes (or maximizes)
            This is passed to the `key` arguments of `sorted()`.
        reverse (bool): The sorting direction.
            This is passed to the `reverse` arguments of `sorted()`.
            If False, sorting is ascending and the minimum of the optimization
            function is picked.
            Otherwise, sorting is descending and the maximum of the
            optimization
            function is picked.

    Returns:
        ratios (tuple[int]): The optimal ratio for tensor dims.

    Examples:
        >>> n1, n2 = 40, 46
        >>> [optimal_shape(i) for i in range(n1, n2)]
        [(8, 5), (41, 1), (7, 6), (43, 1), (11, 4), (9, 5)]
        >>> [optimal_shape(i, sort=max) for i in range(n1, n2)]
        [(5, 8), (1, 41), (6, 7), (1, 43), (4, 11), (5, 9)]
        >>> [optimal_shape(i, sort=min) for i in range(n1, n2)]
        [(2, 20), (1, 41), (2, 21), (1, 43), (2, 22), (3, 15)]
        >>> [optimal_shape(i, 3) for i in range(n1, n2)]
        [(5, 4, 2), (41, 1, 1), (7, 3, 2), (43, 1, 1), (11, 2, 2), (5, 3, 3)]
    r  r   )r  rl  )r;  dimsr  r  r  s        ru   optimal_shaper     s%    H 'sD1N.dG<Q??rw   c                     |r
|| |z  }} |r
| S )a  
    Compute the greatest common divisor (GCD) of a and b.

    Unless b==0, the result will have the same sign as b (so that when
    b is divided by it, the result comes out positive).

    Examples:
        >>> _gcd(123, 45)
        3
        >>> _gcd(45, 123)
        3
        >>> _gcd(0, 1)
        1
        >>> _gcd(-3, 1)
        1
        >>> _gcd(211815584, 211815584)
        211815584

    Note:
        This should never be used as `math.gcd` offers identical functionality,
        but it is faster.
    r   r  s     ru   _gcdr  !  s    . !a%1 Hrw   c                 |    t        |       }t        |      }|D ]   }t        j                  ||      }|dk(  s |S  |S )a  
    Find the greatest common divisor (GCD) of a list of numbers.

    Args:
        values (Iterable[int]): The input numbers.

    Returns:
        result (int): The value of the greatest common divisor (GCD).

    Examples:
        >>> gcd((12, 24, 18))
        6
        >>> gcd((12, 24, 18, 42, 600, 66, 666, 768))
        6
        >>> gcd((12, 24, 18, 42, 600, 66, 666, 768, 101))
        1
        >>> gcd((12, 24, 18, 3))
        3
    r/   r   r   rd  rh  )rZ  iter_valuesr   r<  s       ru   rh  rh  1!  sL    ( v,K+F &#&Q;M	 Mrw   c                      t        |       S )a  
    Star magic version of `flyingcircus.gcd()`.

    Examples:
        >>> gcd_(12, 24, 18)
        6
        >>> gcd_(12, 24, 18, 42, 600, 66, 666, 768)
        6
        >>> gcd_(12, 24, 18, 42, 600, 66, 666, 768, 101)
        1
        >>> gcd_(12, 24, 18, 3)
        3
    )rh  rZ  s    ru   gcd_r  O!  s     v;rw   c                 x    t        |       }t        |      }|D ]  }||t        j                  ||      z  z  }  |S )aa  
    Find the least common multiple (LCM) of a list of numbers.

    Args:
        values (Iterable[int]): The input numbers.

    Returns:
        result (int): The value of the least common multiple (LCM).

    Examples:
        >>> lcm((2, 3, 4))
        12
        >>> lcm((9, 8))
        72
        >>> lcm((12, 23, 34, 45, 56))
        985320
    r  )rZ  r  r   r[  s       ru   lcmr  a!  sE    $ v,K+F 3%488FE2223Mrw   c                      t        |       S )z
    Star magic version of `flyingcircus.lcm()`.

    Examples:
        >>> lcm_(2, 3, 4)
        12
        >>> lcm_(9, 8)
        72
        >>> lcm_(12, 23, 34, 45, 56)
        985320
    )r  )r   s    ru   lcm_r  {!  s     u:rw   c                 r    t        j                  | |      }|r|s|dk  r| r|  |z  | |z  fS | |z  ||z  fS y)a"  
    Simply a fraction.

    Args:
        a (int): The numerator.
        b (int): The denominator.
        sign (bool): Keep the sign in both the numerator and the denominator.
            If False, the sign is kept only in the numerator,
            unless the numerator is 0.

    Returns:
        result (tuple[int]): The tuple contains:
            - `a_n`: The simplified numerator.
            - `b_n`: The simplified denominator.

    Examples:
        >>> simplify_frac(12 * 7, 13 * 7)
        (12, 13)
        >>> simplify_frac(123, 321, True)
        (41, 107)
        >>> simplify_frac(123, -321, True)
        (41, -107)
        >>> simplify_frac(-123, 321, True)
        (-41, 107)
        >>> simplify_frac(-123, -321, True)
        (-41, -107)
        >>> simplify_frac(123, 321, False)
        (41, 107)
        >>> simplify_frac(123, -321, False)
        (-41, 107)
        >>> simplify_frac(-123, 321, False)
        (-41, 107)
        >>> simplify_frac(-123, -321, False)
        (41, 107)
        >>> simplify_frac(1234, 0)
        (1, 0)
        >>> simplify_frac(0, 1234)
        (0, 1)
        >>> simplify_frac(-1234, 0)
        (-1, 0)
        >>> simplify_frac(0, -1234)
        (0, -1)
        >>> simplify_frac(0, 0)
        (0, 0)
        >>> simplify_frac(-0, 0)
        (0, 0)
    r   )r   r   rg  )r5   rI   r  rh  s       ru   simplify_fracr  !  sO    f ((1a.C
Q129qbCi''8Q#X%%rw   c           	         |P	 t        |       }	 t        |      }|dk  r|dk  rt        d      |dkD  r|dkD  rt        ||      }nt	        ||      }	 | d| } 	 |d| }	 t        |       }	 t        |      }d}	d\  }
}t        ||t        |            D ]  \  }	}}||
|z  z   |
|	z  }}
 ||	z  }t        |
|      S # t        $ r d}Y w xY w# t        $ r d}Y w xY w# t        $ r Y w xY w# t        $ r Y w xY w# t        $ rb 	 t        t        | t        |            D cg c]  \  }}|	 nc c}}w c}}      }n%# t        $ r t        j                  | g      }Y nw xY wY w xY w# t        $ rc 	 t        t        |t        |            D cg c]  \  }}|	 nc c}}w c}}      }n%# t        $ r t        j                  |g      }Y nw xY wY ]w xY w)u  
    Compute a fractional approximation of a continued fraction.

    The continued fraction is expressed in terms of the denominators and the
    numerators sequences:

    .. math::

        \frac{a}{b} = b_0 + \frac{a_1}{b_1 +} \frac{a_2}{b_2 +} \ldots

    Args:
        b (int|Iterable[int]): Generalized continued fraction denominators.
            This should include also the first integer.
            The first `limit` values are used.
            All but the first value must be positive.
        a (int|Iterable[int]): Generalized continued fraction numerators.
            The first value does not contribute to `a_n / b_n` and can
            safely be set to 1.
            All values must be positive.
        limit (int|None): Maximum number of iterations to produce.

    Returns:
        result (tuple[int]): The tuple contains:
            - `a_n`: The numerator at the specified iteration level.
            - `b_n`: The denominator at the specified iteration level.

    Examples:
        >>> num, den = cont_frac([1, 2])
        >>> print(num, den, round(num / den, 8))
        3 2 1.5

        >>> num, den = cont_frac([0, 1, 5, 2, 2])
        >>> print(num, den, round(num / den, 8))
        27 32 0.84375

        >>> num, den = cont_frac([2, 2, 2], [1, 2, 2])
        >>> print(num, den, round(num / den, 8))
        8 3 2.66666667

        >>> num, den = cont_frac([2, 2, 2, 2], [1, 2, 2, 2])
        >>> print(num, den, round(num / den, 8))
        11 4 2.75

        >>> num, den = cont_frac([1, 2, 3, 4], [1, 2, 3, 4])
        >>> print(num, den, round(num / den, 8))
        19 11 1.72727273

        >>> num, den = cont_frac(range(1, 10), range(1, 20), 4)
        >>> print(num, den, round(num / den, 8))
        19 11 1.72727273

        >>> num, den = cont_frac(2, 2, 3)
        >>> print(num, den, round(num / den, 8))
        8 3 2.66666667

        >>> num, den = cont_frac([1, 2, 3, 4], 2)
        >>> print(num, den, round(num / den, 8))
        16 9 1.77777778

        >>> num, den = cont_frac(2, [1, 2, 3, 4])
        >>> print(num, den, round(num / den, 8))
        30 11 2.72727273

        >>> num, den = cont_frac(2, [12, 2, 3, 4])
        >>> print(num, den, round(num / den, 8))
        30 11 2.72727273

        Computing √2

        >>> n = 12
        >>> b = [1] + [2] * (n - 1)
        >>> num, den = cont_frac(b)
        >>> print(num, den, round(num / den, 8))
        19601 13860 1.41421356

        Computing Archimede's constant π

        >>> num, den = cont_frac([3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1])
        >>> print(num, den, round(num / den, 8))
        5419351 1725033 3.14159265
        >>> n = 16
        >>> b = [0] + [(2 * i + 1) for i in range(n - 1)]
        >>> a = [1, 4] + [(i + 1) ** 2 for i in range(n - 2)]
        >>> num, den = cont_frac(b, a)
        >>> print(num, den, round(num / den, 8))
        10392576224 3308059755 3.14159265
        >>> n = 16
        >>> b = [3] + [6] * (n - 1)
        >>> a = [1] + [(2 * i + 1) ** 2 for i in range(n - 1)]
        >>> num, den = cont_frac(b, a)
        >>> print(num, den, round(num / den, 8))
        226832956041173 72201776446800 3.14165339
        >>> n = 16
        >>> b = [0, 1] + [2] * (n - 2)
        >>> a = [1, 4] + [(2 * i + 1) ** 2 for i in range(n - 2)]
        >>> num, den = cont_frac(b, a)
        >>> print(num, den, round(num / den, 8))
        467009482388 145568097675 3.20818565

        Computing Euler's Number e

        >>> n = 16
        >>> b = [2] + [2 * i // 3 if not i % 3 else 1 for i in range(2, n + 1)]
        >>> num, den = cont_frac(b)
        >>> print(num, den, round(num / den, 8))
        566827 208524 2.71828183

        Computing Golden ratio φ

        >>> n = 24
        >>> num, den = cont_frac(1, 1, n)
        >>> print(num, den, round(num / den, 8))
        75025 46368 1.61803399
    Nr<   r   z.Limit must be specified for given `a` and `b`.r/   )r/   r   )r   r   r"  r  r   r  r  r{  r   r   r  )rI   r5   r%  len_blen_ar_iter_brH   r  r_iter_aa_ia_nb_nb_is                ru   	cont_fracr  !  s   f }	FE	FE 19MNNQY519u%Eu%EfuIfuI,A;,A; CHC8XuU|< .S!s?C#IS.CKCc""O  	E	  	E	      ,	,s1eEl/C Dtq! D DEH 	, s+H	,,  ,	,s1eEl/C Dtq! D DEH 	, s+H	,,s   B; C C C,  C; ,E) ;C	C	CC	C)(C),	C87C8;	E&D>"D/.D>=E&>E E&E  E&%E&)	G3F,FF,+G,GGGGGc              #     K   |j                         }t        |      s|f}|dk(  r6t        |       dz
  |D ]"  t        fdt	        |       D               $ y|dk(  r|D ]  d}| D ]
  }|z  |z   } |  yyw)u  
    Yield the result of the polynomial evaluation at given points.

    y_i = sum(p_i * x_i ** i for i, p_i in enumerate(p[::-1]))

    Args:
        p (Sequence[Number]): The coefficients of the polynomial.
            Must be given in decreasing order.
        x (Number|Iterable[Number]): The evaluation point(s).
        method (str): Polynomial computation method.
            Accepted values are:
             - 'direct': use the direct computation (slower)
             - 'horner': use Horner's formula (faster, but less accurate)

    Yields:
        y_i (Number): The evaluated polynomial.

    Examples:
        >>> list(polynomial([1, 2, 3], range(16)))
        [3, 6, 11, 18, 27, 38, 51, 66, 83, 102, 123, 146, 171, 198, 227, 258]
        >>> list(polynomial([4, 0, 2, 1], range(10)))
        [1, 7, 37, 115, 265, 511, 877, 1387, 2065, 2935]
        >>> list(polynomial([1, -2], range(-8, 8)))
        [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]
        >>> list(polynomial([1], range(-8, 8)))
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        >>> list(polynomial([1, 0], range(-8, 8)))
        [-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7]
        >>> list(polynomial([4, 0, 2, 1], 1))
        [7]
        >>> list(polynomial([1, 2, 3], range(16), 'direct'))
        [3, 6, 11, 18, 27, 38, 51, 66, 83, 102, 123, 146, 171, 198, 227, 258]

    References:
        - Horner, W. G., and Davies Gilbert. “XXI. A New Method of Solving
          Numerical Equations of All Orders, by Continuous Approximation.”
          Philosophical Transactions of the Royal Society of London 109 (
          January 1, 1819): 308–35. https://doi.org/10.1098/rstl.1819.0023.
        - https://en.wikipedia.org/wiki/Horner%27s_method
    directr/   c              3   :   K   | ]  \  }}||z
  z  z    y wr   r   )r   rM   p_ir>   x_is      ru   r   zpolynomial.<locals>.<genexpr>"  s"     Fvq#cCAEN*Fr  hornerr   N)r^  r  r   rV  r  )r3   rH   methody_ir  r>   r  s        @@ru   
polynomialr  j"  s     X \\^F1:DFQJ 	GCF1FFF	G	8	 	CC &Ci#o&I		 
s   A=Bc                 0    t        |       t        |       z  S )a  
    Compute the arithmetic mean of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_amean()`
     - `flyingcircus.i_amean()`
     - `flyingcircus.i_mean()`

    This is substantially faster than `statistics.mean()`.

    Args:
        seq (Sequence[Number]): The input items.

    Returns:
        result (Number): The arithmetic mean.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean(items)
        25.0
        >>> statistics.mean(items) == mean(items)
        True

        >>> [mean(range(n + 1)) for n in range(10)]
        [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]

    See Also:
        - flyingcircus.gmean()
        - flyingcircus.hmean()
        - flyingcircus.mean_and_soad()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.mean_and_var()
        - flyingcircus.mean_and_stdev()
        - flyingcircus.mean_and_mean_abs_dev()
        - flyingcircus.median()
        - flyingcircus.next_amean()
        - flyingcircus.i_amean()
        - flyingcircus.i_mean()
    )rV  r   r1  s    ru   meanr  "  s    P s8c#hrw   c                 h    |r| rt        d | D              } | syt        |       dt        |       z  z  S )a  
    Compute the geometric mean of a numeric sequence.

    Args:
        seq (Sequence[Number]): The input items.
        valid (bool): Include only valid (non-zero) items.

    Returns:
        result (Number): The geometric mean.

    Examples:
        >>> items = range(0, 52, 2)
        >>> round(gmean(items), 3)
        20.354
        >>> gmean(items, False)
        0.0

        >>> [round(gmean(range(n + 1)), 3) for n in range(10)]
        [1.0, 1.0, 1.414, 1.817, 2.213, 2.605, 2.994, 3.38, 3.764, 4.147]

    See Also:
        - flyingcircus.mean()
        - flyingcircus.hmean()
        - flyingcircus.median()
        - flyingcircus.next_gmean()
        - flyingcircus.i_gmean()
    c              3   &   K   | ]	  }|s|  y wr   r   r   rM   s     ru   r   zgmean.<locals>.<genexpr>"  s     (!aA(r        ?r/   )r   r  r   r   r  s     ru   gmeanr  "  s6    < (s((9SX&&rw   c                 p    |rt        d | D              } | syt        d | D              } dt        |       z  S )aM  
    Compute the harmonic mean of a numeric sequence.

    Args:
        seq (Sequence[Number]): The input items.
            The values within the sequence should be numeric.
        valid (bool): Include only valid (non-zero) items.

    Returns:
        result (Number): The harmonic mean.

    Examples:
        >>> items = range(0, 52, 2)
        >>> round(hmean(items), 3)
        13.103
        >>> hmean(items, False)
        Traceback (most recent call last):
            ...
        ZeroDivisionError: division by zero

        >>> [round(hmean(range(n + 1)), 3) for n in range(10)]
        [0.0, 1.0, 1.333, 1.636, 1.92, 2.19, 2.449, 2.7, 2.943, 3.181]

    See Also:
        - flyingcircus.mean()
        - flyingcircus.gmean()
        - flyingcircus.median()
        - flyingcircus.next_hmean()
        - flyingcircus.i_hmean()
    c              3   ,   K   | ]  }|sd |z    ywr  r   r  s     ru   r   zhmean.<locals>.<genexpr>#  s     .AQU.s   
        c              3   &   K   | ]	  }d |z    ywr  r   r  s     ru   r   zhmean.<locals>.<genexpr>#  s     )QU)s   r/   )r   r  r  s     ru   hmeanr  "  s<    B .S..)S))tCy=rw   c              #   :   K   |D ]  }t        | |z
          yw)a  
    Yield the absolute deviations of the items from a value.

    Args:
        value (Number): The input value.
        seq (Sequence[Number]): The input items.

    Yields:
        result (Number): The next absolute deviation.

    Examples:
        >>> list(absolute_deviations(10, range(16)))
        [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5]

    See Also:
        - flyingcircus.squared_deviations()
        - flyingcircus.soad()
        - flyingcircus.mean_and_soad()
        - flyingcircus.mean_abs_dev()
    N)r!  r[  r   r   s      ru   absolute_deviationsr  ##  s&     .   %$, s   c              #   4   K   |D ]  }| |z
  | |z
  z    yw)a  
    Yield the squared deviations of the items from a value.

    Args:
        value (Number): The input value.
        seq (Sequence[Number]): The input items.

    Yields:
        result (Number): The next squared deviation.

    Examples:
        >>> list(squared_deviations(10, range(16)))
        [100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25]

    See Also:
        - flyingcircus.absolute_deviations()
        - flyingcircus.sosd()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.var()
    Nr   r  s      ru   squared_deviationsr  ?#  s*     .  .t|--.s   c                 J    t        |       }t        t        ||             }||fS )a  
    Compute the mean and the sum-of-absolute-deviations of a numeric sequence.

    The sum-of-absolute-deviations (SoAD) is useful for computing the
    mean absolute deviation (MeanAD).

    Args:
        seq (Sequence[Number]): The input items.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_val` (Number): The mean of the items.
             - `soad_val` (Number): The SoAD of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_and_soad(items)
        (25.0, 338.0)
        >>> mean_and_soad(items) == (mean(items), soad(items))
        True

    See Also:
        - flyingcircus.absolute_deviations()
        - flyingcircus.mean()
        - flyingcircus.soad()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.mean_and_mean_abs_dev()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_mean_and_sosd()
    )r  rV  r  )r   mean_valsoad_vals      ru   mean_and_soadr  [#  s+    @ CyH&x56HXrw   c                 >    t        t        t        |       |             S )aL  
    Compute the sum-of-absolute-deviations of a numeric sequence.

    The sum-of-absolute-deviations (SoAD) is useful for computing the
    mean absolute deviation (MeanAD).

    Args:
        seq (Sequence[Number]): The input items.

    Returns:
        result (Number): The sum-of-absolute-deviations (SoAD) of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> soad(items)
        338.0

    See Also:
        - flyingcircus.absolute_deviations()
        - flyingcircus.mean_and_soad()
        - flyingcircus.sosd()
        - flyingcircus.mean_and_sosd()
    )rV  r  r  r1  s    ru   soadr  #  s    0 "49c233rw   c                 J    t        |       }t        t        ||             }||fS )aA  
    Compute the mean and the sum-of-squared-deviations of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_mean_and_sosd()`
     - `flyingcircus.i_mean_and_sosd()`

    The sum-of-squared-deviations (SoSD) is useful for numerically stable
    computation of the variance and the standard deviation.

    Args:
        seq (Sequence[Number]): The input items.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_val` (Number): The mean of the items.
             - `sosd_val` (Number): The SoSD of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_and_sosd(items)
        (25.0, 5850.0)
        >>> mean_and_sosd(items) == (mean(items), sosd(items))
        True

    See Also:
        - flyingcircus.squared_deviations()
        - flyingcircus.mean()
        - flyingcircus.sosd()
        - flyingcircus.mean_and_soad()
        - flyingcircus.mean_and_var()
        - flyingcircus.mean_and_stdev()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_mean_and_sosd()
    )r  rV  r  )r   r  sosd_vals      ru   mean_and_sosdr  #  s+    J CyH%h45HXrw   c                 >    t        t        t        |       |             S )a  
    Compute the mean and the sum-of-squared-deviations of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_mean_and_sosd()`
     - `flyingcircus.i_mean_and_sosd()`

    The sum-of-squared-deviations (SoSD) is useful for numerically stable
    computation of the variance and the standard deviation.

    Args:
        seq (Sequence[Number]): The input items.

    Returns:
        result (Number): The sum-of-squared-deviations (SoSD) of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> sosd(items)
        5850.0

    See Also:
        - flyingcircus.squared_deviations()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.soad()
        - flyingcircus.sosd2var()
        - flyingcircus.var2sosd()
        - flyingcircus.sosd2stdev()
        - flyingcircus.stdev2sosd()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_mean_and_sosd()
    )rV  r  r  r1  s    ru   sosdr  #  s    B !$s)S122rw   c                     | ||z
  z  S )a  
    Compute the variance from the sum-of-squared-deviations.

    Args:
        sosd_ (Number): The sum-of-squared-deviations value.
        num (int): The number of items.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (Number): The variance value.

    Examples:
        >>> sosd2var(8, 3, 1)
        4.0

        >>> all([var2sosd(sosd2var(x, n, m), n, m) == x
        ...     for x in range(9) for n in range(3, 9) for m in range(3)])
        True

    See Also:
        - flyingcircus.sosd()
        - flyingcircus.var()
        - flyingcircus.var2sosd()
        - flyingcircus.sosd2stdev()
        - flyingcircus.stdev2sosd()
    r   sosd_r;  ddofs      ru   sosd2varr  #  s    < C$Jrw   c                     | ||z
  z  S )a  
    Compute the sum-of-squared-deviations from the variance.

    Args:
        var_ (Number): The variance value.
        num (int): The number of items.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (Number): The sum-of-squared-deviations value.

    Examples:
        >>> var2sosd(4, 3, 1)
        8

        >>> all([sosd2var(var2sosd(x, n, m), n, m) == x
        ...     for x in range(9) for n in range(3, 9) for m in range(3)])
        True

    See Also:
        - flyingcircus.sosd()
        - flyingcircus.var()
        - flyingcircus.sosd2var()
        - flyingcircus.sosd2stdev()
        - flyingcircus.stdev2sosd()
    r   )var_r;  r  s      ru   var2sosdr  $  s    < 3:rw   c                     | ||z
  z  dz  S )a  
    Compute the standard deviation from the sum-of-squared-deviations.

    Args:
        sosd_ (Number): The sum-of-squared-deviations value.
        num (int): The number of items.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (Number): The standard deviation value.

    Examples:
        >>> sosd2stdev(8, 3, 1)
        2.0

        >>> all([sosd2stdev(stdev2sosd(x, n, m), n, m) == x
        ...     for x in range(9) for n in range(3, 9) for m in range(3)])
        True

    See Also:
        - flyingcircus.sosd()
        - flyingcircus.stdev()
        - flyingcircus.sosd2var()
        - flyingcircus.var2sosd()
        - flyingcircus.stdev2sosd()
          ?r   r  s      ru   
sosd2stdevr  1$  s    < S4Z S((rw   c                     | | z  ||z
  z  S )a  
    Compute the sum-of-squared-deviations from the standard deviation.

    Args:
        stdev_ (Number): The variance value.
        num (int): The number of items.
        ddof (int): The number of degrees of freedom.

    Returns:
        sosd (Number): The sum-of-squared-deviations value.

    Examples:
        >>> stdev2sosd(2, 3, 1)
        8

        >>> all([abs(stdev2sosd(sosd2stdev(x, n, m), n, m) - x) < 1e-9
        ...     for x in range(9) for n in range(3, 9) for m in range(3)])
        True

    See Also:
        - flyingcircus.sosd()
        - flyingcircus.stdev()
        - flyingcircus.sosd2var()
        - flyingcircus.var2sosd()
        - flyingcircus.sosd2stdev()
    r   )stdev_r;  r  s      ru   
stdev2sosdr  S$  s    < VOd
++rw   c                 J    t        |       \  }}t        |t        |       |      S )a3  
    Compute the variance of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_mean_and_var()`
     - `flyingcircus.next_mean_and_sosd()` and `.sosd2var()`.
     - `flyingcircus.i_var()`
     - `flyingcircus.i_mean_and_var()`

    This is substantially faster than `statistics.variance()`.

    Note that the variance is the mean of squared deviations.

    Args:
        seq (Sequence[Number]): The input items.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (Number): The variance of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> var(items)
        225.0
        >>> statistics.variance(items) == var(items, 1)
        True

    See Also:
        - flyingcircus.i_var()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.mean_and_var()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.stdev()
        - flyingcircus.sosd2var()
        - flyingcircus.var2sosd()
    r  r  r   r   r  r  r  s       ru   varr  u$  s'    R 's+HhHc#h--rw   c                 J    t        |       \  }}t        |t        |       |      S )a  
    Compute the standard deviation of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_mean_and_stdev()`
     - `flyingcircus.next_mean_and_sosd()` and `.sosd2stdev()`.
     - `flyingcircus.i_stdev()`
     - `flyingcircus.i_mean_and_stdev()`

    This is substantially faster than `statistics.stdev()`.

    Note that the standard deviation is the square root of the variance,
    and hence it is also the square root of the mean of squared deviations.

    Args:
        seq (Sequence[Number]): The input items.
            The values within the sequence should be numeric.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (Number): The standard deviation of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> stdev(items)
        15.0
        >>> statistics.stdev(items) == stdev(items, 1)
        True

    See Also:
        - flyingcircus.i_stdev()
        - flyingcircus.i_mean_and_stdev()
        - flyingcircus.next_mean_and_stdev()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.mean_and_stdev()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.var()
        - flyingcircus.sosd2stdev()
        - flyingcircus.stdev2sosd()
    r  r  r   r  s       ru   stdevr  $  s'    V 's+HhhC$//rw   c                 N    t        |       \  }}|t        |t        |       |      fS )a  
    Compute the mean and variance of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_amean()`
     - `flyingcircus.next_mean_and_var()`
     - `flyingcircus.next_mean_and_sosd()` and `.sosd2var()`.
     - `flyingcircus.i_mean()`
     - `flyingcircus.i_var()`
     - `flyingcircus.i_mean_and_var()`
     - `flyingcircus.i_mean_and_sosd()` and `.sosd2var()`

    This is faster than computing the two values separately.

    Args:
        seq (Sequence[Number]): The input items.
            The values within the sequence should be numeric.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (Number): The mean of the items.
             - `var_` (Number): The variance of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_and_var(items)
        (25.0, 225.0)
        >>> mean_and_var(items) == (mean(items), var(items))
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.var()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.sosd2var()
        - flyingcircus.var2sosd()
    r  r   r  mean_r   s       ru   mean_and_varr  $  s+    Z !%LE5(5#c(D111rw   c                 N    t        |       \  }}|t        |t        |       |      fS )aL  
    Compute the mean and the standard deviation of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_amean()`
     - `flyingcircus.next_mean_and_stdev()`
     - `flyingcircus.next_mean_and_sosd()` and `.sosd2stdev()`.
     - `flyingcircus.i_mean()`
     - `flyingcircus.i_stdev()`
     - `flyingcircus.i_mean_and_stdev()`
     - `flyingcircus.i_mean_and_sosd()` and `.sosd2stdev()`

    This is faster than computing the two values separately.

    Args:
        seq (Sequence[Number]): The input items.
            The values within the sequence should be numeric.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (Number): The mean of the items.
             - `stdev_` (Number): The standard deviation of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_and_stdev(items)
        (25.0, 15.0)
        >>> mean_and_stdev(items) == (mean(items), stdev(items))
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.stdev()
        - flyingcircus.i_mean_and_stdev()
        - flyingcircus.next_mean_and_stdev()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.sosd2stdev()
        - flyingcircus.stdev2sosd()
    r  r  s       ru   mean_and_stdevr  %  s+    Z !%LE5*UCHd333rw   c                 B    t        |       \  }}|t        |       z  }||fS )a%  
    Compute the mean and the mean absolute deviation of a numeric sequence.

    This is faster than computing the two values separately.

    Args:
        seq (Sequence[Number]): The input items.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (Number): The mean of the items.
             - `mean_abs_dev_` (Number): The mean abs. dev. of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_and_mean_abs_dev(items)
        (25.0, 13.0)
        >>> mean_and_mean_abs_dev(items) == (mean(items), mean_abs_dev(items))
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.mean_abs_dev()
        - flyingcircus.mean_and_soad()
        - flyingcircus.soad()
        - flyingcircus.absolute_deviations()
    )r  r   )r   r  soad_mean_abs_dev_s       ru   mean_and_mean_abs_devr  7%  s+    : !%LE5CH$M-rw   c                 0    t        |       t        |       z  S )a  
    Compute the mean absolute deviation of a numeric sequence.

    Args:
        seq (Sequence[Number]): The input items.

    Returns:
        result (Number): The mean absolute deviation of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_abs_dev(items)
        13.0

    See Also:
        - flyingcircus.mean()
        - flyingcircus.mean_abs_dev()
        - flyingcircus.mean_and_soad()
        - flyingcircus.soad()
        - flyingcircus.absolute_deviations()
    )r  r   r1  s    ru   mean_abs_devr  Z%  s    , 9s3xrw   c                     t        |       }|dz  }|rt        |       n| }|dz  s!||dz
     ||   k7  r||dz
     ||   z   dz  }|S ||   }|S )a  
    Compute the median of a numeric sequence.

    For iterative computation see:
     - `flyingcircus.next_median()`
     - `flyingcircus.next_medoid_and_median()`
     - `flyingcircus.i_median()`
     - `flyingcircus.i_medoid_and_median()`
     - `flyingcircus.i_median_and_median_abs_dev()`

    This is roughly comparable to `statistics.median()`.

    If more than one among median, medoid, quantile, quantiloid is needed,
    it is more efficient to sort the items prior to calling and then
    perform the multiple required calle with `force_sort` set to False.

    Args:
        seq (Sequence[Number]): The input items.
        force_sort (bool): Force sorting of the input items.
            If the items are already sorted, this can be safely set to False.
            Otherwise, it should be set to True.

    Returns:
        result (Number): The median of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> median(items)
        25.0
        >>> statistics.median(items) == median(items)
        True

    See Also:
        - flyingcircus.median_and_median_abs_dev()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid_and_median()
        - flyingcircus.i_median()
        - flyingcircus.i_medoid_and_median()
        - flyingcircus.i_median_and_median_abs_dev()
        - flyingcircus.medoid()
        - flyingcircus.quantile()
        - flyingcircus.interquantilic_range()
        - flyingcircus.sym_interquantilic_range()
        - flyingcircus.median_abs_dev()
    r9   r/   )r   rl  )r   
force_sortr2   rM   sorted_itemsmedian_s         ru   medianr#  t%  sr    ` 	CA	QA",6#;#LE|AE*l1o=A&a8A= N q/Nrw   c                 d    t        | |      }t        t        ||             }|t        ||      fS )a  
    Compute the median and the median absolute deviation of a numeric sequence.

    Args:
        seq (Sequence[Number]): The input items.
        force_sort (bool): Force sorting of the input items.
            If the items are already sorted, this can be safely set to False.
            Otherwise, it must be set to True.

    Returns:
        result (Number): The median deviation of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> median_and_median_abs_dev(items)
        (25.0, 13.0)
        >>> (median_and_median_abs_dev(items) ==
        ...     (median(items), median_abs_dev(items)))
        True

    See Also:
        - flyingcircus.median()
        - flyingcircus.median_abs_dev()
        - flyingcircus.i_median()
        - flyingcircus.i_median_abs_dev()
        - flyingcircus.i_median_and_median_abs_dev()
        - flyingcircus.absolute_deviations()

    r   r#  r   r  )r   r   
median_valdelta_itemss       ru   median_and_median_abs_devr)  %  s5    @ 
3J+J<=KvkjAAArw   c           
      X    t        t        t        t        | |      |             |      S )a  
    Compute the median absolute deviation of a numeric sequence.

    Args:
        seq (Sequence[Number]): The input items.
        force_sort (bool): Force sorting of the input items.
            If the items are already sorted, this can be safely set to False.
            Otherwise, it must be set to True.

    Returns:
        result (Number): The median absolute deviation of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> median_abs_dev(items)
        13.0

    See Also:
        - flyingcircus.median()
        - flyingcircus.median_and_median_abs_dev()
        - flyingcircus.i_median()
        - flyingcircus.i_median_abs_dev()
        - flyingcircus.i_median_and_median_abs_dev()
        - flyingcircus.absolute_deviations()
    r%  r&  )r   r   s     ru   median_abs_devr+  %  s.    8 sz BCH	J rw   d   c                 x   t        |      }t        |ddd      }t        |       }|j                         }|rt	        |       n| }	|dz
  |z   }
|dz
  |z  }g }|D ]M  }|dk  rt        t        d            t        |t              r||kD  rt        t        d            t        |t              r|
r||z  }|	|   }nt        |t              r||z  }|dkD  rt        t        d            ||dz
  z  }t        |      }|dz  }t        |      |k  r|	|   }n|dk(  r|	t        t        |               }nc|d	k(  r|	|   }nX|d
k(  r	|	|dz      }nJ|dv r2|	|   }|	|dz      }||k(  r|	|   }n.|dk(  r|||z
  |z  z   }n||z   dz  }nt        t        d            |j                  |       P |rt        |      S |d   S )as  
    Compute the quantile of a numeric sequence.

    There is no efficient iterative version for any arbitrary factor.

    If more than one among median, medoid, quantile, quantiloid is needed,
    it is more efficient to sort the items prior to calling and then
    perform the multiple required calle with `force_sort` set to False.

    Args:
        seq (Sequence[Number]): The input items.
        factor (int|float|Iterable[int|float]): The quantile index.
            If float, must be a number in the [0, 1] range.
            If int, it is divided by `int_base` and its value must be in the
            [0, int_base] range.
        int_base (int|float): The denominator used to scale integer factors.
        interp (str): Interpolation for inexact quantiles.
            This determines
        tol (float): Tolerance for detecting exact indices.
        force_sort (bool): Force sorting of the input items.
            If the items are already sorted, this can be safely set to False.
            Otherwise, it must be set to True.

    Returns:
        result (Number|tuple[Number]): The quantiles of the item(s).
            Returns the tuple if `factor` is a sequence (of any length),
            otherwise returns a Number.

    Examples:
        >>> items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> quantile(items, 0.25)
        2.5
        >>> quantile(items, range(0, 101, 10))
        (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
        >>> ((min(items), median(items), max(items))
        ...     == quantile(items, (0.0, 0.5, 1.0)))
        True

        >>> interps = 'linear', 'lower', 'upper', 'midpoint', 'nearest'
        >>> [quantile([0, 1], 1, 2, interp) for interp in interps]
        [0.5, 0, 1, 0.5, 0]

        >>> [quantile([0, 1], 0.6, interp=interp) for interp in interps]
        [0.6, 0, 1, 0.5, 1]

        >>> [quantile([0, 1, 2], 1, 2, interp=interp) for interp in interps]
        [1, 1, 1, 1, 1]

        >>> [quantile([0, 1, 2], 0.5, interp=interp) for interp in interps]
        [1, 1, 1, 1, 1]

        >>> quantile([10, 11, 12], [0, 1, 2], 2)
        (10, 11, 12)

        >>> quantile([10, 11, 12], -1)
        Traceback (most recent call last):
            ...
        ValueError: Invalid factor `-1` (negative)
        >>> quantile([10, 11, 12], -0.1)
        Traceback (most recent call last):
            ...
        ValueError: Invalid factor `-0.1` (negative)
        >>> quantile([10, 11, 12], 101)
        Traceback (most recent call last):
            ...
        ValueError: Invalid integer factor `101` (larger than `int_base=100`)
        >>> quantile([10, 11, 12], 1.1)
        Traceback (most recent call last):
            ...
        ValueError: Invalid factor `1.1` (larger than 1.0)

    See Also:
        - flyingcircus.median()
        - flyingcircus.quantiloid()
        - flyingcircus.interquantilic_range()
        - flyingcircus.sym_interquantilic_range()
    r/   Fr   zInvalid factor `{k}` (negative)z@Invalid integer factor `{k}` (larger than `int_base={int_base}`)r  z&Invalid factor `{k}` (larger than 1.0)nearestr^  r\  )r  linearr/  r9   z!Invalid interpolation `{interp}`.)r  r  r   r^  rl  r"  r   r  rY   r!  rg  r   r   )r   r  int_baseinterptolr   	use_tuplekkr2   r!  is_exactexact_factorr   r'   rM   rH   int_ifrac_ir5   rI   s                       ru   quantiler9  %  s   j I	VQu	-BCA\\^F",6#;#LUh&'HEh&LF )q5679 93AL < => > a(L AQA!S!L3w ABD DQUAFEUF6{S  'Y&$Sq]3Aw&$U+Aw&$UQY/A55$U+A$UQY/AAv(/!X- !QUf$4 4A!"Q!A$T*M%NOOaS)T &5=46!94rw   c                     t        |       }|rt        t        |dz
  dz              n|dz  }|rt        |       n| }||   }|S )a  
    Compute the medoid of an arbitrary sequence.

    For some inputs `select_ordinal()` may be faster.

    If more than one among median, medoid, quantile, quantiloid is needed,
    it is more efficient to sort the items prior to calling and then
    perform the multiple required calls with `force_sort` set to False.

    For iterative computation see:
     - `flyingcircus.next_medoid()`
     - `flyingcircus.i_medoid()`

    If the number of items is not odd, returns the medoid from the lower or
    upper half depending on the value of `lower` being True or False.

    Args:
        seq (Sequence[Number|Any]): The input items.
            The values within the sequence do not need to be numeric.
        force_sort (bool): Force sorting of the input items.
            If the items are already sorted, this can be safely set to False.
            Otherwise, it should be set to True.
        lower (bool): Pick the lower medoid for even-sized items.

    Returns:
        result (Any): The medoid of the items.

    Examples:
        >>> items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> medoid(items)
        5
        >>> medoid(items, lower=False)
        5

        >>> items = range(0, 20, 2)
        >>> medoid(items)
        8
        >>> medoid(items, lower=False)
        10

        >>> items = string.ascii_lowercase
        >>> medoid(items)
        'm'
        >>> medoid(items, lower=False)
        'n'

    See Also:
        - flyingcirucs.select_ordinal()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
        - flyingcircus.i_medoid()
        - flyingcircus.i_medoid_and_median()
        - flyingcircus.median()
        - flyingcircus.quantiloid()
        - flyingcircus.interquantilic_range()
        - flyingcircus.sym_interquantilic_range()
    r/   r9   )r   rY   rg  rl  )r   r   r^  r2   rM   r!  medoid_s          ru   medoidr<  &  sH    z 	CA#(E1q5A+a1fA",6#;#L1oGNrw   c                     t        |       st        |ddd      }t        fd|D              }|rt        |       n| } t	        j
                  | |      }|S )a	  
    Compute the quantiloid of an arbitrary sequence.

    There is no efficient iterative version for any arbitrary factor.
    For some inputs `select_ordinal()` may be faster.

    If more than one among median, medoid, quantile, quantiloid is needed,
    it is more efficient to sort the items prior to calling and then
    perform the multiple required calle with `force_sort` set to False.

    Args:
        seq (Sequence[Number|Any]): The input items.
            The values within the sequence do not need to be numeric.
        factor (int|float|Iterable[int|float]): The quantile index.
            If float, must be a number in the [0, 1] range.
            If int, it is divided by `int_base` to get to the [0, 1] range.
        int_base (int|None): The denominator for scaling integer factors.
            If None (or zero), uses `len(items)`.
        rounding (callable): The rounding to use for determining the index.
            Use `round` to pick the closest index.
            Use `math.floor` to pick the lower index.
            Use `math.ceil` to pick the upper index.
        force_sort (bool): Force sorting of the input items.
            If the items are in the intended order, it should be set to False.
            Otherwise, it should be set to True.

    Returns:
        result (Any|tuple[Any]): The quantiloid of the item(s).
            Returns the tuple if `factor` is a sequence (of any length),
            otherwise returns an element of items.

    Examples:
        >>> items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> quantiloid(items, 0.25)
        2
        >>> quantiloid(items, range(0, 101, 10))
        (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
        >>> ((min(items), medoid(items), max(items))
        ...     == quantiloid(items, (0.0, 0.5, 1.0)))
        True

        >>> items = string.ascii_lowercase
        >>> quantiloid(items, 0.25)
        'g'
        >>> quantiloid(items, range(0, 101, 10))
        ('a', 'c', 'f', 'i', 'k', 'm', 'p', 's', 'u', 'w', 'z')
        >>> ((min(items), medoid(items), max(items))
        ...     == quantiloid(items, (0.0, 0.5, 1.0)))
        True

    See Also:
        - flyingcircus.medoid()
        - flyingcircus.quantile()
        - flyingcircus.interquantilic_range()
        - flyingcircus.sym_interquantilic_range()
    r/   Fc           	   3   v   K   | ]0  }t         t        |t              r|n|z  d z
  z               2 ywr  )rY   r  r_   )r   r'   r0  r2   roundings     ru   r   zquantiloid.<locals>.<genexpr>
'  s;       	H:a/aQ\a!eLMNs   69)r   r  r   rl  r^  r_  )	r   r  r0  r?  r   r4  r!  r   r2   s	     ``    @ru   
quantiloidr@  &  si    | 	CA	VQu	-B	  
B #-6#;#L%X  "%l3FMrw   g      ?g      ?c                 N    |t        |      ni } || ||f|fd|i|\  }}||z
  S )a  
    Compute the interquantilic range of a numeric sequence.

    If more than one among median, medoid, quantile, quantiloid is needed,
    it is more efficient to sort the items prior to calling and then
    perform the multiple required calle with `force_sort` set to False.

    Args:
        seq (Sequence[Number]): The input items.
            The values within the sequence may not need to be numeric,
            depending on the `quantilic_func` used.
        lower_factor (int|float): The lower quantilic index.
            If float, must be a number in the [0, 1] range.
            If int, it is divided by `int_base` to get to the [0, 1] range.
        upper_factor (int|float): The upper quantilic index.
            If float, must be a number in the [0, 1] range.
            If int, it is divided by `int_base` to get to the [0, 1] range.
        int_base (int|float): The denominator used to scale integer factors.
        force_sort (bool): Force sorting of the input items.
            If the items are already sorted, this can be safely set to False.
            Otherwise, it must be set to True.
        quantilic_func (callable): The quantilic function.
            Must be either `flyingcircus.quantile()` or
            `flyingcircus.quantiloid()` (or any other callable
            implementing the same signature).
        quantilic_kws (Mapping|None): Keyword parameters for `quantilic_func`.

    Returns:
        result (Number): The inter-quantilic range.

    Examples:
        >>> items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> interquantilic_range(items)
        5.0

    See Also:
        - flyingcircus.quantile()
        - flyingcircus.quantiloid()
        - flyingcircus.sym_interquantilic_range()
        - flyingcircus.median_abs_dev()
    r   )r  )	r   lower_factorupper_factorr0  r   quantilic_funcquantilic_kwsq_aq_bs	            ru   interquantilic_rangerH  '  sL    b ,9+DD'"MlL)8@J
HC 9rw   c                 0    t        | d|z
  d|z   |||      S )ar  
    Compute the symmetric interquantilic range of a numeric sequence.

    The quantilic range is defined as: [0.5 - sym_factor, 0.5 + sym_factor].

    If more than one among median, medoid, quantile, quantiloid is needed,
    it is more efficient to sort the items prior to calling and then
    perform the multiple required calle with `force_sort` set to False.

    Args:
        seq (Sequence[Number]): The input items.
        sym_factor (float): The symmetric quantilic factor.
            Must be a number in the [0, 0.5] range.
        force_sort (bool): Force sorting of the input items.
            If the items are already sorted, this can be safely set to False.
            Otherwise, it must be set to True.
        quantilic_func (callable): The quantilic function.
            Must be either `flyingcircus.quantile()` or
            `flyingcircus.quantiloid()` (or any other callable
            implementing the same signature).
        quantilic_kws (Mapping|None): Keyword parameters for `quantilic_func`.

    Returns:
        result (Number): The inter-quantilic range.

    Examples:
        >>> items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> sym_interquantilic_range(items)
        5.0

    See Also:
        - flyingcircus.quantile()
        - flyingcircus.quantiloid()
        - flyingcircus.interquantilic_range()
        - flyingcircus.median_abs_dev()
    r  )r   rD  rE  )rH  )r   
sym_factorr   rD  rE  s        ru   sym_interquantilic_rangerK  L'  s,    T  S:sZ/J%]D Drw   c                 (    ||z  | z   |dz   z  |dz   fS )a  
    Compute the arithmetic mean for (num + 1) items.

    This is useful for low memory footprint computation.

    Args:
        value (Number): The next value to consider.
        mean_ (Number): The aggregate mean of the previous n items.
        num (int): The number of items in the aggregate.

    Returns:
        result (tuple): The tuple
            contains:
             - mean_ (Number): The updated arithmetic mean.
             - num_ (int): The number of items included.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_ = 0.0
        >>> for i, val in enumerate(items):
        ...     mean_, num_ = next_amean(val, mean_, i)
        >>> print((mean_, num_))
        (25.0, 26)
        >>> mean_ == mean(items)
        True
        
    See Also:
        - flyingcircus.mean()
        - flyingcircus.i_amean()
        - flyingcircus.i_mean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.next_gmean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    r/   r   r[  r  r;  s      ru   
next_ameanrN  |'  s&    V %K%C!G,cAg55rw   c                 `    | s|s'|s| |dz   fS |||dz   z  z  | d|dz   z  z  z  }||dz   fS ||fS )a  
    Compute the geometric mean for (num + 1) items.

    This is useful for low memory footprint computation.

    Args:
        value (Number): The next value to consider.
        gmean_ (Number): The aggregate geometric mean of the previous n items.
        num (int): The number of items in the aggregate.
        valid (bool): Include only valid items.
            Valid items must be non-zero.

    Returns:
        result (tuple): The tuple
            contains:
             - gmean_ (Number): The updated geometric mean.
             - num_ (int): The number of items included.

    Examples:
        >>> items = range(0, 52, 2)
        >>> gmean_, num_ = 1.0, 0
        >>> for i in items:
        ...     gmean_, num_ = next_gmean(i, gmean_, num_)
        >>> print((round(gmean_, 3), num_))
        (20.354, 25)
        >>> gmean_ == gmean(items)
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.gmean()
        - flyingcircus.i_gmean()
        - flyingcircus.next_amean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    r/   r   )r[  gmean_r;  r  s       ru   
next_gmeanrQ  '  sY    Z E #'>!sQw05Q#']3KKF37?"s{rw   c                 T    | s|s!|s| |dz   fS |dz   ||z  d| z  z   z  }||dz   fS ||fS )a   
    Compute the harmonic mean for (num + 1) items.

    This is useful for low memory footprint computation.

    Args:
        value (Number): The next value to consider.
        hmean_ (Number): The aggregate harmonic mean of the previous n items.
        num (int): The number of items in the aggregate.
        valid (bool): Include only valid (non-zero) items.

    Returns:
        result (tuple): The tuple
            contains:
             - hmean_ (Number): The updated geometric mean.
             - num_ (int): The number of items included.

    Examples:
        >>> items = range(0, 52, 2)
        >>> hmean_, num_ = 0.0, 0
        >>> for i in items:
        ...     hmean_, num_ = next_hmean(i, hmean_, num_)
        >>> print((round(hmean_, 3), num_))
        (13.103, 25)
        >>> abs(hmean_ - hmean(items)) < 1e-14
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.hmean()
        - flyingcircus.i_hmean()
        - flyingcircus.next_amean()
        - flyingcircus.next_gmean()
        - flyingcircus.next_mean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    r/   r   )r[  hmean_r;  r  s       ru   
next_hmeanrT  '  sN    Z E#'>!Ag#,U":;F37?"s{rw   c                     ||z  | z   |dz   z  S )am  
    Compute the arithmetic mean for (num + 1) items.

    This is useful for low memory footprint computation.

    The difference between `flyingcircus.next_amean()` and
    `flyingcircus.next_mean()` is that the latter does return the number
    of items processed.

    Args:
        value (Number): The next value to consider.
        mean_ (Number): The aggregate mean of the previous n items.
        num (int): The number of items in the aggregate.

    Returns:
        result (tuple): The tuple
            contains:
             - mean_ (Number): The updated arithmetic mean.
             - num_ (int): The number of items included.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_ = 0.0
        >>> for i, val in enumerate(items):
        ...     mean_, num_ = next_amean(val, mean_, i)
        >>> print((mean_, num_))
        (25.0, 26)
        >>> mean_ == mean(items)
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.i_amean()
        - flyingcircus.i_mean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.next_amean()
        - flyingcircus.next_gmean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    r/   r   rM  s      ru   	next_meanrV  (  s    ` %K%C!G,,rw   c                 X    |}t        | ||      \  }}||z  | |z
  | |z
  z  z   |z  }|||fS )a  
    Compute the mean and variance for (num + 1) numeric items.

    This is useful for low memory footprint computation.

    Note that both mean and variance MUST be updated at each iteration,
    therefore a stand-alone `next_var()` is sub-optimal.

    Warning! This algorithm is not numerically stable.
    For better numerical stability use `next_mean_and_sosd()`.

    Args:
        value (Number): The next value to consider.
        mean_ (Number): The aggregate mean of the previous n items.
        var_ (Number): The aggregate variance of the previous n items.
        num (int): The number of items in the aggregate.

    Returns:
        result (tuple): The tuple
            contains:
             - mean (Number): The updated mean.
             - var (Number): The updated variance.
             - num_ (int): The number of items included.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_, var_, num_ = 0.0, 0.0, 0
        >>> for i in items:
        ...     mean_, var_, num_ = next_mean_and_var(i, mean_, var_, num_)
        >>> print((mean_, var_))
        (25.0, 225.0)
        >>> (mean_, var_) == mean_and_var(items)
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.var()
        - flyingcircus.i_amean()
        - flyingcircus.i_var()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.next_amean()
        - flyingcircus.next_gmean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    rN  )r[  r  r  r;  rg  num_s         ru   next_mean_and_varrZ  Q(  sI    l DUE3/KE4CZEDLUU];;tCD$rw   c                 L    |}t        | ||      \  }}|| |z
  | |z
  z  z  }|||fS )u  
    Compute the mean and sum-of-squared-deviations for (num + 1) numeric items.

    This is useful for low memory footprint computation.
    This algorithm is numerically stable.

    The sum-of-squared-deviations (SoSD) can be computed as below and
    it is equivalent to the variance multiplied by the number of items:

    sosd = sum((x_i - mu) ** 2)
    sosd = var * n

    Note that both mean and variance MUST be updated at each iteration,
    therefore a stand-alone `next_sosd()` is sub-optimal.

    Args:
        value (Number): The next value to consider.
        mean_ (Number): The aggregate mean of the previous n items.
        sosd_ (Number): The aggregate SoSD of the previous n items.
        num (int): The number of items in the aggregate value.

    Returns:
        result (tuple): The tuple
            contains:
             - mean_ (Number): The updated mean.
             - sosd_ (Number): The updated sum-of-squared-deviations.
             - num_ (int): The number of items included.

    Examples:
        >>> items = range(0, 52, 2)
        >>> mean_, sosd_, num_ = 0.0, 0.0, 0
        >>> for i in items:
        ...     mean_, sosd_, num_ = next_mean_and_sosd(i, mean_, sosd_, num_)
        >>> print((mean_, sosd_, num_))
        (25.0, 5850.0, 26)
        >>> (mean_, sosd_) == mean_and_sosd(items)
        True

    References:
         - Welford, B.P. (1962). "Note on a method for calculating corrected
           sums of squares and products". Technometrics 4(3):419–420.
           doi:10.2307/1266577

    See Also:
        - flyingcircus.squared_deviations()
        - flyingcircus.mean()
        - flyingcircus.sosd()
        - flyingcircus.mean_and_sosd()
        - flyingcircus.mean_and_var()
        - flyingcircus.mean_and_stdev()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.next_amean()
        - flyingcircus.next_gmean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    rX  )r[  r  r   r;  rg  s        ru   next_mean_and_sosdr\  (  s@    D DE5#.JE3	edluu}--E%rw   c                     |s| }|j                  |        |S t        j                  ||        t        |d      }t	        |      |kD  r| |kD  r|d= |S |d= |S )ax  
    Compute the (approximate) median for (num + 1) items.

    This computes an approximate value.
    Relies on the robustness of the median.
    Requires the values to be shuffled (within at least half the buffer size),
    or that the buffer size is larger than the number of values.

    This is useful for low memory footprint approximate computation.

    Args:
        value (Number): The next value to consider.
        buffer (list): The buffer from previous iterations.
        max_buffer (int): The maximum size of the buffer.
            If `max_buffer >= len(items)` the result is exact.

    Returns:
        median_ (Number): The updated median.

    Examples:
        >>> items = range(0, 52, 2)
        >>> buffer = []
        >>> for i in items:
        ...     median_ = next_median(i, buffer)
        >>> print(median_)
        25.0

        >>> buffer = []
        >>> for i in items:
        ...     median_ = next_median(i, buffer, max_buffer=4)
        >>> print(median_)
        46

        >>> items = list(items)
        >>> random.seed(0)
        >>> items = shuffle(items)
        >>> buffer = []
        >>> for i in items:
        ...     median_ = next_median(i, buffer, max_buffer=4)
        >>> print(median_)
        12

    See Also:
        - flyingcircus.median()
        - flyingcircus.i_median()
        - flyingcircus.next_amean()
        - flyingcircus.next_gmean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    Fr   r<   )r   bisectinsortr#  r   )r[  rx  
max_bufferr"  s       ru   next_medianra  (  sk    t e N 	fe$'v;#w1I N 2JNrw   c                     |s| }|j                  |        |S t        j                  ||        t        |d|      }t	        |      |kD  r| |kD  r|d= |S |d= |S )a  
    Compute the (approximate) medoid for (num + 1) items.

    This computes an approximate value.
    Relies on the robustness of the medoid.
    Requires the values to be shuffled (within at least half the buffer size),
    or that the buffer size is larger than the number of values.

    This is useful for low memory footprint approximate computation.
    If the number of items is not odd, returns the medoid from the lower or
    upper half depending on the value of `lower` being True or False.

    Args:
        value (Any): The next value to consider.
        buffer (list): The buffer from previous iterations.
        lower (bool): Pick the lower medoid for even-sized buffer.
        max_buffer (int): The maximum size of the buffer.
            If `max_buffer >= len(items)` the result is exact.

    Returns:
        result (Any): The updated approximate medoid value.

    Examples:
        >>> items = range(0, 52, 2)
        >>> buffer = []
        >>> for i in items:
        ...     medoid_ = next_medoid(i, buffer)
        >>> print(medoid_)
        24

        >>> buffer = []
        >>> for i in items:
        ...     medoid_ = next_medoid(i, buffer, max_buffer=4)
        >>> print(medoid_)
        46

        >>> items = list(items)
        >>> random.seed(0)
        >>> items = shuffle(items)
        >>> buffer = []
        >>> for i in items:
        ...     medoid_ = next_medoid(i, buffer, max_buffer=4)
        >>> print(medoid_)
        12

    See Also:
        - flyingcircus.medoid()
        - flyingcircus.i_medoid()
        - flyingcircus.i_medoid_and_median()
        - flyingcircus.next_amean()
        - flyingcircus.next_gmean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
    Fr   r<   )r   r^  r_  r<  r   )r[  rx  r^  r`  r;  s        ru   next_medoidrc  !)  sm    @ e N 	fe$.v;#w1I N 2JNrw   c                     |s| }| }|j                  |        ||fS t        j                  ||        t        |d|      }t	        |d      }t        |      |kD  r| |kD  r|d= ||fS |d= ||fS )a  
    Compute the (approximate) medoid and median for (num + 1) items.

    This computes an approximate value.
    Relies on the robustness of the medoid and the median.
    Requires the values to be shuffled (within at least half the buffer size),
    or that the buffer size is larger than the number of values.

    This is used when both the medoid and the median are needed. In this case,
    a single buffer could be used. Note that calling both `next_medoid()` and
    `next_median()` consecutively over the same buffer MUST be avoided,
    because this introduces a bias, since the elements are inserted twice in
    buffer.

    This is useful for low memory footprint approximate computation.
    If the number of items is not odd, returns the medoid from the lower or
    upper half depending on the value of `lower` being True or False.

    Args:
        value (Any): The next value to consider.
        buffer (list): The buffer from previous iterations.
        lower (bool): Pick the lower medoid for even-sized buffer.
        max_buffer (int): The maximum size of the buffer.
            If `max_buffer >= len(items)` the result is exact.

    Returns:
        result (tuple): The tuple
            contains:
             - `medoid_` (Any): The updated approximate median value.
             - `median_` (Number): The updated medoid value.

    Examples:
        >>> items = range(0, 52, 2)
        >>> buffer = []
        >>> for i in items:
        ...     medoid_, median_ = next_medoid_and_median(i, buffer)
        >>> print((medoid_, median_))
        (24, 25.0)

        >>> buffer = []
        >>> for i in items:
        ...     medoid_, median_ = next_medoid_and_median(
        ...         i, buffer, max_buffer=4)
        >>> print((medoid_, median_))
        (46, 46)

        >>> items = list(items)
        >>> random.seed(0)
        >>> items = shuffle(items)
        >>> buffer = []
        >>> for i in items:
        ...     medoid_, median_ = next_medoid_and_median(
        ...         i, buffer, max_buffer=4)
        >>> print((medoid_, median_))
        (12, 12)
        >>> buffer = []
        >>> for i in items:
        ...     medoid_, median_ = next_medoid_and_median(
        ...         i, buffer, max_buffer=12)
        >>> print((medoid_, median_))
        (24, 24)

    See Also:
        - flyingcircus.median()
        - flyingcircus.i_median()
        - flyingcircus.medoid()
        - flyingcircus.i_medoid()
        - flyingcircus.i_medoid_and_median()
        - flyingcircus.next_amean()
        - flyingcircus.next_gmean()
        - flyingcircus.next_hmean()
        - flyingcircus.next_mean()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
    Fr   r<   )r   r^  r_  r<  r#  r   )r[  rx  r^  r`  r;  r"  s         ru   next_medoid_and_medianre  q)  s    d e G 	fe$.'v;#w1I G 2JGrw   c                 8    | D ]  }t        |||      \  }} ||fS )a  
    Compute the arithmetic mean a numeric iterable.

    Internally uses `flyingcircus.next_amean()`.

    This is substantially faster than `statistics.mean()`.

    The difference between `flyingcircus.i_amean()` and
    `flyingcircus.i_mean()` is that the latter does return the number
    of items processed.

    Args:
        items (Iterable[Number]): The input items.
        mean_ (Number): The start mean value.
        num (int): The number of items included in the start value.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (: The mean of the items.
             - `num`: The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_amean(items)
        (25.0, 26)
        >>> (mean(items), len(items)) == i_amean(items)
        True
        
    See Also:
        - flyingcircus.mean()
        - flyingcircus.next_amean()
        - flyingcircus.i_amean()
        - flyingcircus.i_gmean()
        - flyingcircus.i_hmean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.i_var()
        - flyingcircus.i_stdev()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.i_mean_and_stdev()
    rX  )r   r  r;  r   s       ru   i_ameanrg  )  s0    Z  2eS1
s2#:rw   c                 :    | D ]  }t        ||||      \  }} ||fS )a  
    Compute the mean a numeric iterable.

    Internally uses `flyingcircus.next_gmean()`.

    This is substantially faster than `statistics.mean()`.

    Args:
        items (Iterable[Number]): The input items.
        gmean_ (Number): The start mean value.
        num (int): The number of items included in the start value.
        valid (bool): Include only valid (non-zero) items.

    Returns:
        result (tuple): The tuple
            contains:
             - `gmean_` (Number): The mean of the items.
             - `num` (int): The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> gmean_, num = i_gmean(items)
        >>> print((round(gmean_, 3), num))
        (20.354, 25)
        >>> (gmean(items), len(items) - 1) == i_gmean(items)
        True
    )rQ  )r   rP  r;  r  r   s        ru   i_gmeanri  *  s2    @  ; vsE:;3;rw   c                 R    t        |       D ]  \  }}t        ||||      \  }} ||fS )a  
    Compute the mean a numeric iterable.

    Internally uses `flyingcircus.next_hmean()`.

    This is substantially faster than `statistics.mean()`.

    Args:
        items (Iterable[Number]): The input items.
        hmean_ (Number): The start harmonic mean value.
        num (int): The number of items included in the start value.
        valid (bool): Include only valid (non-zero) items.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (Number): The mean of the items.
             - `num` (int): The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> hmean_, num = i_hmean(items)
        >>> print((round(hmean_, 3), num))
        (13.103, 25)
        >>> abs(hmean_ - hmean(items)) < 1e-14
        True
    )r  rT  )r   rS  r;  r  rM   r   s         ru   i_hmeanrk  .*  s;    @ U# ;4 vsE:;3;rw   c                 &    t        | ||      \  }}|S )a  
    Compute the arithmetic mean a numeric iterable.

    Internally uses `flyingcircus.i_amean()`.

    This is substantially faster than `statistics.mean()`.

    The difference between `flyingcircus.i_amean()` and
    `flyingcircus.i_mean()` is that the latter does return the number
    of items processed.

    Args:
        items (Iterable[Number]): The input items.
        mean_ (Number): The start mean value.
        num (int): The number of items included in the start value.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (: The mean of the items.
             - `num`: The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_mean(items)
        25.0
        >>> mean(items) == i_mean(items)
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.next_amean()
        - flyingcircus.i_amean()
        - flyingcircus.i_gmean()
        - flyingcircus.i_hmean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.i_var()
        - flyingcircus.i_stdev()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.i_mean_and_stdev()
    )rg  )r   r  r;  s      ru   i_meanrm  T*  s    Z s+JE3Lrw   c                 V    t        |       D ]  \  }}t        ||||      \  }}} |||fS )a#  
    Compute the mean and the variance of a numeric iterable.

    Internally uses `flyingcircus.next_mean_and_sosd()`.

    This is useful for low memory footprint computation.

    This is substantially faster than computing the two values separately.

    Args:
        items (Iterable[Number]): The input items.
        mean_ (Number): The start mean value.
        sosd_ (Number): The start sum-of-squared-deviation value.
        num (int): The number of items included in the start mean value.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (Number): The mean of the items.
             - `var_` (Number): The variance of the items.
             - `num` (int): The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_mean_and_sosd(items)
        (25.0, 5850.0, 26)
        >>> mean_and_sosd(items) + (len(items),) == i_mean_and_sosd(items)
        True

    See Also:
        - flyingcircus.mean_and_sosd()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_amean()
        - flyingcircus.i_gmean()
        - flyingcircus.i_hmean()
        - flyingcircus.i_mean()
        - flyingcircus.i_var()
        - flyingcircus.i_stdev()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.i_mean_and_stdev()
    )r  r\  )r   r  r   r;  rM   r   s         ru   i_mean_and_sosdro  *  sC    \ U# H4.tUE3GucH%rw   c                 Z    t        |||      }t        | |||      \  }}}t        |||      S )aD  
    Compute the variance of a numeric iterable.

    Internally uses `flyingcircus.next_mean_and_sosd()` and
    `flyingcircus.sosd2var()`.

    This is useful for low memory footprint computation.

    Note that both mean and variance MUST be updated at each iteration,
    therefore if both the mean and the variance are required use
    `flyingcircus.i_mean_var()`.

    Args:
        items (Iterable[Number]): The input items.
        mean_ (Number): The start mean value.
        var_ (Number): The start variance value.
        num (int): The number of items included in the start values.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (Number): The variance of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_var(items)
        225.0
        >>> var(items) == i_var(items)
        True

    See Also:
        - flyingcircus.var()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_amean()
        - flyingcircus.i_gmean()
        - flyingcircus.i_hmean()
        - flyingcircus.i_mean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.i_stdev()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.i_mean_and_stdev()
        - flyingcircus.sosd2var()
        - flyingcircus.var2sosd()
    r  ro  r  )r   r  r  r;  r  r   rY  s          ru   i_varrr  *  s9    d T3%E(ucBE5$E4&&rw   c                 Z    t        |||      }t        | |||      \  }}}t        |||      S )a  
    Compute the standard deviation of a numeric iterable.

    Internally uses `flyingcircus.next_mean_and_sosd()` and
    `flyingcircus.sosd2stdev()`.

    This is useful for low memory footprint computation.

    Note that both mean and std. deviation MUST be updated at each iteration,
    therefore if both the mean and the standard deviation are required use
    `flyingcircus.i_mean_stdev()`.

    Args:
        items (Iterable[Number]): The input items.
        mean_ (Number): The start mean value.
        stdev_ (Number): The start standard deviation value.
        num (int): The number of items included in the start values.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (Number): The standard deviation of the items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_stdev(items)
        15.0
        >>> stdev(items) == i_stdev(items)
        True

    See Also:
        - flyingcircus.stdev()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_amean()
        - flyingcircus.i_gmean()
        - flyingcircus.i_hmean()
        - flyingcircus.i_mean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.i_var()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.i_mean_and_stdev()
        - flyingcircus.sosd2stdev()
        - flyingcircus.stdev2sosd()
    r  ro  r  r   r  r
  r;  r  r   s         ru   i_stdevrv  *  s9    d vsD)E'ueSAE5#eS$''rw   c                 `    t        |||      }t        | |||      \  }}}|t        |||      |fS )a+  
    Compute the mean and the variance of a numeric iterable.

    Internally uses `flyingcircus.next_mean_and_sosd()` and
    `flyingcircus.sosd2var()`.

    This is useful for low memory footprint computation.

    This is substantially faster than:
     - computing the two values separately.
     - both `statistics.mean()` and `statistics.variance()`.

    Args:
        items (Iterable[Number]): The input items.
        mean_ (Number): The start mean value.
        var_ (Number): The start variance value.
        num (int): The number of items included in the start values.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (Number): The mean of the items.
             - `var_` (Number): The variance of the items.
             - `num` (int): The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_mean_and_var(items)
        (25.0, 225.0, 26)
        >>> mean_and_var(items) + (len(items),) == i_mean_and_var(items)
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.var()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_amean()
        - flyingcircus.i_gmean()
        - flyingcircus.i_hmean()
        - flyingcircus.i_mean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.i_var()
        - flyingcircus.i_stdev()
        - flyingcircus.i_mean_and_stdev()
        - flyingcircus.sosd2var()
        - flyingcircus.var2sosd()
    rq  )r   r  r  r;  r  r   s         ru   i_mean_and_varrx  *+  s@    n T3%E'ueSAE5#(5#t,c11rw   c                 `    t        |||      }t        | |||      \  }}}|t        |||      |fS )aK  
    Compute the mean and standard deviation of a numeric iterable.

    Internally uses `flyingcircus.next_mean_and_sosd()` and
    `flyingcircus.sosd2stdev()`.

    This is useful for low memory footprint computation.

    This is substantially faster than:
     - computing the two values separately.
     - both `statistics.mean()` and `statistics.stdev()`.

    Args:
        items (Iterable[Number]): The input items.
        mean_ (Number): The start mean value.
        stdev_ (Number): The start standard deviation value.
        num (int): The number of items included in the start values.
        ddof (int): The number of degrees of freedom.

    Returns:
        result (tuple): The tuple
            contains:
             - `mean_` (Number): The mean of the items.
             - `stdev_` (Number): The std. deviation of the items.
             - `num` (int): The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_mean_and_stdev(items)
        (25.0, 15.0, 26)
        >>> mean_and_stdev(items) + (len(items),) == i_mean_and_stdev(items)
        True

    See Also:
        - flyingcircus.mean()
        - flyingcircus.var()
        - flyingcircus.next_mean_and_var()
        - flyingcircus.next_mean_and_sosd()
        - flyingcircus.i_amean()
        - flyingcircus.i_gmean()
        - flyingcircus.i_hmean()
        - flyingcircus.i_mean()
        - flyingcircus.i_mean_and_sosd()
        - flyingcircus.i_var()
        - flyingcircus.i_stdev()
        - flyingcircus.i_mean_and_var()
        - flyingcircus.sosd2stdev()
        - flyingcircus.stdev2sosd()
    rt  ru  s         ru   i_mean_and_stdevrz  g+  s@    n vsD)E'ueSAE5#*UC.33rw   c                 b    t        |       }t        |      }|g}|D ]  }t        |||      } |S )ar  
    Compute the (approximate) median of a numeric iterable.

    This computes an approximate value.
    Relies on the robustness of the median.

    This is useful for low memory footprint approximate computation.

    Args:
        items (Iterable[Number]): The input items.
        max_buffer (int): The maximum size of the buffer.
            If `max_buffer >= len(items)` the result is exact.

    Returns:
        result (Number): The median value.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_median(items)
        25.0
        >>> i_median(items) == median(items)
        True

        >>> items = [1, 2, 3, 4, 100]
        >>> i_median(items)
        3

        >>> items = [81, 73, 99, 86, 94, 40, 75, 46, 8, 50, 51, 53, 35, 87]
        >>> sorted(items)
        [8, 35, 40, 46, 50, 51, 53, 73, 75, 81, 86, 87, 94, 99]
        >>> i_median(items)
        63.0
        >>> [i_median(items, i) for i in range(3, len(items))]
        [50.5, 51, 52.0, 51, 52.0, 51, 52.0, 51, 52.0, 53, 63.0]

        >>> items = [81, 73, 99, 86, 94, 40, 75, 46, 8, 50, 51, 53, 35]
        >>> sorted(items)
        [8, 35, 40, 46, 50, 51, 53, 73, 75, 81, 86, 94, 99]
        >>> i_median(items)
        53
        >>> [i_median(items, i) for i in range(3, len(items))]
        [50.5, 51, 52.0, 51, 52.0, 51, 52.0, 51, 52.0, 53]

        >>> all([i_median(range(i)) == median(range(i)) for i in range(1, 40)])
        True
        >>> all([i_median(range(0, i)) == i_medoid(range(0, i))
        ...     for i in range(1, 40, 2)])
        True

    See Also:
        - flyingcircus.median()
        - flyingcircus.median_and_median_abs_dev()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid_and_median()
        - flyingcircus.i_median()
        - flyingcircus.i_medoid()
        - flyingcircus.i_medoid_and_median()
        - flyingcircus.i_median_and_median_abs_dev()
    )rx  r`  )r   r   ra  )r   r`  r  r"  rx  r   s         ru   i_medianr|  +  sC    | eJ:GYF Jd6jIJNrw   c                     |r0t        |       }t        |      }|g}|D ]  }t        ||||      } |S t        t	        |       dd      }|S )av  
    Compute the (approximate) medoid of an arbitrary iterable.

    This computes an approximate value.
    Relies on the robustness of the medoid.

    This is useful for low memory footprint approximate computation.

    Args:
        items (Iterable[Any]): The input items.
        lower (bool): Pick the lower medoid for even-sized items.
        max_buffer (int): The maximum size of the buffer.
            If `max_buffer >= len(items)` the result is exact.
            If `max_buffer` is 0, use all items.

    Returns:
        result (Any): The medoid value.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_medoid(items)
        24
        >>> i_medoid(items) == medoid(items)
        True

        >>> items = [1, 2, 2, 2, 100]
        >>> i_medoid(items)
        2

        >>> items = [81, 73, 99, 86, 94, 40, 75, 46, 8, 50, 51, 53, 35, 87]
        >>> sorted(items)
        [8, 35, 40, 46, 50, 51, 53, 73, 75, 81, 86, 87, 94, 99]
        >>> i_medoid(items)
        53
        >>> [i_medoid(items, max_buffer=i) for i in range(3, len(items))]
        [51, 51, 51, 51, 51, 51, 51, 51, 53, 53, 53]

        >>> items = [81, 73, 99, 86, 94, 40, 75, 46, 8, 50, 53, 35, 87]
        >>> sorted(items)
        [8, 35, 40, 46, 50, 53, 73, 75, 81, 86, 87, 94, 99]
        >>> i_medoid(items)
        73
        >>> [i_medoid(items, max_buffer=i) for i in range(3, len(items))]
        [50, 50, 50, 50, 50, 50, 50, 53, 73, 73]

        >>> all([i_medoid(range(i)) == medoid(range(i)) for i in range(1, 40)])
        True
        >>> all([i_medoid(range(0, i)) == i_median(range(0, i))
        ...     for i in range(1, 40, 2)])
        True

    See Also:
        - flyingcircus.medoid()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
        - flyingcircus.i_median()
        - flyingcircus.i_medoid()
        - flyingcircus.i_medoid_and_median()
    TF)r   r   rc  r<  r   )r   r^  r`  r  r;  rx  r   s          ru   i_medoidr~  +  sc    ~ %[
z" 	CD!$zBG	C N utU3Nrw   c                     |rGd}t        |       }t        |      }|}|g}t        |      D ]  \  }}t        ||||      \  }} |dz   }	n1t	        |       } t        | d|      }t        | d      }t        |       }	|||	fS )a  
    Compute the (approximate) medoid and median for a numeric iterable.

    This computes an approximate value.
    Relies on the robustness of the medoid.

    This is useful for low memory footprint approximate computation.

    This is faster and more memory efficient than computing both
    `flyingcircus.i_medoid()` and `flyingcircus.i_median()`
    separately.

    Args:
        items (Iterable[Number]): The input items.
        lower (bool): Pick the lower medoid for even-sized items.
        max_buffer (int): The maximum size of the buffer.
            If `max_buffer >= len(items)` the result is exact.
            If `max_buffer` is 0, use all items.

    Returns:
        result (tuple): The tuple
            contains:
             - `medoid_`: The medoid of the items.
             - `median_`: The median of the items.
             - `num`: The number of items.

    Examples:
        >>> items = range(0, 52, 2)
        >>> i_medoid_and_median(items)
        (24, 25.0, 26)
        >>> (i_medoid_and_median(items)
        ...     == (medoid(items), median(items), len(items)))
        True

        >>> items = [1, 2, 2, 2, 100]
        >>> i_medoid_and_median(items)
        (2, 2, 5)

        >>> items = [81, 73, 99, 86, 94, 40, 75, 46, 8, 50, 51, 53, 35, 87]
        >>> sorted(items)
        [8, 35, 40, 46, 50, 51, 53, 73, 75, 81, 86, 87, 94, 99]
        >>> i_medoid_and_median(items)
        (53, 63.0, 14)
        >>> [i_medoid_and_median(items, max_buffer=i) for i in range(10, 14)]
        [(51, 51, 14), (53, 52.0, 14), (53, 53, 14), (53, 63.0, 14)]

        >>> items = [81, 73, 99, 86, 94, 40, 75, 46, 8, 50, 53, 35, 87]
        >>> sorted(items)
        [8, 35, 40, 46, 50, 53, 73, 75, 81, 86, 87, 94, 99]
        >>> i_medoid_and_median(items)
        (73, 73, 13)
        >>> [i_medoid_and_median(items, max_buffer=i) for i in range(10, 14)]
        [(53, 53, 13), (73, 63.0, 13), (73, 73, 13), (73, 73, 13)]

    See Also:
        - flyingcircus.median()
        - flyingcircus.medoid()
        - flyingcircus.next_median()
        - flyingcircus.next_medoid()
        - flyingcircus.next_medoid_and_median()
        - flyingcircus.i_median()
        - flyingcircus.i_medoid()
        - flyingcircus.i_medoid_and_median()
    r   r9   F)r   r^  r%  )r   r   r  re  rl  r<  r#  r   )
r   r^  r`  rM   r  r;  r"  rx  r   r;  s
             ru   i_medoid_and_medianr  6,  s    H %[
z" , 	1GAt5feZ 1GW	1 !eu5>51%jGS  rw   c                    t         j                  ddgt         j                  ddgt        ddgi}t	        |      }||v r	||   d   }nt        dj                  |            |rt        |t              rx|j                  d	      rLt        |t        d	      d
       }t        j                  | |      }t        |t         ||            z        } | S t        dj                  |            t        |t        t        f      r| |z  }|  |||z        |z  |z
  z  } | S t        j                  dj                  | |             | S )a  
    Align (round) a number to a multiple of the specified base.

    The resulting number is computed using the formula:

    num = num + func(num % base / base) * base - num % base

    where `func` is a rounding function, as determined by `mode`.

    Args:
        num (int|float): The input number.
        base (int|float|str|None): The number to align to.
            If int, then calculate a multiple of `align` close to `num`.
            If str, possible options are:
             - 'powX' (where X >= 2 must be an int): calculate a power of X
               that is close to `num`.
            The exact number being calculated depends on the value of `mode`.
        mode (int|str|bool): Determine the rounding mode.
            If str, valid inputs are:
             - 'upper', '+': round to smallest multiple larger than `num`.
             - 'lower', '-': round to the largest multiple smaller than `num`.
             - 'closest', '~': round to the multiple closest to `num`.
            If int, valid inputs are `+1`, `0` and `-1`, mapping to 'upper',
            'closest' and 'lower' respectively.

    Returns:
        num (int): The aligned number.

    Examples:
        >>> align(9, 2)
        8
        >>> align(447, 32, mode=1)
        448
        >>> align(447, 32, mode=-1)
        416
        >>> align(447, 32, mode=0)
        448
        >>> align(432, 'pow2', mode=1)
        512
        >>> align(432, 'pow2', mode=-1)
        256
        >>> align(432, 'pow2', mode=0)
        512
        >>> align(45, 90, mode=0)
        0
        >>> align(6, 'pow2', mode=0)
        8
        >>> align(6543, 'pow10', mode=0)
        10000
        >>> align(1543, 'pow10', mode=0)
        1000
        >>> align(128, 128, mode=1)
        128
        >>> align(123.37, 0.5, mode=1)
        123.5
        >>> align(123.37, 0.5, mode=0)
        123.5
        >>> align(123.37, 0.5, mode=-1)
        123.0
        >>> align(123.37, None)
        123.37
    r\  r/   r^  r<   r`  r   rb  rc  r^  NzInvalid align `{align}`)alignz$Will not align `{num}` to `{align}`.)r;  r  )rd  re  rf  rg  r\  r"  r  r  ra   rs   rY   r   logr_   warningswarn)r;  r_  r   ri  rj  rk  r  moduluss           ru   r  r  ,  sK   F 			GQ<

WbM	1~N !0Eu}tQ077T7BCCdC u%4E
,-hhsD)$#fSk"223 J !!:!A!A!A!MNNsEl+DjG6'D.)D07::C J MM@GGt H % & Jrw   c                 $    | |z  | dz  |dz  z   z  S )a?  
    Compute the pseudo-ratio of x, y: 1 / ((x / y) + (y / x))

    .. math::
        \frac{1}{\frac{x}{y}+\frac{y}{x}} = \frac{xy}{x^2+y^2}

    Args:
        x (int|float): First input value.
        y (int|float): Second input value.

    Returns:
        result (float): 1 / ((x / y) + (y / x))

    Examples:
        >>> p_ratio(2, 2)
        0.5
        >>> p_ratio(200, 200)
        0.5
        >>> p_ratio(1, 2)
        0.4
        >>> p_ratio(100, 200)
        0.4
        >>> items = 100, 200
        >>> (p_ratio(*items) == p_ratio(*items[::-1]))
        True
    r9   r   r  s     ru   p_ratior  ,  s    6 Ea1fqAvo&&rw   c                 T    dt        d t        j                  | d      D              z  S )a  
    Compute the generalized pseudo-ratio of x_i: 1 / sum_ij [ x_i / x_j ]

    .. math::
        \frac{1}{\sum_{ij} \frac{x_i}{x_j}}

    Args:
        values (Iterable[int|float]): Input values.

    Returns:
        result (float): 1 / sum_ij [ x_i / x_j ]

    Examples:
        >>> gen_p_ratio((2, 2, 2, 2, 2))
        0.05
        >>> gen_p_ratio((200, 200, 200, 200, 200))
        0.05
        >>> gen_p_ratio((1, 2))
        0.4
        >>> gen_p_ratio((100, 200))
        0.4
        >>> values1 = [x * 10 for x in range(2, 10)]
        >>> values2 = [x * 1000 for x in range(2, 10)]
        >>> gen_p_ratio(values1) - gen_p_ratio(values2) < 1e-10
        True
        >>> items = list(range(2, 10))
        >>> gen_p_ratio(values2) - gen_p_ratio(items[::-1]) < 1e-10
        True
    r/   c              3   ,   K   | ]  \  }}||z    y wr   r   r   rH   r7   s      ru   r   zgen_p_ratio.<locals>.<genexpr>+-  s     GTQ1q5Gr  r9   )rV  r   r  r  s    ru   gen_p_ratior  -  s'    < sGY%;%;FA%FGGGGrw   c                     |t        | t              rdndn|}|D ]  }| j                  ||      }  || j                         S |r(t	        t        t        | j                  |                  S | j                  |      S )a  
    Split a string using multiple separators.

    This is achieved by replacing all (secondary) separators with a base
    separator, and finally split using the base separator.

    Args:
        text (str|bytes|bytearray): The input string.
        seps (Iterable[str|bytes|bytearray]): The additional separators.
        sep (str|bytes||bytearrayNone): The separator for the final splitting.
            If None, uses `.split(None)` default behavior,
            i.e. splits blank character (' ', '	', '
') simultaneously.
        filter_empty (bool): Remove empty string from the results.

    Returns:
        result (list[str|bytes|bytearray]): The split strings.

    Examples:
        >>> text = 'no-go to go, where? anti-matter'
        >>> print(multi_split(text, ('-', ',')))
        ['no', 'go', 'to', 'go', 'where?', 'anti', 'matter']
        >>> print(multi_split(text, ('-', ','), '?'))
        ['no', 'go to go', ' where', ' anti', 'matter']
        >>> print(multi_split(text, ('-', ',', ' '), '?'))
        ['no', 'go', 'to', 'go', 'where', 'anti', 'matter']
        >>> print(multi_split(text, ('-', ',', ' '), '?', False))
        ['no', 'go', 'to', 'go', '', 'where', '', 'anti', 'matter']

        >>> text = b'no-go to go, where? anti-matter'
        >>> print(multi_split(text, (b'-', b',')))
        [b'no', b'go', b'to', b'go', b'where?', b'anti', b'matter']
        >>> print(multi_split(text, (b'-', b','), b'?'))
        [b'no', b'go to go', b' where', b' anti', b'matter']
        >>> print(multi_split(text, (b'-', b',', b' '), b'?'))
        [b'no', b'go', b'to', b'go', b'where', b'anti', b'matter']
        >>> print(multi_split(text, (b'-', b',', b' '), b'?', False))
        [b'no', b'go', b'to', b'go', b'', b'where', b'', b'anti', b'matter']
    r      )r  ra   replacerZ  r  r  rT   )r  sepssepfilter_empty_sepsep_s         ru   multi_splitr  /-  sv    V 8;{:dC(CdD (||D$'(
{zz|	F4C122zz#rw   c                 2    |D ]  } | j                   | }  | S )a$  
    Perform multiple replacements in a string.

    The replaces are concatenated together, therefore the order may matter.

    Args:
        text (str|bytes|bytearray): The input string.
        replaces (tuple[tuple[str|bytes|bytearray]]): The replacements.
            Format: ((<old>, <new>), ...).

    Returns:
        text (str|bytes): The string after the performed replacements.

    Examples:
        >>> multi_replace('X Y', (('X', 'flying'), ('Y', 'circus')))
        'flying circus'
        >>> multi_replace('x-x-x-x', (('x', 'est'), ('est', 'test')))
        'test-test-test-test'
        >>> multi_replace('x-x-', (('-x-', '.test'),))
        'x.test'
        >>> multi_replace('x-x-', (('x', 'test'), ('te', 'be')))
        'best-best-'
        >>> multi_replace('x-x-', (('te', 'be'), ('x-', 'test-')))
        'test-test-'

        >>> multi_replace(b'X Y', ((b'X', b'flying'), (b'Y', b'circus')))
        b'flying circus'
        >>> multi_replace(b'x-x-x-x', ((b'x', b'est'), (b'est', b'test')))
        b'test-test-test-test'
        >>> multi_replace(b'x-x-', ((b'-x-', b'.test'),))
        b'x.test'
        >>> multi_replace(b'x-x-', ((b'x', b'test'), (b'te', b'be')))
        b'best-best-'
        >>> multi_replace(b'x-x-', ((b'te', b'be'), (b'x-', b'test-')))
        b'test-test-'
    )r  )r  replacesr  s      ru   multi_replacer  g-  s)    P  &t||W%&Krw   c                 N    t              dj                  fd| D              S )a  
    Perform multiple replacements of single characters in a string.

    The replaces are concatenated together, therefore the order may matter.

    Args:
        text (str): The input string.
        replaces (tuple[tuple[str]]|dict): The listing of the replacements.
            Format: ((<old>, <new>), ...) where <old> must be a single char.

    Returns:
        text (str): The string after the performed replacements.

    Examples:
        >>> multi_replace_char('X Y', (('X', 'flying'), ('Y', 'circus')))
        'flying circus'
        >>> multi_replace_char('X Y', (('Y', 'circus'), ('X', 'flying')))
        'flying circus'
        >>> multi_replace_char('x-y-x-y', (('x', 'flying'), ('y', 'circus')))
        'flying-circus-flying-circus'
        >>> multi_replace_char('x-y-x-y', (('x', 'flying'), ('g', 'circus')))
        'flying-y-flying-y'
        >>> multi_replace_char('x-g-x-g', (('x', 'flying'), ('g', 'circus')))
        'flying-circus-flying-circus'
        >>> multi_replace_char('x-x-', (('x-', 'bye'),))
        'x-x-'
    r8   c              3   B   K   | ]  }j                  ||        y wr   )get)r   r'   r  s     ru   r   z%multi_replace_char.<locals>.<genexpr>-  s     4!8<<1%4r  )r  r   )r  r  s    `ru   multi_replace_charr  -  s#    < H~H774t444rw   c                    t        t        |       dz         D cg c]'  }t        t        |      dz         D cg c]  }d c}) }}d}g }t        |       D ]  \  }}t        |      D ]n  \  }	}
||
k(  s||   |	   dz   }|||dz      |	dz   <   ||kD  r"g }|}|j                  | ||z
  dz   |dz           L||k(  sR|j                  | ||z
  dz   |dz           p  ||S t	        ||      S c c}w c c}w )ao  
    Find the longest common consecutive subsequence(s).
    This version works for two Sequences.

    This is known as the `longest common substring` problem, or LCS for short.

    Args:
        seq1 (Sequence): The first input sequence.
            Must be of the same type as seq2.
        seq2 (Sequence): The second input sequence.
            Must be of the same type as seq1.
        sorting (callable): Sorting function passed to 'sorted' via `key` arg.

    Returns:
        commons (list[Sequence]): The longest common subsequence(s).

    Examples:
        >>> common_subseq_2('academy', 'abracadabra')
        ['acad']
        >>> common_subseq_2('los angeles', 'lossless')
        ['los', 'les']
        >>> common_subseq_2('los angeles', 'lossless', lambda x: x)
        ['les', 'los']
        >>> common_subseq_2((1, 2, 3, 4, 5), (0, 1, 2))
        [(1, 2)]
    r/   r   r/  )r{  r   r  r   rl  )seq1seq2sortingr  counterr  commonsi1item1i2item2tmps               ru   common_subseq_2r  -  s   > ;@D	A:NOQ5TQ/0a0OGOGGt_ 
>	E"4 		>IB~bk"o)*-QQ'= G!GNN4S1R!V#<=G^NN4S1R!V#<=		>
> g7++# 1Os   C1	C,
C1,C1c           	         t        |       } t        |       g}| D ]b  }g }|D ]W  }t        |||      }t        |      dk(  st        |d         t        |d         k(  s<|j	                  t        |||             Y |}d |S )a  
    Find the longest common consecutive subsequence(s).
    This version works for an Iterable of Sequences.

    This is known as the `longest common substring` problem, or LCS for short.

    Args:
        seqs (Iterable[Sequence]): The input sequences.
            All the items must be of the same type.
        sorting (callable): Sorting function passed to 'sorted' via `key` arg.

    Returns:
        commons (list[Sequence]): The longest common subsequence(s).

    Examples:
        >>> common_subseq(['academy', 'abracadabra', 'cadet'])
        ['cad']
        >>> common_subseq(['los angeles', 'lossless', 'les alos'])
        ['los', 'les']
        >>> common_subseq(['los angeles', 'lossless', 'les alos', 'losles'])
        ['los', 'les']
        >>> common_subseq(['los angeles', 'lossless', 'dolos'])
        ['los']
        >>> common_subseq([(1, 2, 3, 4, 5), (1, 2, 3), (0, 1, 2)])
        [(1, 2)]
    r   )r   r   r  r   r   )r  r  r  r  tmpscommonr  s          ru   common_subseqr  -  s    : :DDzlG  	DF!&$8C4yA~SVDG!<OFD'BC	D  Nrw   c                 p    t        | t        j                        xr t        | t        j                         S )a  
    Check if a file object is in binary mode.

    Args:
        file_obj (File): The input file.

    Returns:
        result (bool): The binary mode status.

    Examples:
        >>> with open(__file__, 'rb') as file_obj:
        ...     is_bin_file(file_obj)
        True
        >>> with open(__file__, 'r') as file_obj:
        ...     is_bin_file(file_obj)
        False
        >>> is_bin_file(io.BytesIO(b'ciao'))
        True
        >>> is_bin_file(io.StringIO('ciao'))
        False
    )r  ioIOBase
TextIOBasefile_objs    ru   is_bin_filer  .  s-    , h		* 7h667rw   c                 @    t        | j                  d      t              S )a@  
    Check if reading from a file object will return bytes.

    Args:
        file_obj (File): The input file.

    Returns:
        result (bool): The result of the check.

    Examples:
        >>> is_reading_bytes(io.BytesIO(b'ciao'))
        True
        >>> is_reading_bytes(io.StringIO('ciao'))
        False
    r   )r  readr   r  s    ru   is_reading_bytesr  1.  s      hmmA&..rw   c                 F    	 | j                  d       y# t        $ r Y yw xY w)aA  
    Check if writing from a file object will require bytes.

    Args:
        file_obj (File): The input file.

    Returns:
        result (bool): The result of the check.

    Examples:
        >>> is_writing_bytes(io.BytesIO(b'ciao'))
        True
        >>> is_writing_bytes(io.StringIO('ciao'))
        False
    rw   TF)writer   r  s    ru   is_writing_bytesr  E.  s,     s   s    	  )on_errorc                    	 t        d |D              }t        d |D              }t        | |      S # t        j                  $ r |cY S w xY w)aA  
    Determine if two or more file objects refer to the same file.

    Args:
        files (Iterable[file]): The input file objects.
        grouper (callable): Determine how to group multiple comparisons.
            Must accept the following signature:
            grouper(*Iterable[bool]): bool
            Can be either `all` or `any`, or any callable with the supported
            signature.
        on_error (bool): Determine what to do if `fileno` is unsupported.

    Returns:
        result (bool): If the the two file objects refer to the same file.

    Examples:
        >>> same_file(all, open(__file__, 'r'), open(__file__, 'r'))
        True
        >>> same_file(all, open(__file__, 'r'), io.StringIO('FlyingCircus'))
        False
        >>> same_file(all, io.StringIO('fc'), io.StringIO('fc'))
        False
    c              3   b   K   | ]'  }t        j                  |j                                ) y wr   )rp   fstatfileno)r   file_s     ru   r   zsame_file.<locals>.<genexpr>z.  s     B5bhhu||~.Bs   -/c              3   L   K   | ]  }|j                   |j                  f  y wr   )st_inost_dev)r   ry   s     ru   r   zsame_file.<locals>.<genexpr>~.  s     LdT[[9Ls   "$)r   r  r  UnsupportedOperation)r  r  filesstatsinodes_devicess        ru   	same_filer  ^.  sR    66BEBB LeLLWn55	 "" s   2 A
	A
   c              #   d   K   |r| j                  d       	 | j                  |      }|sy| w)u^  
    Yields the data within a file in blocks of given (max) size.

    For non-binary file objects, the size of the block may be reduced as a
    result of multi-byte support in the encoding.
    The last block may have a smaller size regardless of the opening mode.

    Args:
        file_obj (file): The input file.
        size (int|None): The block size.
            If int, the file is yielded in blocks of the specified size.
            If None, the file is yielded at once.
        reset_offset (bool): Reset the file offset.
            If True, starts reading from the beginning of the file.
            Otherwise, starts reading from where the file current position is.

    Yields:
        block (bytes|str): The data within the blocks.

    Examples:
        >>> src = 'flyingcircus-' * 4
        >>> tgt = ''.join(blocks(io.StringIO(src), 3))
        >>> print(tgt)
        flyingcircus-flyingcircus-flyingcircus-flyingcircus-
        >>> print(src == tgt)
        True

        >>> src = b'flyingcircus-' * 4
        >>> tgt = b''.join(blocks(io.BytesIO(src), 3))
        >>> print(tgt)
        b'flyingcircus-flyingcircus-flyingcircus-flyingcircus-'
        >>> print(src == tgt)
        True

        >>> src = 'φλυινγκιρκυσ-' * 4
        >>> tgt = ''.join(blocks(io.StringIO(src), 3))
        >>> print(tgt)
        φλυινγκιρκυσ-φλυινγκιρκυσ-φλυινγκιρκυσ-φλυινγκιρκυσ-
        >>> print(src == tgt)
        True

        >>> with open(__file__, 'r') as file_obj:
        ...     src = file_obj.read()
        ...     tgt = ''.join([b for b in blocks(file_obj, 100)])
        ...     src == tgt
        True
        >>> with open(__file__, 'rb') as file_obj:
        ...     src = file_obj.read()
        ...     tgt = b''.join([b for b in blocks(file_obj, 100)])
        ...     src == tgt
        True
    r   N)seekr  )r  r	  reset_offsetblocks       ru   blocksr  .  s9     p a
d#K s   .0c              #      K   |r | j                  dt        j                        n| j                         }|dkD  r>t	        ||      }||z  }| j                  |       | j                  |      }| |dkD  r=yyw)uZ
  
    Yields the data within a file in reverse order blocks of given (max) size.

    Note that:
     - the content of the block is NOT reversed.
     - files opened in text mode are not supported!

    Args:
        file_obj (file): The input file.
        size (int|None): The block size.
            If int, the file is yielded in blocks of the specified size.
            If None, the file is yielded at once.
        reset_offset (bool): Reset the file offset.
            If True, starts reading from the end of the file.
            Otherwise, starts reading from where the file current position is.

    Yields:
        block (bytes|str): The data within the blocks.

    Examples:
        >>> src = 'flyingcircus' * 4
        >>> my_blocks = list(blocks_r(io.StringIO(src), 12))
        >>> tgt = ''.join(my_blocks[::-1])
        >>> print(my_blocks)
        ['flyingcircus', 'flyingcircus', 'flyingcircus', 'flyingcircus']
        >>> print(src)
        flyingcircusflyingcircusflyingcircusflyingcircus
        >>> print(tgt)
        flyingcircusflyingcircusflyingcircusflyingcircus
        >>> print(src == tgt)
        True

        >>> src = b'flyingcircus' * 4
        >>> my_blocks = list(blocks_r(io.BytesIO(src), 12))
        >>> tgt = b''.join(my_blocks[::-1])
        >>> print(my_blocks)
        [b'flyingcircus', b'flyingcircus', b'flyingcircus', b'flyingcircus']
        >>> print(src)
        b'flyingcircusflyingcircusflyingcircusflyingcircus'
        >>> print(tgt)
        b'flyingcircusflyingcircusflyingcircusflyingcircus'
        >>> print(src == tgt)
        True

        >>> src = 'φλυινγκιρκυσ' * 4
        >>> my_blocks = list(blocks_r(io.StringIO(src), 12))
        >>> tgt = ''.join(my_blocks[::-1])
        >>> print(my_blocks)
        ['φλυινγκιρκυσ', 'φλυινγκιρκυσ', 'φλυινγκιρκυσ', 'φλυινγκιρκυσ']
        >>> print(src)
        φλυινγκιρκυσφλυινγκιρκυσφλυινγκιρκυσφλυινγκιρκυσ
        >>> print(tgt)
        φλυινγκιρκυσφλυινγκιρκυσφλυινγκιρκυσφλυινγκιρκυσ
        >>> print(src == tgt)
        True

        # THIS IS NOT SUPPORTED!
        # >>> with open(__file__, 'r') as file_obj:
        # ...     src = file_obj.read()
        # ...     tgt = ''.join([b for b in blocks_r(file_obj, 100)][::-1])
        # ...     src == tgt
        # True
        >>> with open(__file__, 'rb') as file_obj:
        ...     src = file_obj.read()
        ...     tgt = b''.join([b for b in blocks_r(file_obj, 100)][::-1])
        ...     src == tgt
        True
    r   N)r  rp   SEEK_ENDtellr  r  )r  r	  r  r
  
block_sizer  s         ru   blocks_rr  .  sl     R /;X]]1bkk*F
1*&
*fj) 1*s   A4A97A9c                 P    t        | t        t        f      rt        | g|i |S | S )a"  
    Automatically open a file if a path is provided.

    Args:
        file_ (str|bytes|file): The file path or file object.
        *_args: Positional arguments for `open()`.
        **_kws: Keyword arguments for `open()`.

    Returns:
        file_ (file): The opened file object.
    )r  ra   r   open)r  r   r   s      ru   	auto_openr  /  s3      ec5\* &&& 6056rw   @c                    t        | d      }||j                  ||       |t        |      z   t        |   z   }t	        j
                  |      }t	        j                  ||j                  |            }	| |ur|j                          |	S )a!  
    Read data from stream.

    Args:
        in_file (str|bytes|file): The input file.
            Can be either a valid file path or a readable binary file object.
            See `flyingcircus.auto_open()` for more details.
        offset (int|None): The offset where to start reading.
        dtype (str): The data type to read.
            Accepted values are:
             - 'bool': boolean type (same as: '?', 1B)
             - 'char': signed char type (same as: 'b', 1B)
             - 'uchar': unsigned char type (same as: 'B', 1B)
             - 'short': signed short int type (same as: 'h', 2B)
             - 'ushort': unsigned short int  type (same as: 'H', 2B)
             - 'int': signed int type (same as: 'i', 4B)
             - 'uint': unsigned int type (same as: 'I', 4B)
             - 'long': signed long type (same as: 'l', 4B)
             - 'ulong': unsigned long type (same as: 'L', 4B)
             - 'llong': signed long long type (same as: 'q', 8B)
             - 'ullong': unsigned long long type (same as: 'Q', 8B)
             - 'float': float type (same as: 'f', 4B)
             - 'double': double type (same as: 'd', 8B)
             - 'str': c-str type (same as: 's', 'p')
            See Python's `struct` module for more information.
        num_blocks (int): The number of blocks to read.
        mode (str): Determine the byte order, size and alignment.
            Accepted values are:
             - '@': endianness: native,  size: native,   align: native.
             - '=': endianness:	native,  size: standard, align: none.
             - '<': endianness:	little,  size: standard, align: none.
             - '>': endianness:	big,     size: standard, align: none.
             - '!': endianness:	network, size: standard, align: none.
        whence (int): Where to reference the offset.
            Accepted values are:
             - '0': absolute file positioning.
             - '1': seek relative to the current position.
             - '2': seek relative to the file's end.

    Returns:
        data (tuple): The data read.
    rb)	r  r  ra   	DTYPE_STRstructcalcsizeunpack_fromr  close)
in_filedtyper   
num_blocksr
  whencein_file_objstruct_format	read_sizer  s
             ru   read_streamr  -/  s    b GT*K(3z?*Yu-==M.Im[-=-=i-HIDk!Krw   c                 
   t        | d      }||j                  ||       g }	 |j                  d      j                  d      }||dk(  rn|j	                  |       :dj                  |      }| |ur|j                          |S )aW  
    Read a C-type string from file.

    Args:
        in_file (str|bytes|file): The input file.
            Can be either a valid file path or a readable binary file object.
            See `flyingcircus.auto_open()` for more details.
        offset (int|None): The offset where to start reading.
        whence (int): Where to reference the offset.
            Accepted values are:
             - '0': absolute file positioning.
             - '1': seek relative to the current position.
             - '2': seek relative to the file's end.

    Returns:
        text (str): The string read.
    r  r/   r3   r8   )r  r  r  r  r   r   r  )r  r
  r  r  rx  r?   r  s          ru   	read_cstrr  j/  s    * GT*K(F
Q&&w/9T	MM!  776?Dk!Krw   c                 j   |t        |      nd}|t        |      ni }|rdnd}t        | d|z         }t        |d|z         }	t        t        ||	      }
|
r-|j                         } ||g|i |}|	j                          n|dk  r&|D ]   } ||g|i |}|	j                  |f       " n]|dkD  r/t        ||      D ]  } ||g|i |}|	j                  |       ! n)|	j                   ||j                         g|i |       | |ur|j                          ||	ur|	j                          yy)a	  
    Process the content of the input file and write it to the output file.

    Note: `in_file` and `out_file` should be different!

    Args:
        in_file (str|bytes|file): The input file.
            Can be either a valid file path or readable file object.
            Should not be the same as `out_file`.
            See `flyingcircus.auto_open()` for more details.
        out_file (str|bytes|file): The output file.
            Can be either a valid file path or writable file object.
            Should not be the same as `in_file`.
            See `flyingcircus.auto_open()` for more details.
        func (callable): The conversion function.
            Must accept a string or bytes (depending on `as_binary`) as first
            argument: func(str|bytes, *args, **kws): str|bytes
        args (Iterable|None): Positional arguments for `func()`.
        kws (Mappable|None): Keyword arguments for `func()`.
        block_size (int): The block size to use.
            If positive, apply `func` iteratively on input blocks of the
            specified size.
            If negative, apply `func` iteratively on each line.
            If zero, apply `func` on the entire input at once.
        as_binary (bool): Force binary I/O operations.
            If True, the first argument of `func` must be of type `bytes`.
            Otherwise, must be of type `str`.

    Returns:
        None.

    Examples:
        >>> text = 'flyingcircus-' * 4

        >>> i_file = io.StringIO(text)
        >>> o_file = io.StringIO('')
        >>> process_stream(i_file, o_file, str.upper)
        >>> pos = o_file.seek(0)
        >>> print(o_file.read())
        FLYINGCIRCUS-FLYINGCIRCUS-FLYINGCIRCUS-FLYINGCIRCUS-

        >>> i_file = io.BytesIO(text.encode())
        >>> o_file = io.BytesIO(b'')
        >>> process_stream(i_file, o_file, bytes.upper)
        >>> pos = o_file.seek(0)
        >>> print(o_file.read())
        b'FLYINGCIRCUS-FLYINGCIRCUS-FLYINGCIRCUS-FLYINGCIRCUS-'

        >>> i_file = io.StringIO(text)
        >>> o_file = io.StringIO('')
        >>> process_stream(
        ...     i_file, o_file, lambda x: '*' if x in 'irc' else x,
        ...     block_size=1)
        >>> pos = o_file.seek(0)
        >>> print(o_file.read())
        fly*ng****us-fly*ng****us-fly*ng****us-fly*ng****us-
    Nr   rI   r8   r  r:  r   )
r   r  r  r  r  r  r  
writelinesr  r  )r  out_filer   r   r   r  	as_binarybin_moder  out_file_objinput_equals_outputcontentliner  s                 ru   process_streamr  /  s]   B *5;D$s)BCsRHGS8^4KXsX~6L#ClC""$w---># 1D/4/3/''01 !^Z8 *U1T1S1""5)* tK$4$4$6EEEFk!|# $rw   c                 8    |       }t        | d      5 }|j                  d       t        ||      D ]  }|j                  |        	 ddd       |j	                         }|r ||      }|r!t        |t              r|j                  |      }|S # 1 sw Y   HxY w)a  
    Compute the hash of a file.

    Args:
        in_file (str|bytes|file): The input file.
            Can be either a valid file path or a readable binary file object.
            See `flyingcircus.auto_open()` for more details.
        hash_algorithm (callable): The hashing algorithm.
            This must support the methods provided by `hashlib` module, like
            `md5`, `sha1`, `sha256`, `sha512`.
        filtering (callable|None): The filtering function.
            If callable, must have the following signature:
            filtering(bytes): bytes|str.
            If None, no additional filering is performed.
        coding (str): The coding for converting the returning object to str.
            If str, must be a valid coding.
            If None, the object is kept as bytes.
        block_size (int|None): The block size.
            See `size` argument of `flyingcircus.blocks` for exact
            behavior.

    Returns:
        hash_key (str|bytes): The result of the hashing.
    r  r   N)r  r  r  r  r7  r  r   r  )	r  hash_algorithm	filteringcodingr  hash_objr  r  hash_keys	            ru   	hash_filer  /  s    < H	7D	! #XaHj1 	#EOOE"	##  HX&*Xu-??6*O# #s   4BBc                 4    t        |       j                         S r   )reprr!  r  s    ru   r  r  0  s    T!W^^- rw   c                      ||       } ||      j                         }|r ||      }|r!t        |t              r|j                  |      }|S )aj  
    Compute the hash of an object.

    Args:
        obj: The input object.
        serializer (callable): The function used to convert the object.
            Must have the following signature:
            serializer(Any): bytes
        hash_algorithm (callable): The hashing algorithm.
            This must support the methods provided by `hashlib` module, like
            `md5`, `sha1`, `sha256`, `sha512`.
        filtering (callable|None): The filtering function.
            If callable, must have the following signature:
            filtering(bytes): bytes.
            If None, no additional filering is performed.
        coding (str): The coding for converting the returning object to str.
            If str, must be a valid coding.
            If None, the object is kept as bytes.

    Returns:
        hash_key (str|bytes): The result of the hashing.
    )r7  r  r   r  )r+  
serializerr  r  r  	obj_bytesr  s          ru   hash_objectr  0  sL    8 3Ii(//1HX&*Xu-??6*Orw   z{hash_key}.pc                    |rt        |      ni }t        | |f      }t        j                  j	                  |t        |            }t        j                  j                  |      r!|st        |d      5 }	 ||	      }
ddd       |
S  | di |}
t        |d      5 }	 ||	|
       ddd       |
S # 1 sw Y   
S xY w# 1 sw Y   |
S xY w)ar  
    Compute or load from cache the result of a computation.

    Args:
        func (callable): The computation to perform.
        kws (dict|None): Keyword arguments for `func`.
        dirpath (str): The path of the caching directory.
        filename (str): The filename of the caching file.
            This is processed by `format` with `locals()`.
        save_func (callable): The function used to save caching file.
            Must have the following signature:
            save_func(file_obj, Any): None
            The value returned from `save_func` is not used.
        load_func (callable): The function used to load caching file.
            Must have the following signature:
            load_func(file_obj): Any
        force (bool): Force the calculation, regardless of caching state.

    Returns:
        result (Any): The result of the cached computation.
    r  Nwbr   )r  r  rp   rq   r   r   isfiler  )r   r   dirpathfilename	save_func	load_funcr  r  rt   r  r   s              ru   from_cachedr  B0  s    : $s)CD#;'Hww||GT(^4H	ww~~h(D! 	)Xx(F	) M (D! 	(Xh'	(M	) M	(Ms   3	B.
B;.B8;Cc              #     K   t        |       }|rdnd}|rdnd}|}	t        ||      }
|st        }nt        } || fi |
D ]m  }|j	                  |      }|	r|s|	|d   z   |d<   n|d   |	z   |d<   ||sdnd   }	|st        ddd      nt        ddd      }||   D ]  }|s|r||r|n|z     o |	s|s|	|r|n|z    y	y	w)
a  
    Flexible function for reading lines incrementally.

    Args:
        file_obj (file): The input file.
        reverse (bool): Read the file in reverse mode.
            If True, the lines will be read in reverse order.
            This requires a binary file object.
            The content of each line will NOT be reversed.
        skip_empty (bool): Skip empty lines.
        append_newline (bool):
        block_size (int|None): The block size.
            If int, the file is processed in blocks of the specified size.
            If None, the file is processed at once.
        reset_offset (bool): Reset the file offset.
            If True, starts reading from the beginning of the file.
            Otherwise, starts reading from where the file current position is.
            This is passed to `blocks()` or `blocks_r()` (depending on the
            value of reverse).

    Yields:
        line (str|bytes): The next line.

    Examples:
        >>> with open(__file__, 'rb') as file_obj:
        ...     lines = [l for l in readline(file_obj, False)]
        ...     lines_r = [l for l in readline(file_obj, True)][::-1]
        ...     lines == lines_r
        True
       

rw   r8   )r	  r  r   r<   r/   N)r  r  r  r  rZ  r  )r  r  
skip_emptyappend_newliner  r  is_bytesnewliner   	remainderblock_generator_kwsblock_generatorr  linesmaskr  s                   ru   readliner  m0  s    J  )HeTGCEIJ\J " A-@A DG$$uQx/a!"I	1b	G"3	&-uQA5Q3C$K 	DD:gUCC	DD 
n7%@@ #s   BC" Cc           
   #     K   d }	 t        j                  |       D ]  }t         j                  j                  | |      }	t        j                  |	      }
|
j
                  } ||t         j                  j                  |	            xrN  ||t         j                  j                  |	            xr&  ||t        |            xr  ||t        |	            }|s|	|
f t         j                  j                  |	      s|dk7  st        |	|||||dz
  |      }|D ]  \  }}||f   y# t        $ r}| ||       Y d}~yd}~ww xY ww)a  
    Recursively walk through sub-paths of a base directory.

    This produces a generator for the next sub-path item.

    Args:
        base (str): Directory where to operate.
        follow_links (bool): Follow links during recursion.
        follow_mounts (bool): Follow mount points during recursion.
        allow_special (bool): Include special files.
        allow_hidden (bool): Include hidden files.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        on_error (callable): Function to call on error.

    Yields:
        result (tuple): The tuple
            contains:
             - path (str): Path to the next object.
             - stats (stat_result): File stats information.

    Returns:
        None.
    c                     | xs |  xr | S r   r   )flagrU  s     ru   _or_not_and_notziwalk2.<locals>._or_not_and_not0  s    -4x-I-rw   r   r/   N)rp   listdirrq   r   ry   st_modeislinkismountr   rv   isdiriwalk2OSError)r_  follow_linksfollow_mountsallow_specialallow_hiddenr  r  r  r-  rq   r  r   allowr  	next_path
next_statserrors                    ru   r  r  0  sB    F.JJt$ 	8D77<<d+DGGDME==D  bggnnT.BC @rwwt/DE@{4/@A@  j.>?	 
 Ek!77==& A~%+ ,)<Q$&&
 6@ 81Iz"+Z"778%	8*  UOsA   ECD. %D.  D. 'D. -E.	E7
EEEEc           	      L    t        | ||||||      D cg c]  }| c}S c c}w )a  
    Recursively walk through sub paths of a base directory.

    This differs from `iwalk2()` in that it returns a list.

    Args:
        base (str): Directory where to operate.
        follow_links (bool): Follow links during recursion.
        follow_mounts (bool): Follow mount points during recursion.
        allow_special (bool): Include special files.
        allow_hidden (bool): Include hidden files.
        max_depth (int): Maximum depth to reach. Negative for unlimited.
        on_error (callable): Function to call on error.

    Returns:
        items (list[tuple]): The list of items.
            Each item contains the tuple with:
             - path (str): Path to the next object.
             - stats (stat_result): File stats information.
    )r   r!  r"  r#  r  r  )r  )r_  r   r!  r"  r#  r  r  r   s           ru   walk2r)  0  s7    8 $!#,h	0 1TD 1 1 1s   	!c                    d }	 t        j                  |       } t        j                  j                  | d         }t        j                  j                  |      \  }}|r	 ||      }nvd}t        j                  d   j                  t        j                        D ]A  }|j                  d      }t        j                  j                  ||      } ||      }|s?|} n |g| dd z   |fS # t        $ r Y w xY w)a&  
    Determine the full path of an executable, if possible.

    It mimics the behavior of the POSIX command `which`.

    This has a similar behavior as `shutil.which()` except that it works on
    a list of arguments as returned by `shlex.split()` where the first item
    is the command to test.

    Args:
        args (str|list[str]): Command to execute as a list of tokens.
            If str this is filtered (tokenized) by `shlex.split()`.
            Otherwise, assumes an input like the output of `shlex.split()`.

    Returns:
        args (str|list[str]): Command to execute as a list of tokens.
            The first item of the list is the full path of the executable.
            If the executable is not found in path, returns the first token of
            the input.
            Other items are identical to input, if the input was a str list.
            Otherwise it will be the tokenized version of the passed string,
            except for the first token.
        is_valid (bool): True if path of executable is found, False otherwise.
    c                     t         j                  j                  |       xr$ t        j                  | t         j                        S r   )rp   rq   r  accessX_OK)	file_paths    ru   is_executablezwhich.<locals>.is_executable-1  s)    ww~~i(JRYYy"''-JJrw   r   Fr   "r/   N)
shlexrZ  r  rp   rq   
expanduserenvironpathsepstripr   )r   r/  cmdr  r  is_validr  s          ru   whichr8  1  s    4K{{4  ''

T!W
%Cc*GX %zz&)//

; 	GmmC(G'',,w,C$S)H	 548X%%!  s   C* *	C65C6callzutf-8c           	         d\  }}	}
t        |       \  } }|r@t        dj                  |rdnddj                  |             ||rt        nt
        d          n,t        dj                  t        t        |                          |sb|r_|#t        d	j                  |      |t
        d
          t        j                  | |r|dk(  st        j                  nd|dk7  rt        j                  nd|dk(  rt        j                  nt        j                  d      }|dk(  r|sd}	|j                         k|j                  j                         j                  |      }|	|z  }	t        |dd       t         j                  j#                          |j                         k|j%                         }n|dk(  r|j'                  |r|j)                  |      nd      \  }	}
|	j                  |      }	|
j                  |      }
|	rt        |	|t
        d   d       |
rt        |
|t
        d   d       |j%                         }n*|j+                          t        dj                  |             |rt,        j.                  j1                  | d         }|j2                  }|	df|
dffD ]H  \  }}|s	t5        |      }t7        |d      5 }|j9                  |j)                  |             ddd       J ||	|
fS # 1 sw Y   YxY w)a  
    Execute command and retrieve/print output at the end of execution.

    Better handles `stdin`, `stdout` and `stderr`, as well as timeout,
    dry-run and verbosity compared to the many alternatives
    (as provided by the `subprocess` module, `os.system()`, `os.spawn*()`).

    For some applications the high-level API provided by `subprocess.run()`
    may be more appropriate.

    Args:
        cmd (str|Iterable[str]): The command to execute.
            If str, must be a shell-compatible invocation.
            If Iterable, each token must be a separate argument.
        in_pipe (str|None): Input data to be used as stdin of the process.
        mode (str): Set the execution mode (affects the return values).
            Allowed modes:
             - 'spawn': Spawn a new process. stdout and stderr will be lost.
             - 'call': Call new process and wait for execution.
                Once completed, obtain the return code, stdout, and stderr.
             - 'flush': Call new process and get stdout+stderr immediately.
                Once completed, obtain the return code.
        timeout (float): Timeout of the process in seconds.
        encoding (str): The encoding to use.
        log (str): The template filename to be used for logs.
            If None, no logs are produced.
        dry (bool): Print rather than execute the command (dry run).
        verbose (int): Set level of verbosity.

    Returns:
        ret_code (int|None): if mode not `spawn`, return code of the process.
        p_stdout (str|None): if mode not `spawn`, the stdout of the process.
        p_stderr (str|None): if mode is `call`, the stderr of the process.
    )NNN{} {}z$$>>r  mediumz%W: `{}` is not in available in $PATH.Nz< {}highestflushspawnr9  F)stdinstdoutstderrshellr8   )fmttendhigh)rE  z)E: mode `{}` and `in_pipe` not supported.r   outerrr  )r8  r   r  r   r
   r	   r   r   
subprocessPopenPIPESTDOUTpollrB  r  r  sysr?  waitcommunicater!  killrp   rq   rr   pidr   r  r  )r6  in_piper   timeoutencodingr  dryverboseret_codep_stdoutp_stderrr7  procout_buffr-  rS  streamsourcelog_filepathfileobjs                       ru   executerb  G1  s   V $4 Hh#JMCGNN34D#((3-@3ZHX,>	@ 	3::4S	?KL8g&),. %,TW_*//$&*go:??4&*fn:??*:K:K 7?7H))+%;;//188BH$H22.

  "	 ))+%
 yy{HV^!%!1!1,3x("?Hh
  x0Hx0HHgx'7bAHgx'7bAyy{HIIK;BB4HI77##CF+D((C$,e#4x6G"H ?#'9LlD1 ?WfmmH&=>? ??
 Xx''? ?s   =!K--K6	<   c           	      b   |t        |      nd}|t        |      ni }|st        j                         dz   }t	        |       }d}t
        j
                  j                         }	| d| D 
cg c]  }
t        j                  |
d       }}
|D ]?  }t        dj                  dd	j                  |j                              |t        d
          A t	        |      }d}|sCt	        |      }|D cg c]  }|j                         | }}t	        |      }||z
  }|dkD  r||z  }| |||z    D 
cg c]  }
t        j                  |
d       }}
|D ]?  }t        dj                  dd	j                  |j                              |t        d
          A |t	        |      z  }||z  }t	        |      }t
        j
                  j                         |	z
  }t        d      }t        ||t                t#        |      r ||i | ||k(  rd}nt%        j&                  |       |sByyc c}
w c c}w c c}
w )a  
    Spawn parallel processes and wait until all processes are completed.

    Args:
        cmds (Sequence[str|Iterable[str]): The commands to execute.
            Each item must be a separate command.
            If the item is a str, it must be a shell-compatible invocation.
            If the item is an Iterable, each token must be a separate argument.
        pool_size (int|None): The size of the parallel pool.
            This corresponds to the maximum number of concurrent processes
            spawned by the function.
        poll_interval (int): The poll interval in s.
            This is the time between status updates in seconds.
        callback (callable|None): A callback function.
            This is called after each poll interval.
        callback_args (Iterable|None): Positional arguments for `callback()`.
        callback_kws (Mappable|None): Keyword arguments for `callback()`.
        verbose (int): Set level of verbosity.

    Returns:
        None.
    Nr   r/   r   T)rD  r;  r<  r  r=  FzZI: {num_processed} / {num_total} processed ({num_running} running) - Elapsed: {elapsed_dt})r   r  multiprocessing	cpu_countr   datetimenowrJ  rK  r   r  r   r   r	   rN  r   r
   r   timesleep)cmds	pool_sizepoll_intervalcallbackcallback_argscallback_kwsrX  	num_totalnum_processedbegin_dtr6  procsr\  num_submitteddone	num_batchnum_runningnum_done	new_procs
elapsed_dtr  s                        ru   parallel_executer|  1  s?   < -:,EE-(2M*6*B4&L#--/!3	D	IM  $$&H59*95EG.1
D)GE G )GNN4$))!45Xh'	)) JMDJ	"'?$499;+>??%j{*a<X%M  mh.FGI   D1II I " 1GNN4$)))<=Xh/11 S^+MYEe*K&&**,x7
?@ 	D':&Hm4|4I%DJJ}%5 G @
Is   *H"-H'H'-H,c                 4   t         j                  j                  t         j                  j                  t         j                  j	                  |                   }t         j                  j                  |      s|rt        j                  |       |S t        |S )aD  
    Get the expanded absolute path from its short or relative counterpart.

    Args:
        path (str): The path to expand.
        create (bool): Automatically create the path if not exists.

    Returns:
        new_path (str): the expanded path.

    Raises:
        OSError: if the expanded path does not exists.
    )rp   rq   abspathrealpathr2  existsmakedirsr  )rq   createnew_paths      ru   r  r  1  sf      wwrww//0B0B40HIJH77>>(#KK! O MOrw   c           	         |t        dj                  |       |t        d          t        j                  |       D cg c]c  }t        j
                  j                  t        j
                  j                  | |            r$|r t        j
                  j                  | |      n|e }}nt        dj                  |rd|z   dz   nd|       |t        d          t        j                  |       D cg c]S  }|j                         j                  |j                               r$|r t        j
                  j                  | |      n|U }}|rt        |      }|S c c}w c c}w )a  
    Retrieve a sorted list of files matching specified extension and pattern.

    Args:
        path (str): Path to search.
        file_ext (str|None): File extension. Empty string for all files.
            None for directories.
        full_path (bool): Include the full path.
        sorting (bool): Sort results alphabetically.
        verbose (int): Set level of verbosity.

    Returns:
        list[str]: List of file names/paths
    zScanning for dirs on:
{}debugzScanning for {} on:
{}`r  )r   r  r	   rp   r  rq   r  r   r^  endswithrl  )rq   file_ext	full_pathr  rX  r  	filepathss          ru   r  r  2  s5   ( '..t4Xg&	( JJt,<ww}}RWW\\$9: -6BGGLLx(8C<	 <
 	%,,&.S8^c!GTCXg&	( JJt,<~~(()9: -6BGGLLx(8C<	 < 9%	<<s   A(E-AEc              #     K   t        | t              r| f} |
t               }|
t               }| D ]  }|rt        j                  |      }t        j                  |fi |}t        j                  |fi |D ]F  \  }}}	||	z   D ]8  }
t        j                  j                  ||
      }|j                  |      s5| : H  yw)a  
    Recursively list the content of a directory matching the pattern(s).

    Args:
        dirpath (str): The base directory.
        patterns (str|Iterable[str]): The pattern(s) to match.
            These must be either a Unix-style pattern or a regular expression,
            depending on the value of `unix_style`.
        unix_style (bool): Interpret the patterns as Unix-style.
            This is achieved by using `fnmatch`.
        re_kws (Mappable|None): Keyword arguments for `re.compile()`.
        walk_kws (Mappable|None): Keyword arguments for `os.walk()`.

    Yields:
        filepath (str): The next matched filepath.
    N)r  ra   r  fnmatchr  recompilerp   walkrq   r   match)patternsr  
unix_stylere_kwswalk_kwspatternre_objrootdirsr  r_  rt   s               ru   	iflistdirr  <2  s     , (C ;~6 #''0GG.v.!#!=H!= 	#D$ #77<<d3<<)"N#	#	#s   B4C7Cc                 H    t        | ||||      D cg c]  }| c}S c c}w )a  
    Recursively list the content of a directory matching the pattern(s).

    Args:
        dirpath (str): The base directory.
        patterns (str|Iterable[str]): The pattern(s) to match.
            These must be either a Unix-style pattern or a regular expression,
            depending on the value of `unix_style`.
        unix_style (bool): Interpret the patterns as Unix-style.
            This is achieved by using `fnmatch`.
        re_kws (Mappable|None): Keyword arguments for `re.compile()`.
        walk_kws (Mappable|None): Keyword arguments for `os.walk()`.

    Returns:
        filepaths (list[str]): The matched filepaths.
    )r  r  r  r  r  )r  )r  r  r  r  r  r   s         ru   flistdirr  d2  s2    . #w:H.// / /s   	c                 @    | d} | j                  |      rd| z   S || z   S )a  
    Add a extsep char to a filename extension, if it does not have one.

    Args:
        ext (str|None): Filename extension to which the dot has to be added.
        extsep (str): The string to use a filename extension separator.

    Returns:
        ext (str): Filename extension with a prepending dot.

    Examples:
        >>> add_extsep('txt')
        '.txt'
        >>> add_extsep('.txt')
        '.txt'
        >>> add_extsep('')
        '.'
        >>> add_extsep(None)
        '.'
    r8   rH  )extextseps     ru   
add_extsepr  2  s.    . {..(B;;f;;rw   c                    | }|ft        |      }|s-| j                         j                  |j                               n| j                  |      }|r| dt        |        }||fS d}||fS |rhd}d}|r^t        j
                  j                  |      \  }}|r5|r3|d   j                         xr |d   j                          }|r
|}||z   }nd}|r^||fS t        j
                  j                  |       \  }}||fS )a.  
    Split the filepath into a pair (root, ext), so that: root + ext == path.
    root is everything that precedes the first extension separator.
    ext is the extension (including the separator).

    It can automatically detect multiple extensions.
    Since `os.path.extsep` is often '.', a `os.path.extsep` between digits is
    not considered to be generating and extension.

    Args:
        filepath (str): The input filepath.
        ext (str|None): The expected extension (with or without the dot).
            If None, it will be obtained automatically.
            If empty, no split is performed.
        case_sensitive (bool): Case-sensitive match of old extension.
            If `ext` is None or empty, it has no effect.
        auto_multi_ext (bool): Automatically detect multiple extensions.
            If True, include multiple extensions.
            If False, only the last extension is detected.
            If `ext` is not None or empty, it has no effect.

    Returns:
        result (tuple): The tuple
            contains:
             - root (str): The filepath without the extension.
             - ext (str): The extension including the separator.

    Examples:
        >>> split_ext('test.txt', '.txt')
        ('test', '.txt')
        >>> split_ext('test.txt')
        ('test', '.txt')
        >>> split_ext('test.txt.gz')
        ('test', '.txt.gz')
        >>> split_ext('test_1.0.txt')
        ('test_1.0', '.txt')
        >>> split_ext('test.0.txt')
        ('test', '.0.txt')
        >>> split_ext('test.txt', '')
        ('test.txt', '')
    Nr8   Tr/   r<   F)r  r^  r  r   rp   rq   splitextisdigit)	rt   r  case_sensitiveauto_multi_extr  has_extr7  tmp_filepath_noexttmp_exts	            ru   	split_extr  2  s*   \ D
o! .."++CIIK8'/'8'8'= 	Jc#hY'D& 9# C" 9 CH.0gg.>.>t.D+"G%'$+AJ$6$6$8 %E$6r$:$B$B$D FH1%m$H  9 ((2ID#9rw   c                 p    t         j                  j                  |       \  }}t        ||      \  }}|||fS )a  
    Split the filepath into (root, base, ext).

    Note that: root + os.path.sep + base + ext == path.
    (and therfore: root + base + ext != path).

    root is everything that preceeds the last path separator.
    base is everything between the last path separator and the first
    extension separator.
    ext is the extension (including the separator).

    Note that this separation is performed only on the string and it is not
    aware of the filepath actually existing, being a file, a directory,
    or similar aspects.

    Args:
        filepath (str): The input filepath.
        auto_multi_ext (bool): Automatically detect multiple extensions.
            Refer to `split_ext()` for more details.

    Returns:
        result (tuple): The tuple
            contains:
             - root (str): The filepath without the last item.
             - base (str): The file name without the extension.
             - ext (str): The extension including the extension separator.

    Examples:
        >>> split_path('/path/to/file.txt')
        ('/path/to', 'file', '.txt')
        >>> split_path('/path/to/file.tar.gz')
        ('/path/to', 'file', '.tar.gz')
        >>> split_path('file.tar.gz')
        ('', 'file', '.tar.gz')
        >>> split_path('/path/to/file')
        ('/path/to', 'file', '')

        >>> root, base, ext = split_path('/path/to/file.ext')
        >>> root + os.path.sep + base + ext
        '/path/to/file.ext'

    See Also:
        - flyingcircus.join_path()
        - flyingcircus.multi_split_path()
    r  )rp   rq   rZ  r  )rt   r  r  base_extr_  r  s         ru   
split_pathr  2  s7    ` WW]]8,ND((>BID#s?rw   c                 ,   t         j                  j                  |       \  }}t        ||      \  }}|rO|j                  t         j                  j                        }|d   dk(  r t         j                  j                  |d<   nd}t        |      ||fz   S )a  
    Split the filepath into (subdir, subdir, ..., base, ext).

    Note that: splits[0] + os.path.sep.join(splits[1:-1]) + ext == path
    (and therfore e.g.: ''.join(splits) != path).

    `root` is everything that preceeds the last path separator.
    `base` is everything between the last path separator and the first
    extension separator.
    `ext` is the extension (including the separator).

    Note that this separation is performed only on the string and it is not
    aware of the filepath actually existing, being a file, a directory,
    or similar aspects.

    Args:
        filepath (str): The input filepath.
        auto_multi_ext (bool): Automatically detect multiple extensions.
            Refer to `split_ext()` for more details.

    Returns:
        result (tuple[str]): The parts of the file.
            If the first char of the filepath is `os.path.sep`, then
            the first item is set to `os.path.sep`.
            The first (n - 2) items are subdirectories, the penultimate item
            is the file name without the extension, the last item is the
            extension including the extension separator.

    Examples:
        >>> multi_split_path('/path/to/file.txt')
        ('/', 'path', 'to', 'file', '.txt')
        >>> multi_split_path('/path/to/file.tar.gz')
        ('/', 'path', 'to', 'file', '.tar.gz')
        >>> multi_split_path('file.tar.gz')
        ('file', '.tar.gz')
        >>> multi_split_path('/path/to/file')
        ('/', 'path', 'to', 'file', '')

        >>> splits = multi_split_path('/path/to/file.ext')
        >>> splits[0] + os.path.sep.join(splits[1:-1]) + splits[-1]
        '/path/to/file.ext'

    See Also:
        - flyingcircus.join_path()
        - flyingcircus.split_path()
    r  r   r8   r   )rp   rq   rZ  r  r  r   )rt   r  r  r  r_  r  r  s          ru   multi_split_pathr  3  sx    b WW]]8,ND((>BID#zz"''++&7b=ggkkDG;$$$rw   c                 ~    | dd rt        j                  j                  | dd  nd| d   rt        | d         z   S dz   S )a  
    Join a list of items into a filepath.

    The last item is treated as the file extension.
    Path and extension separators do not need to be manually included.

    Note that this is the inverse of `split_path()`.

    Args:
        texts (Iterable[str]): The path elements to be concatenated.
            The last item is treated as the file extension.

    Returns:
        filepath (str): The output filepath.

    Examples:
        >>> join_path(('/path/to', 'file', '.txt'))
        '/path/to/file.txt'
        >>> join_path(('/path/to', 'file', '.tar.gz'))
        '/path/to/file.tar.gz'
        >>> join_path(('', 'file', '.tar.gz'))
        'file.tar.gz'
        >>> join_path(('path/to', 'file', ''))
        'path/to/file'
        >>> paths = [
        ...     '/path/to/file.txt', '/path/to/file.tar.gz', 'file.tar.gz']
        >>> all(path == join_path(split_path(path)) for path in paths)
        True
        >>> paths = [
        ...     '/path/to/file.txt', '/path/to/file.tar.gz', 'file.tar.gz']
        >>> all(path == join_path(multi_split_path(path)) for path in paths)
        True

    See Also:
        - flyingcircus.split_path()
        - flyingcircus.multi_split_path()
    Nr<   r8   )rp   rq   r   r  )textss    ru   	join_pathr  \3  sN    L +0*RWW\\5":&"&+BiZb	"9 :579 :rw   c                 f    t         j                  j                  |       } t        | |||      \  }}|S )a  
    Remove path AND the extension from a filepath.

    Args:
        filepath (str): The input filepath.
        ext (str|None): The expected extension (with or without the dot).
            Refer to `split_ext()` for more details.
        case_sensitive (bool): Case-sensitive match of expected extension.
            Refer to `split_ext()` for more details.
        auto_multi_ext (bool): Automatically detect multiple extensions.
            Refer to `split_ext()` for more details.

    Returns:
         root (str): The file name without path and extension.

    Examples:
        >>> basename('/path/to/file/test.txt', '.txt')
        'test'
        >>> basename('/path/to/file/test.txt.gz')
        'test'
    )rp   rq   rr   r  )rt   r  r  r  r  s        ru   rr   rr   3  s4    4 ww)H#~~7ID#Krw   c                 T    t        | |||      \  } }| |rt        |      z   }|S dz   }|S )a  
    Substitute the old extension with a new one in a filepath.

    Args:
        root (str): The input filepath.
        new_ext (str): The new extension (with or without the dot).
        ext (str|None): The expected extension (with or without the dot).
            Refer to `split_ext()` for more details.
        case_sensitive (bool): Case-sensitive match of expected extension.
            Refer to `split_ext()` for more details.
        auto_multi_ext (bool): Automatically detect multiple extensions.
            Refer to `split_ext()` for more details.

    Returns:
        filepath (str): Output filepath

    Examples:
        >>> change_ext('test.txt', 'dat', 'txt')
        'test.dat'
        >>> change_ext('test.txt', '.dat', 'txt')
        'test.dat'
        >>> change_ext('test.txt', '.dat', '.txt')
        'test.dat'
        >>> change_ext('test.txt', 'dat', '.txt')
        'test.dat'
        >>> change_ext('test.txt', 'dat', 'TXT', False)
        'test.dat'
        >>> change_ext('test.txt', 'dat', 'TXT', True)
        'test.txt.dat'
        >>> change_ext('test.tar.gz', 'tgz')
        'test.tgz'
        >>> change_ext('test.tar.gz', 'tgz', 'tar.gz')
        'test.tgz'
        >>> change_ext('test.tar.gz', 'tgz', auto_multi_ext=False)
        'test.tar.tgz'
        >>> change_ext('test.tar', 'gz', '')
        'test.tar.gz'
        >>> change_ext('test.tar', 'gz', None)
        'test.gz'
        >>> change_ext('test.tar', '')
        'test'
    r8   )r  r  )r  new_extr  r  r  rt   s         ru   
change_extr  3  sA    ` c>>3ID#gz'*>HO <>>HOrw   z{basepath}__{counter}{ext}c                    t         j                  j                  |       rt        dj	                  |       |t
        d          t        |       \  }}}t         j                  j                  ||      }d}t         j                  j                  |       r0|dz  }t        |      } t         j                  j                  |       r0t        dj	                  |       |t
        d          | S )aN  
    Generate a non-existing filepath if current exists.

    Args:
        filepath (str): The input filepath.
        out_template (str): Template for the output filepath.
            The following variables are available for interpolation:
             - `dirpath`: The directory of the input.
             - `base`: The input base file name without extension.
             - `ext`: The input file extension (with leading separator).
             - `basepath`: The input filepath without extension.
        verbose (int): Set level of verbosity.

    Returns:
        filepath (str)
    zOLD: {}rG  r   r/   zNEW: {}r=  )	rp   rq   r  r   r  r	   r  r   r   )rt   out_templaterX  r  r_  r  basepathr  s           ru   next_filepathr  3  s    ( 
ww~~hIX&&1AB'1s77<<.ggnnX&qLGL)H ggnnX& 	IX&(1CDOrw   c                 Z    t        j                  dj                  ||rdnd      ||       S )a  
    Return a string containing a safe filename.

    Args:
        text (str): The input string.
        allowed (str):  The valid characters.
            Must comply to Python's regular expression syntax.
        replacing (str): The replacing text.
        group_consecutive (bool): Group consecutive non-allowed.
            If True, consecutive non-allowed characters are replaced by a
            single instance of `replacing`.
            Otherwise, each character is replaced individually.

    Returns:
        text (str): The filtered text.

    Examples:
        >>> safe_filename('flyingcircus.txt')
        'flyingcircus.txt'
        >>> safe_filename('flyingcircus+12.txt')
        'flyingcircus_12.txt'
        >>> safe_filename('flyingcircus+12.txt')
        'flyingcircus_12.txt'
        >>> safe_filename('flyingcircus+++12.txt')
        'flyingcircus_12.txt'
        >>> safe_filename('flyingcircus+++12.txt', group_consecutive=False)
        'flyingcircus___12.txt'
        >>> safe_filename('flyingcircus+12.txt', allowed='a-zA-Z0-9._+-')
        'flyingcircus+12.txt'
        >>> safe_filename('flyingcircus+12.txt', replacing='-')
        'flyingcircus-12.txt'
    z[^{allowed}]{greedy}r]  r8   )allowedgreedy)r  r   r  )r  r  	replacinggroup_consecutives       ru   safe_filenamer   4  s9    J 66&&+<C" 	' 	F4 rw   c                    d}d}|D ]Q  }	 t        j                  |      } |j                  | g|i |}|j                  d       |j	                  d        n |st        | g|i |}|S # t
        t        t        t        f$ r d}Y w xY w)a%  
    Auto-magically open a compressed file.

    Supports `gzip` and `bzip2`.

    Note: all compressed files should be opened as binary.
    Opening in text mode is not supported.

    Args:
        filepath (str|bytes): The file path.
        *_args: Positional arguments for `open()`.
        **_kws: Keyword arguments for `open()`.

    Returns:
        file_obj: A file object.

    Raises:
        IOError: on failure.

    See Also:
        open(), gzip.open(), bz2.open()

    Examples:
        >>> file_obj = magic_open(__file__, 'rb')
    )r!   r   Nr/   r   )		importlibimport_moduler  r  r  r  IOErrorr  ImportError)rt   r   r   zip_module_namesr  zip_module_name
zip_modules          ru   
magic_openr  ,4  s    : %H+ 		"00AJ&zx@%@4@HMM! MM!	 151D1O .+> 	H	s   <A..BBc                    d|vr	d|vr|dz  }d|v xr d|v}t        | g|d|i|}|rr	 ddl}	 |j                  d      }|dd dk(  }|dd d	k(  xr' |dd
 dk(  xr |d
d j	                         xr |dd dk(  }	|dd dk(  }
|j                  d       t        |       d   j                  t        t        d               }t        |       d   j                  t        t        d               }t        |       d   j                  t        t        d               xs- t        |       d   j                  t        t        d               }|s|rt        j                  ||      }|S |	s|rt        j                  ||      }|S |r|
s|r|j!                  ||      }|S # t        $ r d}Y }w xY w# t
        j                  $ r
 d}d}	d}
Y Jw xY w# |j                  d       w xY w)a1  
    Auto-magically open a compressed file.

    Supports the following file formats:
     - `gzip`: GNU Zip -- DEFLATE (Lempel-Ziv 77 + Huffman coding)
     - `bzip2`: Burrows-Wheeler algorithm
     - `lzma`/`xz`: Lempel-Ziv-Markov chain algorithm (LZMA) (Python 3 only)

    This is achieved through the Python standard library modules.

    Notes:
    - All compressed files should be opened as binary.
      Opening in text mode is not supported.

    Args:
        filepath (str): The file path.
            This cannot be a file object.
        mode (str): The mode for file opening.
            See `open()` for more info.
            If the `t` mode is not specified, `b` mode is assumed.
            If `t` mode is specified, the file cannot be compressed.
        *_args: Positional arguments for `open()`.
        **_kws: Keyword arguments for `open()`.

    Returns:
        file_obj: A file object.

    Raises:
        IOError: on failure.

    See Also:
        open(), gzip.open(), bz2.open()

    Examples:
        >>> file_obj = zopen(__file__, 'rb')
    rI  rI   r   r   N   r9   s   s   BZr&      hrb   
   s   1AY&SYr/   s   7zXz Fr!   r#   r   r   )ra  r   )r  r   )r  r   r  r  r  r  r  r  r  r  r  EXTr!   GzipFiler   BZ2FileLZMAFile)rt   r   r   r   
valid_moder  r   headgz_by_headerbz2_by_headerxz_by_header	gz_by_ext
bz2_by_ext	xz_by_exts                 ru   zopenr  [4  s"   R $3d?0DJ H8%84848H
		==$D8{2L!H% B$q)t*; BQq	))+BQr
&AA   86L MM!h'*33Js6{4KL	x(+44ZG5MN
(#A&//
3t90EF I(#A&//
3v;0GH 	 9}}XDAH O j{{H4@H O li}}hT}BHO=  	D	 && 	! L!M L	!
 MM!s5   F  AF2  F/.F/2GG GG G%c                    	 t        |       } d}d}d}t        |       dkD  rt        j                  t        |             nd}| dk  r||z
  n|}|t        |      kD  s|t        |dz
         k  r||dz   z  }dj	                  | |      }|S t        |dz
         |cxk  rdk  rn n||z  }dj	                  | |      }|S | dz  dk(  rd	}dj	                  | |      }|S ||t        |      z   z  }|d	k  rd	}dj	                  | |      }	 |S # t        t        f$ r* t        j                  d
j	                  |              d}Y |S w xY w)a  
    Convert a number into the most informative string within specified limit.

    Args:
        val (int|float): The number to be converted to string.
        max_lim (int): The maximum number of characters allowed for the string.

    Returns:
        val_str (str): The string with the formatted number.

    Examples:
        >>> compact_num_str(100.0, 3)
        '100'
        >>> compact_num_str(100.042, 6)
        '100.04'
        >>> compact_num_str(100.042, 9)
        '100.04200'
    rB  r9   r/   r  z{:.{size}e})r	  z{:.{size}f}r  r   z%Could not convert value `{}` to floatNaN)
r_   r!  rd  log10r  rY   r   r"  r  r  )r<  max_limextra_char_in_expextra_char_in_decextra_char_in_signorderr%  val_strs           ru   compact_num_strr  4  s   ,Cj(+C3

3s8$A03c	,,w5<5E2Ca2G,H+H#H&**E#**3U*;G  N %)**e9c9&&E#**3U*;G N 3Y#E#**3U*;G N '#e*45Eqy#**3U*;G N z" =DDSIJNs$   BD 
3D ?D +D 5EEc
           
      V    t        |      r	 |       }
n0t        |t              rt         |      }
n|t	         g| }
ndx}
}|1r|ffd	}|j                  fdt         |ddd      D              }n|j                   fd|D              }dj                  ||
|||	      S )aB  
    Generate a meaningful string representation of an object.

    Args:
        obj (Any): The input object.
        names (Iterable[str]|None): The attribute names to consider.
            If None, uses `idir(obj, skip, methods=False, attributes=True)`
        skip (str|callable): The skip criterion.
            If str, names starting with it are skipped.
            If callable, skips when `skip(name)` evaluates to True.
        base (str|Iterable[str]|callable): The base object name.
            If str, uses `getattr(obj, base)`.
            If Iterable[str], uses `get_nested_attr(obj, *base)`.
            If callable, uses `base(obj)`.
            If None, the base part is skipped.
        base_sep (str): The name-to-attributes separator.
        attr_sep (str): The attributes separator.
        kv_sep (str): The key-value separator.
        pre_delim (str): The prefix delimiter.
            This is prepended to the final string.
        post_delim (str): The postfix delimiter.
            This is appended to the final string.

    Returns:
        str: A text representation of the object.

    Examples:
        >>> print(obj2str(1))
        <int: denominator=1, imag=0, numerator=1, real=1>
        >>> print(obj2str(1, base=None))
        <denominator=1, imag=0, numerator=1, real=1>
        >>> print(obj2str(1, ('real', 'imag')))
        <int: real=1, imag=0>
        >>> print(obj2str(1, pre_delim='{', post_delim='}'))
        {int: denominator=1, imag=0, numerator=1, real=1}
        >>> print(obj2str(1, pre_delim='<!', post_delim='>'))
        <!int: denominator=1, imag=0, numerator=1, real=1>
        >>> print(obj2str(1, base_sep='::', attr_sep=',', kv_sep=':'))
        <int::denominator:1,imag:0,numerator:1,real:1>
        >>> print(obj2str(1, blacklist=('denominator', 'imag')))
        <int: numerator=1, real=1>
    r8   c                 2    | j                  |      xs | v S r   rH  )r-  rS   	blacklists     ru   rJ  zobj2str.<locals>.skip,5  s    q)>TY->>rw   c              3   J   K   | ]  \  }}d j                  ||        ywz{}{}{}N)r  )r   r-  rO  kv_seps      ru   r   zobj2str.<locals>.<genexpr>.5  s,      Bd OOD&$/Br  FTc           	   3   p   K   | ]-  }t        |      rd j                  |t        |             / ywr  )r   r  r*  )r   r-  r  r+  s     ru   r   zobj2str.<locals>.<genexpr>25  s6      5d!3 OOD&'#t*<=5s   36z
{}{}{}{}{})r   r  ra   r*  r.  r   rQ  r  )r+  r,  rJ  r  r_  base_sepattr_sepr  	pre_delim
post_delim	base_namer  s   `  `   `    ru   obj2strr  4  s    j ~I		D#	C&			#C/$/	!!	H}! ?}} B"3eT4@B B }} 55 5 9hj: :rw   c                 J    | j                  |      xr | j                  |      S )a  
    Determine if a string is delimited by some characters (decorators).

    Args:
        text (str): The text input string.
        pre_delim (str): initial string decorator.
        post_delim (str): final string decorator.

    Returns:
        has_delim (bool): True if text is delimited by the specified chars.

    Examples:
        >>> has_delim('"test"')
        True
        >>> has_delim('"test')
        False
        >>> has_delim('<test>', '<', '>')
        True
    )rs   r  )r  r  r  s      ru   	has_delimr  :5  s!    . ??9%C$--
*CCrw   c                     | j                  |      rt        |      nd}| j                  |      rt        |       nd}| || S )a  
    Strip initial and final character sequences (decorators) from a string.

    Args:
        text (str): The text input string.
        pre_delim (str): initial string decorator.
        post_delim (str): final string decorator.

    Returns:
        text (str): the text without the specified decorators.

    Examples:
        >>> strip_delim('"test"')
        'test'
        >>> strip_delim('"test')
        'test'
        >>> strip_delim('<test>', '<', '>')
        'test'
    N)rs   r   r  )r  r  r  beginrF  s        ru   strip_delimr  U5  s?    . #ooi8C	NdE"mmJ73z?
TCc?rw   c                 
   t        | t              ri|r| j                         } |s"| j                         } t	        d |D              }|D ]#  }t        |      D ]  \  }}| |k(  s|dkD  c c S  % t        d      t        |       S )a$  
    Conversion to boolean value.

    This is especially useful to interpret strings are booleans, because
    the built-in `bool()` method evaluates to False for empty strings and
    True for non-empty strings.

    Args:
        value (str|Any): The input value.
            If not string, attempt the built-in `bool()` casting.
        mappings (Sequence[Sequence]): The string values to map as boolean.
            Each item consists of an Sequence. Within the inner Sequence,
            the first element is mapped to False and all other elements map
            to True.
        case_sensitive (bool): Perform case-sensitive comparison.
        strip (bool): Strip whitespaces from input string.
            If input is not `str`, it has no effect.

    Returns:
        result (bool): The value converted to boolean.

    Raises:
        ValueError: if the conversion to boolean fails.

    Examples:
        >>> to_bool('false')
        False
        >>> to_bool('true')
        True
        >>> to_bool('0')
        False
        >>> to_bool('off')
        False
        >>> to_bool('0 ')
        False
        >>> to_bool(1)
        True
        >>> to_bool(0)
        False
        >>> to_bool(0j)
        False
        >>> to_bool('False')
        False
        >>> to_bool('False', case_sensitive=True)
        Traceback (most recent call last):
            ....
        ValueError: Cannot convert to bool
        >>> to_bool('False ', strip=False)
        Traceback (most recent call last):
            ....
        ValueError: Cannot convert to bool
    c              3   @   K   | ]  }t        d  |D                yw)c              3   <   K   | ]  }|j                           y wr   )r^  )r   r  s     ru   r   z$to_bool.<locals>.<genexpr>.<genexpr>5  s     9ekkm9s   Nr  )r   rT  s     ru   r   zto_bool.<locals>.<genexpr>5  s#      ) 999)s   r   zCannot convert to bool)r  ra   r5  r^  r   r  r"  rT   )r[  mappingsr  r5  rT  rM   r  s          ru   to_boolr  r5  s    r %KKMEKKME )') )H   	7G%g. !5E> q5L	!	7 566E{rw   c                     t        | t              r7|r|rt        | ||      rt        | ||      } d}|D ]  }	  ||       } n || }|S | }|S # t        t
        f$ r Y *w xY w)a|  
    Convert value to numeric if possible, or strip delimiters from string.

    Args:
        text (str|Number): The text input string.
        pre_delim (str): initial string decorator.
        post_delim (str): final string decorator.
        casts (Iterable[callable]): The cast conversion methods.
            Each callable must be able to perform the desired conversion,
            or raise either a ValueError or a TypeError on failure.

    Returns:
        val (Number): The numeric value of the string.

    Examples:
        >>> auto_convert('<100>', '<', '>')
        100
        >>> auto_convert('<100.0>', '<', '>')
        100.0
        >>> auto_convert('100.0+50j')
        (100+50j)
        >>> auto_convert('1e3')
        1000.0
        >>> auto_convert(1000)
        1000
        >>> auto_convert(1000.0)
        1000.0
        >>> auto_convert('False')
        False
    N)r  ra   r  r  r   r"  )r  r  r  castsr<  casts         ru   r  r  5  s    F $$	:6tY
;D 	D4j 	 ;C J J z* s   AAAc                 R    	 t        |        d}|S # t        t        f$ r d}Y |S w xY w)a[  
    Determine if a variable contains a number.

    Args:
        val (str): The variable to test.

    Returns:
        result (bool): True if the values can be converted, False otherwise.

    Examples:
        >>> is_number('<100.0>')
        False
        >>> is_number('100.0+50j')
        True
        >>> is_number('1e3')
        True
    TF)complexr   r"  )r<  r   s     ru   	is_numberr   5  s<    $ M	 z"  M	s    &&r  c                 f    | dk7  r+t        t        j                  t        |       |      |z        S dS )aZ  
    Determine the order of magnitude of a number.

    Args:
        val (Number): The input number.
        base (Number): The base for defining the magnitude order.
        exp (Number): The exp for defining the magnitude order.

    Returns:
        result (int): The order of magnitude according to `(base ** exp)`.

    Examples:
        >>> order_of_magnitude(10.0)
        1
        >>> order_of_magnitude(1.0)
        0
        >>> order_of_magnitude(0.1)
        -1
        >>> order_of_magnitude(0.0)
        0
        >>> order_of_magnitude(-0.0)
        0
        >>> order_of_magnitude(634.432)
        2
        >>> order_of_magnitude(1024, 2)
        10
        >>> order_of_magnitude(1234, 10, 3)
        1
        >>> order_of_magnitude(1234, 2, 10)
        1
        >>> all(order_of_magnitude(i) == 0 for i in range(10))
        True
    r  r   )rY   rd  r  r!  )r<  r_  r  s      ru   order_of_magnituder  6  s/    J 47#:3txxC$'3./D1Drw   c                 R    t        |      }| |v r||    S t        t        d            )u  
    Get the order corresponding to a given prefix.

    This works chiefly with SI prefixes.

    Args:
        prefix (str): The prefix to inspect.
        prefixes (Mappable): The conversion table for the prefixes.
            This must have the form: (prefix, order).
            Multiple prefixes can point to the same order.

    Returns:
        order (int|float): The order associate to the prefix.

    Raises:
        IndexError: If no prefix is found in the given prefixes.

    Examples:
        >>> [prefix_to_order(x)
        ...     for x in ('', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')]
        [0, 3, 6, 9, 12, 15, 18, 21, 24]
        >>> [prefix_to_order(x)
        ...     for x in ('', 'm', 'µ', 'n', 'p', 'f', 'a', 'z', 'y')]
        [0, -3, -6, -9, -12, -15, -18, -21, -24]
        >>> [prefix_to_order(x) for x in ('u', 'µ', 'μ')]
        [-6, -6, -6]
        >>> all(prefix_to_order(order_to_prefix(i)) == i
        ...     for i in range(-24, 25, SI_ORDER_STEP))
        True

    See Also:
        - flyingcircus.order_to_prefix()
        - flyingcircus.prefix_to_factor()
        - flyingcircus.factor_to_prefix()
        - flyingcircus.order_to_factor()
        - flyingcircus.factor_to_order()
    zPrefix `{prefix}` not found!)r  r   r   )prefixprefixess     ru   prefix_to_orderr  <6  s2    P H~H<=>>rw   c                 d    t        t        |            }| |v r||    S t        t        d            )u8  
    Get the prefix corresponding to the order of magnitude.

    This works chiefly with SI prefixes.

    Args:
        order (int): The order of magnitude.
        prefixes (Mappable): The conversion table for the prefixes.
            This must have the form: (prefix, order). Must be 1-to-1.

    Returns:
        prefix (str): The prefix for the corresponding order.

    Raises:
        ValueError: If no prefix exists for the input order.

    Examples:
        >>> [order_to_prefix(i * SI_ORDER_STEP) for i in range(9)]
        ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
        >>> [order_to_prefix(-i * SI_ORDER_STEP) for i in range(9)]
        ['', 'm', 'μ', 'n', 'p', 'f', 'a', 'z', 'y']
        >>> order_to_prefix(10)
        Traceback (most recent call last):
            ...
        ValueError: Invalid order `10` for given prefixes.

    See Also:
        - flyingcircus.prefix_to_order()
        - flyingcircus.prefix_to_factor()
        - flyingcircus.factor_to_prefix()
        - flyingcircus.order_to_factor()
        - flyingcircus.factor_to_order()
    z+Invalid order `{order}` for given prefixes.)rV  r  r"  r   )r  r  reverted_prefixess      ru   order_to_prefixr	  l6  s9    H (X7!! ''KLMMrw   c                     || z  S )a  
    Compute the factor from the order (for a given base).

    Args:
        order (int|float): The order.
        base (int|float): The base to use for the order-to-factor conversion.
            Must be strictly positive.

    Returns:
        factor (int|float): The factor.

    Examples:
        >>> [order_to_factor(i) for i in range(-5, 6)]
        [1e-05, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000, 100000]
        >>> [order_to_factor(float(i)) for i in range(-5, 5)]
        [1e-05, 0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0]

    See Also:
        - flyingcircus.prefix_to_order()
        - flyingcircus.order_to_prefix()
        - flyingcircus.prefix_to_factor()
        - flyingcircus.factor_to_prefix()
        - flyingcircus.factor_to_order()
    r   )r  r_  s     ru   order_to_factorr  6  s    6 5=rw   c                 R    t        t        t        j                  | |                  S )a  
    Compute the order from the factor (for a given base).

    Args:
        factor (int|float): The factor.
        base (int|float): The base to use for the order-to-factor conversion.
            Must be strictly positive.

    Returns:
        order (int|float): The order.

    Examples:
        >>> [factor_to_order(x) for x in (1, 10, 1000, 100000)]
        [0, 1, 3, 5]
        >>> [factor_to_order(x) for x in (1.0, 100.0, 10000.0, 123.45)]
        [0, 2, 4, 2]

    See Also:
        - flyingcircus.prefix_to_order()
        - flyingcircus.order_to_prefix()
        - flyingcircus.prefix_to_factor()
        - flyingcircus.factor_to_prefix()
        - flyingcircus.order_to_factor()
    )rY   rg  rd  r  )r  r_  s     ru   factor_to_orderr  6  s    6 uTXXfd+,--rw   c                 .    t        t        | |      |      S )u  
    Get the order corresponding to a given prefix.

    This works chiefly with SI prefixes.

    Args:
        prefix (str): The prefix to inspect.
        prefixes (Mappable): The conversion table for the prefixes.
            This must have the form: (prefix, order).
            Multiple prefixes can point to the same order.
        base (int|float): The base to use for the factor.

    Returns:
        factor (int|float): The factor associate to the prefix.

    Examples:
        >>> [prefix_to_factor(x) for x in ('', 'k', 'M', 'G', 'T', 'P')]
        [1, 1000, 1000000, 1000000000, 1000000000000, 1000000000000000]
        >>> [prefix_to_factor(x)
        ...     for x in ('', 'm', 'µ', 'n', 'p', 'f', 'a', 'z', 'y')]
        [1, 0.001, 1e-06, 1e-09, 1e-12, 1e-15, 1e-18, 1e-21, 1e-24]
        >>> [prefix_to_factor(x) for x in ('u', 'µ', 'μ')]
        [1e-06, 1e-06, 1e-06]
        >>> all(prefix_to_order(order_to_prefix(i)) == i
        ...     for i in range(-24, 25, SI_ORDER_STEP))
        True

    See Also:
        - flyingcircus.prefix_to_order()
        - flyingcircus.order_to_prefix()
        - flyingcircus.factor_to_prefix()
        - flyingcircus.order_to_factor()
        - flyingcircus.factor_to_order()
    )r  r  )r  r  r_  s      ru   prefix_to_factorr  6  s    L ?68<dCCrw   c                 .    t        t        | |      |      S )aq  
    Get the order corresponding to a given prefix.

    This works chiefly with SI prefixes.

    Args:
        factor (int|float): The factor to inspect.
        prefixes (Mappable): The conversion table for the prefixes.
            This must have the form: (prefix, order). Must be 1-to-1.
        base (int|float): The base to use for the order-to-factor conversion.
            Must be strictly positive.

    Returns:
        prefix (str): The prefix for the corresponding factor.

    Examples:
        >>> factor_to_prefix(1e12)
        'T'
        >>> [factor_to_prefix(10 ** i) for i in range(-3, 4)]
        ['m', 'c', 'd', '', 'da', 'h', 'k']

    See Also:
        - flyingcircus.prefix_to_order()
        - flyingcircus.order_to_prefix()
        - flyingcircus.prefix_to_factor()
        - flyingcircus.order_to_factor()
        - flyingcircus.factor_to_order()
    )r	  r  )r  r  r_  s      ru   factor_to_prefixr   7  s    @ ?648(CCrw   c                 6    |t        | ||      }| ||z  |z  z  S )a  
    Scale a number according to its order of magnitude.

    Args:
        val (Number): The input number.
        base (Number): The base for defining the magnitude order.
        exp (Number): The exp for defining the magnitude order.
        order (int|None): The order of magnitude.
            If None, this is computed from `base` and `exp`.

    Returns:
        result (Number): The scaled value.

    Examples:
        >>> round(scale_to_order(1234.123), 6)
        1.234123
        >>> round(scale_to_order(1234.123, 10, 1, 2), 6)
        12.34123
        >>> round(scale_to_order(1234.123, 10, 3, 2), 6)
        0.001234
        >>> round(scale_to_order(0.001234, 10, 3, -2), 6)
        1234.0

        >>> n = 12345.67890
        >>> all(
        ...     round(scale_to_order(scale_to_order(n, order=i), order=-i), 5)
        ...     == n
        ...     for i in range(10))
        True
    )r  )r<  r_  r  r  s       ru   scale_to_orderr  $7  s,    F }"3c2$#+%'''rw   c                    d}d}|t        j                  t        j                  t        |                   z  }d| z  }| t	        |       z
  |z  }|t        |      z
  |kD  rt        ||z        |cxk  rt        |      k  rfn |S ||k  r]|t	        |      z
  |z  }d||z
  |z
   z  }|dz  }|t        |      z
  |kD  r*t        ||z        |cxk  rt        |      k  r
n |S ||k  r]|S )a  
    Guess the number of decimals in a given float number.

    Args:
        val (float): The input value.
        n_max (int): Maximum number of guessed decimals.
        base (int): The base used for the number representation.
        fp (int): The floating point maximum precision.
            A number with precision is approximated by the underlying platform.
            The default value corresponds to the limit of the IEEE-754 floating
            point arithmetic, i.e. 53 bits of precision: log10(2 ** 53) = 16
            approximately. This value should not be changed unless the
            underlying platform follows a different floating point arithmetic.

    Returns:
        prec (int): the guessed number of decimals.

    Examples:
        >>> guess_decimals(10)
        0
        >>> guess_decimals(1)
        0
        >>> guess_decimals(0.1)
        1
        >>> guess_decimals(0.01)
        2
        >>> guess_decimals(10.01)
        2
        >>> guess_decimals(0.000001)
        6
        >>> guess_decimals(-0.72)
        2
        >>> guess_decimals(0.9567)
        4
        >>> guess_decimals(0.12345678)
        8
        >>> guess_decimals(0.9999999999999)
        13
        >>> guess_decimals(0.1234567890123456)
        16
        >>> guess_decimals(0.9999999999999999)
        16
        >>> guess_decimals(0.1234567890123456, 6)
        6
        >>> guess_decimals(0.54235, 10)
        5
        >>> guess_decimals(0x654321 / 0x10000, 16, 16)
        4
    r9   r   r  r/   )rd  re  r  r!  rY   )r<  n_maxr_  fpr
  precr2  rH   s           ru   guess_decimalsr  M7  s    l FD$))DJJs3x(
))B
)C	s3x4A
Q-#
#a#g,"=s1v"= K	 CG,QZ4b4i&())	 Q-#
#a#g,"=s1v"= K	 CG, Krw   c                 
   t        |       } t        |      }t        | dd      }||z
  dz
  }d}t        | |      } t	        |      |kD  r | d| z  z  } |dz
  }dj                  |      }n|dk  rd}dj                  | ||      } | S )a   
    Format a number with the correct number of significant figures.

    Args:
        val (str|float|int): The numeric value to be correctly formatted.
        num (str|int): The number of significant figures to be displayed.
        keep_zeros (int): The number of zeros to keep after the figures.
            This is useful for preventing the use of the scientific notation.

    Returns:
        val (str): String containing the properly formatted number.

    Examples:
        >>> significant_figures(1.2345, 1)
        '1'
        >>> significant_figures(1.2345, 4)
        '1.234'
        >>> significant_figures(1.234e3, 2)
        '1200'
        >>> significant_figures(-1.234e3, 3)
        '-1230'
        >>> significant_figures(12345678, 4)
        '12350000'
        >>> significant_figures(1234567890, 4)
        '1.235e+9'
        >>> significant_figures(-0.1234, 1)
        '-0.1'
        >>> significant_figures(0.0001, 2)
        '1.0e-4'

    See Also:
        The 'decimal' Python standard module.
    r  r/   r8   ze{:+d}r   z{val:.{prec}f}{ofm})r<  r  ofm)r_   rY   r  rg  r!  r  )r<  r;  
keep_zerosr  r  r  s         ru   significant_figuresr  7  s    J *C
c(CsB*E;?D
C
T
C
4y:BE6N"Qwooe$	  
&
&3Ts
&
CCJrw   c                     t        |       } t        |      }|dk7  rt        |      nd}t        | dd      }t        |dd      }	 t        | ||z
  |z   |      }t        |||      }||fS # t        $ r t        |       }t        |      }Y ||fS w xY w)a  
    Outputs correct value/error pairs formatting.

    Args:
        val (str|float|int): The numeric value to be correctly formatted.
        err (str|float|int): The numeric error to be correctly formatted.
        num (str|int): The precision to be used for the error (usually 1 or 2).
        keep_zeros (int): The number of zeros to keep after the figures.
            This is useful for preventing the use of the scientific notation.

    Returns:
        val_str (str): The string with the correctly formatted numeric value.
        err_str (str): The string with the correctly formatted numeric error.

    Examples:
        >>> format_value_error(1234.5, 6.7)
        ('1234.5', '6.7')
        >>> format_value_error(123.45, 6.7, 1)
        ('123', '7')
        >>> format_value_error(12345.6, 7.89, 2)
        ('12345.6', '7.9')
        >>> format_value_error(12345.6, 78.9, 2)
        ('12346', '79')
        >>> format_value_error(12345.6, 0)
        ('12345.6', '0.0')
        >>> format_value_error(12345.6, 0, 0)
        ('12346', '0')
        >>> format_value_error(12345.6, 67)
        ('12346', '67')
        >>> format_value_error(12345.6, 670)
        ('12350', '670')
        >>> format_value_error(1234567890.0, 123456.0)
        ('1234570000', '120000')
        >>> format_value_error(1234567890.0, 1234567.0)
        ('1.2346e+9', '1.2e+6')
        >>> format_value_error(-0.470, 1.722)
        ('-0.5', '1.7')
        >>> format_value_error(0.0025, 0.0001)
        ('2.50e-3', '1.0e-4')
    r   r/   r  )r_   rY   r  r  r"  ra   )r<  rI  r;  r  	val_order	err_orderr  err_strs           ru   format_value_errorr!  7  s    Z *C
*CQh#c(AC"3A.I"3A.I	 &Y&,j:%j"
 G  c(c(Gs    A( (BBc                     t         )z
    Extract factor and base units from units.

    Supports all base SI units, a number of non-base SI units and some
    non-SI units.

    Args:
        text (str):
        force_si:
        check_si:

    Returns:
        result (tuple):
    )NotImplementedError)r  force_sicheck_sis      ru   parse_units_prefixr&  8  s
    $ rw   c              #   <  K   | j                         } | dv r"d}||k  s|| ||z  }|dz  }||k  r|yy| dv r"d}||k  s|| ||z  }|dz  }||k  r|yy| dv r%d}||k  s|d|z   ||z  }|dz  }||k  r|yyt        t        d            w)a  
    Compute the n-th term of a notable progression.

    Args:
        name (str): The name of the progression.
            Accepted values are:
             - `a`, `arithmetic`: Use arithmetic progression.
             - `g`, `geometric`: Use geometric progression.
             - `h`, `harmonic`: Use harmonic progression.
        start (Number): The initial value.
        step (Number): The step value.
        num (int|None): The number of values to yield.
            If None, yields values indefinitely (use with care!).

    Yields:
        result (Number): The n-th term of the progression.

    Raises:
        ValueError: If `name` is not supported.

    Examples:
        >>> list(i_progression('a', 10, 2, 12))
        [10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32]
        >>> list(i_progression('g', 10, 2, 12))
        [10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240, 20480]
        >>> [round(x, 3) for x in i_progression('h', 1, 1, 10)]
        [1.0, 0.5, 0.333, 0.25, 0.2, 0.167, 0.143, 0.125, 0.111, 0.1]
        >>> list(i_progression('ga', 10, 2, 12))
        Traceback (most recent call last):
            ...
        ValueError: Unknown progression `ga`

    References:
        - https://en.wikipedia.org/wiki/Arithmetic_progression
        - https://en.wikipedia.org/wiki/Geometric_progression
        - https://en.wikipedia.org/wiki/Harmonic_progression_(mathematics)
    r5   
arithmeticr   Nr/   g	geometricr;   harmonicUnknown progression `{name}`r^  r"  r   )r-  r~  r}  r;  rM   s        ru   i_progressionr1  8  s     T ::<D""#gKTMEFA #g 
#	##gKTMEFA #g 
"	"#ge)OTMEFA #g
 <=>>s'   1BB"BB%BBBc                     | j                         } | dv r|||z  z   S | dv r|||z  z  S | dv rd|||z  z   z  S t        t        d            )a  
    Compute the n-th term of a notable progression.

    Args:
        name (str): The name of the progression.
            Accepted values are:
             - `a`, `arithmetic`: Use arithmetic progression.
             - `g`, `geometric`: Use geometric progression.
             - `h`, `harmonic`: Use harmonic progression.
        start (Number): The initial value.
        step (Number): The step value.
        num (int): The ordinal of the progression.
            The initial value is the 0th.

    Returns:
        result (Number): The n-th term of the progression.

    Raises:
        ValueError: If `name` is not supported.

    Examples:
        >>> progression('a', 0, 6, 7)
        42
        >>> progression('g', 1, 2, 10)
        1024
        >>> progression('h', 1, 1, 99)
        0.01
        >>> progression('ga', 10, 2, 12)
        Traceback (most recent call last):
            ...
        ValueError: Unknown progression `ga`

    References:
        - https://en.wikipedia.org/wiki/Arithmetic_progression
        - https://en.wikipedia.org/wiki/Geometric_progression
        - https://en.wikipedia.org/wiki/Harmonic_progression_(mathematics)
    r(  r*  r-  r/   r/  r0  r-  r~  r}  r;  s       ru   progressionr4  `8  sn    T ::<D""tcz!!	#	#ts{""	"	"ED3J&''<=>>rw   c                    | j                         } | dv r|||z  ||dz
  z  dz  |z  z   S |S | dv r5||dk(  r||z  S |d||z  z
  z  d|z
  z  S t        |      dk  r|d|z
  z  S |S |t        t        | |||            S t	        | ||d       |S )aM  
    Compute the sum of the first n terms of a notable progression.

    The sum of the elements of a progression is also called a series.

    Args:
        name (str): The name of the progression.
            Accepted values are:
             - `a`, `arithmetic`: Use arithmetic progression.
             - `g`, `geometric`: Use geometric progression.
             - `h`, `harmonic`: Use harmonic progression.
        start (Number): The initial value.
        step (Number): The step value.
        num (int|None): The number of values to sum.
            If None, sums to infinity.

    Returns:
        result (Number|None): The sum value.
            If the series diverges, returns None.

    Examples:
        >>> sum_progression('a', 10, 2, 12)
        252
        >>> sum_progression('g', 10, 2, 12)
        40950.0
        >>> round(sum_progression('h', 1, 1, 10), 3)
        2.929

        >>> print(sum_progression('a', 10, 2, None))
        None
        >>> print(sum_progression('g', 10, 0.5, None))
        20.0
        >>> print(sum_progression('h', 10, 0.5, None))
        None

        >>> sum_progression('ga', 10, 2, 12)
        Traceback (most recent call last):
            ...
        ValueError: Unknown progression `ga`
        >>> print(sum_progression('ga', 10, 2, None))
        Traceback (most recent call last):
            ...
        ValueError: Unknown progression `ga`

        >>> a, d, n = 10, 2, 20
        >>> all(
        ...     sum(i_progression(s, a, d, n)) == sum_progression(s, a, d, n)
        ...     for s in ('a', 'g', 'h'))
        True

    References:
        - https://en.wikipedia.org/wiki/Series_(mathematics)
        - https://en.wikipedia.org/wiki/Arithmetic_progression
        - https://en.wikipedia.org/wiki/Geometric_progression
        - https://en.wikipedia.org/wiki/Harmonic_progression_(mathematics)
    r(  r/   r9   r*  )r^  r!  rV  r1  r4  r3  s       ru   sum_progressionr6  8  s    r ::<D""?;#q/Q"6$!>>>J	#	#?qys{"DCK0AH==Y]AH%%J?}T5$<==eT1-Jrw   c           	         dt        d | D               z  d}t        t        |             }|d   t        fd|D              rkk  r*dj	                  t        | d   |      t        |             }|S dj	                  t        | d   |      t        | d   z   |      t        |            }|S t        t        |             }|d   t        fd	|D              rkg g g g f\  }}}}d}	d
}
d}d}d}|dk\  rd
|
z  z  }|j                  |       |j                  t        j                  | d         t        j                  |      z         |j                  t        j                  | d   |z        t        j                  |      z         |j                  |
       t        ||	   ||	   ||	   ||	   fD cg c]  }|rt        |      nd c}      }||k  r|}|	}|
d
z  }
|	d
z  }	|dk\  rt        t        ||||            |   \  }}}}
dj	                  t        ||      t        ||      t        ||      t        |
|            }|S c c}w )aH
  
    Guess a compact expression for a numerical sequence.

    Args:
        seq (Sequence[Number]): The input items.
        rounding (int|None): The maximum number of decimals to show.

    Returns:
        result (str): The compact expression.
            Supported numerical sequences are:
             - constant sequences: '[val] * len(items)'
             - linear sequences: 'range(start, stop, step)'
               Note that both float and complex number will be detected
               (contrarily to Python's `range()`).
             - geometric sequences: 'base ** range(start, stop, step)'

    Examples:
        >>> items = [1.0] * 10
        >>> print(items)
        [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
        >>> print(guess_numerical_sequence(items))
        [1.0] * 10

        >>> items = list(range(10))
        >>> print(items)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> print(guess_numerical_sequence(items))
        range(0, 10, 1)

        >>> items = list(range(5, 25, 2))
        >>> print(items)
        [5, 7, 9, 11, 13, 15, 17, 19, 21, 23]
        >>> print(guess_numerical_sequence(items))
        range(5, 25, 2)

        >>> items = [x / 1000 for x in range(5, 25, 2)]
        >>> print(items)
        [0.005, 0.007, 0.009, 0.011, 0.013, 0.015, 0.017, 0.019, 0.021, 0.023]
        >>> print(guess_numerical_sequence(items))
        range(0.005, 0.025, 0.002)

        >>> items = [2 ** x for x in range(5, 25, 2)]
        >>> print(items)
        [32, 128, 512, 2048, 8192, 32768, 131072, 524288, 2097152, 8388608]
        >>> print(guess_numerical_sequence(items))
        2.0 ** range(5.0, 24.0, 2)

        >>> items = [10 ** x for x in range(8)]
        >>> print(items)
        [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]
        >>> print(guess_numerical_sequence(items))
        10.0 ** range(0.0, 8.0, 1)

        >>> items = [3.5 ** x for x in range(5, 13, 2)]
        >>> print(items)
        [525.21875, 6433.9296875, 78815.638671875, 965491.5737304688]
        >>> print(guess_numerical_sequence(items))
        3.5 ** range(5.0, 12.0, 2)

        >>> items = [4.1 ** x for x in range(4, 11, 3)]
        >>> print(items)
        [282.5760999999999, 19475.42738809999, 1342265.9310152389]
        >>> print(guess_numerical_sequence(items))
        4.1 ** range(4.0, 11.0, 3)

        >>> items = [4.1 ** (x / 10) for x in range(4, 11, 3)]
        >>> # 4.1 ** range(0.4, 1.1, 0.3)
        >>> print(items)
        [1.7583832685816119, 2.6850272626520213, 4.1]
        >>> print(guess_numerical_sequence(items))
        1.527 ** range(1.333, 4.333, 1)
    r  c              3   8   K   | ]  }|st        |        y wr   )r  )r   r   s     ru   r   z+guess_numerical_sequence.<locals>.<genexpr>39  s     BdT^D)Bs   Nr   c              3   .   K   | ]  }|z
  k    y wr   r   r   rH   r_  r2  s     ru   r   z+guess_numerical_sequence.<locals>.<genexpr>79  s     
)a1t8c>
)r   z	[{}] * {}zrange({}, {}, {})r<   c              3   .   K   | ]  }|z
  k    y wr   r   r:  s     ru   r   z+guess_numerical_sequence.<locals>.<genexpr>C9  s     ,!q4x#~,r   r/   r9   @   g       @z{} ** range({}, {}, {}))r  r   r  r  r  rg  r   r  r   rd  log2rV  r  r  r  )r   r?  r   diffsdivsr[  firstslastsr#  rM   r}  new_basemin_sum_decimalsmin_irH   sum_decimals
first_itemrA  r_  r2  s                     @@ru   guess_numerical_sequencerG  8  sz   V BcBBB
BCF$s)E8D

)5
))#: ''c!fh(?SJFL MG )//c!fh's2w~x)HdH%'FF M? SXAw,t,,*,b"b.'E65%ADH%Ec/AH-X&diiA/$))H2EEFIIc"g01DIIh4GGIT""$QxE!HeAhGI +,^A&2 I J  "22'3$E	Q c/" Su56u= 2Hj)T.55h)5X+Fi*E$,ACF MIs   =Ic                     t        |       |kD  rZ|t        |      nd}|"	 ||z
  | ||z
  dd   j                  |      z
  }nt        ||z
  d      }|| |||z    |d| z   S | |||z    S | S # t        $ r d}Y 'w xY w)ah  
    Cut and append an ellipsis sequence at the end if length is excessive.

    This is especially useful for strings.

    Args:
        seq (Sequence): The input sequence.
        length (int): The maximum allowed length
        ending (Sequence|None): The ending to append.
            If None, it is ignored.
        sep (Any|None): The separator to use.
            If None, it is ignored, otherwise cuts only at the separator.
        start (int): The start index for the output.

    Returns:
        result (Sequence): The sequence with the elision.

    Examples:
        >>> elide('Flying Circus FlyingCircus', 24)
        'Flying Circus FlyingCi..'
        >>> elide('Flying Circus FlyingCircus', 24, '...')
        'Flying Circus FlyingC...'
        >>> elide('Flying Circus FlyingCircus', 24, '...', ' ')
        'Flying Circus...'
        >>> elide('Flying Circus FlyingCircus', 24, None)
        'Flying Circus FlyingCirc'
        >>> elide('Flying Circus FlyingCircus', 24, '..', None, 1)
        'lying Circus FlyingCir..'
        >>> elide('Flying Circus FlyingCircus', 1)
        '.'
        >>> elide('Flying Circus FlyingCircus', 2)
        '..'
        >>> elide('Flying Circus FlyingCircus', 3)
        'F..'
        >>> elide('Flying Circus FlyingCircus', 5, '..', ' ')
        '..'
        >>> elide('a bc def ghij klmno pqrstu vwxyzab', 16, '..', ' ')
        'a bc def ghij..'
        >>> elide('a bc def ghij klmno pqrstu vwxyzab', 5, '..', ' ')
        'a..'
        >>> elide('a bc def ghij klmno pqrstu vwxyzab', 6, '..', ' ')
        'a bc..'

        >>> elide(list(range(100)), 16, [-1])
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1]
        >>> elide(list(range(100)), 16, [-1, -1])
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -1, -1]
        >>> elide([x % 3 for x in range(100)], 16, [-1, -1], 0)
        [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, -1, -1]
        >>> elide(list(range(100)), 16, None)
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
        >>> elide(list(range(100)), 16, [-1, -1], None, 1)
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, -1]
    Nr   r<   )r   rc  r"  r   )r   rC  endingr  r~  
len_endingr%  s          ru   eliderK  d9  s    x 3x&$*$6S[A
?+fz15256<<SAB
 +Q/EuUU]+fWfo==uUU]++
  s    A+ +A98A9c                    t        | ||      rt        | ||      } | j                  |      }i }	|D ]  }
|
j                  |      }t        |      dk(  r|d   d}}n`t        |      dk(  r|\  }}|j	                  |      }n;t        |      dkD  r)|d   |dd }}|D cg c]  }|j	                  |       }}ndx}}|j	                  |      }||rt        |      }||	|<    |	S c c}w )a[  
    Convert a string to a dictionary.

    Escaping and quotes are not supported.
    Dictionary name is always a string.

    Args:
        in_str (str): The input string.
        entry_sep (str): The entry separator.
        key_val_sep (str): The key-value separator.
        pre_delim (str): initial decorator (to be removed before parsing).
        post_delim (str): final decorator (to be removed before parsing).
        strip_key_str (str): Chars to be stripped from both ends of the key.
            If None, whitespaces are stripped. Empty string for no stripping.
        strip_val_str (str): Chars to be stripped from both ends of the value.
            If None, whitespaces are stripped. Empty string for no stripping.
        convert (bool): Enable automatic conversion of string to numeric.

    Returns:
        out_dict (dict): The output dictionary generated from the string.

    Examples:
        >>> d = str2dict('{a=10,b=20,c=test}')
        >>> for k in sorted(d.keys()): print(k, ':', d[k])  # display dict
        a : 10
        b : 20
        c : test

    See Also:
        dict2str
    r/   r   Nr9   )r  r  rZ  r   r5  r  )in_str	entry_sepkey_val_sepr  r  strip_key_strstrip_val_strconvertentriesout_dictentrykey_valr  r<  tmp_vals                  ru   str2dictrX  9  s   P J/VY
;ll9%GH  ++k*w<1qz4C\QHC))M*C\Aqz712;C?BCG7==/CCCC#ii&?"3'HSM) * O Ds   C$c                    t        | j                         |      }g }	|D ]R  }
|
j                  |      }
t        | |
         j                  |      }|	j	                  |j                  |
|g             T ||j                  |	      z   |z   }|S )a  
    Convert a dictionary to a string.

    Args:
        in_dict (dict): The input dictionary.
        entry_sep (str): The entry separator.
        key_val_sep (str): The key-value separator.
        pre_delim (str): initial decorator (to be appended to the output).
        post_delim (str): final decorator (to be appended to the output).
        strip_key_str (str): Chars to be stripped from both ends of the key.
            If None, whitespaces are stripped. Empty string for no stripping.
        strip_val_str (str): Chars to be stripped from both ends of the value.
            If None, whitespaces are stripped. Empty string for no stripping.
        sorting (callable): Sorting function passed to 'sorted' via `key` arg.
            Used for sorting the dictionary keys.

    Returns:
        out_str (str): The output string generated from the dictionary.

    Examples:
        >>> dict2str({'a': 10, 'b': 20, 'c': 'test'})
        '{a=10,b=20,c=test}'

    See Also:
        str2dict
    r/  )rl  r  r5  ra   r   r   )in_dictrN  rO  r  r  rP  rQ  r  r  out_listr  r<  out_strs                ru   dict2strr]  9  s    F ',,.g.DH 6ii&'#,%%m4((#s456 )..22Z?GNrw   c                    |st        |      nd}|rt        |      nd}|| v rd|| v r`|r)| j                  |      |z   }| j                  |      |z   }n.| j                  |      |z   }| |d j                  |      |z   |z   }| || } | S d} | S )a  
    Isolate the string contained between two tokens

    Args:
        text (str): String to parse
        begin_str (str): Token at the beginning
        end_str (str): Token at the ending
        incl_begin (bool): Include 'begin_string' in the result
        incl_end (bool): Include 'end_str' in the result.
        greedy (bool): Output the largest possible string.

    Returns:
        text (str): The string contained between the specified tokens (if any)

    Examples:
        >>> string_between('roses are red violets are blue', 'ses', 'lets')
        ' are red vio'
        >>> string_between('roses are red, or not?', 'a', 'd')
        're re'
        >>> string_between('roses are red, or not?', ' ', ' ')
        'are red, or'
        >>> string_between('roses are red, or not?', ' ', ' ', greedy=False)
        'are'
        >>> string_between('roses are red, or not?', 'r', 'r')
        'oses are red, o'
        >>> string_between('roses are red, or not?', 'r', 'r', greedy=False)
        'oses a'
        >>> string_between('roses are red, or not?', 'r', 's', True, False)
        'rose'
        >>> string_between('roses are red violets are blue', 'x', 'y')
        ''
    r   Nr8   )r   findrfind)r  	begin_strend_str
incl_beginincl_endr  r  rF  s           ru   string_betweenre  &:  s    N (2YqJ's7|QHDW_IIi(:5E**W%0CIIi(:5Euv,##G,x7%?CE# K Krw   c                 P    t        j                  d      }|j                  d|       S )aS  
    Remove ANSI escape sequences from text.

    Args:
        text (str): The input text.

    Returns:
        result (str): The output text.

    Examples:
        >>> s = '[0;35mfoo[0m [0;36mbar[0m'
        >>> print(repr(remove_ansi_escapes(s)))
        'foo bar'
        >>> remove_ansi_escapes(s) == 'foo bar'
        True
    z(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]r8   )r  r  r   )r  ansi_escapes     ru   remove_ansi_escapesrh  ]:  s$    " **>?K??2t$$rw   c                    t        | t              r| g} t        |t              r|g}|s-|D ](  }|st        j                  j	                  |      r&d} n |r|r~|D ]y  }t        j                  j                  |      }t        j                  j                  |      rBt        dj                  |      |t        d          t        j                  |       { |s| r| D ],  }t        j                  j	                  |      r#t        d       t        j                  | |      D ]H  \  }}t        j                  j                  |      t        j                  j                  |      kD  sFd} n n|rt        d      |rHt        dj                  |      |t        d          t        dj                  |       |t        d          |S t        d	j                  |      |t        d          t        dj                  |       |t        d          |S )
a  
    Check if input files are newer than output files, to force calculation.

    Args:
        in_filepaths (str|Iterable[str]|None): Input filepaths for computation.
        out_filepaths (str|Iterable[str]): Output filepaths for computation.
        force (bool): Force computation to be re-done.
        verbose (int): Set level of verbosity.
        makedirs (bool): Create output dirpaths if not existing.
        no_empty_input (bool): Check if the input filepath list is empty.

    Returns:
        force (bool): True if the computation is to be re-done.

    Raises:
        IndexError: If the input filepath list is empty.
            Only if `no_empty_input` is True.
        IOError: If any of the input files do not exist.
    Tz	mkdir: {}r>  zInput file does not exists.zInput file list is empty.zCalc: {}higherzFrom: {}zSkip: {})r  ra   rp   rq   r  dirnamer  r   r  r	   r  r  r   r  getmtime)	in_filepathsout_filepathsr  rX  r  no_empty_inputout_filepathout_dirpathin_filepaths	            ru   
check_redors  s:  s   6 ,$$~-%& ) 	LBGGNN<$@	 ) 	)L''//,7K77==-K&&{3Xi02K(	) + Aww~~k2!"?@@A
 %%lMB)\77##K02773C3C$4& & E 566Jm,gx7IJJl+Why6IJ L 	Jm,gx7IJJl+Why6IJLrw   c                     t        |       }t        |       |rt        d t        | |      D              }|S t        d t        | |      D              }|S )a  
    Check if items are increasing.

    Args:
        items (Iterable): The items to check.
        strict (bool): Check for strict monotonicity.
            If True, consecutive items cannot be equal.
            Otherwise they can be also equal.

    Returns:
        result (bool): True if items are increasing, False otherwise.

    Examples:
        >>> is_increasing([-20, -2, 1, 3, 5, 7, 8, 9])
        True
        >>> is_increasing([1, 3, 5, 7, 8, 9, 9, 10, 400])
        False
        >>> is_increasing([1, 3, 5, 7, 8, 9, 9, 10, 400], False)
        True
        >>> is_increasing([-20, -2, 1, 3, 5, 7, 8, 9])
        True
        >>> is_increasing([-2, -2, 1, 30, 5, 7, 8, 9])
        False
    c              3   ,   K   | ]  \  }}||k    y wr   r   r  s      ru   r   z is_increasing.<locals>.<genexpr>:       :tq!QU:r  c              3   ,   K   | ]  \  }}||k    y wr   r   r  s      ru   r   z is_increasing.<locals>.<genexpr>:       ;1Q!V;r  r   r   r  r  r   strictothersr   s       ru   is_increasingr}  :  S    6 %[FL:s5&'9:: M ;E6(:;;Mrw   c                     t        |       }t        |       |rt        d t        | |      D              }|S t        d t        | |      D              }|S )a  
    Check if items are decreasing.

    Args:
        items (Iterable): The items to check.
        strict (bool): Check for strict monotonicity.
            If True, consecutive items cannot be equal.
            Otherwise they can be also equal.

    Returns:
        result (bool): True if items are decreasing, False otherwise.

    Examples:
        >>> is_decreasing([312, 54, 53, 7, 3, -5, -100])
        True
        >>> is_decreasing([312, 53, 53, 7, 3, -5, -100])
        False
        >>> is_decreasing([312, 53, 53, 7, 3, -5, -100], False)
        True
        >>> is_decreasing([312, 54, 53, 7, 3, -5, -100])
        True
        >>> is_decreasing([312, 5, 53, 7, 3, -5, -100])
        False
    c              3   ,   K   | ]  \  }}||kD    y wr   r   r  s      ru   r   z is_decreasing.<locals>.<genexpr>;  rv  r  c              3   ,   K   | ]  \  }}||k\    y wr   r   r  s      ru   r   z is_decreasing.<locals>.<genexpr>;  rx  r  ry  rz  s       ru   is_decreasingr  :  r~  rw   c                 x    t        |       }	 t        |      dk\  }|D ]  }||dk\  k7  s y y# t        $ r Y yw xY w)as  
    Determine if all items in an Iterable have the same sign.

    Args:
        items (Iterable): The items to check.
            The comparison operators '>=' and '<' with `0` must be defined
            for all items.

    Returns:
        same_sign (bool): The result of the comparison.
            True if the items are all positive or all negative.
            False otherwise, i.e. they have mixed signs.

    Examples:
        >>> is_same_sign((0, 1, 2 ,4))
        True
        >>> is_same_sign((-1, -2 , -4))
        True
        >>> is_same_sign((-1, 1))
        False
        >>> is_same_sign([])
        True
    r   TFr@  r  s       ru   is_same_signr  	;  sZ    4 eJZ A%
  TQY   s   - 	99c                 r    t        | t              xr& t        j                  d| j	                               duS )a  
    Determine if the input string contains a percent value.

    Args:
        text (str): The input string.

    Returns:
        result (bool): The result of the check.
            True if the input contains a valid percentage.
            False otherwise.

    Examples:
        >>> print(is_percent('100%'))
        True
        >>> print(is_percent('0.5%'))
        True
        >>> print(is_percent('421.43%'))
        True
        >>> print(is_percent('-433%'))
        True
        >>> print(is_percent('421.%'))
        True
        >>> print(is_percent('.5%'))
        True
        >>> print(is_percent('1.e2%'))
        True
        >>> print(is_percent('421.43'))
        False
        >>> print(is_percent('ciao421.43%'))
        False
    $[+-]?\d*(?:\.\d*)?(?:[eE][+-]?\d+)?%N)r  ra   r  r  r5  )r  s    ru   
is_percentr  1;  s6    B 
D#	 
XX=tzz|Lrw   c                     t        j                  d| j                               }|rt        |j                  dd       dz  S y)a  
    Convert the input string to a float value as percentage.

    Args:
        text (str): The input string.

    Returns:
        result (float|None): The percent value.
            If the input is invalid, returns None.

    Examples:
        >>> print(to_percent('100%'))
        1.0
        >>> print(to_percent('0.5%'))
        0.005
        >>> print(to_percent('421.43%'))
        4.2143
        >>> print(to_percent('-433%'))
        -4.33
        >>> print(to_percent('421.%'))
        4.21
        >>> print(to_percent('.1%'))
        0.001
        >>> print(to_percent('421.43'))
        None
        >>> print(to_percent('ciao421.43%'))
        None
    r  Nr<   r,  )r  r  r5  r_   string)r  r  s     ru   
to_percentr  X;  s<    : HH<djjlKEU\\#2&'#--rw   c                 N    t        | t              st         || |z              S | S )a  
    Scale a float value by the specified size.

    Args:
        val (int|float): The value to scale.
            If int, the number is left untouched.
        scale (int|float): The scale size.
        rounding (callable): Rounding function.
            Sensible choices are: `math.floor()`, `math.ceil()`, `round()`.

    Returns:
        result (Any): The scaled value.

    Examples:
        >>> scale_to_int(0.1, 10)
        1
        >>> scale_to_int(0.1, 10.0)
        1
        >>> scale_to_int(1.0, 10.0)
        10
        >>> scale_to_int(0.5, 11.0)
        6
        >>> scale_to_int(0.5, 11.0, math.floor)
        5
        >>> scale_to_int(1, 10.0)
        1
        >>> scale_to_int(1, 10)
        1
    )r  rY   )r<  scaler?  s      ru   scale_to_intr  };  s'    B .8S-A3xe$%JsJrw   c                     t        fd|D              }t        | |      } t        |      r |      t        fd| D              }|S t        d t        |       D              }|S )a
  
    Ensure values scaling of multiple values.

    Args:
        vals (int|float|Sequence[int|float|Sequence]): The input value(s)
            If Sequence, a value for each scale must be specified.
            If not Sequence, all pairs will have the same value.
            If any value is int, it is not scaled further.
            If any value is float, it is scaled to the corresponding scale,
            if `combine` is None, otherwise it is scaled to a combined scale
            according to the result of `combine(scales)`.
        scales (Sequence[int]): The scale sizes for the pairs.
        shape (Sequence[int|None]): The shape of the output.
            It must be a 2-tuple of int or None.
            None entries are replaced by `len(scales)`.
        combine (callable|None): The function for combining pad width scales.
            Must accept: combine(Sequence[int]): int|float
            This is used to compute a reference scaling value for the
            float to int conversion, using `combine(scales)`.
            For the int values of `width`, this parameter has no effect.
            If None, uses the corresponding scale from the scales.

    Returns:
        vals (int|tuple[tuple[int]]): The scaled value(s).

    See Also:
        - flyingcircus.stretch()
        - flyingcircus.scale_to_int()

    Examples:
        >>> scales = (10, 20, 30)

        >>> multi_scale_to_int(0.1, scales)
        ((1, 1), (2, 2), (3, 3))
        >>> multi_scale_to_int(0.1, scales, combine=max)
        ((3, 3), (3, 3), (3, 3))
        >>> multi_scale_to_int(2, scales)
        ((2, 2), (2, 2), (2, 2))
        >>> multi_scale_to_int((1, 1, 2), scales)
        ((1, 1), (1, 1), (2, 2))
        >>> multi_scale_to_int((0.1, 1, 2), scales)
        ((1, 1), (1, 1), (2, 2))
        >>> multi_scale_to_int((0.1, 1, 2), scales, combine=max)
        ((3, 3), (1, 1), (2, 2))
        >>> multi_scale_to_int(((0.1, 0.5),), scales)
        ((1, 5), (2, 10), (3, 15))
        >>> multi_scale_to_int(((2, 3),), scales)
        ((2, 3), (2, 3), (2, 3))
        >>> multi_scale_to_int(((2, 3), (1, 2)), scales)
        Traceback (most recent call last):
            ...
        ValueError: Cannot stretch `((2, 3), (1, 2))` to `(3, 2)`.
        >>> multi_scale_to_int(((0.1, 0.2),), scales, combine=min)
        ((1, 2), (1, 2), (1, 2))
        >>> multi_scale_to_int(((0.1, 0.2),), scales, combine=max)
        ((3, 6), (3, 6), (3, 6))
        >>> multi_scale_to_int(((1, 2), (3, 4)), (2, 3))
        ((1, 2), (3, 4))
        >>> multi_scale_to_int(((1, 2), 3), (2, 3))
        ((1, 2), (3, 3))
        >>> multi_scale_to_int(((1, 2), 0.7), (2, 3))
        ((1, 2), (2, 2))
        >>> multi_scale_to_int(((1, 1),), (3,), (None, 2))
        ((1, 1),)
    c              3   <   K   | ]  }|r|n
t                y wr   r   )r   rH   scaless     ru   r   z%multi_scale_to_int.<locals>.<genexpr>;  s     9aq!c&k)9s   c              3   F   K   | ]  }t        fd |D                yw)c              3   6   K   | ]  }t        |        y wr   r  )r   rH   combineds     ru   r   z/multi_scale_to_int.<locals>.<genexpr>.<genexpr>;  s     9,q(+9r  Nr  )r   r<  r  s     ru   r   z%multi_scale_to_int.<locals>.<genexpr>;  s$       9S99r  c              3   L   K   | ]  \  }t        fd |D                yw)c              3   6   K   | ]  }t        |        y wr   r  )r   rH   r  s     ru   r   z/multi_scale_to_int.<locals>.<genexpr>.<genexpr>;  s     6Q,q%(6r  Nr  )r   r<  r  s     @ru   r   z%multi_scale_to_int.<locals>.<genexpr>;  s(      1U 6#661s   !$)r   r  r   r  )valsr  r  r  r   r  s    `   @ru   multi_scale_to_intr  ;  sx    L 9599E4D6?   M  1!$/1 1 Mrw   c           
      ,   t        | d   dt              }t        | d   dt              }t        | d   dt              }t        | d   dt              }t        t        | d   dt        |      t        | d   dt        |      t        d      \  }	}
t	        |t
        d         }t        t        t        | d   dt        |                  }t        t        t        | d   dt        |                  }t	        |t
        d         }t	        |t
        d         }|r| d   j                         D cg c]   \  }}t        |d	z   t        |      z   |      " }}}| d
   D cg c]  }t        t        |      |dz         }}ddj                  ||z         z   dz   }t        |      |dz  kD  rddj                  ||z         z   dz   }nd}| d   }t        d      }t        d      }t        d      }t        d      }t        d      }t        d      }t        d      }t        d      }t        d      }t        d      } |rt        |      }!nddj                  |||| g      z   }!|dkD  r7d j                  |!j                         D "cg c]  }"t        |"|       c}"      }!|sj|rh|!d!t        | d"         z   d#z   d$z   t        | d%         z   d&z   d'z   t        | d(         z   d#z   d)z   t        | d*         z   d&z   d+z   t        | d,         z   z  }!|!S c c}}w c c}w c c}"w )-a#  
    Format summary result from `flyingcircus.time_profile()`.

    Args:
        summary (dict): The input summary.
        template (str): The template to use
        kws_limit (int): The maximum number of chars to use for input preview.
            This is only effective if `more == True`.
        line_limit (int): Limit for single line.
        more (bool): Display more information.

    Returns:
        result (str): The formatted summary.

    See Also:
        - flyingcircus.time_profile()
        - flyingcircus.multi_benchmark()
    r<  r  rI  r;  rO  rC  rB   r   r  r   r9   (, )z(
	z,
	z(..)r   z{name}{args}u   ({val} ± {err}) {t_units}szt = {time_s}ztime = {time_s}z{loop_num}{l_units}zl = {loop_s}zloops = {loop_s}z{batch_num}{b_units}zb = {batch_s}zbatch = {batch_s}: z; r   r
  z
  mean_time = r  z;  zstdev_time = r  z;
  zmin_time = r  zmax_time = r   zminmax_range = minmax_range)r  SI_ORDER_STEPr!  r  r	  	SI_PREFIXrY   rg  r   rK  ra   r   r   r   
splitlines)#summarytemplate	kws_limit
line_limitmorer  r  
loop_orderbatch_orderr<  rI  t_unitsloop_num	batch_numl_unitsb_unitsr'   r  kws_listr   	args_listr   r-  name_stime_stime_sstime_lsloop_sloop_ssloop_lsbatch_sbatch_ssbatch_lsr   r  s#                                      ru   _format_summaryr  ;  sd   0 #75>2}EI"75>2}EI#GENBFJ$WW%5r=IK!wu~r=)Dwu~r=)DqHC i:)>?G5wu~r=*EG HHEww']KHJ KIj)J*?@Gk9Z+@AG  ,,.01 !c'CF"I.0 0 9@I14E#c(J!O,I	 ITYYy8344s:t9zQ&',,y8/C"DDsJD;D.!F/0F>"G$%G'(F>"G%&G)*GO$H'(Hh		67GX"FGGA~171B1B1DEU4$EG#c'&/&::UB #GG$4 568?@!'%.1249:  "'%.12 5<<  	  #&gn&=">	?	? MG0I0 Fs   +%LL>Lc                 |    d}t        |      D ]+  } |        } |        }t        ||z
        }t        |||      }- |S )a  
    Estimate the error associated to a specific timer.

    Args:
        timer (callable):
        num (int): The number of repetitions.

    Returns:
        result (float): The estimated timer error.

    Examples:
        >>> timers = time.perf_counter, time.process_time, time.time
        >>> timer_errors = [estimate_timer_error(timer) for timer in timers]
        >>> print([math.log10(t) < 0 for t in timer_errors])
        [True, True, True]
    r  )r{  r!  rV  )timerr;  r   rM   
begin_timeend_time
time_deltas          ru   estimate_timer_errorr  F<  sN    & F3Z 2W
7J./
:vq1	2
 Mrw   g      0@r  i   rS  z/: {name_s};  {time_ss};  {loop_ss};  {batch_ss}c                 z    	
 t        j                          	
fd       }|S )u  
    Estimate the execution time of a function using multiple repetitions.

    The function is repeated in batches.
    Each batch repeats the function to ensure a reliable time measurement.
    The run time is computed as the the batch time divided by the batch size.

    This is similar to the functionality provided by the `timeit` module,
    but also works as a decorator.

    Note that the default values are choosen under the assumption that the
    timer error is much larger than the execution time of the function.

    Args:
        func (callable): The input function.
        timeout (int|float): Maximum time for testing in s.
            There will be at least `min_iter` iterations.
        batch_timeout (int|float): Maximum time for each batch in s.
            If quick is False, there will be at least `min_batch` repetitions.
        max_iter (int): Maximum number of iterations.
            Must be at least 2.
        min_iter (int): Minimum number of iterations.
            If the min number of iterations requires longer than `max_time`,
            the `max_time` limit is ignored until at least `min_iter`
            iterations are performed.
        max_batch (int): Maximum size of the batch.
            Must be at least 1.
        min_batch (int): Minimum size of the batch.
            Must be at least 1.
        val_func (callable|None): Compute timing value from batch times.
            If callable, must have the signature:
            func(Sequence[int|float]): int|float
            If None, uses the mean of the runtimes.
        err_func (callable|None): Compute timing error from batch times.
            If callable, must have the signature:
            func(Sequence[int|float]): int|float
            If None, uses the standard deviation of the mean of the runtimes.
        timer (callable): The function used to measure the timings.
        use_gc (bool): Use the garbage collection during the timing.
        quick (bool): Force exiting the repetition loops.
            If this is True, the `max_time` is forced within the execution
            time of a single instance.
            The minimum number of iterations is always performed.
        text (str): Text to use for printing output.
        fmtt (str|bool|None): Format of the printed output.
            This is passed to `flyingcircus.msg()`.
        verbose (int): Set level of verbosity.

    Returns:
        decorator_profile_time (callable): The decorator.

    Examples:
        >>> @time_profile(timeout=0.1, fmtt=False)
        ... def my_func(a, b):
        ...     return [0 for _ in range(a) for _ in range(b)]
        >>> x, summary = my_func(100, 100)  # doctest:+ELLIPSIS
        : my_func(..);  t = (... ± ...) ...s;  l = ...;  b = ...
        >>> x, summary = my_func(1000, 1000)  # doctest:+ELLIPSIS
        : my_func(..);  t = (... ± ...) ...s;  l = ...;  b = ...

        >>> def my_func(a, b):
        ...     return [0 for _ in range(a) for _ in range(b)]
        >>> my_func = time_profile(timeout=0.1)(my_func)
        >>> x, summary = my_func(100, 100)  # doctest:+ELLIPSIS
        : my_func(..);  t = (... ± ...) ...s;  l = ...;  b = ...
        >>> x, summary = my_func(1000, 1000)  # doctest:+ELLIPSIS
        : my_func(..);  t = (... ± ...) ...s;  l = ...;  b = ...

        >>> print(list(summary.keys()))
        ['result', 'func_name', 'args', 'kws', 'num', 'val', 'err', 'mean', 'sosd', 'var', 'stdev', 'min', 'max', 'median', 'medoid', 'batch']
 
    See Also:
        - flyingcircus.multi_benchmark()
        - flyingcircus._format_summary()
    c                     t        j                         }+rt        j                         nt        j                          dx}x}x}} *       }d}d }	t	        ,      xs t	               }
t        *      }rn|#z  }|}|
rg }ng }d})}d}t        $      D ]  }d}d} *       }t        #      D ]8  } "| i |}	 *       }||z
  }||z
  }|)kD  r	|&k\  s'r n||kD  s0|%k\  s's8 n t        |dz   ||      }||z
  |dz   z  }|
rj                  |       t        ||||      \  }}}|
st        |      \  }}}||k  r|}||kD  r|}|)kD  s|&k\  s n t        |      t        ||      }}|
rt              }t        |      }|}||dz  z  }|
r&t	        ,      r ,      }t	               r        }t        di d|	d"j                   d| d|d	|dz   d
|d|d|d|d|d|d|d|dddt#        |      }|rt        j                         nt        j                          (rt%        t'        |(      -t(        !       |	|fS )Nr  r   r/   r  r   r   r   r   r;  r<  rI  r  r  r  r  r  r   r#  r<  rO  r   )gc	isenabledenabledisabler   r  r{  rV  r   r\  re  r  r  r#  r<  r  r   rY   r   r  r
   ).r   r   gc_was_enabled	mean_time	sosd_timemin_timemax_time	init_time
total_timer   collect_runtimes	timer_err	b_timeout	batch_err	run_timessorted_run_times
mean_batchrM   r  
batch_timer  r  run_timer  medoid_timemedian_timevar_time
stdev_timeval_timeerr_timer  batch_timeouterr_funcrE  r   	max_batchmax_iter	min_batchmin_iterquickr  rU  r  use_gcval_funcrX  s.                                  ru   r   ztime_profile.<locals>.wrapper<  s    		2::<699	9I98G	
#H-C(1C(/	%2M	I8M		I!
x 	AAJJ9% u-- 7%	1
%
2
'Q(]e	)qI~ #1q5*a8J"Y.1q59H  *&8)Y'3#Iy!#.D./0+[!("#("#G#X7	8  (	15z)Q7O* +K +KS(!#I.!#I. ##%)]]#9>#DH#A###)1# # "+# 	# !+	# 19	# ?G	#
 #
 (3# j/# &		2::<.TJwrw   r   )r   rU  r  r  r  r  r  r  r  r  r  r  r  rE  rX  r   s   ``````````````` ru   time_profiler  c<  s8    ~ __TC C C CJ Nrw   c              #   J   K   | ]  }t        d d d|z  dz  z   z          yw)r9   r&   rb   Nr  r  s     ru   r   r   =  s&     I!#aAQ!O45Ir  c                 b    t        |       D cg c]  }t        j                          c}S c c}w r   )r{  r  )r2   r  s     ru   r  r  =  s    eAh?V]]_? ?s   ,c                     | |k(  S r   r   r  s     ru   r  r  =  s
    !q& rw   zj:{lbl_s:<{len_lbls}s}  N={input_size!s:<{len_n}s} {is_equal_s:>3s}  {time_s:<26s}  {loop_s:>5} * {batch_s}r  c           
         | D cg c]  }|j                    }}|d}|d}|t        |      ni }d|vrt        d   |d<   d|vrd|d<   t        t	        d |            dz   }t        t	        t
        |            t        d	      z   }t        t        d
      |t        |
       g }g }t        |      D ]0  \  }}g } ||      }d}|	rt        t        |	      |t        |
       t        t        j                  | ||            D ]  \  }\  }}}|t        |      nd}|t        |      ni } t        di ||      } ||g|i |\  }}|dk(  r|} |||      }|rdnd}t        |d      r|j                   nd}|d	z  }|r%t        t        |t        |            |t        |
       ||d<   |j!                  |       |s|j!                  |        |j!                  |       3 |||fS c c}w )u  
    Benchmark multiple functions for varying input sizes.

    Sensible choices for input sizes are:

    - for linear (n) problems

      - (5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000)
      - tuple(10 * (i + 1) for i in range(24))
        # lin-spaced
      - tuple(2 ** (i + 1) for i in range(16))
        # geom-spaced
      - tuple(int(2 ** (2 + (3 * i) / 4)) for i in range(20))
        # frac-pow-spaced

    - for quadratic (n²) problems

      - (5, 10, 50, 100, 500, 1000, 5000)
      - tuple(10 * (i + 1) for i in range(24))
        # lin-space
      - tuple(2 ** (i + 1) for i in range(12))
        # geom-space
      - tuple(int(2 ** (2 + (3 * i) / 4)) for i in range(15))
        # frac-pow-spaced

    Args:
        funcs (Iterable[callable]): The functions to test.
            Must have the signature: func(input_, *args, **kws).
            `args` and `kws` are from `argss` and `kwss` respectively.
        argss (Iterable|None): The positional arguments for `funcs`.
            Each item correspond to an element of `funcs`.
        kwss (Iterable|None): The keyword arguments for `funcs`.
            Each item correspond to an element of `funcs`.
        input_sizes (Iterable[int]): The input sizes.
        gen_input (callable): The function used to generate the input.
            Must have the signature: gen_input(int): Any.
        equal_output (callable): The function used to compare the output.
            Must have the signature: gen_input(Any, Any): bool.
        time_prof_kws (Mappable|None): Keyword parameters for `time_profile`.
            These are passed to `flyingcircus.time_profile()`.
        store_all (bool): Store all results.
            If True, all results are stores.
        text_funcs (str): Text to use for printing output for functions.
        text_inputs (str): Text to use for printing output for inputs.
        fmtt (str|bool|None): Format of the printed output.
            This is passed to `flyingcircus.msg()`.
        verbose (int): Set level of verbosity.

    Returns:
        result (tuple): The tuple
            contains:
             - summaries (list[list[dict]]): The summary for each benchmark.
             - labels (list[str]): The function names.
             - results (list): The full results.
                This is non-empty only if `store_all` is True.

    Examples:
        >>> def f1(items):
        ...     return [item * item for item in items]
        >>> def f2(items):
        ...     return [item ** 2 for item in items]
        >>> def func_with_longer_name(items):
        ...     return [item ** 2 for item in items]
        >>> funcs = f1, f2, func_with_longer_name
        >>> summaries, labels, results = multi_benchmark(
        ...     funcs, input_sizes=tuple(10 ** (i + 1) for i in range(3)),
        ...     time_prof_kws=dict(timeout=0.1, batch_timeout=0.01),
        ...     fmtt=False)  # doctest:+ELLIPSIS
        N = (10, 100, 1000)
        <BLANKLINE>
        :f1()                     N=10    OK  (... ± ...) ...s  ... * ...
        :f2()                     N=10    OK  (... ± ...) ...s  ... * ...
        :func_with_longer_name()  N=10    OK  (... ± ...) ...s  ... * ...
        <BLANKLINE>
        :f1()                     N=100   OK  (... ± ...) ...s  ... * ...
        :f2()                     N=100   OK  (... ± ...) ...s  ... * ...
        :func_with_longer_name()  N=100   OK  (... ± ...) ...s  ... * ...
        <BLANKLINE>
        :f1()                     N=1000  OK  (... ± ...) ...s  ... * ...
        :f2()                     N=1000  OK  (... ± ...) ...s  ... * ...
        :func_with_longer_name()  N=1000  OK  (... ± ...) ...s  ... * ...
        >>> print(labels, results)
        ['f1', 'f2', 'func_with_longer_name'] []
        >>> summary_headers = list(summaries[0][0].keys())
        >>> print(summary_headers)
        ['result', 'func_name', 'args', 'kws', 'num', 'val', 'err', 'mean', 'sosd', 'var', 'stdev', 'min', 'max', 'median', 'medoid', 'batch', 'is_equal']

    See Also:
        - flyingcircus.time_profile()
        - flyingcircus._format_summary()
    Nr   rX  noner  Tc                 d    t        t        j                  t        j                  |                   S r   )rY   rd  re  r  r  s    ru   r  z!multi_benchmark.<locals>.<lambda>=  s    c$))DJJqM":; rw   r/   z()zN = {input_sizes}r   OKERRr   z	<UNNAMED>is_equal)r   r  r	   r   r   r   r   r   r
   r  r   r  r   r  r   r  r   )funcsargsskwssinput_sizes	gen_inputequal_outputtime_prof_kws	store_all
text_funcstext_inputsrE  rX  r   r  len_nlen_lbls	summariesresultsrM   
input_sizeinner_summaries
input_datatruthr  r   r   r   r  r  
is_equal_slbl_ss                                  ru   multi_benchmarkr  =  s   V )..dmm.F.}|+8+DD'"M%#+F#3i m#!%g;[IJQNE3sF#$s4y0H !7J=IG";/ *:z*
[!7J=)//udCD	' A dC"&"25;D"$s)BC0<0-06D":<<<OFGAv#E62H!)uJ%,T:%>DMMKETMEOGT*-=>Z/"*GJ""7+v&%	'& 	)3*4 fg%%S /s   G+__main__r   )r   F)Nr   )T)r  TTF)r   r<   )NN)r/   r   T)r<   )FF)FN)F)Nr<   N)r   r<   N)r9   r/   TNF)r/   TNF)r]  TF)NF)TTT)NNNNr  )TF)r<   r   r/   )r<   r7  r/   )r3  )r9   NF)r9   r  Tr  )r  rU  )r,  r/  gHz>T)TT)i   )TrG  )r  r   )r  r   T)r  r   T)r  r  r   )r  r  r   r   )r`  r  )r  T)NNr,  F)FTTr  T)FFFTr<   N)*ro   TNN)NFT)za-zA-Z0-9._-r  T)r  )	Nr  r   )	__class__r   r  r  r  <>)r0  r0  )))falsetrue)01)offonFT)r  r/   )r  )r  r/   N)r  r  r  )rb   )r9   rb   )r&   )O   z..Nr   ),r  {}NNT)r  r  r  r  NNN)FFT))Nr9   N)r8   r%   r  F)r  (v  r  
__future__r   r   r   r   rp   r  rO  rd  r  
statisticsri  r   r   r^  r  rJ  re  rg  r   ry   r1  r  r  r!   r   copyr  r  r  r^  r4  r>  pickler  r  flyingcircusr   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   CSV_DELIMITERCSV_COMMENT_TOKENr  
D_TAB_SIZEr  r  r  r  r   deepcopySI_PREFIX_ASCIISI_PREFIX_EXTRA_modera   	maketransr  SUBSCRIPT_MAP_STRUCT_TYPESr  rT   rY   r_   r8  r6  r=  partialr   r   rv   r   r   r   r   r   objectr   r  r  r(  r.  dumpsrE  rQ  rV  rX  r\  ra  rd  rh  rx  r  r  r   r   r  r  r  r  r  r  r  r  r   r  r  r  r  r  r  r  r  r  r  r  r  r  getrandbitsr  r  r  r  r  r  r  r%  r(  r+  r-  r2  r4  r8  r>  rB  rL  rP  rS  rZ  rm  rq  rs  rv  ry  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r
  r  r  r  r  r  r  r  r#  r&  r*  r,  r.  r2  r6  r<  r>  r   r@  rD  r{  SMALL_PRIMESr  r\  rU  rT  rx  rz  r~  r  r  r  r  r  r  r  rh  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r#  r)  r+  r9  r<  rg  r@  rH  rK  rN  rQ  rT  rV  rZ  r\  ra  rc  re  rg  ri  rk  rm  ro  rr  rv  rx  rz  r|  r~  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  SEEK_SETr  r  r  md5urlsafe_b64encoder  r  dumploadr  r  r  r)  r8  rb  r|  r  r  r  r  rq   r  r  r  r  r  r  rr   r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r	  r  r  r  r  r  r  r  r!  r&  r1  r4  r6  rG  rK  rX  r]  re  rh  rs  r}  r  r  r  r  r  r  r  r  perf_counterr  r  __file__r   )r  r'   rS   s   000ru   <module>r/     s  C C
 
 	 
                  
   	       	 $ = = 6 6 = = - % A A  
 
  FGIAq 	
AE	I
 GHJAq 	
QU8J Q1ga b!
#	 
*   Y{3 4 	*   Y{3 4 	)   %.{%;%A%A%CE!QAq=E F 	)   %.{%;%A%A%CE!QAq=E F 	(   9Y/ 0 	(   9Y/ 0$--	*$--	*; %E"+E"24"8OE3#,U#3D#9OE4 "+E"24"8OE3t$	% --8: :<* ))aQT)	 	  #	3	  
   $ !,/2!E ?  Ichh+	6"*:4p  l l`  M MbX.F X.xF J yz">B \\J` 6;x *\<&Z 8Cx+d 	Nl 	@CH  A$ A$P 8| 5)$&MX 5)$-#f 5)$*#` 6z ;;	):` ;;"L'Z 5)$=F 5)$J?b 	fZ ]F eY'CR F G.VcR  N 5)$>&J @&N 5)$	3r 5)$H&\ $$<BD AN 
Lf 	BR x| AN Wz 	Tt y~ Vx sn"8R 	@H@ /l 4!t \=H ?L 	4Av A-P Vx 8>F <D =J t<v T1v L1d Uv Q1n +.b N#h qp 8F~ 	]MH 9J 	Wz $6tVz 	|D =F ;#~,,.!>**4.8*( D @ B> F>C@@H@H*\BLHX3n, `$T 5r0%j&%T`BL S1p 'X 2p .h 	T0p-b e 9QQ'9 9 9 "%l3h+\ Vv !	lb !	x"| :@ "aJ10 \@7Nz 	7Iz 	dT (	%@R:<$4& :|]#H 8x(\ "'P 'V 8.8"L48'V!3P  J J )J ,H *.` ,0d .2h .4d F 8 7z "BP L G5Z AP GZ 5v ,D`+6f 	6| 	4p0-h9B 	EX FZ 	Lf 	`L /j 	"P 	"P .h 	0l 4't 4(t 92~ 94~ CR GZ S!t ]B'>HJ 	4p*\5L 0,l &T76/(8 !6N ?J Of6. {{9~ {{"T \D {{**(\ .{{**"P ++++'Z ;AB ?J  1H0&l c(R F&X 8 &V $#R /> ww~~<> 	GZ 2p 9%z':Z 	H 3r 2F 	(X+b Vx Q4t &I:^ D: > @	J` E7G,	3n< %EV 12,?d 	(+,(N\ B .B 12&DX 	(+, DL 	%(V 	?L 4v 	<D 4 	>?D2?lNh x| Kb AN *d 3n%2 H\ !N !J$P#N!P !KP 	Qn Kb :  >d dT IuRyII?(NT&p " # z ejIJEE\ *s   )n
n>n
:n

n"