Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_ratio.py154 linesDownload Raw Back to rich
1from fractions import Fraction2from math import ceil3from typing import cast, List, Optional, Sequence, Protocol4 5 6class Edge(Protocol):7    """Any object that defines an edge (such as Layout)."""8 9    size: Optional[int] = None10    ratio: int = 111    minimum_size: int = 112 13 14def ratio_resolve(total: int, edges: Sequence[Edge]) -> List[int]:15    """Divide total space to satisfy size, ratio, and minimum_size, constraints.16 17    The returned list of integers should add up to total in most cases, unless it is18    impossible to satisfy all the constraints. For instance, if there are two edges19    with a minimum size of 20 each and `total` is 30 then the returned list will be20    greater than total. In practice, this would mean that a Layout object would21    clip the rows that would overflow the screen height.22 23    Args:24        total (int): Total number of characters.25        edges (List[Edge]): Edges within total space.26 27    Returns:28        List[int]: Number of characters for each edge.29    """30    # Size of edge or None for yet to be determined31    sizes = [(edge.size or None) for edge in edges]32 33    _Fraction = Fraction34 35    # While any edges haven't been calculated36    while None in sizes:37        # Get flexible edges and index to map these back on to sizes list38        flexible_edges = [39            (index, edge)40            for index, (size, edge) in enumerate(zip(sizes, edges))41            if size is None42        ]43        # Remaining space in total44        remaining = total - sum(size or 0 for size in sizes)45        if remaining <= 0:46            # No room for flexible edges47            return [48                ((edge.minimum_size or 1) if size is None else size)49                for size, edge in zip(sizes, edges)50            ]51        # Calculate number of characters in a ratio portion52        portion = _Fraction(53            remaining, sum((edge.ratio or 1) for _, edge in flexible_edges)54        )55 56        # If any edges will be less than their minimum, replace size with the minimum57        for index, edge in flexible_edges:58            if portion * edge.ratio <= edge.minimum_size:59                sizes[index] = edge.minimum_size60                # New fixed size will invalidate calculations, so we need to repeat the process61                break62        else:63            # Distribute flexible space and compensate for rounding error64            # Since edge sizes can only be integers we need to add the remainder65            # to the following line66            remainder = _Fraction(0)67            for index, edge in flexible_edges:68                size, remainder = divmod(portion * edge.ratio + remainder, 1)69                sizes[index] = size70            break71    # Sizes now contains integers only72    return cast(List[int], sizes)73 74 75def ratio_reduce(76    total: int, ratios: List[int], maximums: List[int], values: List[int]77) -> List[int]:78    """Divide an integer total in to parts based on ratios.79 80    Args:81        total (int): The total to divide.82        ratios (List[int]): A list of integer ratios.83        maximums (List[int]): List of maximums values for each slot.84        values (List[int]): List of values85 86    Returns:87        List[int]: A list of integers guaranteed to sum to total.88    """89    ratios = [ratio if _max else 0 for ratio, _max in zip(ratios, maximums)]90    total_ratio = sum(ratios)91    if not total_ratio:92        return values[:]93    total_remaining = total94    result: List[int] = []95    append = result.append96    for ratio, maximum, value in zip(ratios, maximums, values):97        if ratio and total_ratio > 0:98            distributed = min(maximum, round(ratio * total_remaining / total_ratio))99            append(value - distributed)100            total_remaining -= distributed101            total_ratio -= ratio102        else:103            append(value)104    return result105 106 107def ratio_distribute(108    total: int, ratios: List[int], minimums: Optional[List[int]] = None109) -> List[int]:110    """Distribute an integer total in to parts based on ratios.111 112    Args:113        total (int): The total to divide.114        ratios (List[int]): A list of integer ratios.115        minimums (List[int]): List of minimum values for each slot.116 117    Returns:118        List[int]: A list of integers guaranteed to sum to total.119    """120    if minimums:121        ratios = [ratio if _min else 0 for ratio, _min in zip(ratios, minimums)]122    total_ratio = sum(ratios)123    assert total_ratio > 0, "Sum of ratios must be > 0"124 125    total_remaining = total126    distributed_total: List[int] = []127    append = distributed_total.append128    if minimums is None:129        _minimums = [0] * len(ratios)130    else:131        _minimums = minimums132    for ratio, minimum in zip(ratios, _minimums):133        if total_ratio > 0:134            distributed = max(minimum, ceil(ratio * total_remaining / total_ratio))135        else:136            distributed = total_remaining137        append(distributed)138        total_ratio -= ratio139        total_remaining -= distributed140    return distributed_total141 142 143if __name__ == "__main__":144    from dataclasses import dataclass145 146    @dataclass147    class E:148        size: Optional[int] = None149        ratio: int = 1150        minimum_size: int = 1151 152    resolved = ratio_resolve(110, [E(None, 1, 1), E(None, 1, 1), E(None, 1, 1)])153    print(sum(resolved))154 
codekingpro/portable-devtools · Team Ai