package tsm1_test

import (
	"fmt"
	"math/rand"
	"reflect"
	"testing"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/google/go-cmp/cmp"
	"github.com/influxdata/influxdb/v2/tsdb/tsm1"
)

func TestEncoding_FloatBlock(t *testing.T) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, float64(i))
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if !reflect.DeepEqual(decodedValues, values) {
		t.Fatalf("unexpected results:\n\tgot: %s\n\texp: %s\n", spew.Sdump(decodedValues), spew.Sdump(values))
	}
}

func TestEncoding_FloatBlock_ZeroTime(t *testing.T) {
	values := make([]tsm1.Value, 3)
	for i := 0; i < 3; i++ {
		values[i] = tsm1.NewValue(0, float64(i))
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if !reflect.DeepEqual(decodedValues, values) {
		t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values)
	}
}

func TestEncoding_FloatBlock_SimilarFloats(t *testing.T) {
	values := make([]tsm1.Value, 5)
	values[0] = tsm1.NewValue(1444238178437870000, 6.00065e+06)
	values[1] = tsm1.NewValue(1444238185286830000, 6.000656e+06)
	values[2] = tsm1.NewValue(1444238188441501000, 6.000657e+06)
	values[3] = tsm1.NewValue(1444238195286811000, 6.000659e+06)
	values[4] = tsm1.NewValue(1444238198439917000, 6.000661e+06)

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if !reflect.DeepEqual(decodedValues, values) {
		t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values)
	}
}

func TestEncoding_IntBlock_Basic(t *testing.T) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, int64(i))
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if len(decodedValues) != len(values) {
		t.Fatalf("unexpected results length:\n\tgot: %v\n\texp: %v\n", len(decodedValues), len(values))
	}

	for i := 0; i < len(decodedValues); i++ {
		if decodedValues[i].UnixNano() != values[i].UnixNano() {
			t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues[i].UnixNano(), values[i].UnixNano())
		}

		if decodedValues[i].Value() != values[i].Value() {
			t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues[i].Value(), values[i].Value())
		}
	}
}

func TestEncoding_IntBlock_Negatives(t *testing.T) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		v := int64(i)
		if i%2 == 0 {
			v = -v
		}
		values[i] = tsm1.NewValue(t, int64(v))
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if !reflect.DeepEqual(decodedValues, values) {
		t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values)
	}
}

func TestEncoding_UIntBlock_Basic(t *testing.T) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, uint64(i))
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if len(decodedValues) != len(values) {
		t.Fatalf("unexpected results length:\n\tgot: %v\n\texp: %v\n", len(decodedValues), len(values))
	}

	for i := 0; i < len(decodedValues); i++ {
		if decodedValues[i].UnixNano() != values[i].UnixNano() {
			t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues[i].UnixNano(), values[i].UnixNano())
		}

		if decodedValues[i].Value() != values[i].Value() {
			t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues[i].Value(), values[i].Value())
		}
	}
}

// TestEncoding_UIntBlock_MaxValues encodes uint64 numbers starting at max (18446744073709551615)
// down to 18446744073709550616
func TestEncoding_UIntBlock_MaxValues(t *testing.T) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, ^uint64(i))
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if !reflect.DeepEqual(decodedValues, values) {
		t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values)
	}
}

func TestEncoding_BooleanBlock_Basic(t *testing.T) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		v := true
		if i%2 == 0 {
			v = false
		}
		values[i] = tsm1.NewValue(t, v)
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if !reflect.DeepEqual(decodedValues, values) {
		t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values)
	}
}

func TestEncoding_StringBlock_Basic(t *testing.T) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i))
	}

	b, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	decodedValues, err = tsm1.DecodeBlock(b, decodedValues)
	if err != nil {
		t.Fatalf("unexpected error decoding block: %v", err)
	}

	if !reflect.DeepEqual(decodedValues, values) {
		t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values)
	}
}

func TestEncoding_BlockType(t *testing.T) {
	tests := []struct {
		value     interface{}
		blockType byte
	}{
		{value: float64(1.0), blockType: tsm1.BlockFloat64},
		{value: int64(1), blockType: tsm1.BlockInteger},
		{value: uint64(1), blockType: tsm1.BlockUnsigned},
		{value: true, blockType: tsm1.BlockBoolean},
		{value: "string", blockType: tsm1.BlockString},
	}

	for _, test := range tests {
		var values []tsm1.Value
		values = append(values, tsm1.NewValue(0, test.value))

		b, err := tsm1.Values(values).Encode(nil)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		bt, err := tsm1.BlockType(b)
		if err != nil {
			t.Fatalf("unexpected error decoding block type: %v", err)
		}

		if got, exp := bt, test.blockType; got != exp {
			t.Fatalf("block type mismatch: got %v, exp %v", got, exp)
		}
	}

	_, err := tsm1.BlockType([]byte{10})
	if err == nil {
		t.Fatalf("expected error decoding block type, got nil")
	}
}

