// Generated by tmpl
// https://github.com/benbjohnson/tmpl
//
// DO NOT EDIT!
// Source: arrayvalues.gen.go.tmpl

package cursors

type FloatArray struct {
	Timestamps []int64
	Values     []float64
}

func NewFloatArrayLen(sz int) *FloatArray {
	return &FloatArray{
		Timestamps: make([]int64, sz),
		Values:     make([]float64, sz),
	}
}

func (a *FloatArray) MinTime() int64 {
	return a.Timestamps[0]
}

func (a *FloatArray) MaxTime() int64 {
	return a.Timestamps[len(a.Timestamps)-1]
}

func (a *FloatArray) Len() int {
	return len(a.Timestamps)
}

// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *FloatArray) search(v int64) int {
	// Define: f(x) → a.Timestamps[x] < v
	// Define: f(-1) == true, f(n) == false
	// Invariant: f(lo-1) == true, f(hi) == false
	lo := 0
	hi := a.Len()
	for lo < hi {
		mid := int(uint(lo+hi) >> 1)
		if a.Timestamps[mid] < v {
			lo = mid + 1 // preserves f(lo-1) == true
		} else {
			hi = mid // preserves f(hi) == false
		}
	}

	// lo == hi
	return lo
}

// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling FindRange or the results
// are undefined.
func (a *FloatArray) FindRange(min, max int64) (int, int) {
	if a.Len() == 0 || min > max {
		return -1, -1
	}

	minVal := a.MinTime()
	maxVal := a.MaxTime()

	if maxVal < min || minVal > max {
		return -1, -1
	}

	return a.search(min), a.search(max)
}

// Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *FloatArray) Exclude(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() {
		if a.Timestamps[rmax] == max {
			rmax++
		}
		rest := a.Len() - rmax
		if rest > 0 {
			ts := a.Timestamps[:rmin+rest]
			copy(ts[rmin:], a.Timestamps[rmax:])
			a.Timestamps = ts

			vs := a.Values[:rmin+rest]
			copy(vs[rmin:], a.Values[rmax:])
			a.Values = vs
			return
		}
	}

	a.Timestamps = a.Timestamps[:rmin]
	a.Values = a.Values[:rmin]
}

// Include returns the subset values between min and max inclusive. The values must
// be deduplicated and sorted before calling Include or the results are undefined.
func (a *FloatArray) Include(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		a.Timestamps = a.Timestamps[:0]
		a.Values = a.Values[:0]
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() && a.Timestamps[rmax] == max {
		rmax++
	}

	if rmin > -1 {
		ts := a.Timestamps[:rmax-rmin]
		copy(ts, a.Timestamps[rmin:rmax])
		a.Timestamps = ts
		vs := a.Values[:rmax-rmin]
		copy(vs, a.Values[rmin:rmax])
		a.Values = vs
	} else {
		a.Timestamps = a.Timestamps[:rmax]
		a.Values = a.Values[:rmax]
	}
}

// Merge overlays b to top of a.  If two values conflict with
// the same timestamp, b is used.  Both a and b must be sorted
// in ascending order.
func (a *FloatArray) Merge(b *FloatArray) {
	if a.Len() == 0 {
		*a = *b
		return
	}

	if b.Len() == 0 {
		return
	}

	// Normally, both a and b should not contain duplicates.  Due to a bug in older versions, it's
	// possible stored blocks might contain duplicate values.  Remove them if they exists before
	// merging.
	// a = a.Deduplicate()
	// b = b.Deduplicate()

	if a.MaxTime() < b.MinTime() {
		a.Timestamps = append(a.Timestamps, b.Timestamps...)
		a.Values = append(a.Values, b.Values...)
		return
	}

	if b.MaxTime() < a.MinTime() {
		var tmp FloatArray
		tmp.Timestamps = append(b.Timestamps, a.Timestamps...)
		tmp.Values = append(b.Values, a.Values...)
		*a = tmp
		return
	}

	out := NewFloatArrayLen(a.Len() + b.Len())
	i, j, k := 0, 0, 0
	for i < len(a.Timestamps) && j < len(b.Timestamps) {
		if a.Timestamps[i] < b.Timestamps[j] {
			out.Timestamps[k] = a.Timestamps[i]
			out.Values[k] = a.Values[i]
			i++
		} else if a.Timestamps[i] == b.Timestamps[j] {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			i++
			j++
		} else {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			j++
		}
		k++
	}

	if i < len(a.Timestamps) {
		n := copy(out.Timestamps[k:], a.Timestamps[i:])
		copy(out.Values[k:], a.Values[i:])
		k += n
	} else if j < len(b.Timestamps) {
		n := copy(out.Timestamps[k:], b.Timestamps[j:])
		copy(out.Values[k:], b.Values[j:])
		k += n
	}

	a.Timestamps = out.Timestamps[:k]
	a.Values = out.Values[:k]
}

