split() loses or invents cents
Done- Plan (done)
- Code (done)
- Test (done)
- Review (done)
- Finish (done)
Cost
$0.0071
Tests
Passed
Iterations
1
Re-plans
0
LLM calls
11
Prompt read from cache
57%
Usage by agent
- Cache read
- Cache write
- Input
- Output
View as table
| Agent | Cost | Cache read | Cache write | Input | Output |
|---|---|---|---|---|---|
| planner | $0.0013 | 4,299 | 2,531 | 8 | 1,931 |
| coder | $0.0022 | 13,465 | 6,482 | 10 | 2,525 |
| tester | $0.0022 | 11,362 | 8,021 | 8 | 2,101 |
| reviewer | $0.0014 | 0 | 5,183 | 4 | 1,435 |
Timeline
-
planner iteration 1 Finished 3 calls · 8,769 tokens · $0.0013 · 11.6s
Loading…
-
coder iteration 1 Finished 4 calls · 22.5K tokens · $0.0022 · 14.5s
Loading…
-
tester iteration 1 Finished 3 calls · 21.5K tokens · $0.0022 · 15.1s
Loading…
-
verify iteration 1 Passed 0.5s
................ [100%] 16 passed in 0.03s
-
reviewer iteration 1 Finished 1 calls · 6,622 tokens · $0.0014 · 8.0s
Loading…
Final diff
diff --git a/money.py b/money.py index 6c4f693..0347da4 100644 --- a/money.py +++ b/money.py @@ -1,3 +1,30 @@ def split(amount_cents: int, weights: list[int]) -> list[int]: + """Split an integer number of cents by integer weights. + + Uses the largest-remainder method so the shares always sum to + ``amount_cents``. Leftover cents go to the largest fractional remainders, + with ties going to the earlier position. Zero-weight shares are always 0. + """ + if any(weight < 0 for weight in weights): + raise ValueError("weights must be non-negative") total = sum(weights) - return [round(amount_cents * weight / total) for weight in weights] + if total <= 0: + raise ValueError("weights must sum to more than 0") + + sign = -1 if amount_cents < 0 else 1 + n = abs(amount_cents) + + shares = [] + remainders = [] + for weight in weights: + floor, rem = divmod(n * weight, total) + shares.append(floor) + remainders.append(rem) + + leftover = n - sum(shares) + order = sorted(range(len(weights)), key=lambda i: (-remainders[i], i)) + for i in order[:leftover]: + if weights[i] > 0: + shares[i] += 1 + + return [sign * share if share else 0 for share in shares] diff --git a/test_money.py b/test_money.py index e667503..1e42712 100644 --- a/test_money.py +++ b/test_money.py @@ -7,3 +7,88 @@ def test_even(): def test_weighted(): assert split(90, [1, 2]) == [30, 60] + + +def test_leftover_cent_goes_to_earlier_position(): + assert split(100, [1, 1, 1]) == [34, 33, 33] + assert sum(split(100, [1, 1, 1])) == 100 + + +def test_negative_amount(): + assert split(-100, [1, 1, 1]) == [-34, -33, -33] + + +def test_zero_weight_gets_zero(): + assert split(10, [0, 1, 1]) == [0, 5, 5] + + +def test_tie_ordering(): + assert split(1, [1, 1]) == [1, 0] + + +def test_sum_equals_amount(): + for amount in range(-50, 51): + for weights in ([1], [1, 1], [1, 2, 3], [0, 1, 1], [7, 0, 3, 3], [1, 1, 1]): + assert sum(split(amount, weights)) == amount + + +def test_large_amount_exact(): + amount = 10**18 + 1 + shares = split(amount, [1, 1, 1]) + assert sum(shares) == amount + + +def test_zero_total_weight_raises(): + try: + split(5, [0, 0]) + except ValueError: + pass + else: + raise AssertionError("expected ValueError") + + +def test_empty_weights_raises(): + try: + split(5, []) + except ValueError: + pass + else: + raise AssertionError("expected ValueError") + + +def test_largest_remainder_beats_earlier_position(): + # 1*1/3 -> floor 0 rem 1; 1*2/3 -> floor 0 rem 2; the larger remainder wins. + assert split(1, [1, 2]) == [0, 1] + + +def test_zero_weight_earlier_does_not_take_leftover(): + assert split(1, [0, 1, 1]) == [0, 1, 0] + + +def test_zero_amount(): + assert split(0, [1, 2]) == [0, 0] + assert split(0, [1, 1, 1]) == [0, 0, 0] + + +def test_negative_large_amount_sum(): + amount = -(10**18 + 1) + shares = split(amount, [1, 1, 1]) + assert sum(shares) == amount + assert shares == [-333333333333333334, -333333333333333334, -333333333333333333] + + +def test_exact_large_amount_no_float_error(): + # 2**53 + 1 cannot be represented as a float; integer arithmetic must keep it exact. + amount = 2**53 + 1 + shares = split(amount, [1, 1]) + assert sum(shares) == amount + assert shares == [2**52 + 1, 2**52] + + +def test_mixed_sign_weights_rejected(): + try: + split(10, [1, -1, 2]) + except ValueError: + pass + else: + raise AssertionError("expected ValueError")