func TestEncoding_Count(t *testing.T) {
	tests := []struct {
		value     interface{}
		blockType byte
	}{
		{value: float64(1.0), blockType: tsm1.BlockFloat64},
		{value: int64(1), blockType: tsm1.BlockInteger},
		{value: uint64(1), blockType: tsm1.BlockUnsigned},
		{value: true, blockType: tsm1.BlockBoolean},
		{value: "string", blockType: tsm1.BlockString},
	}

	for _, test := range tests {
		var values []tsm1.Value
		values = append(values, tsm1.NewValue(0, test.value))

		b, err := tsm1.Values(values).Encode(nil)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		if got, exp := tsm1.BlockCount(b), 1; got != exp {
			t.Fatalf("block count mismatch: got %v, exp %v", got, exp)
		}
	}
}

func TestValues_MergeFloat(t *testing.T) {
	tests := []struct {
		a, b, exp []tsm1.Value
	}{

		{ // empty a
			a: []tsm1.Value{},

			b: []tsm1.Value{
				tsm1.NewValue(1, 1.2),
				tsm1.NewValue(2, 2.2),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(1, 1.2),
				tsm1.NewValue(2, 2.2),
			},
		},
		{ // empty b
			a: []tsm1.Value{
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.1),
			},

			b: []tsm1.Value{},
			exp: []tsm1.Value{
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.1),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.1),
			},
			b: []tsm1.Value{
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(2, 2.2), // duplicate data
			},
			exp: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.2),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(1, 1.1), // duplicate data
				tsm1.NewValue(2, 2.1),
			},
			b: []tsm1.Value{
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(2, 2.2), // duplicate data
			},
			exp: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.2),
			},
		},

		{
			a: []tsm1.Value{
				tsm1.NewValue(1, 1.1),
			},
			b: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.2), // overwrites a
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(3, 3.2),
				tsm1.NewValue(4, 4.2),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.2),
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(3, 3.2),
				tsm1.NewValue(4, 4.2),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.1),
				tsm1.NewValue(3, 3.1),
				tsm1.NewValue(4, 4.1),
			},

			b: []tsm1.Value{
				tsm1.NewValue(1, 1.2), // overwrites a
				tsm1.NewValue(2, 2.2), // overwrites a
			},
			exp: []tsm1.Value{
				tsm1.NewValue(1, 1.2),
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(3, 3.1),
				tsm1.NewValue(4, 4.1),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.1),
				tsm1.NewValue(3, 3.1),
				tsm1.NewValue(4, 4.1),
			},

			b: []tsm1.Value{
				tsm1.NewValue(1, 1.2), // overwrites a
				tsm1.NewValue(2, 2.2), // overwrites a
				tsm1.NewValue(3, 3.2),
				tsm1.NewValue(4, 4.2),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(1, 1.2),
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(3, 3.2),
				tsm1.NewValue(4, 4.2),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.1),
				tsm1.NewValue(3, 3.1),
				tsm1.NewValue(4, 4.1),
			},
			b: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(4, 4.2),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(0, 0.0),
				tsm1.NewValue(1, 1.1),
				tsm1.NewValue(2, 2.2),
				tsm1.NewValue(3, 3.1),
				tsm1.NewValue(4, 4.2),
			},
		},

		{
			a: []tsm1.Value{
				tsm1.NewValue(1462498658242869207, 0.0),
				tsm1.NewValue(1462498658288956853, 1.1),
			},
			b: []tsm1.Value{
				tsm1.NewValue(1462498658242870810, 0.0),
				tsm1.NewValue(1462498658262911238, 2.2),
				tsm1.NewValue(1462498658282415038, 4.2),
				tsm1.NewValue(1462498658282417760, 4.2),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(1462498658242869207, 0.0),
				tsm1.NewValue(1462498658242870810, 0.0),
				tsm1.NewValue(1462498658262911238, 2.2),
				tsm1.NewValue(1462498658282415038, 4.2),
				tsm1.NewValue(1462498658282417760, 4.2),
				tsm1.NewValue(1462498658288956853, 1.1),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(4, 4.0),
				tsm1.NewValue(5, 5.0),
				tsm1.NewValue(6, 6.0),
			},
			b: []tsm1.Value{
				tsm1.NewValue(1, 1.0),
				tsm1.NewValue(2, 2.0),
				tsm1.NewValue(3, 3.0),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(1, 1.0),
				tsm1.NewValue(2, 2.0),
				tsm1.NewValue(3, 3.0),
				tsm1.NewValue(4, 4.0),
				tsm1.NewValue(5, 5.0),
				tsm1.NewValue(6, 6.0),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(5, 5.0),
				tsm1.NewValue(6, 6.0),
			},
			b: []tsm1.Value{
				tsm1.NewValue(1, 1.0),
				tsm1.NewValue(2, 2.0),
				tsm1.NewValue(3, 3.0),
				tsm1.NewValue(4, 4.0),
				tsm1.NewValue(7, 7.0),
				tsm1.NewValue(8, 8.0),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(1, 1.0),
				tsm1.NewValue(2, 2.0),
				tsm1.NewValue(3, 3.0),
				tsm1.NewValue(4, 4.0),
				tsm1.NewValue(5, 5.0),
				tsm1.NewValue(6, 6.0),
				tsm1.NewValue(7, 7.0),
				tsm1.NewValue(8, 8.0),
			},
		},
		{
			a: []tsm1.Value{
				tsm1.NewValue(1, 1.0),
				tsm1.NewValue(2, 2.0),
				tsm1.NewValue(3, 3.0),
			},
			b: []tsm1.Value{
				tsm1.NewValue(4, 4.0),
				tsm1.NewValue(5, 5.0),
				tsm1.NewValue(6, 6.0),
			},
			exp: []tsm1.Value{
				tsm1.NewValue(1, 1.0),
				tsm1.NewValue(2, 2.0),
				tsm1.NewValue(3, 3.0),
				tsm1.NewValue(4, 4.0),
				tsm1.NewValue(5, 5.0),
				tsm1.NewValue(6, 6.0),
			},
		},
	}

	for i, test := range tests {
		got := tsm1.Values(test.a).Merge(test.b)
		if exp, got := len(test.exp), len(got); exp != got {
			t.Fatalf("test(%d): value length mismatch: exp %v, got %v", i, exp, got)
		}

		dedup := tsm1.Values(append(test.a, test.b...)).Deduplicate()

		for i := range test.exp {
			if exp, got := test.exp[i].String(), got[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}

			if exp, got := test.exp[i].String(), dedup[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}
		}
	}
}