type IntegerArray struct {
	Timestamps []int64
	Values     []int64
}

func NewIntegerArrayLen(sz int) *IntegerArray {
	return &IntegerArray{
		Timestamps: make([]int64, sz),
		Values:     make([]int64, sz),
	}
}

func (a *IntegerArray) MinTime() int64 {
	return a.Timestamps[0]
}

func (a *IntegerArray) MaxTime() int64 {
	return a.Timestamps[len(a.Timestamps)-1]
}

func (a *IntegerArray) Len() int {
	return len(a.Timestamps)
}

// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *IntegerArray) search(v int64) int {
	// Define: f(x) → a.Timestamps[x] < v
	// Define: f(-1) == true, f(n) == false
	// Invariant: f(lo-1) == true, f(hi) == false
	lo := 0
	hi := a.Len()
	for lo < hi {
		mid := int(uint(lo+hi) >> 1)
		if a.Timestamps[mid] < v {
			lo = mid + 1 // preserves f(lo-1) == true
		} else {
			hi = mid // preserves f(hi) == false
		}
	}

	// lo == hi
	return lo
}

// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling FindRange or the results
// are undefined.
func (a *IntegerArray) FindRange(min, max int64) (int, int) {
	if a.Len() == 0 || min > max {
		return -1, -1
	}

	minVal := a.MinTime()
	maxVal := a.MaxTime()

	if maxVal < min || minVal > max {
		return -1, -1
	}

	return a.search(min), a.search(max)
}

// Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *IntegerArray) Exclude(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() {
		if a.Timestamps[rmax] == max {
			rmax++
		}
		rest := a.Len() - rmax
		if rest > 0 {
			ts := a.Timestamps[:rmin+rest]
			copy(ts[rmin:], a.Timestamps[rmax:])
			a.Timestamps = ts

			vs := a.Values[:rmin+rest]
			copy(vs[rmin:], a.Values[rmax:])
			a.Values = vs
			return
		}
	}

	a.Timestamps = a.Timestamps[:rmin]
	a.Values = a.Values[:rmin]
}

// Include returns the subset values between min and max inclusive. The values must
// be deduplicated and sorted before calling Include or the results are undefined.
func (a *IntegerArray) Include(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		a.Timestamps = a.Timestamps[:0]
		a.Values = a.Values[:0]
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() && a.Timestamps[rmax] == max {
		rmax++
	}

	if rmin > -1 {
		ts := a.Timestamps[:rmax-rmin]
		copy(ts, a.Timestamps[rmin:rmax])
		a.Timestamps = ts
		vs := a.Values[:rmax-rmin]
		copy(vs, a.Values[rmin:rmax])
		a.Values = vs
	} else {
		a.Timestamps = a.Timestamps[:rmax]
		a.Values = a.Values[:rmax]
	}
}

// Merge overlays b to top of a.  If two values conflict with
// the same timestamp, b is used.  Both a and b must be sorted
// in ascending order.
func (a *IntegerArray) Merge(b *IntegerArray) {
	if a.Len() == 0 {
		*a = *b
		return
	}

	if b.Len() == 0 {
		return
	}

	// Normally, both a and b should not contain duplicates.  Due to a bug in older versions, it's
	// possible stored blocks might contain duplicate values.  Remove them if they exists before
	// merging.
	// a = a.Deduplicate()
	// b = b.Deduplicate()

	if a.MaxTime() < b.MinTime() {
		a.Timestamps = append(a.Timestamps, b.Timestamps...)
		a.Values = append(a.Values, b.Values...)
		return
	}

	if b.MaxTime() < a.MinTime() {
		var tmp IntegerArray
		tmp.Timestamps = append(b.Timestamps, a.Timestamps...)
		tmp.Values = append(b.Values, a.Values...)
		*a = tmp
		return
	}

	out := NewIntegerArrayLen(a.Len() + b.Len())
	i, j, k := 0, 0, 0
	for i < len(a.Timestamps) && j < len(b.Timestamps) {
		if a.Timestamps[i] < b.Timestamps[j] {
			out.Timestamps[k] = a.Timestamps[i]
			out.Values[k] = a.Values[i]
			i++
		} else if a.Timestamps[i] == b.Timestamps[j] {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			i++
			j++
		} else {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			j++
		}
		k++
	}

	if i < len(a.Timestamps) {
		n := copy(out.Timestamps[k:], a.Timestamps[i:])
		copy(out.Values[k:], a.Values[i:])
		k += n
	} else if j < len(b.Timestamps) {
		n := copy(out.Timestamps[k:], b.Timestamps[j:])
		copy(out.Values[k:], b.Values[j:])
		k += n
	}

	a.Timestamps = out.Timestamps[:k]
	a.Values = out.Values[:k]
}

