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

package reads

import (
	"errors"

	"github.com/influxdata/influxdb/v2/tsdb/cursors"
)

const (
	// MaxPointsPerBlock is the maximum number of points in an encoded
	// block in a TSM file. It should match the value in the tsm1
	// package, but we don't want to import it.
	MaxPointsPerBlock = 1000
)

// ********************
// Float Array Cursor

type floatArrayFilterCursor struct {
	cursors.FloatArrayCursor
	cond expression
	m    *singleValue
	res  *cursors.FloatArray
	tmp  *cursors.FloatArray
}

func newFloatFilterArrayCursor(cond expression) *floatArrayFilterCursor {
	return &floatArrayFilterCursor{
		cond: cond,
		m:    &singleValue{},
		res:  cursors.NewFloatArrayLen(MaxPointsPerBlock),
		tmp:  &cursors.FloatArray{},
	}
}

func (c *floatArrayFilterCursor) reset(cur cursors.FloatArrayCursor) {
	c.FloatArrayCursor = cur
	c.tmp.Timestamps, c.tmp.Values = nil, nil
}

func (c *floatArrayFilterCursor) Stats() cursors.CursorStats { return c.FloatArrayCursor.Stats() }

func (c *floatArrayFilterCursor) Next() *cursors.FloatArray {
	pos := 0
	c.res.Timestamps = c.res.Timestamps[:cap(c.res.Timestamps)]
	c.res.Values = c.res.Values[:cap(c.res.Values)]

	var a *cursors.FloatArray

	if c.tmp.Len() > 0 {
		a = c.tmp
	} else {
		a = c.FloatArrayCursor.Next()
	}

LOOP:
	for len(a.Timestamps) > 0 {
		for i, v := range a.Values {
			c.m.v = v
			if c.cond.EvalBool(c.m) {
				c.res.Timestamps[pos] = a.Timestamps[i]
				c.res.Values[pos] = v
				pos++
				if pos >= MaxPointsPerBlock {
					c.tmp.Timestamps = a.Timestamps[i+1:]
					c.tmp.Values = a.Values[i+1:]
					break LOOP
				}
			}
		}

		// Clear bufferred timestamps & values if we make it through a cursor.
		// The break above will skip this if a cursor is partially read.
		c.tmp.Timestamps = nil
		c.tmp.Values = nil

		a = c.FloatArrayCursor.Next()
	}

	c.res.Timestamps = c.res.Timestamps[:pos]
	c.res.Values = c.res.Values[:pos]

	return c.res
}

type floatArrayCursor struct {
	cursors.FloatArrayCursor
	cursorContext
	filter *floatArrayFilterCursor
}

func (c *floatArrayCursor) reset(cur cursors.FloatArrayCursor, cursorIterator cursors.CursorIterator, cond expression) {
	if cond != nil {
		if c.filter == nil {
			c.filter = newFloatFilterArrayCursor(cond)
		}
		c.filter.reset(cur)
		cur = c.filter
	}

	c.FloatArrayCursor = cur
	c.cursorIterator = cursorIterator
	c.err = nil
}

func (c *floatArrayCursor) Err() error { return c.err }

func (c *floatArrayCursor) Stats() cursors.CursorStats {
	return c.FloatArrayCursor.Stats()
}

func (c *floatArrayCursor) Next() *cursors.FloatArray {
	for {
		a := c.FloatArrayCursor.Next()
		if a.Len() == 0 {
			if c.nextArrayCursor() {
				continue
			}
		}
		return a
	}
}

func (c *floatArrayCursor) nextArrayCursor() bool {
	if c.cursorIterator == nil {
		return false
	}

	c.FloatArrayCursor.Close()

	cur, _ := c.cursorIterator.Next(c.ctx, c.req)
	c.cursorIterator = nil

	var ok bool
	if cur != nil {
		var next cursors.FloatArrayCursor
		next, ok = cur.(cursors.FloatArrayCursor)
		if !ok {
			cur.Close()
			next = FloatEmptyArrayCursor
			c.cursorIterator = nil
			c.err = errors.New("expected float cursor")
		} else {
			if c.filter != nil {
				c.filter.reset(next)
				next = c.filter
			}
		}
		c.FloatArrayCursor = next
	} else {
		c.FloatArrayCursor = FloatEmptyArrayCursor
	}

	return ok
}

type floatArraySumCursor struct {
	cursors.FloatArrayCursor
	ts  [1]int64
	vs  [1]float64
	res *cursors.FloatArray
}

func newFloatArraySumCursor(cur cursors.FloatArrayCursor) *floatArraySumCursor {
	return &floatArraySumCursor{
		FloatArrayCursor: cur,
		res:              &cursors.FloatArray{},
	}
}

