Skip to content

Commit

Permalink
quantity: Add multiplication methods
Browse files Browse the repository at this point in the history
Add multiplication functionality to Quantity.

Signed-off-by: Yuki Iwai <[email protected]>

Kubernetes-commit: 4381eb7237e96d51caba7d4e81b93607e6822262
  • Loading branch information
tenzen-y authored and k8s-publishing-bot committed Apr 17, 2023
1 parent be91880 commit 0773351
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 0 deletions.
44 changes: 44 additions & 0 deletions pkg/api/resource/amount.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,50 @@ func (a *int64Amount) Sub(b int64Amount) bool {
return a.Add(int64Amount{value: -b.value, scale: b.scale})
}

// Mul multiplies two int64Amounts together, matching scales.
// It will return false and not mutate a if overflow or underflow would result.
// Mul adds the value of a to itself n times.
// It will return false and not mutate a if overflow or underflow would result.
func (a *int64Amount) Mul(b int64Amount) bool {
switch {
case b.value == 0:
a.value = 0
return true
case a.value == 0:
a.value = 0
a.scale = b.scale
return true
case a.scale == b.scale:
c, ok := int64Multiply(a.value, b.value)
if !ok {
return false
}
a.value = c
case a.scale > b.scale:
c, ok := positiveScaleInt64(a.value, a.scale-b.scale)
if !ok {
return false
}
c, ok = int64Multiply(c, b.value)
if !ok {
return false
}
a.scale = b.scale
a.value = c
default:
c, ok := positiveScaleInt64(b.value, b.scale-a.scale)
if !ok {
return false
}
c, ok = int64Multiply(a.value, c)
if !ok {
return false
}
a.value = c
}
return true
}

// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) {
Expand Down
48 changes: 48 additions & 0 deletions pkg/api/resource/amount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,54 @@ func TestInt64AmountAdd(t *testing.T) {
}
}
}

func TestInt64AmountMul(t *testing.T) {
for _, test := range []struct {
a, b, c int64Amount
ok bool
}{
{int64Amount{value: 100, scale: 1}, int64Amount{value: 10, scale: 2}, int64Amount{value: 10000, scale: 1}, true},
{int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 2}, int64Amount{value: 1000, scale: 1}, true},
{int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 100}, int64Amount{value: 1, scale: 100}, false},
{int64Amount{value: -5, scale: 2}, int64Amount{value: 50, scale: 1}, int64Amount{value: -2500, scale: 1}, true},
{int64Amount{value: -5, scale: 2}, int64Amount{value: 5, scale: 2}, int64Amount{value: -25, scale: 2}, true},

{int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 1, scale: -1}, int64Amount{value: 9223372036854775807, scale: -1}, true},
{int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 0, scale: -1}, int64Amount{value: 0, scale: -1}, true},
{int64Amount{value: mostPositive / 10, scale: 1}, int64Amount{value: 10, scale: 0}, int64Amount{value: mostPositive, scale: -1}, false},
} {
c := test.a
ok := c.Mul(test.b)
if ok != test.ok {
t.Errorf("%v: unexpected ok: %t", test, ok)
}
if ok {
if c != test.c {
t.Errorf("%v: unexpected result: %d", test, c)
}
} else {
if c != test.a {
t.Errorf("%v: overflow multiplication mutated source: %d", test, c)
}
}

// multiplication is commutative
c = test.b
if ok = c.Mul(test.a); ok != test.ok {
t.Errorf("%v: unexpected ok: %t", test, ok)
}
if ok {
if c != test.c {
t.Errorf("%v: unexpected result: %d", test, c)
}
} else {
if c != test.b {
t.Errorf("%v: overflow multiplication mutated source: %d", test, c)
}
}
}
}

func TestInt64AsCanonicalString(t *testing.T) {
for _, test := range []struct {
value int64
Expand Down
13 changes: 13 additions & 0 deletions pkg/api/resource/quantity.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,19 @@ func (q *Quantity) Sub(y Quantity) {
q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec())
}

// Mul multiplies the provided y quantity to the current value. If the current value is zero,
// the format of the quantity will be updated to the format of y.
func (q *Quantity) Mul(y Quantity) {
q.s = ""
if q.IsZero() {
q.Format = y.Format
}
if q.d.Dec == nil && y.d.Dec == nil && q.i.Mul(y.i) {
return
}
q.ToDec().d.Dec.Mul(q.d.Dec, y.AsDec())
}

// Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
// quantity is greater than y.
func (q *Quantity) Cmp(y Quantity) int {
Expand Down
22 changes: 22 additions & 0 deletions pkg/api/resource/quantity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,28 @@ func TestAdd(t *testing.T) {
}
}

func TestMul(t *testing.T) {
tests := []struct {
a Quantity
b Quantity
expected Quantity
}{
{decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(100, 0, DecimalSI)},
{decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(10, 0, DecimalSI)},
{decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(10, 0, BinarySI)},
{Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(0, 0, DecimalSI)},
{decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)},
{Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)},
}

for i, test := range tests {
test.a.Mul(test.b)
if test.a.Cmp(test.expected) != 0 {
t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String())
}
}
}

func TestAddSubRoundTrip(t *testing.T) {
for k := -10; k <= 10; k++ {
q := Quantity{Format: DecimalSI}
Expand Down

0 comments on commit 0773351

Please sign in to comment.