diff --git a/common/coords.go b/common/coords.go index 0ab4585..bc1d913 100644 --- a/common/coords.go +++ b/common/coords.go @@ -6,6 +6,7 @@ package common import ( "encoding/binary" + "fmt" "strconv" ) @@ -16,13 +17,25 @@ const ( ) type ( + Coord struct { + X, Y, Z int16 + } + KeyTransformer func(int64) int64 KeyEncoder func(int64) ([]byte, error) KeyDecoder func([]byte) (int64, error) - KeySplitter func(int64) (int16, int16, int16) - KeyJoiner func(int16, int16, int16) int64 + KeySplitter func(int64) Coord + KeyJoiner func(Coord) int64 ) +func (c Coord) String() string { + return fmt.Sprintf("(%d, %d, %d)", c.X, c.Y, c.Z) +} + +func (c Coord) Equals(o Coord) bool { + return c.X == o.X && c.Y == o.Y && c.Z == o.Z +} + // Constructs a database key out of byte slice. func DecodeStringFromBytes(key []byte) (pos int64, err error) { return strconv.ParseInt(string(key), 10, 64) @@ -43,19 +56,19 @@ func DecodeFromBigEndian(key []byte) (int64, error) { return int64(binary.BigEndian.Uint64(key)), nil } -func XYZToInterleaved(x, y, z int16) (result int64) { +func CoordToInterleaved(c Coord) (result int64) { const end = 1 << (numBitsPerComponent + 1) setmask := int64(1) for mask := int16(1); mask != end; mask <<= 1 { - if x&mask != 0 { + if c.X&mask != 0 { result |= setmask } setmask <<= 1 - if y&mask != 0 { + if c.Y&mask != 0 { result |= setmask } setmask <<= 1 - if z&mask != 0 { + if c.Z&mask != 0 { result |= setmask } setmask <<= 1 @@ -63,21 +76,22 @@ func XYZToInterleaved(x, y, z int16) (result int64) { return } -func InterleavedToXYZ(c int64) (x, y, z int16) { +func InterleavedToCoord(pos int64) Coord { const end = 1 << (numBitsPerComponent + 1) + var x, y, z int16 for mask := int16(1); mask != end; mask <<= 1 { - if c&1 == 1 { + if pos&1 == 1 { x |= mask } - c >>= 1 - if c&1 == 1 { + pos >>= 1 + if pos&1 == 1 { y |= mask } - c >>= 1 - if c&1 == 1 { + pos >>= 1 + if pos&1 == 1 { z |= mask } - c >>= 1 + pos >>= 1 } if x >= 1<= 1<> numBitsPerComponent - y = unsignedToSigned(pythonModulo(int16(i))) - i = (i - int64(y)) >> numBitsPerComponent - z = unsignedToSigned(pythonModulo(int16(i))) +func PlainToCoord(i int64) (c Coord) { + c.X = unsignedToSigned(pythonModulo(int16(i))) + i = (i - int64(c.X)) >> numBitsPerComponent + c.Y = unsignedToSigned(pythonModulo(int16(i))) + i = (i - int64(c.Y)) >> numBitsPerComponent + c.Z = unsignedToSigned(pythonModulo(int16(i))) return } func TransformPlainToInterleaved(pos int64) int64 { - x, y, z := PlainToXYZ(pos) - return XYZToInterleaved(x, y, z) + return CoordToInterleaved(PlainToCoord(pos)) } func TransformInterleavedPlain(pos int64) int64 { - x, y, z := InterleavedToXYZ(pos) - return XYZToPlain(x, y, z) + return CoordToPlain(InterleavedToCoord(pos)) } diff --git a/common/coords_test.go b/common/coords_test.go index 84e32c9..9ff5f4a 100644 --- a/common/coords_test.go +++ b/common/coords_test.go @@ -8,11 +8,11 @@ var data = []int16{ -150, -1, 0, 1, 88, 524, 527, 549, 1783, 1817, 1826, 2028, 2032} -func allData(f func(int16, int16, int16)) { +func allData(f func(Coord)) { for _, z := range data { for _, y := range data { for _, x := range data { - f(x, y, z) + f(Coord{X: x, Y: y, Z: z}) } } } @@ -22,84 +22,79 @@ func checkEncodeDecode( desc string, join KeyJoiner, encode KeyEncoder, decode KeyDecoder, - x, y, z int16, t *testing.T) { + c Coord, t *testing.T) { - k1 := join(x, y, z) + k1 := join(c) var err error var b []byte if b, err = encode(k1); err != nil { - t.Errorf("%s: Failed to encode (%d, %d, %d) %s\n", - desc, x, y, z, err) + t.Errorf("%s: Failed to encode %s %s\n", desc, c, err) return } var k2 int64 if k2, err = decode(b); err != nil { - t.Errorf("%s: Failed to decode (%d, %d, %d) %s\n", - desc, x, y, z, err) + t.Errorf("%s: Failed to decode %s %s\n", desc, c, err) return } if k1 != k2 { - t.Errorf("%s: Expected %d got %d for (%d, %d, %d) %b\n", - desc, k1, k2, x, y, z) + t.Errorf("%s: Expected %d got %d for %s\n", desc, k1, k2, c) } } func TestEncodeDecode(t *testing.T) { - allData(func(x, y, z int16) { + allData(func(c Coord) { checkEncodeDecode( "Big endian", - XYZToInterleaved, + CoordToInterleaved, EncodeToBigEndian, DecodeFromBigEndian, - x, y, z, t) + c, t) }) - allData(func(x, y, z int16) { + allData(func(c Coord) { checkEncodeDecode( "String", - XYZToInterleaved, + CoordToInterleaved, EncodeStringToBytes, DecodeStringFromBytes, - x, y, z, t) + c, t) }) } func checkJoinSplit( desc string, join KeyJoiner, split KeySplitter, - x1, y1, z1 int16, t *testing.T) { + c Coord, t *testing.T) { - k1 := join(x1, y1, z1) - x2, y2, z2 := split(k1) - if x1 != x2 || y1 != y2 || z1 != z2 { - t.Errorf("%s: Expected (%d, %d, %d) got (%d, %d, %d) %b\n", - desc, x1, y1, z1, x2, y2, z2, k1) + k := join(c) + s := split(k) + if !s.Equals(c) { + t.Errorf("%s: Expected %s got %s %b\n", desc, c, s, k) } } func TestJoinSplit(t *testing.T) { - allData(func(x, y, z int16) { + allData(func(c Coord) { checkJoinSplit( - "P2XYZ(XYZ2P(xyz))", - XYZToPlain, PlainToXYZ, - x, y, z, t) + "P2C(C2P(xyz))", + CoordToPlain, PlainToCoord, + c, t) }) - allData(func(x, y, z int16) { + allData(func(c Coord) { checkJoinSplit( - "I2XYZ(XYZ2I(xyz))", - XYZToInterleaved, InterleavedToXYZ, - x, y, z, t) + "I2C(C2I(xyz))", + CoordToInterleaved, InterleavedToCoord, + c, t) }) } func checkTransformer( desc string, joiner KeyJoiner, transform KeyTransformer, - x, y, z int16, t *testing.T) { + c Coord, t *testing.T) { - k1 := joiner(x, y, z) + k1 := joiner(c) k2 := transform(k1) if k2 != k1 { - t.Errorf("%s: Expected %v got %v for (%d, %d, %d)\n", - desc, k1, k2, x, y, z) + t.Errorf("%s: Expected %v got %v for %s\n", desc, k1, k2, c) } } @@ -114,25 +109,25 @@ func compose(transforms ...KeyTransformer) KeyTransformer { func TestTransforms(t *testing.T) { // Mainly to check the test itself. - allData(func(x, y, z int16) { + allData(func(c Coord) { checkTransformer( "plain", - XYZToPlain, + CoordToPlain, compose(), - x, y, z, t) + c, t) }) - allData(func(x, y, z int16) { + allData(func(c Coord) { checkTransformer( "I2P(P2I(plain))", - XYZToPlain, + CoordToPlain, compose(TransformPlainToInterleaved, TransformInterleavedPlain), - x, y, z, t) + c, t) }) - allData(func(x, y, z int16) { + allData(func(c Coord) { checkTransformer( "P2I(I2P(interleaved))", - XYZToInterleaved, + CoordToInterleaved, compose(TransformInterleavedPlain, TransformPlainToInterleaved), - x, y, z, t) + c, t) }) }