type UnsignedArray struct {
	Timestamps []int64
	Values     []uint64
}

func NewUnsignedArrayLen(sz int) *UnsignedArray {
	return &UnsignedArray{
		Timestamps: make([]int64, sz),
		Values:     make([]uint64, sz),
	}
}

func (a *UnsignedArray) MinTime() int64 {
	return a.Timestamps[0]
}

func (a *UnsignedArray) MaxTime() int64 {
	return a.Timestamps[len(a.Timestamps)-1]
}

func (a *UnsignedArray) Len() int {
	return len(a.Timestamps)
}

// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *UnsignedArray) search(v int64) int {
	// Define: f(x) → a.Timestamps[x] < v
	// Define: f(-1) == true, f(n) == false
	// Invariant: f(lo-1) == true, f(hi) == false
	lo := 0
	hi := a.Len()
	for lo < hi {
		mid := int(uint(lo+hi) >> 1)
		if a.Timestamps[mid] < v {
			lo = mid + 1 // preserves f(lo-1) == true
		} else {
			hi = mid // preserves f(hi) == false
		}
	}

	// lo == hi
	return lo
}

// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling FindRange or the results
// are undefined.
func (a *UnsignedArray) FindRange(min, max int64) (int, int) {
	if a.Len() == 0 || min > max {
		return -1, -1
	}

	minVal := a.MinTime()
	maxVal := a.MaxTime()

	if maxVal < min || minVal > max {
		return -1, -1
	}

	return a.search(min), a.search(max)
}

// Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *UnsignedArray) Exclude(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() {
		if a.Timestamps[rmax] == max {
			rmax++
		}
		rest := a.Len() - rmax
		if rest > 0 {
			ts := a.Timestamps[:rmin+rest]
			copy(ts[rmin:], a.Timestamps[rmax:])
			a.Timestamps = ts

			vs := a.Values[:rmin+rest]
			copy(vs[rmin:], a.Values[rmax:])
			a.Values = vs
			return
		}
	}

	a.Timestamps = a.Timestamps[:rmin]
	a.Values = a.Values[:rmin]
}

// Include returns the subset values between min and max inclusive. The values must
// be deduplicated and sorted before calling Include or the results are undefined.
func (a *UnsignedArray) Include(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		a.Timestamps = a.Timestamps[:0]
		a.Values = a.Values[:0]
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() && a.Timestamps[rmax] == max {
		rmax++
	}

	if rmin > -1 {
		ts := a.Timestamps[:rmax-rmin]
		copy(ts, a.Timestamps[rmin:rmax])
		a.Timestamps = ts
		vs := a.Values[:rmax-rmin]
		copy(vs, a.Values[rmin:rmax])
		a.Values = vs
	} else {
		a.Timestamps = a.Timestamps[:rmax]
		a.Values = a.Values[:rmax]
	}
}

