Repository navigation
Expand file tree
/
Copy pathbreakeven.go
More file actions
78 lines (73 loc) · 3.03 KB
/
Copy pathbreakeven.go
File metadata and controls
78 lines (73 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package common //nolint:revive // pkg/common is the module's core domain package (Recommendation, ProviderType, ...), imported by name (common.X) throughout this repo and by consumer-mcp; renaming would break every consumer import
import (
"math"
"strings"
)
// BreakEven returns the number of months until the upfront payment of rec is
// recovered by its recurring discount, and whether that is known.
//
// - A positive rec.BreakEvenMonths set by a provider wins.
// - No upfront (CommitmentCost == 0): (0, true). There is nothing to recover.
// Exception: a zero upfront on a payment option that carries an upfront
// (see UpfrontPriceUnknown) is a missing price, not a known zero: (0, false).
// - Otherwise upfront / gross monthly savings, where gross monthly savings is
// EstimatedSavings plus the upfront amortized over the term. EstimatedSavings
// is the net monthly figure (already after the amortized upfront), so using
// it alone would overstate the break-even and push all-upfront commitments
// past their own term.
// - Net savings so low that gross monthly savings is not positive: (+Inf, true).
// The commitment never pays back its upfront.
// - Negative or non-finite money fields, or a term that is not 1yr/3yr:
// (0, false). Unknown is not 0 and is never reported as a break-even.
//
// The result is invariant under ScaleRecommendationCosts: upfront and savings
// scale together.
func BreakEven(rec Recommendation) (months float64, known bool) {
if rec.BreakEvenMonths > 0 && !math.IsInf(rec.BreakEvenMonths, 0) && !math.IsNaN(rec.BreakEvenMonths) {
return rec.BreakEvenMonths, true
}
upfront, savings := rec.CommitmentCost, rec.EstimatedSavings
if !isFinite(upfront) || !isFinite(savings) || upfront < 0 {
return 0, false
}
if upfront == 0 {
return 0, !UpfrontPriceUnknown(rec)
}
termMonths, ok := breakEvenTermMonths(rec.Term)
if !ok {
return 0, false
}
gross := savings + upfront/termMonths
if gross <= 0 {
return math.Inf(1), true
}
return upfront / gross, true
}
func breakEvenTermMonths(term string) (float64, bool) {
switch normalizeReservationTerm(term) {
case "1yr":
return 12, true
case "3yr":
return 36, true
default:
return 0, false
}
}
func isFinite(v float64) bool { return !math.IsNaN(v) && !math.IsInf(v, 0) }
// UpfrontPriceUnknown reports whether rec's zero CommitmentCost is a missing
// price rather than a known zero upfront: the payment option says an upfront
// is charged (all, partial or Azure "upfront"), but the provider gave no
// figure. Azure Advisor "upfront" variants and AWS recommendations with a nil
// UpfrontCost have this shape. No-upfront, monthly and empty or unrecognized
// payment options keep a zero as a known zero upfront.
func UpfrontPriceUnknown(rec Recommendation) bool {
if rec.CommitmentCost != 0 {
return false
}
switch strings.NewReplacer("-", "", "_", "", " ", "").Replace(strings.ToLower(strings.TrimSpace(rec.PaymentOption))) {
case "allupfront", "upfront", "partialupfront", "partial":
return true
default:
return false
}
}