func TestValues_Contains(t *testing.T) {
	makeValues := func(count int, min, max int64) tsm1.Values {
		vals := make(tsm1.Values, count)

		ts := min
		inc := (max - min) / int64(count)

		for i := 0; i < count; i++ {
			vals[i] = tsm1.NewRawIntegerValue(ts, 0)
			ts += inc
		}

		return vals
	}

	cases := []struct {
		n        string
		min, max int64
		exp      bool
	}{
		{"no/lo", 0, 9, false},
		{"no/hi", 19, 30, false},
		{"no/middle", 13, 13, false},

		{"yes/first", 0, 10, true},
		{"yes/first-eq", 10, 10, true},
		{"yes/last", 18, 20, true},
		{"yes/last-eq", 18, 18, true},
		{"yes/all but first and last", 12, 16, true},
		{"yes/middle-eq", 14, 14, true},
		{"yes/middle-overlap", 13, 15, true},
		{"yes/covers", 8, 22, true},
	}

	for _, tc := range cases {
		t.Run(fmt.Sprintf("%s[%d,%d]", tc.n, tc.min, tc.max), func(t *testing.T) {
			vals := makeValues(5, 10, 20)
			if got := vals.Contains(tc.min, tc.max); got != tc.exp {
				t.Errorf("Contains -got/+exp\n%s", cmp.Diff(got, tc.exp))
			}
		})
	}
}

func TestIntegerValues_Merge(t *testing.T) {
	integerValue := func(t int64, f int64) tsm1.IntegerValue {
		return tsm1.NewValue(t, f).(tsm1.IntegerValue)
	}

	tests := []struct {
		a, b, exp []tsm1.IntegerValue
	}{

		{ // empty a
			a: []tsm1.IntegerValue{},

			b: []tsm1.IntegerValue{
				integerValue(1, 10),
				integerValue(2, 20),
			},
			exp: []tsm1.IntegerValue{
				integerValue(1, 10),
				integerValue(2, 20),
			},
		},
		{ // empty b
			a: []tsm1.IntegerValue{
				integerValue(1, 1),
				integerValue(2, 2),
			},

			b: []tsm1.IntegerValue{},
			exp: []tsm1.IntegerValue{
				integerValue(1, 1),
				integerValue(2, 2),
			},
		},
		{
			a: []tsm1.IntegerValue{
				integerValue(1, 1),
			},
			b: []tsm1.IntegerValue{
				integerValue(0, 0),
				integerValue(1, 10), // overwrites a
				integerValue(2, 20),
				integerValue(3, 30),
				integerValue(4, 40),
			},
			exp: []tsm1.IntegerValue{
				integerValue(0, 0),
				integerValue(1, 10),
				integerValue(2, 20),
				integerValue(3, 30),
				integerValue(4, 40),
			},
		},
		{
			a: []tsm1.IntegerValue{
				integerValue(1, 1),
				integerValue(2, 2),
				integerValue(3, 3),
				integerValue(4, 4),
			},

			b: []tsm1.IntegerValue{
				integerValue(1, 10), // overwrites a
				integerValue(2, 20), // overwrites a
			},
			exp: []tsm1.IntegerValue{
				integerValue(1, 10),
				integerValue(2, 20),
				integerValue(3, 3),
				integerValue(4, 4),
			},
		},
		{
			a: []tsm1.IntegerValue{
				integerValue(1, 1),
				integerValue(2, 2),
				integerValue(3, 3),
				integerValue(4, 4),
			},

			b: []tsm1.IntegerValue{
				integerValue(1, 10), // overwrites a
				integerValue(2, 20), // overwrites a
				integerValue(3, 30),
				integerValue(4, 40),
			},
			exp: []tsm1.IntegerValue{
				integerValue(1, 10),
				integerValue(2, 20),
				integerValue(3, 30),
				integerValue(4, 40),
			},
		},
		{
			a: []tsm1.IntegerValue{
				integerValue(0, 0),
				integerValue(1, 1),
				integerValue(2, 2),
				integerValue(3, 3),
				integerValue(4, 4),
			},
			b: []tsm1.IntegerValue{
				integerValue(0, 0),
				integerValue(2, 20),
				integerValue(4, 40),
			},
			exp: []tsm1.IntegerValue{
				integerValue(0, 0.0),
				integerValue(1, 1),
				integerValue(2, 20),
				integerValue(3, 3),
				integerValue(4, 40),
			},
		},
	}

	for i, test := range tests {
		if i != 2 {
			continue
		}

		got := tsm1.IntegerValues(test.a).Merge(test.b)
		if exp, got := len(test.exp), len(got); exp != got {
			t.Fatalf("test(%d): value length mismatch: exp %v, got %v", i, exp, got)
		}

		dedup := tsm1.IntegerValues(append(test.a, test.b...)).Deduplicate()

		for i := range test.exp {
			if exp, got := test.exp[i].String(), got[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}

			if exp, got := test.exp[i].String(), dedup[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}
		}
	}
}