// Merge overlays b to top of a.  If two values conflict with
// the same timestamp, b is used.  Both a and b must be sorted
// in ascending order.
func (a *UnsignedArray) Merge(b *UnsignedArray) {
	if a.Len() == 0 {
		*a = *b
		return
	}

	if b.Len() == 0 {
		return
	}

	// Normally, both a and b should not contain duplicates.  Due to a bug in older versions, it's
	// possible stored blocks might contain duplicate values.  Remove them if they exists before
	// merging.
	// a = a.Deduplicate()
	// b = b.Deduplicate()

	if a.MaxTime() < b.MinTime() {
		a.Timestamps = append(a.Timestamps, b.Timestamps...)
		a.Values = append(a.Values, b.Values...)
		return
	}

	if b.MaxTime() < a.MinTime() {
		var tmp UnsignedArray
		tmp.Timestamps = append(b.Timestamps, a.Timestamps...)
		tmp.Values = append(b.Values, a.Values...)
		*a = tmp
		return
	}

	out := NewUnsignedArrayLen(a.Len() + b.Len())
	i, j, k := 0, 0, 0
	for i < len(a.Timestamps) && j < len(b.Timestamps) {
		if a.Timestamps[i] < b.Timestamps[j] {
			out.Timestamps[k] = a.Timestamps[i]
			out.Values[k] = a.Values[i]
			i++
		} else if a.Timestamps[i] == b.Timestamps[j] {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			i++
			j++
		} else {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			j++
		}
		k++
	}

	if i < len(a.Timestamps) {
		n := copy(out.Timestamps[k:], a.Timestamps[i:])
		copy(out.Values[k:], a.Values[i:])
		k += n
	} else if j < len(b.Timestamps) {
		n := copy(out.Timestamps[k:], b.Timestamps[j:])
		copy(out.Values[k:], b.Values[j:])
		k += n
	}

	a.Timestamps = out.Timestamps[:k]
	a.Values = out.Values[:k]
}

type StringArray struct {
	Timestamps []int64
	Values     []string
}

func NewStringArrayLen(sz int) *StringArray {
	return &StringArray{
		Timestamps: make([]int64, sz),
		Values:     make([]string, sz),
	}
}

func (a *StringArray) MinTime() int64 {
	return a.Timestamps[0]
}

func (a *StringArray) MaxTime() int64 {
	return a.Timestamps[len(a.Timestamps)-1]
}

func (a *StringArray) Len() int {
	return len(a.Timestamps)
}

// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *StringArray) search(v int64) int {
	// Define: f(x) → a.Timestamps[x] < v
	// Define: f(-1) == true, f(n) == false
	// Invariant: f(lo-1) == true, f(hi) == false
	lo := 0
	hi := a.Len()
	for lo < hi {
		mid := int(uint(lo+hi) >> 1)
		if a.Timestamps[mid] < v {
			lo = mid + 1 // preserves f(lo-1) == true
		} else {
			hi = mid // preserves f(hi) == false
		}
	}

	// lo == hi
	return lo
}

// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling FindRange or the results
// are undefined.
func (a *StringArray) FindRange(min, max int64) (int, int) {
	if a.Len() == 0 || min > max {
		return -1, -1
	}

	minVal := a.MinTime()
	maxVal := a.MaxTime()

	if maxVal < min || minVal > max {
		return -1, -1
	}

	return a.search(min), a.search(max)
}

// Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *StringArray) Exclude(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() {
		if a.Timestamps[rmax] == max {
			rmax++
		}
		rest := a.Len() - rmax
		if rest > 0 {
			ts := a.Timestamps[:rmin+rest]
			copy(ts[rmin:], a.Timestamps[rmax:])
			a.Timestamps = ts

			vs := a.Values[:rmin+rest]
			copy(vs[rmin:], a.Values[rmax:])
			a.Values = vs
			return
		}
	}

	a.Timestamps = a.Timestamps[:rmin]
	a.Values = a.Values[:rmin]
}

// Include returns the subset values between min and max inclusive. The values must
// be deduplicated and sorted before calling Include or the results are undefined.
func (a *StringArray) Include(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		a.Timestamps = a.Timestamps[:0]
		a.Values = a.Values[:0]
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() && a.Timestamps[rmax] == max {
		rmax++
	}

	if rmin > -1 {
		ts := a.Timestamps[:rmax-rmin]
		copy(ts, a.Timestamps[rmin:rmax])
		a.Timestamps = ts
		vs := a.Values[:rmax-rmin]
		copy(vs, a.Values[rmin:rmax])
		a.Values = vs
	} else {
		a.Timestamps = a.Timestamps[:rmax]
		a.Values = a.Values[:rmax]
	}
}