func (c floatArraySumCursor) Stats() cursors.CursorStats { return c.FloatArrayCursor.Stats() }

func (c floatArraySumCursor) Next() *cursors.FloatArray {
	a := c.FloatArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return a
	}

	ts := a.Timestamps[0]
	var acc float64

	for {
		for _, v := range a.Values {
			acc += v
		}
		a = c.FloatArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			c.ts[0] = ts
			c.vs[0] = acc
			c.res.Timestamps = c.ts[:]
			c.res.Values = c.vs[:]
			return c.res
		}
	}
}

type integerFloatCountArrayCursor struct {
	cursors.FloatArrayCursor
}

func (c *integerFloatCountArrayCursor) Stats() cursors.CursorStats {
	return c.FloatArrayCursor.Stats()
}

func (c *integerFloatCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.FloatArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return &cursors.IntegerArray{}
	}

	ts := a.Timestamps[0]
	var acc int64
	for {
		acc += int64(len(a.Timestamps))
		a = c.FloatArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			res := cursors.NewIntegerArrayLen(1)
			res.Timestamps[0] = ts
			res.Values[0] = acc
			return res
		}
	}
}

type integerFloatWindowCountArrayCursor struct {
	cursors.FloatArrayCursor
	every int64
}

func (c *integerFloatWindowCountArrayCursor) Stats() cursors.CursorStats {
	return c.FloatArrayCursor.Stats()
}

func (c *integerFloatWindowCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.FloatArrayCursor.Next()
	if a.Len() == 0 {
		return &cursors.IntegerArray{}
	}

	res := cursors.NewIntegerArrayLen(0)
	rowIdx := 0
	var acc int64 = 0

	// enumerate windows
WINDOWS:
	for {
		firstTimestamp := a.Timestamps[rowIdx]
		windowStart := firstTimestamp - firstTimestamp%c.every
		windowEnd := windowStart + c.every
		for ; rowIdx < a.Len(); rowIdx++ {
			ts := a.Timestamps[rowIdx]
			if ts >= windowEnd {
				// new window detected, close the current window
				if acc > 0 {
					res.Timestamps = append(res.Timestamps, windowEnd)
					res.Values = append(res.Values, acc)
				}
				// start the new window
				acc = 0
				continue WINDOWS
			} else {
				acc++
			}
		}
		// get the next chunk
		a = c.FloatArrayCursor.Next()
		if a.Len() == 0 {
			if acc > 0 {
				res.Timestamps = append(res.Timestamps, windowEnd)
				res.Values = append(res.Values, acc)
			}
			break
		}
		rowIdx = 0
	}
	return res
}

type floatEmptyArrayCursor struct {
	res cursors.FloatArray
}

var FloatEmptyArrayCursor cursors.FloatArrayCursor = &floatEmptyArrayCursor{}

func (c *floatEmptyArrayCursor) Err() error                 { return nil }
func (c *floatEmptyArrayCursor) Close()                     {}
func (c *floatEmptyArrayCursor) Stats() cursors.CursorStats { return cursors.CursorStats{} }
func (c *floatEmptyArrayCursor) Next() *cursors.FloatArray  { return &c.res }

// ********************
// Integer Array Cursor

type integerArrayFilterCursor struct {
	cursors.IntegerArrayCursor
	cond expression
	m    *singleValue
	res  *cursors.IntegerArray
	tmp  *cursors.IntegerArray
}

func newIntegerFilterArrayCursor(cond expression) *integerArrayFilterCursor {
	return &integerArrayFilterCursor{
		cond: cond,
		m:    &singleValue{},
		res:  cursors.NewIntegerArrayLen(MaxPointsPerBlock),
		tmp:  &cursors.IntegerArray{},
	}
}

func (c *integerArrayFilterCursor) reset(cur cursors.IntegerArrayCursor) {
	c.IntegerArrayCursor = cur
	c.tmp.Timestamps, c.tmp.Values = nil, nil
}

func (c *integerArrayFilterCursor) Stats() cursors.CursorStats { return c.IntegerArrayCursor.Stats() }

