github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/prolly/tree/z_encoding_test.go (about) 1 // Copyright 2023 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tree 16 17 import ( 18 "bytes" 19 "encoding/binary" 20 "encoding/hex" 21 "fmt" 22 "math" 23 "math/rand" 24 "sort" 25 "testing" 26 27 "github.com/dolthub/go-mysql-server/sql/expression/function/spatial" 28 "github.com/dolthub/go-mysql-server/sql/types" 29 assert "github.com/stretchr/testify/require" 30 ) 31 32 // these are sorted 33 var ps = []types.Point{ 34 {X: -2, Y: -2}, // 0 35 {X: -1, Y: -2}, 36 {X: -2, Y: -1}, 37 {X: -1, Y: -1}, 38 {X: 0, Y: -2}, // 4 39 {X: 1, Y: -2}, 40 {X: 2, Y: -2}, 41 {X: 0, Y: -1}, 42 {X: 1, Y: -1}, // 8 43 {X: 2, Y: -1}, 44 {X: -2, Y: 0}, 45 {X: -2, Y: 1}, 46 {X: -1, Y: 0}, // 12 47 {X: -1, Y: 1}, 48 {X: -2, Y: 2}, 49 {X: -1, Y: 2}, 50 {X: 0, Y: 0}, // 16 51 {X: 1, Y: 0}, 52 {X: 0, Y: 1}, 53 {X: 1, Y: 1}, 54 {X: 2, Y: 0}, // 20 55 {X: 2, Y: 1}, 56 {X: 0, Y: 2}, 57 {X: 1, Y: 2}, 58 {X: 2, Y: 2}, // 24 59 } 60 61 func TestLexFloat(t *testing.T) { 62 t.Run("test edge case lex float values", func(t *testing.T) { 63 assert.Equal(t, uint64(0x0010000000000000), LexFloat(-math.MaxFloat64)) 64 assert.Equal(t, uint64(0x7ffffffffffffffe), LexFloat(-math.SmallestNonzeroFloat64)) 65 assert.Equal(t, uint64(0x8000000000000000), LexFloat(0.0)) 66 assert.Equal(t, uint64(0x8000000000000001), LexFloat(math.SmallestNonzeroFloat64)) 67 assert.Equal(t, uint64(0xffefffffffffffff), LexFloat(math.MaxFloat64)) 68 assert.Equal(t, uint64(0xfff8000000000001), LexFloat(math.NaN())) 69 assert.Equal(t, uint64(0x0007fffffffffffe), LexFloat(-math.NaN())) 70 assert.Equal(t, uint64(0xfff0000000000000), LexFloat(math.Inf(1))) 71 assert.Equal(t, uint64(0x000fffffffffffff), LexFloat(math.Inf(-1))) 72 }) 73 74 t.Run("test reverse lex float values", func(t *testing.T) { 75 assert.Equal(t, -math.MaxFloat64, UnLexFloat(0x0010000000000000)) 76 assert.Equal(t, -math.SmallestNonzeroFloat64, UnLexFloat(0x7ffffffffffffffe)) 77 assert.Equal(t, 0.0, UnLexFloat(0x8000000000000000)) 78 assert.Equal(t, math.SmallestNonzeroFloat64, UnLexFloat(0x8000000000000001)) 79 assert.Equal(t, math.MaxFloat64, UnLexFloat(0xffefffffffffffff)) 80 assert.True(t, math.IsNaN(UnLexFloat(0xfff8000000000001))) 81 assert.True(t, math.IsNaN(UnLexFloat(0xfff7fffffffffffe))) 82 assert.True(t, math.IsInf(UnLexFloat(0xfff0000000000000), 1)) 83 assert.True(t, math.IsInf(UnLexFloat(0x000fffffffffffff), -1)) 84 }) 85 86 t.Run("test sort lex float", func(t *testing.T) { 87 // math.NaN not here, because NaN != NaN 88 sortedFloats := []float64{ 89 math.Inf(-1), 90 -math.MaxFloat64, 91 -1.0, 92 -0.5, 93 -0.123456789, 94 -math.SmallestNonzeroFloat64, 95 -0.0, 96 0.0, 97 math.SmallestNonzeroFloat64, 98 0.5, 99 0.987654321, 100 1.0, 101 math.MaxFloat64, 102 math.Inf(1), 103 } 104 105 randFloats := append([]float64{}, sortedFloats...) 106 rand.Shuffle(len(randFloats), func(i, j int) { 107 randFloats[i], randFloats[j] = randFloats[j], randFloats[i] 108 }) 109 sort.Slice(randFloats, func(i, j int) bool { 110 l1 := LexFloat(randFloats[i]) 111 l2 := LexFloat(randFloats[j]) 112 return l1 < l2 113 }) 114 assert.Equal(t, sortedFloats, randFloats) 115 }) 116 } 117 118 func TestZValue(t *testing.T) { 119 tests := []struct { 120 p types.Point 121 e string 122 }{ 123 { 124 p: types.Point{X: -5000, Y: -5000}, 125 e: "0fff30f03f3fffffffffffffffffffff", 126 }, 127 { 128 p: types.Point{X: -1, Y: -1}, 129 e: "300000ffffffffffffffffffffffffff", 130 }, 131 { 132 p: types.Point{X: -1, Y: 0}, 133 e: "90000055555555555555555555555555", 134 }, 135 { 136 p: types.Point{X: -1, Y: 1}, 137 e: "9aaaaa55555555555555555555555555", 138 }, 139 { 140 p: types.Point{X: 0, Y: -1}, 141 e: "600000aaaaaaaaaaaaaaaaaaaaaaaaaa", 142 }, 143 { 144 p: types.Point{X: 1, Y: -1}, 145 e: "655555aaaaaaaaaaaaaaaaaaaaaaaaaa", 146 }, 147 { 148 p: types.Point{X: 0, Y: 0}, 149 e: "c0000000000000000000000000000000", 150 }, 151 { 152 p: types.Point{X: 1, Y: 0}, 153 e: "c5555500000000000000000000000000", 154 }, 155 { 156 p: types.Point{X: 0, Y: 1}, 157 e: "caaaaa00000000000000000000000000", 158 }, 159 { 160 p: types.Point{X: 1, Y: 1}, 161 e: "cfffff00000000000000000000000000", 162 }, 163 { 164 p: types.Point{X: 2, Y: 2}, 165 e: "f0000000000000000000000000000000", 166 }, 167 { 168 p: types.Point{X: 50000, Y: 50000}, 169 e: "f000fcc03ccc00000000000000000000", 170 }, 171 } 172 173 t.Run("test z-values", func(t *testing.T) { 174 for _, test := range tests { 175 z := ZValue(test.p) 176 assert.Equal(t, test.e, fmt.Sprintf("%016x%016x", z[0], z[1])) 177 } 178 }) 179 180 t.Run("test un-z-values", func(t *testing.T) { 181 for _, test := range tests { 182 v, _ := hex.DecodeString(test.e) 183 z := [2]uint64{} 184 z[0] = binary.BigEndian.Uint64(v[:8]) 185 z[1] = binary.BigEndian.Uint64(v[8:]) 186 assert.Equal(t, test.p, UnZValue(z)) 187 } 188 }) 189 190 t.Run("test sorting points by z-value", func(t *testing.T) { 191 sortedPoints := []types.Point{ 192 {X: -5000, Y: -5000}, 193 {X: -1, Y: -1}, 194 {X: 1, Y: -1}, 195 {X: -1, Y: 0}, 196 {X: -1, Y: 1}, 197 {X: 0, Y: 0}, 198 {X: 1, Y: 0}, 199 {X: 1, Y: 1}, 200 {X: 2, Y: 2}, 201 {X: 100, Y: 100}, 202 } 203 randPoints := append([]types.Point{}, sortedPoints...) 204 rand.Shuffle(len(randPoints), func(i, j int) { 205 randPoints[i], randPoints[j] = randPoints[j], randPoints[i] 206 }) 207 sort.Slice(randPoints, func(i, j int) bool { 208 z1 := ZValue(randPoints[i]) 209 z2 := ZValue(randPoints[j]) 210 if z1[0] != z2[0] { 211 return z1[0] < z2[0] 212 } 213 return z1[1] < z2[1] 214 }) 215 assert.Equal(t, sortedPoints, randPoints) 216 }) 217 218 t.Run("test sorting many points by z-value", func(t *testing.T) { 219 randPoints := append([]types.Point{}, ps...) 220 rand.Shuffle(len(randPoints), func(i, j int) { 221 randPoints[i], randPoints[j] = randPoints[j], randPoints[i] 222 }) 223 sort.Slice(randPoints, func(i, j int) bool { 224 z1 := ZValue(randPoints[i]) 225 z2 := ZValue(randPoints[j]) 226 if z1[0] != z2[0] { 227 return z1[0] < z2[0] 228 } 229 return z1[1] < z2[1] 230 }) 231 assert.Equal(t, ps, randPoints) 232 }) 233 } 234 235 func TestZCell(t *testing.T) { 236 t.Run("test points ZCell", func(t *testing.T) { 237 p := types.Point{X: 1, Y: 2} 238 res := ZCell(p) 239 assert.Equal(t, "00e5555500000000000000000000000000", hex.EncodeToString(res[:])) 240 }) 241 242 t.Run("test linestring ZCell", func(t *testing.T) { 243 a := types.Point{X: 1, Y: 1} 244 b := types.Point{X: 2, Y: 2} 245 c := types.Point{X: 3, Y: 3} 246 l := types.LineString{Points: []types.Point{a, b, c}} 247 res := ZCell(l) 248 assert.Equal(t, "3fc0000000000000000000000000000000", hex.EncodeToString(res[:])) 249 }) 250 251 t.Run("test polygon ZCell", func(t *testing.T) { 252 a := types.Point{X: -1, Y: 1} 253 b := types.Point{X: 1, Y: 1} 254 c := types.Point{X: 1, Y: -1} 255 d := types.Point{X: -1, Y: -1} 256 l := types.LineString{Points: []types.Point{a, b, c, d, a}} 257 p := types.Polygon{Lines: []types.LineString{l}} 258 res := ZCell(p) 259 assert.Equal(t, "4000000000000000000000000000000000", hex.EncodeToString(res[:])) 260 }) 261 262 t.Run("test low level linestring", func(t *testing.T) { 263 line := types.LineString{Points: []types.Point{ 264 {X: 0, Y: 0}, 265 {X: math.SmallestNonzeroFloat64, Y: math.SmallestNonzeroFloat64}, 266 }} 267 poly := types.Polygon{Lines: []types.LineString{line}} 268 z := ZCell(poly) 269 assert.Equal(t, "01c0000000000000000000000000000000", hex.EncodeToString(z[:])) 270 }) 271 272 t.Run("test high level linestring", func(t *testing.T) { 273 line := types.LineString{Points: []types.Point{ 274 {X: -1, Y: -1}, 275 {X: 1, Y: 1}, 276 }} 277 poly := types.Polygon{Lines: []types.LineString{line}} 278 z := ZCell(poly) 279 assert.Equal(t, "4000000000000000000000000000000000", hex.EncodeToString(z[:])) 280 }) 281 282 t.Run("test sorting many points by z-cell", func(t *testing.T) { 283 sortedGeoms := make([]types.GeometryValue, len(ps)) 284 for i, p := range ps { 285 sortedGeoms[i] = p 286 } 287 randGeoms := append([]types.GeometryValue{}, sortedGeoms...) 288 rand.Shuffle(len(randGeoms), func(i, j int) { 289 randGeoms[i], randGeoms[j] = randGeoms[j], randGeoms[i] 290 }) 291 sort.Slice(randGeoms, func(i, j int) bool { 292 zi, zj := ZCell(randGeoms[i]), ZCell(randGeoms[j]) 293 return bytes.Compare(zi[:], zj[:]) < 0 294 }) 295 assert.Equal(t, sortedGeoms, randGeoms) 296 }) 297 298 t.Run("test sorting linestring by z-cell", func(t *testing.T) { 299 sortedLines := []types.GeometryValue{ 300 types.LineString{Points: []types.Point{ps[24], ps[24]}}, 301 types.LineString{Points: []types.Point{ps[16], ps[19]}}, 302 types.LineString{Points: []types.Point{ps[0], ps[3]}}, 303 types.LineString{Points: []types.Point{ps[19], ps[24]}}, 304 types.LineString{Points: []types.Point{ps[3], ps[19]}}, 305 } 306 randPoints := append([]types.GeometryValue{}, sortedLines...) 307 rand.Shuffle(len(randPoints), func(i, j int) { 308 randPoints[i], randPoints[j] = randPoints[j], randPoints[i] 309 }) 310 sort.Slice(randPoints, func(i, j int) bool { 311 zi, zj := ZCell(randPoints[i]), ZCell(randPoints[j]) 312 return bytes.Compare(zi[:], zj[:]) < 0 313 }) 314 assert.Equal(t, sortedLines, randPoints) 315 }) 316 } 317 318 var testZVals = []ZVal{ 319 {0, 0}, // (0, 0) 320 {0, 1}, // (1, 0) 321 {0, 2}, // (0, 1) 322 {0, 3}, // (1, 1) 323 {0, 4}, // (2, 0) 324 {0, 5}, // (3, 0) 325 {0, 6}, // (2, 1) 326 {0, 7}, // (3, 1) 327 {0, 8}, // (0, 2) 328 {0, 9}, // (1, 2) 329 {0, 10}, // (0, 3) 330 {0, 11}, // (1, 3) 331 {0, 12}, // (2, 2) 332 {0, 13}, // (3, 2) 333 {0, 14}, // (2, 3) 334 {0, 15}, // (3, 3) 335 {0, 16}, // (4, 0) 336 } 337 338 func TestSplitZRanges(t *testing.T) { 339 t.Run("split point z-range", func(t *testing.T) { 340 zRange := ZRange{testZVals[0], testZVals[0]} // (0, 0) -> (0, 0) 341 zRanges := SplitZRanges(zRange) 342 assert.Equal(t, []ZRange{zRange}, zRanges) 343 344 zRange = ZRange{testZVals[1], testZVals[1]} // (1, 0) -> (1, 0) 345 zRanges = SplitZRanges(zRange) 346 assert.Equal(t, []ZRange{zRange}, zRanges) 347 348 zRange = ZRange{testZVals[2], testZVals[2]} // (0, 1) -> (0, 1) 349 zRanges = SplitZRanges(zRange) 350 assert.Equal(t, []ZRange{zRange}, zRanges) 351 352 zRange = ZRange{testZVals[3], testZVals[3]} // (1, 1) -> (1, 1) 353 zRanges = SplitZRanges(zRange) 354 assert.Equal(t, []ZRange{zRange}, zRanges) 355 }) 356 357 t.Run("split continuous z-ranges", func(t *testing.T) { 358 zRange := ZRange{testZVals[0], testZVals[1]} // (0, 0) -> (1, 0) 359 zRanges := SplitZRanges(zRange) 360 assert.Equal(t, []ZRange{zRange}, zRanges) 361 362 zRange = ZRange{testZVals[0], testZVals[3]} // (0, 0) -> (1, 1) 363 zRanges = SplitZRanges(zRange) 364 assert.Equal(t, []ZRange{zRange}, zRanges) 365 }) 366 367 t.Run("split small non-continuous z-ranges", func(t *testing.T) { 368 zRange := ZRange{testZVals[0], testZVals[2]} // (0, 0) -> (0, 1) 369 zRanges := SplitZRanges(zRange) 370 assert.Equal(t, []ZRange{{testZVals[0], testZVals[0]}, {testZVals[2], testZVals[2]}}, zRanges) 371 }) 372 373 t.Run("split small non-continuous z-range that should have a merge", func(t *testing.T) { 374 zRange := ZRange{testZVals[0], testZVals[6]} // (0, 0) -> (2, 1) 375 zRanges := SplitZRanges(zRange) 376 assert.Equal(t, []ZRange{{testZVals[0], testZVals[4]}, {testZVals[6], testZVals[6]}}, zRanges) 377 }) 378 379 t.Run("split x-axis bbox", func(t *testing.T) { 380 zRange := ZRange{{0, 0}, {0, 16}} // (0, 0) -> (4, 0) 381 zRanges := SplitZRanges(zRange) 382 assert.Equal(t, []ZRange{{testZVals[0], testZVals[1]}, {testZVals[4], testZVals[5]}, {testZVals[16], testZVals[16]}}, zRanges) 383 }) 384 385 t.Run("split y-axis bbox", func(t *testing.T) { 386 zRange := ZRange{{0, 0}, {0, 32}} // (0, 0) -> (0, 2) 387 zRanges := SplitZRanges(zRange) 388 res := []ZRange{ 389 {{0, 0}, {0, 0}}, 390 {{0, 2}, {0, 2}}, 391 {{0, 8}, {0, 8}}, 392 {{0, 10}, {0, 10}}, 393 {{0, 32}, {0, 32}}, 394 } 395 assert.Equal(t, res, zRanges) 396 }) 397 398 t.Run("split medium x-axis bbox", func(t *testing.T) { 399 zRange := ZRange{{0, 0}, {0, uint64(1 << 42)}} // (0, 0) -> (2^21, 0) 400 zRanges := SplitZRanges(zRange) 401 assert.Equal(t, 5, len(zRanges)) 402 }) 403 404 t.Run("split medium y-axis bbox", func(t *testing.T) { 405 zRange := ZRange{{0, 0}, {0, uint64(1 << 43)}} // (0, 0) -> (0, 2^21) 406 zRanges := SplitZRanges(zRange) 407 assert.Equal(t, 5, len(zRanges)) 408 }) 409 410 t.Run("split x-axis bbox", func(t *testing.T) { 411 zRange := ZRange{{0, 0x0B}, {0, 0x25}} // (1, 3) -> (3, 4) 412 zRanges := SplitZRanges(zRange) 413 res := []ZRange{ 414 {{0, 0x0B}, {0, 0x0B}}, 415 {{0, 0x0E}, {0, 0x0F}}, 416 {{0, 0x21}, {0, 0x21}}, 417 {{0, 0x24}, {0, 0x25}}, 418 } 419 assert.Equal(t, res, zRanges) 420 }) 421 422 t.Run("split large x-axis bbox", func(t *testing.T) { 423 zRange := ZRange{{0, 0}, {1, 0}} // (0, 0) -> (2^33, 0) 424 zRanges := SplitZRanges(zRange) 425 assert.Equal(t, 5, len(zRanges)) 426 }) 427 428 t.Run("split large y-axis bbox", func(t *testing.T) { 429 zRange := ZRange{{0, 0}, {2, 0}} // (0, 0) -> (0, 2^66) 430 zRanges := SplitZRanges(zRange) 431 assert.Equal(t, 5, len(zRanges)) 432 }) 433 434 t.Run("split seattle bbox range", func(t *testing.T) { 435 poly := types.Polygon{Lines: []types.LineString{{Points: []types.Point{ 436 {X: -122.48, Y: 47.41}, 437 {X: -122.48, Y: 47.79}, 438 {X: -122.16, Y: 47.79}, 439 {X: -122.16, Y: 47.41}, 440 {X: -122.48, Y: 47.41}, 441 }}}} 442 bbox := spatial.FindBBox(poly) 443 zMin := ZValue(types.Point{X: bbox[0], Y: bbox[1]}) 444 zMax := ZValue(types.Point{X: bbox[2], Y: bbox[3]}) 445 zRange := ZRange{zMin, zMax} 446 zRanges := SplitZRanges(zRange) 447 assert.Equal(t, 3, len(zRanges)) 448 }) 449 450 t.Run("test tiny dynamic z-ranges", func(t *testing.T) { 451 poly := types.Polygon{Lines: []types.LineString{{Points: []types.Point{ 452 {X: 2, Y: 2}, 453 {X: 2, Y: 2.000001}, 454 {X: 2.000001, Y: 2.000001}, 455 {X: 2.000001, Y: 2}, 456 {X: 2, Y: 2}, 457 }}}} 458 bbox := spatial.FindBBox(poly) 459 zMin := ZValue(types.Point{X: bbox[0], Y: bbox[1]}) 460 zMax := ZValue(types.Point{X: bbox[2], Y: bbox[3]}) 461 zRange := ZRange{zMin, zMax} 462 zRanges := SplitZRanges(zRange) 463 assert.Equal(t, 4, len(zRanges)) 464 }) 465 466 t.Run("test small dynamic z-ranges", func(t *testing.T) { 467 poly := types.Polygon{Lines: []types.LineString{{Points: []types.Point{ 468 {X: 2, Y: 2}, 469 {X: 2, Y: 4}, 470 {X: 4, Y: 4}, 471 {X: 4, Y: 2}, 472 {X: 2, Y: 2}, 473 }}}} 474 bbox := spatial.FindBBox(poly) 475 zMin := ZValue(types.Point{X: bbox[0], Y: bbox[1]}) 476 zMax := ZValue(types.Point{X: bbox[2], Y: bbox[3]}) 477 zRange := ZRange{zMin, zMax} 478 zRanges := SplitZRanges(zRange) 479 assert.Equal(t, 4, len(zRanges)) 480 }) 481 482 t.Run("test medium dynamic z-ranges", func(t *testing.T) { 483 poly := types.Polygon{Lines: []types.LineString{{Points: []types.Point{ 484 {X: 2, Y: 2}, 485 {X: 2, Y: 128}, 486 {X: 128, Y: 128}, 487 {X: 128, Y: 2}, 488 {X: 2, Y: 2}, 489 }}}} 490 bbox := spatial.FindBBox(poly) 491 zMin := ZValue(types.Point{X: bbox[0], Y: bbox[1]}) 492 zMax := ZValue(types.Point{X: bbox[2], Y: bbox[3]}) 493 zRange := ZRange{zMin, zMax} 494 zRanges := SplitZRanges(zRange) 495 assert.Equal(t, 5, len(zRanges)) 496 }) 497 498 t.Run("test degenerate range", func(t *testing.T) { 499 poly := types.Polygon{Lines: []types.LineString{{Points: []types.Point{ 500 {X: -1, Y: -1}, 501 {X: -1, Y: 1}, 502 {X: 1, Y: 1}, 503 {X: 1, Y: -1}, 504 {X: 1, Y: 1}, 505 }}}} 506 bbox := spatial.FindBBox(poly) 507 zMin := ZValue(types.Point{X: bbox[0], Y: bbox[1]}) 508 zMax := ZValue(types.Point{X: bbox[2], Y: bbox[3]}) 509 zRange := ZRange{zMin, zMax} 510 zRanges := SplitZRanges(zRange) 511 assert.Equal(t, 4, len(zRanges)) 512 }) 513 }