func TestUnsignedValues_Merge(t *testing.T) {
	uintValue := func(t int64, f uint64) tsm1.UnsignedValue {
		return tsm1.NewValue(t, f).(tsm1.UnsignedValue)
	}

	tests := []struct {
		a, b, exp []tsm1.UnsignedValue
	}{

		{ // empty a
			a: []tsm1.UnsignedValue{},

			b: []tsm1.UnsignedValue{
				uintValue(1, 10),
				uintValue(2, 20),
			},
			exp: []tsm1.UnsignedValue{
				uintValue(1, 10),
				uintValue(2, 20),
			},
		},
		{ // empty b
			a: []tsm1.UnsignedValue{
				uintValue(1, 1),
				uintValue(2, 2),
			},

			b: []tsm1.UnsignedValue{},
			exp: []tsm1.UnsignedValue{
				uintValue(1, 1),
				uintValue(2, 2),
			},
		},
		{
			a: []tsm1.UnsignedValue{
				uintValue(1, 1),
			},
			b: []tsm1.UnsignedValue{
				uintValue(0, 0),
				uintValue(1, 10), // overwrites a
				uintValue(2, 20),
				uintValue(3, 30),
				uintValue(4, 40),
			},
			exp: []tsm1.UnsignedValue{
				uintValue(0, 0),
				uintValue(1, 10),
				uintValue(2, 20),
				uintValue(3, 30),
				uintValue(4, 40),
			},
		},
		{
			a: []tsm1.UnsignedValue{
				uintValue(1, 1),
				uintValue(2, 2),
				uintValue(3, 3),
				uintValue(4, 4),
			},

			b: []tsm1.UnsignedValue{
				uintValue(1, ^uint64(0)), // overwrites a
				uintValue(2, 20),         // overwrites a
			},
			exp: []tsm1.UnsignedValue{
				uintValue(1, ^uint64(0)),
				uintValue(2, 20),
				uintValue(3, 3),
				uintValue(4, 4),
			},
		},
		{
			a: []tsm1.UnsignedValue{
				uintValue(1, 1),
				uintValue(2, 2),
				uintValue(3, 3),
				uintValue(4, 4),
			},

			b: []tsm1.UnsignedValue{
				uintValue(1, 10), // overwrites a
				uintValue(2, 20), // overwrites a
				uintValue(3, 30),
				uintValue(4, 40),
			},
			exp: []tsm1.UnsignedValue{
				uintValue(1, 10),
				uintValue(2, 20),
				uintValue(3, 30),
				uintValue(4, 40),
			},
		},
		{
			a: []tsm1.UnsignedValue{
				uintValue(0, 0),
				uintValue(1, 1),
				uintValue(2, 2),
				uintValue(3, 3),
				uintValue(4, 4),
			},
			b: []tsm1.UnsignedValue{
				uintValue(0, 0),
				uintValue(2, 20),
				uintValue(4, 40),
			},
			exp: []tsm1.UnsignedValue{
				uintValue(0, 0.0),
				uintValue(1, 1),
				uintValue(2, 20),
				uintValue(3, 3),
				uintValue(4, 40),
			},
		},
	}

	for i, test := range tests {
		if i != 2 {
			continue
		}

		got := tsm1.UnsignedValues(test.a).Merge(test.b)
		if exp, got := len(test.exp), len(got); exp != got {
			t.Fatalf("test(%d): value length mismatch: exp %v, got %v", i, exp, got)
		}

		dedup := tsm1.UnsignedValues(append(test.a, test.b...)).Deduplicate()

		for i := range test.exp {
			if exp, got := test.exp[i].String(), got[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}

			if exp, got := test.exp[i].String(), dedup[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}
		}
	}
}