func (c *integerArrayFilterCursor) Next() *cursors.IntegerArray {
	pos := 0
	c.res.Timestamps = c.res.Timestamps[:cap(c.res.Timestamps)]
	c.res.Values = c.res.Values[:cap(c.res.Values)]

	var a *cursors.IntegerArray

	if c.tmp.Len() > 0 {
		a = c.tmp
	} else {
		a = c.IntegerArrayCursor.Next()
	}

LOOP:
	for len(a.Timestamps) > 0 {
		for i, v := range a.Values {
			c.m.v = v
			if c.cond.EvalBool(c.m) {
				c.res.Timestamps[pos] = a.Timestamps[i]
				c.res.Values[pos] = v
				pos++
				if pos >= MaxPointsPerBlock {
					c.tmp.Timestamps = a.Timestamps[i+1:]
					c.tmp.Values = a.Values[i+1:]
					break LOOP
				}
			}
		}

		// Clear bufferred timestamps & values if we make it through a cursor.
		// The break above will skip this if a cursor is partially read.
		c.tmp.Timestamps = nil
		c.tmp.Values = nil

		a = c.IntegerArrayCursor.Next()
	}

	c.res.Timestamps = c.res.Timestamps[:pos]
	c.res.Values = c.res.Values[:pos]

	return c.res
}

type integerArrayCursor struct {
	cursors.IntegerArrayCursor
	cursorContext
	filter *integerArrayFilterCursor
}

func (c *integerArrayCursor) reset(cur cursors.IntegerArrayCursor, cursorIterator cursors.CursorIterator, cond expression) {
	if cond != nil {
		if c.filter == nil {
			c.filter = newIntegerFilterArrayCursor(cond)
		}
		c.filter.reset(cur)
		cur = c.filter
	}

	c.IntegerArrayCursor = cur
	c.cursorIterator = cursorIterator
	c.err = nil
}

func (c *integerArrayCursor) Err() error { return c.err }

func (c *integerArrayCursor) Stats() cursors.CursorStats {
	return c.IntegerArrayCursor.Stats()
}

func (c *integerArrayCursor) Next() *cursors.IntegerArray {
	for {
		a := c.IntegerArrayCursor.Next()
		if a.Len() == 0 {
			if c.nextArrayCursor() {
				continue
			}
		}
		return a
	}
}

func (c *integerArrayCursor) nextArrayCursor() bool {
	if c.cursorIterator == nil {
		return false
	}

	c.IntegerArrayCursor.Close()

	cur, _ := c.cursorIterator.Next(c.ctx, c.req)
	c.cursorIterator = nil

	var ok bool
	if cur != nil {
		var next cursors.IntegerArrayCursor
		next, ok = cur.(cursors.IntegerArrayCursor)
		if !ok {
			cur.Close()
			next = IntegerEmptyArrayCursor
			c.cursorIterator = nil
			c.err = errors.New("expected integer cursor")
		} else {
			if c.filter != nil {
				c.filter.reset(next)
				next = c.filter
			}
		}
		c.IntegerArrayCursor = next
	} else {
		c.IntegerArrayCursor = IntegerEmptyArrayCursor
	}

	return ok
}

type integerArraySumCursor struct {
	cursors.IntegerArrayCursor
	ts  [1]int64
	vs  [1]int64
	res *cursors.IntegerArray
}

func newIntegerArraySumCursor(cur cursors.IntegerArrayCursor) *integerArraySumCursor {
	return &integerArraySumCursor{
		IntegerArrayCursor: cur,
		res:                &cursors.IntegerArray{},
	}
}

func (c integerArraySumCursor) Stats() cursors.CursorStats { return c.IntegerArrayCursor.Stats() }

func (c integerArraySumCursor) Next() *cursors.IntegerArray {
	a := c.IntegerArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return a
	}

	ts := a.Timestamps[0]
	var acc int64

	for {
		for _, v := range a.Values {
			acc += v
		}
		a = c.IntegerArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			c.ts[0] = ts
			c.vs[0] = acc
			c.res.Timestamps = c.ts[:]
			c.res.Values = c.vs[:]
			return c.res
		}
	}
}

type integerIntegerCountArrayCursor struct {
	cursors.IntegerArrayCursor
}

func (c *integerIntegerCountArrayCursor) Stats() cursors.CursorStats {
	return c.IntegerArrayCursor.Stats()
}

func (c *integerIntegerCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.IntegerArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return &cursors.IntegerArray{}
	}

	ts := a.Timestamps[0]
	var acc int64
	for {
		acc += int64(len(a.Timestamps))
		a = c.IntegerArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			res := cursors.NewIntegerArrayLen(1)
			res.Timestamps[0] = ts
			res.Values[0] = acc
			return res
		}
	}
}

type integerIntegerWindowCountArrayCursor struct {
	cursors.IntegerArrayCursor
	every int64
}

func (c *integerIntegerWindowCountArrayCursor) Stats() cursors.CursorStats {
	return c.IntegerArrayCursor.Stats()
}

