Home/Low-level design/Expense Sharing System

Expense Sharing System

Integer accounting and idempotent operations

Integer arithmetic avoids binary rounding but still needs a remainder policy. Divide cents by participant count, then distribute the remainder to the first sorted IDs. This allocates every cent once and makes caller ordering irrelevant.

cpp
Int base=1000/3,remainder=1000%3; // 333 and 1
// Alphabetically first participant receives 334; the other two receive 333.

Maintain net balances whose sum is always zero. Credit the payer, debit participants, and validate settlement bounds before changing either side. Exact accepted retries must be recognized before checking today's balances; the original payment already changed them. Canonical expense identity sorts participants, while one shared registry distinguishes expense and settlement methods. Suggestions operate on copies and greedily pair alphabetic debtors/creditors; applying suggestions during a read would silently change accounting. This deterministic plan need not minimize transaction count.

C++ contract conventions: numeric values use signed Int (long long); all inputs and intermediate arithmetic are within its representable range. Opt<T> means std::optional<T> and absence is std::nullopt; Vec<T> means std::vector<T>. Invalid documented arguments throw std::invalid_argument. Inputs passed by const reference remain caller owned. Returned containers and records own their contents. Calls use the declared C++ parameter types. Implicit conversions performed by the caller are outside the runtime validation contract. Unless a lesson states otherwise, string ordering is lexicographic over Unicode code points (Text) or UTF-8 identifiers (std::string).

Put it into practice

Implement the exercise in a full-screen workspace, then run its tests.

Open practice