```python
"""Cut a fabric roll for the greatest takings the price board allows."""


def cut_roll(length: int, board: list[tuple[int, int]]) -> dict:
    """Return the maximum takings and the piece lengths for a fabric roll cut.

    Args:
        length: The total length of the fabric roll in metres.
        board: A list of [metres, price] pairs describing sellable pieces.

    Returns:
        A dict with 'takings' (max price) and 'pieces' (list of metre lengths).
    """
    if isinstance(length, bool) or not isinstance(length, int) or length < 0:
        raise ValueError("length must be a non-negative whole number")
    
    # Sort board by piece length descending to prefer longer pieces on ties
    # when we iterate, but the DP logic handles the "longer to shorter" tie
    # via the specific comparison order and update rules.
    order = sorted(board, key=lambda entry: -entry[0])
    
    # best[i] stores the max takings for a roll of length i
    # taken[i] stores the length of the piece cut off the end for length i
    best = [0] * (length + 1)
    taken = [0] * (length + 1)
    
    for metres in range(1, length + 1):
        best[metres] = best[metres - 1]
        taken[metres] = 0
        
        for piece, price in order:
            if piece > metres:
                continue
            
            # Calculate takings if we cut a piece of this length
            candidate = price + best[metres - piece]
            
            # We want to maximize takings.
            # Tie-breaking rules:
            # 1. Prefer cutting a piece to scrapping (i.e., taking a piece > 0)
            #    This is handled because we initialize with scrap (taken=0)
            #    and only update if candidate is strictly better OR equal with
            #    a preference for cutting.
            # 2. Prefer longer piece to shorter.
            #    Since we iterate pieces from longest to shortest, if we find
            #    a candidate that equals the current best, we should update
            #    if the current best came from a shorter piece (or scrap).
            #    However, the problem says "on a tie prefer cutting a piece to scrapping,
            #    and the longer piece to the shorter".
            #    
            #    Let's refine the logic:
            #    We want to pick the (price + best[remaining]) that is maximal.
            #    If there's a tie in value, we prefer the option that involves
            #    cutting a piece (taken > 0) over scrapping (taken == 0).
            #    If there's still a tie (e.g., two different pieces yield same
            #    total price), we prefer the longer piece.
            
            # Current best for this 'metres' is either from scrap (taken=0)
            # or from a previously processed piece in this loop (if we updated).
            # But wait, the loop iterates pieces. We need to compare against
            # the best found SO FAR for this 'metres'.
            
            # Let's restart the inner logic to be clearer.
            pass

    # Re-implementing the DP with clearer tie-breaking
    best = [0] * (length + 1)