func (c *integerIntegerWindowCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.IntegerArrayCursor.Next()
	if a.Len() == 0 {
		return &cursors.IntegerArray{}
	}

	res := cursors.NewIntegerArrayLen(0)
	rowIdx := 0
	var acc int64 = 0

	// enumerate windows
WINDOWS:
	for {
		firstTimestamp := a.Timestamps[rowIdx]
		windowStart := firstTimestamp - firstTimestamp%c.every
		windowEnd := windowStart + c.every
		for ; rowIdx < a.Len(); rowIdx++ {
			ts := a.Timestamps[rowIdx]
			if ts >= windowEnd {
				// new window detected, close the current window
				if acc > 0 {
					res.Timestamps = append(res.Timestamps, windowEnd)
					res.Values = append(res.Values, acc)
				}
				// start the new window
				acc = 0
				continue WINDOWS
			} else {
				acc++
			}
		}
		// get the next chunk
		a = c.IntegerArrayCursor.Next()
		if a.Len() == 0 {
			if acc > 0 {
				res.Timestamps = append(res.Timestamps, windowEnd)
				res.Values = append(res.Values, acc)
			}
			break
		}
		rowIdx = 0
	}
	return res
}

type integerEmptyArrayCursor struct {
	res cursors.IntegerArray
}

var IntegerEmptyArrayCursor cursors.IntegerArrayCursor = &integerEmptyArrayCursor{}

func (c *integerEmptyArrayCursor) Err() error                  { return nil }
func (c *integerEmptyArrayCursor) Close()                      {}
func (c *integerEmptyArrayCursor) Stats() cursors.CursorStats  { return cursors.CursorStats{} }
func (c *integerEmptyArrayCursor) Next() *cursors.IntegerArray { return &c.res }

// ********************
// Unsigned Array Cursor

type unsignedArrayFilterCursor struct {
	cursors.UnsignedArrayCursor
	cond expression
	m    *singleValue
	res  *cursors.UnsignedArray
	tmp  *cursors.UnsignedArray
}

func newUnsignedFilterArrayCursor(cond expression) *unsignedArrayFilterCursor {
	return &unsignedArrayFilterCursor{
		cond: cond,
		m:    &singleValue{},
		res:  cursors.NewUnsignedArrayLen(MaxPointsPerBlock),
		tmp:  &cursors.UnsignedArray{},
	}
}

func (c *unsignedArrayFilterCursor) reset(cur cursors.UnsignedArrayCursor) {
	c.UnsignedArrayCursor = cur
	c.tmp.Timestamps, c.tmp.Values = nil, nil
}

func (c *unsignedArrayFilterCursor) Stats() cursors.CursorStats { return c.UnsignedArrayCursor.Stats() }

func (c *unsignedArrayFilterCursor) Next() *cursors.UnsignedArray {
	pos := 0
	c.res.Timestamps = c.res.Timestamps[:cap(c.res.Timestamps)]
	c.res.Values = c.res.Values[:cap(c.res.Values)]

	var a *cursors.UnsignedArray

	if c.tmp.Len() > 0 {
		a = c.tmp
	} else {
		a = c.UnsignedArrayCursor.Next()
	}

LOOP:
	for len(a.Timestamps) > 0 {
		for i, v := range a.Values {
			c.m.v = v
			if c.cond.EvalBool(c.m) {
				c.res.Timestamps[pos] = a.Timestamps[i]
				c.res.Values[pos] = v
				pos++
				if pos >= MaxPointsPerBlock {
					c.tmp.Timestamps = a.Timestamps[i+1:]
					c.tmp.Values = a.Values[i+1:]
					break LOOP
				}
			}
		}

		// Clear bufferred timestamps & values if we make it through a cursor.
		// The break above will skip this if a cursor is partially read.
		c.tmp.Timestamps = nil
		c.tmp.Values = nil

		a = c.UnsignedArrayCursor.Next()
	}

	c.res.Timestamps = c.res.Timestamps[:pos]
	c.res.Values = c.res.Values[:pos]

	return c.res
}

type unsignedArrayCursor struct {
	cursors.UnsignedArrayCursor
	cursorContext
	filter *unsignedArrayFilterCursor
}

func (c *unsignedArrayCursor) reset(cur cursors.UnsignedArrayCursor, cursorIterator cursors.CursorIterator, cond expression) {
	if cond != nil {
		if c.filter == nil {
			c.filter = newUnsignedFilterArrayCursor(cond)
		}
		c.filter.reset(cur)
		cur = c.filter
	}

	c.UnsignedArrayCursor = cur
	c.cursorIterator = cursorIterator
	c.err = nil
}

func (c *unsignedArrayCursor) Err() error { return c.err }

