44 lines
1.3 KiB
Go
44 lines
1.3 KiB
Go
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
|
|||
|
|
}
|