Shopping Basket with Coupons
Problem
Design ShoppingBasket for a store that keeps products and coupons in one ordered sequence. Each product has a type and an integer price in cents, and each coupon changes the price of its specified products when the total is calculated.
Requirements
- Preserve basket order.
addappends eachProductorCouponafter all existing entries, and total calculation uses that insertion order. - Use distinct coupon types.
AllProductsPercentCoupon,NextProductPercentCoupon, andNthProductOfTypeFixedCouponare separateCouponimplementations accepted asBasketEntryvalues. - Discount all products.
AllProductsPercentCouponreduces the current price of every product, including products before and after the coupon. - Discount the next product.
NextProductPercentCouponreduces the first laterProduct, regardless of its type and ignoring intervening coupon entries. - Discount the Nth product of a type.
NthProductOfTypeFixedCouponsubtracts its fixed discount from the one-based NthProductwith the matchingproductType, ignoring coupon entries and other product types. - Apply every coupon.
netTotalCentsapplies every coupon entry exactly once. - Apply coupons left to right. Each coupon starts from the prices produced by earlier coupons.
- Round each percentage. After each percentage coupon, a target price becomes
floor(currentPriceCents * (100 - percentOff) / 100). - Return the net total.
netTotalCentsreturns the sum of all final product prices; coupon entries add no price. - Keep totals repeatable.
netTotalCentsdoes not mutate stored entries and starts from each product's original price, so an unchanged basket returns the same total on repeated calls.
API
| Signature | Returns | Behavior |
|---|---|---|
BasketEntry | Not applicable | Marker abstraction shared by Product and Coupon. |
Product(productType: string, priceCents: integer) | Product | Creates one immutable priced product of the supplied type. |
Coupon | Not applicable | Abstract basket entry implemented by each coupon type. |
AllProductsPercentCoupon(percentOff: integer) | AllProductsPercentCoupon | Creates a coupon that targets every product. |
NextProductPercentCoupon(percentOff: integer) | NextProductPercentCoupon | Creates a coupon that targets the first later product. |
NthProductOfTypeFixedCoupon(productType: string, productNumber: integer, discountCents: integer) | NthProductOfTypeFixedCoupon | Creates a coupon that targets the one-based Nth product of the supplied type. |
ShoppingBasket() | ShoppingBasket | Creates an empty ordered basket. |
add(entry: BasketEntry) | void | Appends the non-null product or coupon entry. |
netTotalCents() | integer | Re-evaluates the current sequence from original product prices and returns its discounted total. |
All constructors and calls satisfy the constraints below. Behavior outside those constraints is unspecified and is not judged.
Examples
| Step | Operation | Result |
|---|---|---|
| 1 | add(Product("BOOK", 1000)) | Appends a book. |
| 2 | add(NextProductPercentCoupon(10)) | Appends a 10 percent next-product coupon. |
| 3 | add(Product("STATIONERY", 2000)) | Appends the first later Product, which the coupon targets. |
| 4 | add(Product("BOOK", 3000)) | Appends another later Product. |
| 5 | netTotalCents() | 5800 |
For the sequence Product("BOOK", 1000), NthProductOfTypeFixedCoupon("BOOK", 2, 200), AllProductsPercentCoupon(25), NextProductPercentCoupon(10), Product("BOOK", 1000), the product prices become 750 and 540 cents, so netTotalCents() returns 1290.
Constraints
- A basket has 1 to 100 entries and at least one product when
netTotalCentsis called. productTypeis nonempty, has at most 32 characters, and uses exact string equality.1 <= priceCents <= 1,000,000,000.1 <= percentOff <= 100.productNumbernames an existing matching product position.- A next-product coupon always has a later product.
- A fixed discount never exceeds its target's current price when applied.
- Every intermediate product price and final total fits a signed 64-bit integer.
- A testcase calls
netTotalCentsat most 100 times.
Notes
Type-specific product positions count only Product entries with the coupon's exact productType. Calls are sequential; concurrency is outside the contract.