func (c *unsignedArrayCursor) Stats() cursors.CursorStats {
	return c.UnsignedArrayCursor.Stats()
}

func (c *unsignedArrayCursor) Next() *cursors.UnsignedArray {
	for {
		a := c.UnsignedArrayCursor.Next()
		if a.Len() == 0 {
			if c.nextArrayCursor() {
				continue
			}
		}
		return a
	}
}

func (c *unsignedArrayCursor) nextArrayCursor() bool {
	if c.cursorIterator == nil {
		return false
	}

	c.UnsignedArrayCursor.Close()

	cur, _ := c.cursorIterator.Next(c.ctx, c.req)
	c.cursorIterator = nil

	var ok bool
	if cur != nil {
		var next cursors.UnsignedArrayCursor
		next, ok = cur.(cursors.UnsignedArrayCursor)
		if !ok {
			cur.Close()
			next = UnsignedEmptyArrayCursor
			c.cursorIterator = nil
			c.err = errors.New("expected unsigned cursor")
		} else {
			if c.filter != nil {
				c.filter.reset(next)
				next = c.filter
			}
		}
		c.UnsignedArrayCursor = next
	} else {
		c.UnsignedArrayCursor = UnsignedEmptyArrayCursor
	}

	return ok
}

type unsignedArraySumCursor struct {
	cursors.UnsignedArrayCursor
	ts  [1]int64
	vs  [1]uint64
	res *cursors.UnsignedArray
}

func newUnsignedArraySumCursor(cur cursors.UnsignedArrayCursor) *unsignedArraySumCursor {
	return &unsignedArraySumCursor{
		UnsignedArrayCursor: cur,
		res:                 &cursors.UnsignedArray{},
	}
}

func (c unsignedArraySumCursor) Stats() cursors.CursorStats { return c.UnsignedArrayCursor.Stats() }

func (c unsignedArraySumCursor) Next() *cursors.UnsignedArray {
	a := c.UnsignedArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return a
	}

	ts := a.Timestamps[0]
	var acc uint64

	for {
		for _, v := range a.Values {
			acc += v
		}
		a = c.UnsignedArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			c.ts[0] = ts
			c.vs[0] = acc
			c.res.Timestamps = c.ts[:]
			c.res.Values = c.vs[:]
			return c.res
		}
	}
}

type integerUnsignedCountArrayCursor struct {
	cursors.UnsignedArrayCursor
}

func (c *integerUnsignedCountArrayCursor) Stats() cursors.CursorStats {
	return c.UnsignedArrayCursor.Stats()
}

func (c *integerUnsignedCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.UnsignedArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return &cursors.IntegerArray{}
	}

	ts := a.Timestamps[0]
	var acc int64
	for {
		acc += int64(len(a.Timestamps))
		a = c.UnsignedArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			res := cursors.NewIntegerArrayLen(1)
			res.Timestamps[0] = ts
			res.Values[0] = acc
			return res
		}
	}
}

type integerUnsignedWindowCountArrayCursor struct {
	cursors.UnsignedArrayCursor
	every int64
}

func (c *integerUnsignedWindowCountArrayCursor) Stats() cursors.CursorStats {
	return c.UnsignedArrayCursor.Stats()
}

func (c *integerUnsignedWindowCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.UnsignedArrayCursor.Next()
	if a.Len() == 0 {
		return &cursors.IntegerArray{}
	}

	res := cursors.NewIntegerArrayLen(0)
	rowIdx := 0
	var acc int64 = 0

	// enumerate windows
WINDOWS:
	for {
		firstTimestamp := a.Timestamps[rowIdx]
		windowStart := firstTimestamp - firstTimestamp%c.every
		windowEnd := windowStart + c.every
		for ; rowIdx < a.Len(); rowIdx++ {
			ts := a.Timestamps[rowIdx]
			if ts >= windowEnd {
				// new window detected, close the current window
				if acc > 0 {
					res.Timestamps = append(res.Timestamps, windowEnd)
					res.Values = append(res.Values, acc)
				}
				// start the new window
				acc = 0
				continue WINDOWS
			} else {
				acc++
			}
		}
		// get the next chunk
		a = c.UnsignedArrayCursor.Next()
		if a.Len() == 0 {
			if acc > 0 {
				res.Timestamps = append(res.Timestamps, windowEnd)
				res.Values = append(res.Values, acc)
			}
			break
		}
		rowIdx = 0
	}
	return res
}

type unsignedEmptyArrayCursor struct {
	res cursors.UnsignedArray
}

var UnsignedEmptyArrayCursor cursors.UnsignedArrayCursor = &unsignedEmptyArrayCursor{}