// Merge overlays b to top of a.  If two values conflict with
// the same timestamp, b is used.  Both a and b must be sorted
// in ascending order.
func (a *StringArray) Merge(b *StringArray) {
	if a.Len() == 0 {
		*a = *b
		return
	}

	if b.Len() == 0 {
		return
	}

	// Normally, both a and b should not contain duplicates.  Due to a bug in older versions, it's
	// possible stored blocks might contain duplicate values.  Remove them if they exists before
	// merging.
	// a = a.Deduplicate()
	// b = b.Deduplicate()

	if a.MaxTime() < b.MinTime() {
		a.Timestamps = append(a.Timestamps, b.Timestamps...)
		a.Values = append(a.Values, b.Values...)
		return
	}

	if b.MaxTime() < a.MinTime() {
		var tmp StringArray
		tmp.Timestamps = append(b.Timestamps, a.Timestamps...)
		tmp.Values = append(b.Values, a.Values...)
		*a = tmp
		return
	}

	out := NewStringArrayLen(a.Len() + b.Len())
	i, j, k := 0, 0, 0
	for i < len(a.Timestamps) && j < len(b.Timestamps) {
		if a.Timestamps[i] < b.Timestamps[j] {
			out.Timestamps[k] = a.Timestamps[i]
			out.Values[k] = a.Values[i]
			i++
		} else if a.Timestamps[i] == b.Timestamps[j] {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			i++
			j++
		} else {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			j++
		}
		k++
	}

	if i < len(a.Timestamps) {
		n := copy(out.Timestamps[k:], a.Timestamps[i:])
		copy(out.Values[k:], a.Values[i:])
		k += n
	} else if j < len(b.Timestamps) {
		n := copy(out.Timestamps[k:], b.Timestamps[j:])
		copy(out.Values[k:], b.Values[j:])
		k += n
	}

	a.Timestamps = out.Timestamps[:k]
	a.Values = out.Values[:k]
}

type BooleanArray struct {
	Timestamps []int64
	Values     []bool
}

func NewBooleanArrayLen(sz int) *BooleanArray {
	return &BooleanArray{
		Timestamps: make([]int64, sz),
		Values:     make([]bool, sz),
	}
}

func (a *BooleanArray) MinTime() int64 {
	return a.Timestamps[0]
}

func (a *BooleanArray) MaxTime() int64 {
	return a.Timestamps[len(a.Timestamps)-1]
}

func (a *BooleanArray) Len() int {
	return len(a.Timestamps)
}

// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *BooleanArray) search(v int64) int {
	// Define: f(x) → a.Timestamps[x] < v
	// Define: f(-1) == true, f(n) == false
	// Invariant: f(lo-1) == true, f(hi) == false
	lo := 0
	hi := a.Len()
	for lo < hi {
		mid := int(uint(lo+hi) >> 1)
		if a.Timestamps[mid] < v {
			lo = mid + 1 // preserves f(lo-1) == true
		} else {
			hi = mid // preserves f(hi) == false
		}
	}

	// lo == hi
	return lo
}

// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling FindRange or the results
// are undefined.
func (a *BooleanArray) FindRange(min, max int64) (int, int) {
	if a.Len() == 0 || min > max {
		return -1, -1
	}

	minVal := a.MinTime()
	maxVal := a.MaxTime()

	if maxVal < min || minVal > max {
		return -1, -1
	}

	return a.search(min), a.search(max)
}

// Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *BooleanArray) Exclude(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() {
		if a.Timestamps[rmax] == max {
			rmax++
		}
		rest := a.Len() - rmax
		if rest > 0 {
			ts := a.Timestamps[:rmin+rest]
			copy(ts[rmin:], a.Timestamps[rmax:])
			a.Timestamps = ts

			vs := a.Values[:rmin+rest]
			copy(vs[rmin:], a.Values[rmax:])
			a.Values = vs
			return
		}
	}

	a.Timestamps = a.Timestamps[:rmin]
	a.Values = a.Values[:rmin]
}

// Include returns the subset values between min and max inclusive. The values must
// be deduplicated and sorted before calling Include or the results are undefined.
func (a *BooleanArray) Include(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		a.Timestamps = a.Timestamps[:0]
		a.Values = a.Values[:0]
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() && a.Timestamps[rmax] == max {
		rmax++
	}

	if rmin > -1 {
		ts := a.Timestamps[:rmax-rmin]
		copy(ts, a.Timestamps[rmin:rmax])
		a.Timestamps = ts
		vs := a.Values[:rmax-rmin]
		copy(vs, a.Values[rmin:rmax])
		a.Values = vs
	} else {
		a.Timestamps = a.Timestamps[:rmax]
		a.Values = a.Values[:rmax]
	}
}

