diff --git a/common/math/integer_additions.go b/common/math/integer_additions.go new file mode 100644 index 0000000000..f0833be2a5 --- /dev/null +++ b/common/math/integer_additions.go @@ -0,0 +1,76 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +// SafeDiv returns x/y and checks for division by zero. +// Returns (0, true) if y is zero (indicating an error condition). +// Returns (x/y, false) if the division is valid. +func SafeDiv(x, y uint64) (uint64, bool) { + if y == 0 { + return 0, true + } + return x / y, false +} + +// SafeMod returns x%y and checks for division by zero. +// Returns (0, true) if y is zero (indicating an error condition). +// Returns (x%y, false) if the operation is valid. +func SafeMod(x, y uint64) (uint64, bool) { + if y == 0 { + return 0, true + } + return x % y, false +} + +// Min returns the smaller of x or y. +func Min(x, y uint64) uint64 { + if x < y { + return x + } + return y +} + +// Max returns the larger of x or y. +func Max(x, y uint64) uint64 { + if x > y { + return x + } + return y +} + +// Clamp returns x clamped to the inclusive range [min, max]. +// If x is less than min, min is returned. +// If x is greater than max, max is returned. +// Otherwise, x is returned unchanged. +func Clamp(x, min, max uint64) uint64 { + if x < min { + return min + } + if x > max { + return max + } + return x +} + +// AbsDiff returns the absolute difference between x and y. +// This is useful when you need |x - y| without worrying about underflow. +func AbsDiff(x, y uint64) uint64 { + if x > y { + return x - y + } + return y - x +} diff --git a/common/math/integer_additions_test.go b/common/math/integer_additions_test.go new file mode 100644 index 0000000000..1be5feabd1 --- /dev/null +++ b/common/math/integer_additions_test.go @@ -0,0 +1,164 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +import ( + "math" + "testing" +) + +func TestSafeDiv(t *testing.T) { + tests := []struct { + x, y uint64 + expected uint64 + isError bool + }{ + {10, 2, 5, false}, + {10, 3, 3, false}, + {0, 5, 0, false}, + {100, 1, 100, false}, + {math.MaxUint64, 2, math.MaxUint64 / 2, false}, + // Division by zero cases + {10, 0, 0, true}, + {0, 0, 0, true}, + {math.MaxUint64, 0, 0, true}, + } + + for i, test := range tests { + result, isError := SafeDiv(test.x, test.y) + if isError != test.isError { + t.Errorf("test %d: SafeDiv(%d, %d) error = %v, want %v", i, test.x, test.y, isError, test.isError) + } + if !isError && result != test.expected { + t.Errorf("test %d: SafeDiv(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected) + } + } +} + +func TestSafeMod(t *testing.T) { + tests := []struct { + x, y uint64 + expected uint64 + isError bool + }{ + {10, 3, 1, false}, + {10, 2, 0, false}, + {0, 5, 0, false}, + {7, 7, 0, false}, + {math.MaxUint64, 2, 1, false}, + // Modulo by zero cases + {10, 0, 0, true}, + {0, 0, 0, true}, + } + + for i, test := range tests { + result, isError := SafeMod(test.x, test.y) + if isError != test.isError { + t.Errorf("test %d: SafeMod(%d, %d) error = %v, want %v", i, test.x, test.y, isError, test.isError) + } + if !isError && result != test.expected { + t.Errorf("test %d: SafeMod(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected) + } + } +} + +func TestMin(t *testing.T) { + tests := []struct { + x, y uint64 + expected uint64 + }{ + {1, 2, 1}, + {2, 1, 1}, + {5, 5, 5}, + {0, 100, 0}, + {math.MaxUint64, 0, 0}, + {math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64 - 1}, + } + + for i, test := range tests { + result := Min(test.x, test.y) + if result != test.expected { + t.Errorf("test %d: Min(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected) + } + } +} + +func TestMax(t *testing.T) { + tests := []struct { + x, y uint64 + expected uint64 + }{ + {1, 2, 2}, + {2, 1, 2}, + {5, 5, 5}, + {0, 100, 100}, + {math.MaxUint64, 0, math.MaxUint64}, + {math.MaxUint64, math.MaxUint64 - 1, math.MaxUint64}, + } + + for i, test := range tests { + result := Max(test.x, test.y) + if result != test.expected { + t.Errorf("test %d: Max(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected) + } + } +} + +func TestClamp(t *testing.T) { + tests := []struct { + x, min, max uint64 + expected uint64 + }{ + {5, 0, 10, 5}, // within range + {0, 5, 10, 5}, // below min + {15, 5, 10, 10}, // above max + {5, 5, 10, 5}, // at min + {10, 5, 10, 10}, // at max + {5, 5, 5, 5}, // min == max == x + {0, 0, 0, 0}, // all zero + {100, 0, 50, 50}, // clamped to max + } + + for i, test := range tests { + result := Clamp(test.x, test.min, test.max) + if result != test.expected { + t.Errorf("test %d: Clamp(%d, %d, %d) = %d, want %d", i, test.x, test.min, test.max, result, test.expected) + } + } +} + +func TestAbsDiff(t *testing.T) { + tests := []struct { + x, y uint64 + expected uint64 + }{ + {10, 3, 7}, + {3, 10, 7}, + {5, 5, 0}, + {0, 0, 0}, + {math.MaxUint64, 0, math.MaxUint64}, + {0, math.MaxUint64, math.MaxUint64}, + {100, 99, 1}, + } + + for i, test := range tests { + result := AbsDiff(test.x, test.y) + if result != test.expected { + t.Errorf("test %d: AbsDiff(%d, %d) = %d, want %d", i, test.x, test.y, result, test.expected) + } + } +}