func TestFloatValues_Merge(t *testing.T) {
	floatValue := func(t int64, f float64) tsm1.FloatValue {
		return tsm1.NewValue(t, f).(tsm1.FloatValue)
	}

	tests := []struct {
		a, b, exp []tsm1.FloatValue
	}{

		{ // empty a
			a: []tsm1.FloatValue{},

			b: []tsm1.FloatValue{
				floatValue(1, 1.2),
				floatValue(2, 2.2),
			},
			exp: []tsm1.FloatValue{
				floatValue(1, 1.2),
				floatValue(2, 2.2),
			},
		},
		{ // empty b
			a: []tsm1.FloatValue{
				floatValue(1, 1.1),
				floatValue(2, 2.1),
			},

			b: []tsm1.FloatValue{},
			exp: []tsm1.FloatValue{
				floatValue(1, 1.1),
				floatValue(2, 2.1),
			},
		},
		{
			a: []tsm1.FloatValue{
				floatValue(1, 1.1),
			},
			b: []tsm1.FloatValue{
				floatValue(0, 0.0),
				floatValue(1, 1.2), // overwrites a
				floatValue(2, 2.2),
				floatValue(3, 3.2),
				floatValue(4, 4.2),
			},
			exp: []tsm1.FloatValue{
				floatValue(0, 0.0),
				floatValue(1, 1.2),
				floatValue(2, 2.2),
				floatValue(3, 3.2),
				floatValue(4, 4.2),
			},
		},
		{
			a: []tsm1.FloatValue{
				floatValue(1, 1.1),
				floatValue(2, 2.1),
				floatValue(3, 3.1),
				floatValue(4, 4.1),
			},

			b: []tsm1.FloatValue{
				floatValue(1, 1.2), // overwrites a
				floatValue(2, 2.2), // overwrites a
			},
			exp: []tsm1.FloatValue{
				floatValue(1, 1.2),
				floatValue(2, 2.2),
				floatValue(3, 3.1),
				floatValue(4, 4.1),
			},
		},
		{
			a: []tsm1.FloatValue{
				floatValue(1, 1.1),
				floatValue(2, 2.1),
				floatValue(3, 3.1),
				floatValue(4, 4.1),
			},

			b: []tsm1.FloatValue{
				floatValue(1, 1.2), // overwrites a
				floatValue(2, 2.2), // overwrites a
				floatValue(3, 3.2),
				floatValue(4, 4.2),
			},
			exp: []tsm1.FloatValue{
				floatValue(1, 1.2),
				floatValue(2, 2.2),
				floatValue(3, 3.2),
				floatValue(4, 4.2),
			},
		},
		{
			a: []tsm1.FloatValue{
				floatValue(0, 0.0),
				floatValue(1, 1.1),
				floatValue(2, 2.1),
				floatValue(3, 3.1),
				floatValue(4, 4.1),
			},
			b: []tsm1.FloatValue{
				floatValue(0, 0.0),
				floatValue(2, 2.2),
				floatValue(4, 4.2),
			},
			exp: []tsm1.FloatValue{
				floatValue(0, 0.0),
				floatValue(1, 1.1),
				floatValue(2, 2.2),
				floatValue(3, 3.1),
				floatValue(4, 4.2),
			},
		},
	}

	for i, test := range tests {
		got := tsm1.FloatValues(test.a).Merge(test.b)
		if exp, got := len(test.exp), len(got); exp != got {
			t.Fatalf("test(%d): value length mismatch: exp %v, got %v", i, exp, got)
		}

		dedup := tsm1.FloatValues(append(test.a, test.b...)).Deduplicate()

		for i := range test.exp {
			if exp, got := test.exp[i].String(), got[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}

			if exp, got := test.exp[i].String(), dedup[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}
		}
	}
}

