package processor import ( "math/big" "apskel-pos-be/internal/repository" ) // VoucherCostPart is the share of a redemption's face value one source of its // EnakPoint accounts for (docs/rfc-enakgame.md §7.6). type VoucherCostPart struct { repository.RedemptionPointSource Cost int64 } // SplitVoucherCost divides faceValue over the sources in proportion to their // EnakPoint: floor(face_value × points / total points) each, with what rounding left // over going to the largest part (the first of equals), so the parts add up to // face_value exactly. Products are computed without overflow. func SplitVoucherCost(faceValue int64, sources []repository.RedemptionPointSource) []VoucherCostPart { parts := make([]VoucherCostPart, 0, len(sources)) var totalPoints int64 for _, s := range sources { totalPoints += s.Points } if totalPoints <= 0 { return parts } face, total := big.NewInt(faceValue), big.NewInt(totalPoints) var assigned int64 largest := 0 for i, s := range sources { share := new(big.Int).Mul(face, big.NewInt(s.Points)) share.Quo(share, total) parts = append(parts, VoucherCostPart{RedemptionPointSource: s, Cost: share.Int64()}) assigned += share.Int64() if s.Points > sources[largest].Points { largest = i } } parts[largest].Cost += faceValue - assigned return parts }