;; lib/commerce/stack.sx — promotion stacking precedence + best price. ;; ;; Per the miniKanren design rule, precedence is NOT encoded inside the promo ;; rules. promo.sx enumerates which promos apply; this layer enumerates which ;; *combinations* are legal and selects the best one by an explicit cost ;; function (max total discount = min price). ;; ;; Exclusivity is a list of unordered code pairs that may not both apply: ;; exclusions = (list (list code-a code-b) ...) ;; A stacking is a subset of applicable (code amount) pairs containing no ;; excluded pair. valid-stackings enumerates them; best-stacking is the ;; deterministic selection layer; stacking-by-totalo is the backward query ;; ("which legal stacking yields this total discount?"). (define excluded-pair? (fn (exclusions a b) (some (fn (p) (or (and (= (first p) a) (= (nth p 1) b)) (and (= (first p) b) (= (nth p 1) a)))) exclusions))) ;; True when no two distinct codes in the list are mutually excluded. (define compatible? (fn (exclusions codes) (every? (fn (a) (every? (fn (b) (or (= a b) (not (excluded-pair? exclusions a b)))) codes)) codes))) ;; All subsets of xs, preserving element order. 2^n entries. (define powerset (fn (xs) (if (empty? xs) (list (list)) (let ((r (powerset (cdr xs)))) (append r (map (fn (s) (cons (first xs) s)) r)))))) (define stacking-codes (fn (st) (map first st))) (define stacking-total (fn (st) (reduce (fn (acc pair) (+ acc (nth pair 1))) 0 st))) ;; Every legal stacking of the applicable (code amount) pairs. (define valid-stackings (fn (exclusions applicable) (filter (fn (st) (compatible? exclusions (stacking-codes st))) (powerset applicable)))) ;; Deterministic selection: the legal stacking with the greatest total ;; discount; ties keep the earlier (stable) candidate, so the result is a ;; reproducible function of (exclusions, applicable). (define best-stacking (fn (exclusions applicable) (reduce (fn (best st) (if (> (stacking-total st) (stacking-total best)) st best)) (list) (valid-stackings exclusions applicable)))) (define best-discount (fn (exclusions applicable) (stacking-total (best-stacking exclusions applicable)))) (define best-codes (fn (exclusions applicable) (stacking-codes (best-stacking exclusions applicable)))) ;; Backward query: legal stackings (as code lists) whose total discount = D. (define stacking-by-totalo (fn (stackings codes total) (fresh (st) (membero st stackings) (project (st) (mk-conj (== codes (stacking-codes st)) (== total (stacking-total st))))))) ;; --- top-level entry: best discount for a cart under a ruleset --- (define best-promo-discount (fn (ctx cart ruleset exclusions) (best-discount exclusions (applicable-promos ctx cart ruleset)))) (define best-promo-codes (fn (ctx cart ruleset exclusions) (best-codes exclusions (applicable-promos ctx cart ruleset))))