// Columnar property storage: the 8 on-disk column tags (dense/sparse x
// i64/f64/bool/str) and their codecs. Dense bool stays raw LSB-first packed
// bytes, exactly as on disk, keeping the codec dependency-free; the engine
// converts to its own bitset when it materializes a snapshot.

package rcpg

import (
	
	
)

const (
	colDenseI64 byte = iota
	colDenseF64
	colDenseBool
	colDenseStr
	colSparseI64
	colSparseF64
	colSparseBool
	colSparseStr
)

// ColumnData is one property column's storage. Dense columns are indexed by
// ID (node ID, or outgoing-CSR position for rel columns); sparse columns are
// (id, value) pairs sorted by ID. String values are atom IDs into
// GraphSection.Atoms; atom 0 ("") in a dense str column means missing.
type ColumnData interface {
	tag() byte
}

// DenseI64 is a dense int64 column.
type DenseI64 []int64

// DenseF64 is a dense float64 column; values are written as raw IEEE-754
// bits, so NaN payloads and -0.0 survive round trips exactly.
type DenseF64 []float64

// DenseBool is a dense bool column as stored: Len bits packed LSB-first
// within each byte, ceil(Len/8) bytes.
type DenseBool struct {
	Bits []byte
	Len  uint32
}

// Get reports bit i, false when out of range.
func ( DenseBool) ( uint32) bool {
	if  >= .Len {
		return false
	}
	return .Bits[/8]&(1<<(%8)) != 0
}

// Set sets bit i to v; i must be < Len.
func ( DenseBool) ( uint32,  bool) {
	if  {
		.Bits[/8] |= 1 << ( % 8)
	} else {
		.Bits[/8] &^= 1 << ( % 8)
	}
}

// NewDenseBool returns an all-false column of n bits.
func ( uint32) DenseBool {
	return DenseBool{Bits: make([]byte, (+7)/8), Len: }
}

// DenseStr is a dense string column of value atoms.
type DenseStr []uint32

// I64Entry is one sparse int64 entry.
type I64Entry struct {
	ID  uint32
	Val int64
}

// F64Entry is one sparse float64 entry.
type F64Entry struct {
	ID  uint32
	Val float64
}

// BoolEntry is one sparse bool entry.
type BoolEntry struct {
	ID  uint32
	Val bool
}

// StrEntry is one sparse string entry (value is an atom).
type StrEntry struct {
	ID  uint32
	Val uint32
}

// SparseI64 is a sparse int64 column, sorted by ID.
type SparseI64 []I64Entry

// SparseF64 is a sparse float64 column, sorted by ID.
type SparseF64 []F64Entry

// SparseBool is a sparse bool column, sorted by ID.
type SparseBool []BoolEntry

// SparseStr is a sparse string column, sorted by ID.
type SparseStr []StrEntry

func (DenseI64) () byte   { return colDenseI64 }
func (DenseF64) () byte   { return colDenseF64 }
func (DenseBool) () byte  { return colDenseBool }
func (DenseStr) () byte   { return colDenseStr }
func (SparseI64) () byte  { return colSparseI64 }
func (SparseF64) () byte  { return colSparseF64 }
func (SparseBool) () byte { return colSparseBool }
func (SparseStr) () byte  { return colSparseStr }

func encodeColumns( []Column) ([]byte, error) {
	var  bytes.Buffer
	if  := wLenPrefix(&, len());  != nil {
		return nil, 
	}
	for ,  := range  {
		wU32(&, .Key)
		wU8(&, .Data.tag())
		var  error
		switch d := .Data.(type) {
		case DenseI64:
			if  = wLenPrefix(&, len());  == nil {
				for ,  := range  {
					wI64(&, )
				}
			}
		case DenseF64:
			if  = wLenPrefix(&, len());  == nil {
				for ,  := range  {
					wF64(&, )
				}
			}
		case DenseBool:
			wU32(&, .Len)
			.Write(.Bits[:(.Len+7)/8])
		case DenseStr:
			 = wU32Vec(&, )
		case SparseI64:
			if  = wLenPrefix(&, len());  == nil {
				for ,  := range  {
					wU32(&, .ID)
					wI64(&, .Val)
				}
			}
		case SparseF64:
			if  = wLenPrefix(&, len());  == nil {
				for ,  := range  {
					wU32(&, .ID)
					wF64(&, .Val)
				}
			}
		case SparseBool:
			if  = wLenPrefix(&, len());  == nil {
				for ,  := range  {
					wU32(&, .ID)
					if .Val {
						wU8(&, 1)
					} else {
						wU8(&, 0)
					}
				}
			}
		case SparseStr:
			if  = wLenPrefix(&, len());  == nil {
				for ,  := range  {
					wU32(&, .ID)
					wU32(&, .Val)
				}
			}
		default:
			 = corruptf("unknown column data type %T", .Data)
		}
		if  != nil {
			return nil, 
		}
	}
	return .Bytes(), nil
}

func decodeColumns( []byte) ([]Column, error) {
	 := newCursor()
	,  := .lenPrefix(5)
	if  != nil {
		return nil, 
	}
	 := make([]Column, 0, )
	for range  {
		,  := .u32()
		if  != nil {
			return nil, 
		}
		,  := .u8()
		if  != nil {
			return nil, 
		}
		,  := decodeColumnData(, )
		if  != nil {
			return nil, 
		}
		 = append(, Column{Key: , Data: })
	}
	return , nil
}

func decodeColumnData( *cursor,  byte) (ColumnData, error) {
	switch  {
	case colDenseI64:
		,  := .i64Vec()
		return DenseI64(), 
	case colDenseF64:
		,  := .f64Vec()
		return DenseF64(), 
	case colDenseBool:
		,  := .u32()
		if  != nil {
			return nil, 
		}
		,  := .take(int((uint64() + 7) / 8))
		if  != nil {
			return nil, 
		}
		return DenseBool{Bits: bytes.Clone(), Len: }, nil
	case colDenseStr:
		,  := .u32Vec()
		return DenseStr(), 
	case colSparseI64:
		,  := .lenPrefix(12)
		if  != nil {
			return nil, 
		}
		 := make(SparseI64, 0, )
		for range  {
			,  := .u32()
			if  != nil {
				return nil, 
			}
			,  := .i64()
			if  != nil {
				return nil, 
			}
			 = append(, I64Entry{ID: , Val: })
		}
		return , nil
	case colSparseF64:
		,  := .lenPrefix(12)
		if  != nil {
			return nil, 
		}
		 := make(SparseF64, 0, )
		for range  {
			,  := .u32()
			if  != nil {
				return nil, 
			}
			,  := .u64()
			if  != nil {
				return nil, 
			}
			 = append(, F64Entry{ID: , Val: math.Float64frombits()})
		}
		return , nil
	case colSparseBool:
		,  := .lenPrefix(5)
		if  != nil {
			return nil, 
		}
		 := make(SparseBool, 0, )
		for range  {
			,  := .u32()
			if  != nil {
				return nil, 
			}
			,  := .u8()
			if  != nil {
				return nil, 
			}
			 = append(, BoolEntry{ID: , Val:  != 0})
		}
		return , nil
	case colSparseStr:
		,  := .lenPrefix(8)
		if  != nil {
			return nil, 
		}
		 := make(SparseStr, 0, )
		for range  {
			,  := .u32()
			if  != nil {
				return nil, 
			}
			,  := .u32()
			if  != nil {
				return nil, 
			}
			 = append(, StrEntry{ID: , Val: })
		}
		return , nil
	default:
		return nil, corruptf("unknown column tag %d", )
	}
}