func (c *unsignedEmptyArrayCursor) Err() error                   { return nil }
func (c *unsignedEmptyArrayCursor) Close()                       {}
func (c *unsignedEmptyArrayCursor) Stats() cursors.CursorStats   { return cursors.CursorStats{} }
func (c *unsignedEmptyArrayCursor) Next() *cursors.UnsignedArray { return &c.res }

// ********************
// String Array Cursor

type stringArrayFilterCursor struct {
	cursors.StringArrayCursor
	cond expression
	m    *singleValue
	res  *cursors.StringArray
	tmp  *cursors.StringArray
}

func newStringFilterArrayCursor(cond expression) *stringArrayFilterCursor {
	return &stringArrayFilterCursor{
		cond: cond,
		m:    &singleValue{},
		res:  cursors.NewStringArrayLen(MaxPointsPerBlock),
		tmp:  &cursors.StringArray{},
	}
}

func (c *stringArrayFilterCursor) reset(cur cursors.StringArrayCursor) {
	c.StringArrayCursor = cur
	c.tmp.Timestamps, c.tmp.Values = nil, nil
}

func (c *stringArrayFilterCursor) Stats() cursors.CursorStats { return c.StringArrayCursor.Stats() }

func (c *stringArrayFilterCursor) Next() *cursors.StringArray {
	pos := 0
	c.res.Timestamps = c.res.Timestamps[:cap(c.res.Timestamps)]
	c.res.Values = c.res.Values[:cap(c.res.Values)]

	var a *cursors.StringArray

	if c.tmp.Len() > 0 {
		a = c.tmp
	} else {
		a = c.StringArrayCursor.Next()
	}

LOOP:
	for len(a.Timestamps) > 0 {
		for i, v := range a.Values {
			c.m.v = v
			if c.cond.EvalBool(c.m) {
				c.res.Timestamps[pos] = a.Timestamps[i]
				c.res.Values[pos] = v
				pos++
				if pos >= MaxPointsPerBlock {
					c.tmp.Timestamps = a.Timestamps[i+1:]
					c.tmp.Values = a.Values[i+1:]
					break LOOP
				}
			}
		}

		// Clear bufferred timestamps & values if we make it through a cursor.
		// The break above will skip this if a cursor is partially read.
		c.tmp.Timestamps = nil
		c.tmp.Values = nil

		a = c.StringArrayCursor.Next()
	}

	c.res.Timestamps = c.res.Timestamps[:pos]
	c.res.Values = c.res.Values[:pos]

	return c.res
}

type stringArrayCursor struct {
	cursors.StringArrayCursor
	cursorContext
	filter *stringArrayFilterCursor
}

func (c *stringArrayCursor) reset(cur cursors.StringArrayCursor, cursorIterator cursors.CursorIterator, cond expression) {
	if cond != nil {
		if c.filter == nil {
			c.filter = newStringFilterArrayCursor(cond)
		}
		c.filter.reset(cur)
		cur = c.filter
	}

	c.StringArrayCursor = cur
	c.cursorIterator = cursorIterator
	c.err = nil
}

func (c *stringArrayCursor) Err() error { return c.err }

func (c *stringArrayCursor) Stats() cursors.CursorStats {
	return c.StringArrayCursor.Stats()
}

func (c *stringArrayCursor) Next() *cursors.StringArray {
	for {
		a := c.StringArrayCursor.Next()
		if a.Len() == 0 {
			if c.nextArrayCursor() {
				continue
			}
		}
		return a
	}
}

func (c *stringArrayCursor) nextArrayCursor() bool {
	if c.cursorIterator == nil {
		return false
	}

	c.StringArrayCursor.Close()

	cur, _ := c.cursorIterator.Next(c.ctx, c.req)
	c.cursorIterator = nil

	var ok bool
	if cur != nil {
		var next cursors.StringArrayCursor
		next, ok = cur.(cursors.StringArrayCursor)
		if !ok {
			cur.Close()
			next = StringEmptyArrayCursor
			c.cursorIterator = nil
			c.err = errors.New("expected string cursor")
		} else {
			if c.filter != nil {
				c.filter.reset(next)
				next = c.filter
			}
		}
		c.StringArrayCursor = next
	} else {
		c.StringArrayCursor = StringEmptyArrayCursor
	}

	return ok
}

type integerStringCountArrayCursor struct {
	cursors.StringArrayCursor
}

func (c *integerStringCountArrayCursor) Stats() cursors.CursorStats {
	return c.StringArrayCursor.Stats()
}

