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  }