func TestBooleanValues_Merge(t *testing.T) {
	booleanValue := func(t int64, f bool) tsm1.BooleanValue {
		return tsm1.NewValue(t, f).(tsm1.BooleanValue)
	}

	tests := []struct {
		a, b, exp []tsm1.BooleanValue
	}{

		{ // empty a
			a: []tsm1.BooleanValue{},

			b: []tsm1.BooleanValue{
				booleanValue(1, true),
				booleanValue(2, true),
			},
			exp: []tsm1.BooleanValue{
				booleanValue(1, true),
				booleanValue(2, true),
			},
		},
		{ // empty b
			a: []tsm1.BooleanValue{
				booleanValue(1, true),
				booleanValue(2, true),
			},

			b: []tsm1.BooleanValue{},
			exp: []tsm1.BooleanValue{
				booleanValue(1, true),
				booleanValue(2, true),
			},
		},
		{
			a: []tsm1.BooleanValue{
				booleanValue(1, true),
			},
			b: []tsm1.BooleanValue{
				booleanValue(0, false),
				booleanValue(1, false), // overwrites a
				booleanValue(2, false),
				booleanValue(3, false),
				booleanValue(4, false),
			},
			exp: []tsm1.BooleanValue{
				booleanValue(0, false),
				booleanValue(1, false),
				booleanValue(2, false),
				booleanValue(3, false),
				booleanValue(4, false),
			},
		},
		{
			a: []tsm1.BooleanValue{
				booleanValue(1, true),
				booleanValue(2, true),
				booleanValue(3, true),
				booleanValue(4, true),
			},

			b: []tsm1.BooleanValue{
				booleanValue(1, false), // overwrites a
				booleanValue(2, false), // overwrites a
			},
			exp: []tsm1.BooleanValue{
				booleanValue(1, false), // overwrites a
				booleanValue(2, false), // overwrites a
				booleanValue(3, true),
				booleanValue(4, true),
			},
		},
		{
			a: []tsm1.BooleanValue{
				booleanValue(1, true),
				booleanValue(2, true),
				booleanValue(3, true),
				booleanValue(4, true),
			},

			b: []tsm1.BooleanValue{
				booleanValue(1, false), // overwrites a
				booleanValue(2, false), // overwrites a
				booleanValue(3, false),
				booleanValue(4, false),
			},
			exp: []tsm1.BooleanValue{
				booleanValue(1, false),
				booleanValue(2, false),
				booleanValue(3, false),
				booleanValue(4, false),
			},
		},
		{
			a: []tsm1.BooleanValue{
				booleanValue(0, true),
				booleanValue(1, true),
				booleanValue(2, true),
				booleanValue(3, true),
				booleanValue(4, true),
			},
			b: []tsm1.BooleanValue{
				booleanValue(0, false),
				booleanValue(2, false),
				booleanValue(4, false),
			},
			exp: []tsm1.BooleanValue{
				booleanValue(0, false),
				booleanValue(1, true),
				booleanValue(2, false),
				booleanValue(3, true),
				booleanValue(4, false),
			},
		},
	}

	for i, test := range tests {
		got := tsm1.BooleanValues(test.a).Merge(test.b)
		if exp, got := len(test.exp), len(got); exp != got {
			t.Fatalf("test(%d): value length mismatch: exp %v, got %v", i, exp, got)
		}

		dedup := tsm1.BooleanValues(append(test.a, test.b...)).Deduplicate()

		for i := range test.exp {
			if exp, got := test.exp[i].String(), got[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}

			if exp, got := test.exp[i].String(), dedup[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}
		}
	}
}

func TestStringValues_Merge(t *testing.T) {
	stringValue := func(t int64, f string) tsm1.StringValue {
		return tsm1.NewValue(t, f).(tsm1.StringValue)
	}

	tests := []struct {
		a, b, exp []tsm1.StringValue
	}{

		{ // empty a
			a: []tsm1.StringValue{},

			b: []tsm1.StringValue{
				stringValue(1, "10"),
				stringValue(2, "20"),
			},
			exp: []tsm1.StringValue{
				stringValue(1, "10"),
				stringValue(2, "20"),
			},
		},
		{ // empty b
			a: []tsm1.StringValue{
				stringValue(1, "1"),
				stringValue(2, "2"),
			},

			b: []tsm1.StringValue{},
			exp: []tsm1.StringValue{
				stringValue(1, "1"),
				stringValue(2, "2"),
			},
		},
		{
			a: []tsm1.StringValue{
				stringValue(1, "1"),
			},
			b: []tsm1.StringValue{
				stringValue(0, "0"),
				stringValue(1, "10"), // overwrites a
				stringValue(2, "20"),
				stringValue(3, "30"),
				stringValue(4, "40"),
			},
			exp: []tsm1.StringValue{
				stringValue(0, "0"),
				stringValue(1, "10"),
				stringValue(2, "20"),
				stringValue(3, "30"),
				stringValue(4, "40"),
			},
		},
		{
			a: []tsm1.StringValue{
				stringValue(1, "1"),
				stringValue(2, "2"),
				stringValue(3, "3"),
				stringValue(4, "4"),
			},

			b: []tsm1.StringValue{
				stringValue(1, "10"), // overwrites a
				stringValue(2, "20"), // overwrites a
			},
			exp: []tsm1.StringValue{
				stringValue(1, "10"),
				stringValue(2, "20"),
				stringValue(3, "3"),
				stringValue(4, "4"),
			},
		},
		{
			a: []tsm1.StringValue{
				stringValue(1, "1"),
				stringValue(2, "2"),
				stringValue(3, "3"),
				stringValue(4, "4"),
			},

			b: []tsm1.StringValue{
				stringValue(1, "10"), // overwrites a
				stringValue(2, "20"), // overwrites a
				stringValue(3, "30"),
				stringValue(4, "40"),
			},
			exp: []tsm1.StringValue{
				stringValue(1, "10"),
				stringValue(2, "20"),
				stringValue(3, "30"),
				stringValue(4, "40"),
			},
		},
		{
			a: []tsm1.StringValue{
				stringValue(0, "0"),
				stringValue(1, "1"),
				stringValue(2, "2"),
				stringValue(3, "3"),
				stringValue(4, "4"),
			},
			b: []tsm1.StringValue{
				stringValue(0, "0"),
				stringValue(2, "20"),
				stringValue(4, "40"),
			},
			exp: []tsm1.StringValue{
				stringValue(0, "0.0"),
				stringValue(1, "1"),
				stringValue(2, "20"),
				stringValue(3, "3"),
				stringValue(4, "40"),
			},
		},
	}

	for i, test := range tests {
		if i != 2 {
			continue
		}

		got := tsm1.StringValues(test.a).Merge(test.b)
		if exp, got := len(test.exp), len(got); exp != got {
			t.Fatalf("test(%d): value length mismatch: exp %v, got %v", i, exp, got)
		}

		dedup := tsm1.StringValues(append(test.a, test.b...)).Deduplicate()

		for i := range test.exp {
			if exp, got := test.exp[i].String(), got[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}

			if exp, got := test.exp[i].String(), dedup[i].String(); exp != got {
				t.Fatalf("value mismatch:\n exp %v\n got %v", exp, got)
			}
		}
	}
}
func getTimes(n, step int, precision time.Duration) []int64 {
	t := time.Now().Round(precision).UnixNano()
	a := make([]int64, n)
	for i := 0; i < n; i++ {
		a[i] = t + (time.Duration(i*60) * precision).Nanoseconds()
	}
	return a
}