func (c *integerStringCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.StringArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return &cursors.IntegerArray{}
	}

	ts := a.Timestamps[0]
	var acc int64
	for {
		acc += int64(len(a.Timestamps))
		a = c.StringArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			res := cursors.NewIntegerArrayLen(1)
			res.Timestamps[0] = ts
			res.Values[0] = acc
			return res
		}
	}
}

type integerStringWindowCountArrayCursor struct {
	cursors.StringArrayCursor
	every int64
}

func (c *integerStringWindowCountArrayCursor) Stats() cursors.CursorStats {
	return c.StringArrayCursor.Stats()
}

func (c *integerStringWindowCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.StringArrayCursor.Next()
	if a.Len() == 0 {
		return &cursors.IntegerArray{}
	}

	res := cursors.NewIntegerArrayLen(0)
	rowIdx := 0
	var acc int64 = 0

	// enumerate windows
WINDOWS:
	for {
		firstTimestamp := a.Timestamps[rowIdx]
		windowStart := firstTimestamp - firstTimestamp%c.every
		windowEnd := windowStart + c.every
		for ; rowIdx < a.Len(); rowIdx++ {
			ts := a.Timestamps[rowIdx]
			if ts >= windowEnd {
				// new window detected, close the current window
				if acc > 0 {
					res.Timestamps = append(res.Timestamps, windowEnd)
					res.Values = append(res.Values, acc)
				}
				// start the new window
				acc = 0
				continue WINDOWS
			} else {
				acc++
			}
		}
		// get the next chunk
		a = c.StringArrayCursor.Next()
		if a.Len() == 0 {
			if acc > 0 {
				res.Timestamps = append(res.Timestamps, windowEnd)
				res.Values = append(res.Values, acc)
			}
			break
		}
		rowIdx = 0
	}
	return res
}

type stringEmptyArrayCursor struct {
	res cursors.StringArray
}

var StringEmptyArrayCursor cursors.StringArrayCursor = &stringEmptyArrayCursor{}

func (c *stringEmptyArrayCursor) Err() error                 { return nil }
func (c *stringEmptyArrayCursor) Close()                     {}
func (c *stringEmptyArrayCursor) Stats() cursors.CursorStats { return cursors.CursorStats{} }
func (c *stringEmptyArrayCursor) Next() *cursors.StringArray { return &c.res }

// ********************
// Boolean Array Cursor

type booleanArrayFilterCursor struct {
	cursors.BooleanArrayCursor
	cond expression
	m    *singleValue
	res  *cursors.BooleanArray
	tmp  *cursors.BooleanArray
}

func newBooleanFilterArrayCursor(cond expression) *booleanArrayFilterCursor {
	return &booleanArrayFilterCursor{
		cond: cond,
		m:    &singleValue{},
		res:  cursors.NewBooleanArrayLen(MaxPointsPerBlock),
		tmp:  &cursors.BooleanArray{},
	}
}

func (c *booleanArrayFilterCursor) reset(cur cursors.BooleanArrayCursor) {
	c.BooleanArrayCursor = cur
	c.tmp.Timestamps, c.tmp.Values = nil, nil
}

func (c *booleanArrayFilterCursor) Stats() cursors.CursorStats { return c.BooleanArrayCursor.Stats() }

func (c *booleanArrayFilterCursor) Next() *cursors.BooleanArray {
	pos := 0
	c.res.Timestamps = c.res.Timestamps[:cap(c.res.Timestamps)]
	c.res.Values = c.res.Values[:cap(c.res.Values)]

	var a *cursors.BooleanArray

	if c.tmp.Len() > 0 {
		a = c.tmp
	} else {
		a = c.BooleanArrayCursor.Next()
	}

LOOP:
	for len(a.Timestamps) > 0 {
		for i, v := range a.Values {
			c.m.v = v
			if c.cond.EvalBool(c.m) {
				c.res.Timestamps[pos] = a.Timestamps[i]
				c.res.Values[pos] = v
				pos++
				if pos >= MaxPointsPerBlock {
					c.tmp.Timestamps = a.Timestamps[i+1:]
					c.tmp.Values = a.Values[i+1:]
					break LOOP
				}
			}
		}

		// Clear bufferred timestamps & values if we make it through a cursor.
		// The break above will skip this if a cursor is partially read.
		c.tmp.Timestamps = nil
		c.tmp.Values = nil

		a = c.BooleanArrayCursor.Next()
	}

	c.res.Timestamps = c.res.Timestamps[:pos]
	c.res.Values = c.res.Values[:pos]

	return c.res
}

type booleanArrayCursor struct {
	cursors.BooleanArrayCursor
	cursorContext
	filter *booleanArrayFilterCursor
}

