"""Distribute values symmetrically across N parts.""" def calculate_symmetric_weights( n: int, middle_weight: float, factors: list[float] | None = None ) -> list[float]: """Calculate symmetric weights for both even and odd N. Args: n: Number of parts to split into. middle_weight: The middle value for symmetry. factors: If provided, controls the difference in weights. Must have length n // 2 or n // 2 - 1 depending on n. Returns: List of symmetric weights. """ half_n = n // 2 weights_left: list[float] = [middle_weight] if factors: for factor in factors: next_weight = weights_left[-1] + factor weights_left.append(next_weight) else: weights_left.extend(middle_weight - (idx + 1) for idx in range(half_n - 1)) if not n % 2: weights = weights_left[::-1] + weights_left else: weights = [*weights_left[::-1], middle_weight, *weights_left] return weights def scale_to_total(x: float, weights: list[float]) -> list[float]: """Scale the weights so that their sum is proportional to X. Args: x: Total value to distribute. weights: The list of weights to be scaled. Returns: List of scaled values summing to x. """ total_weight = sum(weights) base_unit = x / total_weight return [base_unit * weight for weight in weights] def split_x_into_n_symmetrically(x: float, n: int, factors: list[float]) -> list[float]: """Split X into N parts with symmetric weights controlled by factors.""" weights = calculate_symmetric_weights(n, middle_weight=1, factors=factors) return scale_to_total(x, weights) def split_x_into_n_middle(x: float, n: int, middle_value: float) -> list[float]: """Split X into N parts with symmetric weights using middle_value as peak.""" weights = calculate_symmetric_weights(n, middle_weight=middle_value) return scale_to_total(x, weights)