func BenchmarkDecodeBlock_Float_Empty(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, float64(i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Float_EqualSize(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, float64(i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.Value, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Float_TypeSpecific(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, float64(i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.FloatValue, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeFloatBlock(bytes, &decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Integer_Empty(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, int64(i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Integer_EqualSize(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, int64(i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.Value, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Integer_TypeSpecific(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, int64(i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.IntegerValue, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeIntegerBlock(bytes, &decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Boolean_Empty(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, true)
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Boolean_EqualSize(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, true)
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.Value, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_Boolean_TypeSpecific(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, true)
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.BooleanValue, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBooleanBlock(bytes, &decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBooleanBlock(b *testing.B) {
	cases := []int{
		5,
		55,
		555,
		1000,
	}
	for _, n := range cases {
		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
			valueCount := n
			times := getTimes(valueCount, 60, time.Second)
			values := make([]tsm1.Value, len(times))
			for i, t := range times {
				values[i] = tsm1.NewValue(t, true)
			}

			bytes, err := tsm1.Values(values).Encode(nil)
			if err != nil {
				b.Fatalf("unexpected error: %v", err)
			}

			b.ResetTimer()
			b.ReportAllocs()
			b.SetBytes(int64(tsm1.Values(values).Size()))

			b.RunParallel(func(pb *testing.PB) {
				decodedValues := make([]tsm1.BooleanValue, len(values))

				for pb.Next() {
					_, err = tsm1.DecodeBooleanBlock(bytes, &decodedValues)
					if err != nil {
						b.Fatalf("unexpected error decoding block: %v", err)
					}
				}
			})
		})
	}
}

func BenchmarkDecodeFloatBlock(b *testing.B) {
	cases := []int{
		5,
		55,
		555,
		1000,
	}
	for _, n := range cases {
		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
			valueCount := n
			times := getTimes(valueCount, 60, time.Second)
			values := make([]tsm1.Value, len(times))
			for i, t := range times {
				values[i] = tsm1.NewValue(t, float64(i))
			}

			bytes, err := tsm1.Values(values).Encode(nil)
			if err != nil {
				b.Fatalf("unexpected error: %v", err)
			}

			b.ResetTimer()
			b.ReportAllocs()
			b.SetBytes(int64(tsm1.Values(values).Size()))

			b.RunParallel(func(pb *testing.PB) {
				decodedValues := make([]tsm1.FloatValue, len(values))

				for pb.Next() {
					_, err = tsm1.DecodeFloatBlock(bytes, &decodedValues)
					if err != nil {
						b.Fatalf("unexpected error decoding block: %v", err)
					}
				}
			})
		})
	}
}

func BenchmarkDecodeIntegerBlock(b *testing.B) {
	rle := func(i int) int64 { return int64(i) }
	s8b := func(i int) int64 { return int64(i + int(rand.Int31n(10))) }

	cases := []struct {
		enc string
		gen func(i int) int64
		n   int
	}{
		{enc: "rle", gen: rle, n: 5},
		{enc: "rle", gen: rle, n: 55},
		{enc: "rle", gen: rle, n: 555},
		{enc: "rle", gen: rle, n: 1000},
		{enc: "s8b", gen: s8b, n: 5},
		{enc: "s8b", gen: s8b, n: 55},
		{enc: "s8b", gen: s8b, n: 555},
		{enc: "s8b", gen: s8b, n: 1000},
	}
	for _, bm := range cases {
		b.Run(fmt.Sprintf("%s_%d", bm.enc, bm.n), func(b *testing.B) {
			rand.Seed(int64(bm.n * 1e3))

			valueCount := bm.n
			times := getTimes(valueCount, 60, time.Second)
			values := make([]tsm1.Value, len(times))
			for i, t := range times {
				values[i] = tsm1.NewValue(t, bm.gen(i))
			}

			bytes, err := tsm1.Values(values).Encode(nil)
			if err != nil {
				b.Fatalf("unexpected error: %v", err)
			}

			b.ResetTimer()
			b.ReportAllocs()
			b.SetBytes(int64(tsm1.Values(values).Size()))

			b.RunParallel(func(pb *testing.PB) {
				decodedValues := make([]tsm1.IntegerValue, len(values))

				for pb.Next() {
					_, err = tsm1.DecodeIntegerBlock(bytes, &decodedValues)
					if err != nil {
						b.Fatalf("unexpected error decoding block: %v", err)
					}
				}
			})
		})
	}
}

func BenchmarkDecodeStringBlock(b *testing.B) {
	cases := []int{
		5,
		55,
		555,
		1000,
	}
	for _, n := range cases {
		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
			valueCount := n
			times := getTimes(valueCount, 60, time.Second)
			values := make([]tsm1.Value, len(times))
			for i, t := range times {
				values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i))
			}

			bytes, err := tsm1.Values(values).Encode(nil)
			if err != nil {
				b.Fatalf("unexpected error: %v", err)
			}

			b.ResetTimer()
			b.ReportAllocs()
			b.SetBytes(int64(tsm1.Values(values).Size()))

			b.RunParallel(func(pb *testing.PB) {
				decodedValues := make([]tsm1.StringValue, len(values))

				for pb.Next() {
					_, err = tsm1.DecodeStringBlock(bytes, &decodedValues)
					if err != nil {
						b.Fatalf("unexpected error decoding block: %v", err)
					}
				}
			})
		})
	}
}

func BenchmarkDecodeBlock_String_Empty(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	var decodedValues []tsm1.Value
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_String_EqualSize(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.Value, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeBlock(bytes, decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkDecodeBlock_String_TypeSpecific(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i))
	}

	bytes, err := tsm1.Values(values).Encode(nil)
	if err != nil {
		b.Fatalf("unexpected error: %v", err)
	}

	decodedValues := make([]tsm1.StringValue, len(values))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = tsm1.DecodeStringBlock(bytes, &decodedValues)
		if err != nil {
			b.Fatalf("unexpected error decoding block: %v", err)
		}
	}
}

func BenchmarkValues_Deduplicate(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	values := make([]tsm1.Value, len(times))
	for i, t := range times {
		values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i))
	}
	values = append(values, values...)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tsm1.Values(values).Deduplicate()
	}
}

func BenchmarkValues_Merge(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))
	c := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, float64(i))
		c[i] = tsm1.NewValue(t+1, float64(i))
	}

	b.ResetTimer()
	benchmarkMerge(a, c, b)
}

