// Columnar property storage: the engine-side column representations behind
// Snapshot property reads. Dense columns index directly by position (node id
// for node columns, outgoing-CSR position for rel columns); sparse columns
// binary-search (id, value) pairs sorted by id. The rank/select variants for
// the moderately-sparse band arrive with the builder (M4); the RCPG bridge
// never produces them.

package chickpeas

import (
	

	
)

// Dtype is the logical element type of a column, reported without narrowing.
type Dtype uint8

const (
	// DtypeI64 is an integer column.
	DtypeI64 Dtype = iota
	// DtypeF64 is a float column.
	DtypeF64
	// DtypeBool is a boolean column.
	DtypeBool
	// DtypeStr is an interned-string column.
	DtypeStr
)

// Column is one property column's storage. Get reads the value at a
// position; Entries iterates every (position, value) present, ascending.
// Concrete representations are internal -- read through Get/Entries or the
// typed Col readers.
type Column interface {
	// Get returns the value at pos; ok is false when absent.
	Get(pos uint32) (Value, bool)
	// Entries iterates (position, value) pairs in ascending position order.
	Entries() iter.Seq2[uint32, Value]
	// Dtype is the column's logical element type.
	Dtype() Dtype
	// Len is the number of positions carrying a value.
	Len() int
}

// --- dense ---------------------------------------------------------------------

type denseI64Col []int64

func ( denseI64Col) ( uint32) (Value, bool) {
	if int() >= len() {
		return Value{}, false
	}
	return I64Value([]), true
}

func ( denseI64Col) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for ,  := range  {
			if !(uint32(), I64Value()) {
				return
			}
		}
	}
}

func ( denseI64Col) () Dtype { return DtypeI64 }
func ( denseI64Col) () int     { return len() }

type denseF64Col []float64

func ( denseF64Col) ( uint32) (Value, bool) {
	if int() >= len() {
		return Value{}, false
	}
	return F64Value([]), true
}

func ( denseF64Col) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for ,  := range  {
			if !(uint32(), F64Value()) {
				return
			}
		}
	}
}

func ( denseF64Col) () Dtype { return DtypeF64 }
func ( denseF64Col) () int     { return len() }

type denseBoolCol struct{ bits *bitset.Bits }

func ( denseBoolCol) ( uint32) (Value, bool) {
	if int() >= .bits.Len() {
		return Value{}, false
	}
	return BoolValue(.bits.Get(int())), true
}

func ( denseBoolCol) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for  := range .bits.Len() {
			if !(uint32(), BoolValue(.bits.Get())) {
				return
			}
		}
	}
}

func ( denseBoolCol) () Dtype { return DtypeBool }
func ( denseBoolCol) () int     { return .bits.Len() }

type denseStrCol []uint32

func ( denseStrCol) ( uint32) (Value, bool) {
	if int() >= len() {
		return Value{}, false
	}
	return StrValue([]), true
}

func ( denseStrCol) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for ,  := range  {
			if !(uint32(), StrValue()) {
				return
			}
		}
	}
}

func ( denseStrCol) () Dtype { return DtypeStr }
func ( denseStrCol) () int     { return len() }

// --- sparse --------------------------------------------------------------------

// sparseSlot binary-searches sorted ids for pos, returning its slot.
func sparseSlot( []uint32,  uint32) (int, bool) {
	,  := 0, len()
	for  <  {
		 := ( + ) / 2
		if [] <  {
			 =  + 1
		} else {
			 = 
		}
	}
	if  < len() && [] ==  {
		return , true
	}
	return 0, false
}

type sparseI64Col struct {
	ids  []uint32
	vals []int64
}

func ( sparseI64Col) ( uint32) (Value, bool) {
	if ,  := sparseSlot(.ids, );  {
		return I64Value(.vals[]), true
	}
	return Value{}, false
}

func ( sparseI64Col) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for ,  := range .ids {
			if !(, I64Value(.vals[])) {
				return
			}
		}
	}
}

func ( sparseI64Col) () Dtype { return DtypeI64 }
func ( sparseI64Col) () int     { return len(.ids) }

type sparseF64Col struct {
	ids  []uint32
	vals []float64
}

func ( sparseF64Col) ( uint32) (Value, bool) {
	if ,  := sparseSlot(.ids, );  {
		return F64Value(.vals[]), true
	}
	return Value{}, false
}

func ( sparseF64Col) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for ,  := range .ids {
			if !(, F64Value(.vals[])) {
				return
			}
		}
	}
}

func ( sparseF64Col) () Dtype { return DtypeF64 }
func ( sparseF64Col) () int     { return len(.ids) }

type sparseBoolCol struct {
	ids  []uint32
	vals []bool
}

func ( sparseBoolCol) ( uint32) (Value, bool) {
	if ,  := sparseSlot(.ids, );  {
		return BoolValue(.vals[]), true
	}
	return Value{}, false
}

func ( sparseBoolCol) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for ,  := range .ids {
			if !(, BoolValue(.vals[])) {
				return
			}
		}
	}
}

func ( sparseBoolCol) () Dtype { return DtypeBool }
func ( sparseBoolCol) () int     { return len(.ids) }

type sparseStrCol struct {
	ids  []uint32
	vals []uint32
}

func ( sparseStrCol) ( uint32) (Value, bool) {
	if ,  := sparseSlot(.ids, );  {
		return StrValue(.vals[]), true
	}
	return Value{}, false
}

func ( sparseStrCol) () iter.Seq2[uint32, Value] {
	return func( func(uint32, Value) bool) {
		for ,  := range .ids {
			if !(, StrValue(.vals[])) {
				return
			}
		}
	}
}

func ( sparseStrCol) () Dtype { return DtypeStr }
func ( sparseStrCol) () int     { return len(.ids) }

// isSparse reports the binary-searched pair representations -- the columns a
// position index makes O(1). Dense columns already read in O(1).
func isSparse( Column) bool {
	switch .(type) {
	case sparseI64Col, sparseI64NarrowCol, sparseF64Col, sparseBoolCol, sparseStrCol:
		return true
	}
	return false
}

// valueAtSlot reads a sparse column's value by slot (array index into its
// pairs) -- the O(1) companion to the binary search, used once a position
// index has resolved a position.
func valueAtSlot( Column,  uint32) (Value, bool) {
	 := int()
	switch col := .(type) {
	case sparseI64Col:
		if  < len(.vals) {
			return I64Value(.vals[]), true
		}
	case sparseI64NarrowCol:
		if  < .n() {
			return I64Value(.at()), true
		}
	case sparseF64Col:
		if  < len(.vals) {
			return F64Value(.vals[]), true
		}
	case sparseBoolCol:
		if  < len(.vals) {
			return BoolValue(.vals[]), true
		}
	case sparseStrCol:
		if  < len(.vals) {
			return StrValue(.vals[]), true
		}
	}
	return Value{}, false
}