// Merge overlays b to top of a.  If two values conflict with
// the same timestamp, b is used.  Both a and b must be sorted
// in ascending order.
func (a *BooleanArray) Merge(b *BooleanArray) {
	if a.Len() == 0 {
		*a = *b
		return
	}

	if b.Len() == 0 {
		return
	}

	// Normally, both a and b should not contain duplicates.  Due to a bug in older versions, it's
	// possible stored blocks might contain duplicate values.  Remove them if they exists before
	// merging.
	// a = a.Deduplicate()
	// b = b.Deduplicate()

	if a.MaxTime() < b.MinTime() {
		a.Timestamps = append(a.Timestamps, b.Timestamps...)
		a.Values = append(a.Values, b.Values...)
		return
	}

	if b.MaxTime() < a.MinTime() {
		var tmp BooleanArray
		tmp.Timestamps = append(b.Timestamps, a.Timestamps...)
		tmp.Values = append(b.Values, a.Values...)
		*a = tmp
		return
	}

	out := NewBooleanArrayLen(a.Len() + b.Len())
	i, j, k := 0, 0, 0
	for i < len(a.Timestamps) && j < len(b.Timestamps) {
		if a.Timestamps[i] < b.Timestamps[j] {
			out.Timestamps[k] = a.Timestamps[i]
			out.Values[k] = a.Values[i]
			i++
		} else if a.Timestamps[i] == b.Timestamps[j] {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			i++
			j++
		} else {
			out.Timestamps[k] = b.Timestamps[j]
			out.Values[k] = b.Values[j]
			j++
		}
		k++
	}

	if i < len(a.Timestamps) {
		n := copy(out.Timestamps[k:], a.Timestamps[i:])
		copy(out.Values[k:], a.Values[i:])
		k += n
	} else if j < len(b.Timestamps) {
		n := copy(out.Timestamps[k:], b.Timestamps[j:])
		copy(out.Values[k:], b.Values[j:])
		k += n
	}

	a.Timestamps = out.Timestamps[:k]
	a.Values = out.Values[:k]
}

type TimestampArray struct {
	Timestamps []int64
}

func NewTimestampArrayLen(sz int) *TimestampArray {
	return &TimestampArray{
		Timestamps: make([]int64, sz),
	}
}

func (a *TimestampArray) MinTime() int64 {
	return a.Timestamps[0]
}

func (a *TimestampArray) MaxTime() int64 {
	return a.Timestamps[len(a.Timestamps)-1]
}

func (a *TimestampArray) Len() int {
	return len(a.Timestamps)
}

// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *TimestampArray) search(v int64) int {
	// Define: f(x) → a.Timestamps[x] < v
	// Define: f(-1) == true, f(n) == false
	// Invariant: f(lo-1) == true, f(hi) == false
	lo := 0
	hi := a.Len()
	for lo < hi {
		mid := int(uint(lo+hi) >> 1)
		if a.Timestamps[mid] < v {
			lo = mid + 1 // preserves f(lo-1) == true
		} else {
			hi = mid // preserves f(hi) == false
		}
	}

	// lo == hi
	return lo
}

// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling FindRange or the results
// are undefined.
func (a *TimestampArray) FindRange(min, max int64) (int, int) {
	if a.Len() == 0 || min > max {
		return -1, -1
	}

	minVal := a.MinTime()
	maxVal := a.MaxTime()

	if maxVal < min || minVal > max {
		return -1, -1
	}

	return a.search(min), a.search(max)
}

// Exclude removes the subset of timestamps in [min, max]. The timestamps must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *TimestampArray) Exclude(min, max int64) {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		return
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if rmax < a.Len() {
		if a.Timestamps[rmax] == max {
			rmax++
		}
		rest := a.Len() - rmax
		if rest > 0 {
			ts := a.Timestamps[:rmin+rest]
			copy(ts[rmin:], a.Timestamps[rmax:])
			a.Timestamps = ts
			return
		}
	}

	a.Timestamps = a.Timestamps[:rmin]
}

// Contains returns true if values exist between min and max inclusive. The
// values must be sorted before calling Contains or the results are undefined.
func (a *TimestampArray) Contains(min, max int64) bool {
	rmin, rmax := a.FindRange(min, max)
	if rmin == -1 && rmax == -1 {
		return false
	}

	// a.Timestamps[rmin] ≥ min
	// a.Timestamps[rmax] ≥ max

	if a.Timestamps[rmin] == min {
		return true
	}

	if rmax < a.Len() && a.Timestamps[rmax] == max {
		return true
	}

	return rmax-rmin > 0
}