func (c *booleanArrayCursor) reset(cur cursors.BooleanArrayCursor, cursorIterator cursors.CursorIterator, cond expression) {
	if cond != nil {
		if c.filter == nil {
			c.filter = newBooleanFilterArrayCursor(cond)
		}
		c.filter.reset(cur)
		cur = c.filter
	}

	c.BooleanArrayCursor = cur
	c.cursorIterator = cursorIterator
	c.err = nil
}

func (c *booleanArrayCursor) Err() error { return c.err }

func (c *booleanArrayCursor) Stats() cursors.CursorStats {
	return c.BooleanArrayCursor.Stats()
}

func (c *booleanArrayCursor) Next() *cursors.BooleanArray {
	for {
		a := c.BooleanArrayCursor.Next()
		if a.Len() == 0 {
			if c.nextArrayCursor() {
				continue
			}
		}
		return a
	}
}

func (c *booleanArrayCursor) nextArrayCursor() bool {
	if c.cursorIterator == nil {
		return false
	}

	c.BooleanArrayCursor.Close()

	cur, _ := c.cursorIterator.Next(c.ctx, c.req)
	c.cursorIterator = nil

	var ok bool
	if cur != nil {
		var next cursors.BooleanArrayCursor
		next, ok = cur.(cursors.BooleanArrayCursor)
		if !ok {
			cur.Close()
			next = BooleanEmptyArrayCursor
			c.cursorIterator = nil
			c.err = errors.New("expected boolean cursor")
		} else {
			if c.filter != nil {
				c.filter.reset(next)
				next = c.filter
			}
		}
		c.BooleanArrayCursor = next
	} else {
		c.BooleanArrayCursor = BooleanEmptyArrayCursor
	}

	return ok
}

type integerBooleanCountArrayCursor struct {
	cursors.BooleanArrayCursor
}

func (c *integerBooleanCountArrayCursor) Stats() cursors.CursorStats {
	return c.BooleanArrayCursor.Stats()
}

func (c *integerBooleanCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.BooleanArrayCursor.Next()
	if len(a.Timestamps) == 0 {
		return &cursors.IntegerArray{}
	}

	ts := a.Timestamps[0]
	var acc int64
	for {
		acc += int64(len(a.Timestamps))
		a = c.BooleanArrayCursor.Next()
		if len(a.Timestamps) == 0 {
			res := cursors.NewIntegerArrayLen(1)
			res.Timestamps[0] = ts
			res.Values[0] = acc
			return res
		}
	}
}

type integerBooleanWindowCountArrayCursor struct {
	cursors.BooleanArrayCursor
	every int64
}

func (c *integerBooleanWindowCountArrayCursor) Stats() cursors.CursorStats {
	return c.BooleanArrayCursor.Stats()
}

func (c *integerBooleanWindowCountArrayCursor) Next() *cursors.IntegerArray {
	a := c.BooleanArrayCursor.Next()
	if a.Len() == 0 {
		return &cursors.IntegerArray{}
	}

	res := cursors.NewIntegerArrayLen(0)
	rowIdx := 0
	var acc int64 = 0

	// enumerate windows
WINDOWS:
	for {
		firstTimestamp := a.Timestamps[rowIdx]
		windowStart := firstTimestamp - firstTimestamp%c.every
		windowEnd := windowStart + c.every
		for ; rowIdx < a.Len(); rowIdx++ {
			ts := a.Timestamps[rowIdx]
			if ts >= windowEnd {
				// new window detected, close the current window
				if acc > 0 {
					res.Timestamps = append(res.Timestamps, windowEnd)
					res.Values = append(res.Values, acc)
				}
				// start the new window
				acc = 0
				continue WINDOWS
			} else {
				acc++
			}
		}
		// get the next chunk
		a = c.BooleanArrayCursor.Next()
		if a.Len() == 0 {
			if acc > 0 {
				res.Timestamps = append(res.Timestamps, windowEnd)
				res.Values = append(res.Values, acc)
			}
			break
		}
		rowIdx = 0
	}
	return res
}

type booleanEmptyArrayCursor struct {
	res cursors.BooleanArray
}

var BooleanEmptyArrayCursor cursors.BooleanArrayCursor = &booleanEmptyArrayCursor{}

func (c *booleanEmptyArrayCursor) Err() error                  { return nil }
func (c *booleanEmptyArrayCursor) Close()                      {}
func (c *booleanEmptyArrayCursor) Stats() cursors.CursorStats  { return cursors.CursorStats{} }
func (c *booleanEmptyArrayCursor) Next() *cursors.BooleanArray { return &c.res }
