github.com/searKing/golang/go@v1.2.117/exp/math/serial_number_arithmetic_test.go (about) 1 // Copyright 2023 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package math_test 6 7 import ( 8 "fmt" 9 "testing" 10 "testing/quick" 11 12 math_ "github.com/searKing/golang/go/exp/math" 13 "golang.org/x/exp/constraints" 14 ) 15 16 func TestIsNewer(t *testing.T) { 17 tests := []struct { 18 x, y uint8 19 want bool 20 }{ 21 {0, 0, false}, 22 {0xFF, 0xFF, false}, 23 {0, 1, false}, 24 {0xFE, 0xFF, false}, 25 {0, 0xFF, true}, 26 {0xFF, 0, false}, 27 {0, 0x7F, false}, 28 {0, 0x7E, false}, 29 {0, 0x80, false}, 30 {1, 0, true}, 31 {44, 0, true}, 32 {100, 0, true}, 33 {100, 44, true}, 34 {200, 100, true}, 35 {255, 200, true}, 36 {0, 255, true}, 37 {100, 255, true}, 38 {0, 200, true}, 39 {44, 200, true}, 40 {126, 255, true}, 41 {127, 255, false}, 42 {125, 254, true}, 43 {126, 254, false}, 44 {58, 25, true}, 45 } 46 for _, tt := range tests { 47 t.Run(fmt.Sprintf("math_.IsNewer(%v, %v)", tt.x, tt.y), func(t *testing.T) { 48 { 49 got := math_.IsNewer(tt.x, tt.y) 50 if got != tt.want { 51 t.Errorf("math_.IsNewer(%v, %v) = %v, want %v", tt.x, tt.y, got, tt.want) 52 } 53 } 54 }) 55 } 56 57 if err := quick.CheckEqual(math_.IsNewer[uint8], checkIsNewer[uint8], nil); err != nil { 58 t.Error(err) 59 } 60 } 61 62 func TestIsNewerUint64(t *testing.T) { 63 tests := []struct { 64 x, y uint64 65 want bool 66 }{ 67 {0, 0, false}, 68 {0xFFFFFFFF, 0xFFFFFFFF, false}, 69 {0, 1, false}, 70 {0xFFFFFFFE, 0xFFFFFFFF, false}, 71 {0, 0xFFFFFFFFFFFFFFFF, true}, 72 {0xFFFFFFFFFFFFFFFF, 0, false}, 73 {0, 0x7FFFFFFFFFFFFFFF, false}, 74 {0, 0x7FFFFFFFFFFFFFFE, false}, 75 {0, 0x8000000000000000, false}, 76 {0x7FFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, true}, 77 {0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, false}, 78 {0x7FFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFE, true}, 79 {0x7FFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFE, false}, 80 } 81 for _, tt := range tests { 82 t.Run(fmt.Sprintf("math_.IsNewer(%v, %v)", tt.x, tt.y), func(t *testing.T) { 83 { 84 got := math_.IsNewer(tt.x, tt.y) 85 if got != tt.want { 86 t.Errorf("math_.IsNewer(%v, %v) = %v, want %v", tt.x, tt.y, got, tt.want) 87 } 88 } 89 }) 90 } 91 } 92 93 func TestDistance(t *testing.T) { 94 tests := []struct { 95 x, y uint16 96 want int16 97 }{ 98 {0, 0, 0}, 99 {0x7FFF, 0, 32767}, 100 {0x0001, 0, 1}, 101 {0x0000, 0, 0}, 102 {0xFFFF, 0, -1}, 103 {0xFFFE, 0, -2}, 104 {0x8000, 0, -32768}, 105 } 106 for _, tt := range tests { 107 t.Run(fmt.Sprintf("math_.distance(%v, %v)", tt.x, tt.y), func(t *testing.T) { 108 { 109 got := int16(distance(tt.x, tt.y)) 110 if got != tt.want { 111 t.Errorf("math_.distance(%v, %v) = %v, want %v", tt.x, tt.y, got, tt.want) 112 } 113 } 114 }) 115 } 116 } 117 118 func TestUnwrap(t *testing.T) { 119 tests := []struct { 120 last int64 121 value uint8 122 want int64 123 }{ 124 {0, 0, 0}, 125 {255, 255, 255}, 126 {255, 0, 256}, 127 {255, 1, 257}, 128 {126, 255, 255}, 129 {127, 255, 255}, 130 {125, 254, 254}, 131 {126, 254, 254}, 132 {125, 0, 0}, 133 {126, 0, 0}, 134 {127, 0, 0}, 135 {128, 0, 0}, 136 {129, 0, 256}, 137 {256, 0, 256}, 138 {257, 0, 256}, 139 {255, 0, 256}, 140 {256, 0, 256}, 141 {256, 1, 257}, 142 {256, 2, 258}, 143 {256, 3, 259}, 144 {512, 0, 512}, 145 {512, 1, 513}, 146 {512, 2, 514}, 147 {512, 3, 515}, 148 } 149 for _, tt := range tests { 150 t.Run(fmt.Sprintf("math_.Unwrap(%v, %v)", tt.last, tt.value), func(t *testing.T) { 151 { 152 got := math_.Unwrap(tt.last, tt.value) 153 var u = math_.Unwrapper[uint8]{} 154 u.UpdateLast(tt.last) 155 got = u.Unwrap(tt.value) 156 if got != tt.want { 157 t.Errorf("math_.Unwrap(%v, %v) = %v, want %v", tt.last, tt.value, got, tt.want) 158 } 159 } 160 }) 161 } 162 if err := quick.CheckEqual(math_.Unwrap[uint8], func(lastValue int64, value uint8) int64 { 163 var u = math_.Unwrapper[uint8]{} 164 u.UpdateLast(lastValue) 165 return u.Unwrap(value) 166 }, nil); err != nil { 167 t.Error(err) 168 } 169 } 170 171 // IsNewer implements RFC 1982: Serial Number Arithmetic 172 // See also: https://datatracker.ietf.org/doc/html/rfc1982#section-2 173 // s1 < s2 and (s1 + 1) > (s2 + 1) 174 // See also: https://chromium.googlesource.com/external/webrtc/trunk/webrtc/+/f54860e9ef0b68e182a01edc994626d21961bc4b/modules/include/module_common_types.h 175 func checkIsNewer[T constraints.Unsigned](value T, preValue T) (newer bool) { 176 // kBreakpoint is the half-way mark for the type U. For instance, for a 177 // uint16_t it will be 0x8000, and for a uint32_t, it will be 0x8000000. 178 kBreakpoint := (math_.MaxInt[T]() >> 1) + 1 179 // Distinguish between elements that are exactly kBreakpoint apart. 180 // If t1>t2 and |t1-t2| = kBreakpoint: IsNewer(t1,t2)=true, 181 // IsNewer(t2,t1)=false 182 // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false. 183 if value-preValue == kBreakpoint { 184 return value > preValue 185 } 186 return (value != preValue) && (T(distance(value, preValue)) < kBreakpoint) 187 } 188 189 // distance = (signed)(i1 - i2) 190 // If distance is 0, the numbers are equal. 191 // If it is < 0, then s1 is "less than" or "before" s2. 192 // Simple, clean and efficient, and fully defined. However, not without surprises. 193 func distance[T constraints.Unsigned](s1, s2 T) int64 { 194 return int64(s1) - int64(s2) 195 }