func BenchmarkValues_MergeDisjoint(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))
	c := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, float64(i))
		c[i] = tsm1.NewValue(times[len(times)-1]+int64((i+1)*1e9), float64(i))
	}

	b.ResetTimer()
	benchmarkMerge(a, c, b)
}

func BenchmarkValues_MergeSame(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))
	c := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, float64(i))
		c[i] = tsm1.NewValue(t, float64(i))
	}

	b.ResetTimer()
	benchmarkMerge(a, c, b)
}

func BenchmarkValues_MergeSimilar(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))
	c := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, float64(i))
		if i == 0 {
			t++
		}
		c[i] = tsm1.NewValue(t, float64(i))
	}

	b.ResetTimer()
	benchmarkMerge(a, c, b)
}

func BenchmarkValues_MergeUnevenA(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))
	c := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, float64(i))
		c[i] = tsm1.NewValue(t, float64(i))
	}

	b.ResetTimer()
	benchmarkMerge(a[:700], c[:10], b)
}

func BenchmarkValues_MergeUnevenB(b *testing.B) {
	valueCount := 1000
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))
	c := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, float64(i))
		c[i] = tsm1.NewValue(t, float64(i))
	}

	b.ResetTimer()
	benchmarkMerge(a[:10], c[:700], b)
}

func benchmarkMerge(a, c tsm1.Values, b *testing.B) {
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		aa := make(tsm1.Values, len(a))
		copy(aa, a)
		cc := make(tsm1.Values, len(c))
		copy(cc, c)
		b.StartTimer()
		tsm1.Values(aa).Merge(tsm1.Values(cc))
	}
}

func BenchmarkValues_EncodeInteger(b *testing.B) {
	valueCount := 1024
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, int64(i))
	}

	buf := make([]byte, 1024*8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tsm1.Values(a).Encode(buf)
	}
}

func BenchmarkValues_EncodeFloat(b *testing.B) {
	valueCount := 1024
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, float64(i))
	}

	buf := make([]byte, 1024*8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tsm1.Values(a).Encode(buf)
	}
}
func BenchmarkValues_EncodeString(b *testing.B) {
	valueCount := 1024
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))

	for i, t := range times {
		a[i] = tsm1.NewValue(t, fmt.Sprintf("%d", i))
	}

	buf := make([]byte, 1024*8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tsm1.Values(a).Encode(buf)
	}
}
func BenchmarkValues_EncodeBool(b *testing.B) {
	valueCount := 1024
	times := getTimes(valueCount, 60, time.Second)
	a := make([]tsm1.Value, len(times))

	for i, t := range times {
		if i%2 == 0 {
			a[i] = tsm1.NewValue(t, true)
		} else {
			a[i] = tsm1.NewValue(t, false)
		}
	}

	buf := make([]byte, 1024*8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tsm1.Values(a).Encode(buf)
	}
}
