# Brackets use cumulative upper bounds, with a final uncapped bracket. def progressive_points($qty; $brackets): reduce $brackets[] as $b ({remaining: $qty, lower: 0, points: 0}; (if $b.upTo == null then .remaining else ([.remaining, ($b.upTo - .lower)] | min) end) as $units | .points += $units * $b.pointsPerUnit | .remaining -= $units | .lower = ($b.upTo // .lower)) | .points; def reward_buckets($rules): ($rules.skuRules | INDEX(.sku)) as $bySku | group_by([.accountId, .sku]) | map( .[0] as $first | (map(.netQty) | add) as $qty | ($rules.tierMultipliers[$first.tier] // error("unknown account tier: " + $first.tier)) as $multiplier | $bySku[$first.sku] as $rule | (if $rule == null then 0 else progressive_points($qty; $rule.brackets) end) as $base | {accountId: $first.accountId, sku: $first.sku, netQty: $qty, basePoints: $base, multiplier: $multiplier, points: ($base * $multiplier), missingRate: ($rule == null)});