// Property, column, rel-property, and atom accessors over a Snapshot.

package chickpeas

import (
	
	
)

// Prop reads node's property key, chaining into typed reads:
// g.Prop(n, "age").I64Or(0). The zero Prop means absent.
func ( *Snapshot) ( NodeID,  string) Prop {
	,  := .PropertyKey()
	if ! {
		return Prop{}
	}
	,  := .columns[]
	if ! {
		return Prop{}
	}
	,  := .Get()
	if ! {
		return Prop{}
	}
	return Prop{g: , v: , ok: true}
}

// RelProp reads a relationship property by outgoing-CSR position (as
// carried by RelRef.Pos) -- the rel analogue of Prop.
func ( *Snapshot) ( uint32,  string) Prop {
	,  := .PropertyKey()
	if ! {
		return Prop{}
	}
	,  := .relColumns[]
	if ! {
		return Prop{}
	}
	,  := .Get()
	if ! {
		return Prop{}
	}
	return Prop{g: , v: , ok: true}
}

// RelEndpoints returns the (source, target) node ids of the rel at
// outgoing-CSR position pos -- the tail and head as stored, independent of
// the traversal direction that produced the position (backs startNode/
// endNode). O(log n): the target indexes outNbrs directly; the source is
// the node whose offset range contains pos.
func ( *Snapshot) ( uint32) (,  NodeID,  bool) {
	 := int()
	if  >= len(.outNbrs) {
		return 0, 0, false
	}
	 = .outNbrs[]
	// The source is the last node whose CSR range starts at or before pos;
	// empty ranges share an offset, so a <= partition picks the owner.
	 := sort.Search(len(.outOffsets), func( int) bool { return int(.outOffsets[]) >  })
	if  == 0 {
		return 0, 0, false
	}
	return NodeID( - 1), , true
}

// RelTypeAt returns the type name of the relationship at outgoing-CSR
// position pos (backs the query engine's type(r)). Every relationship has
// exactly one type, so ok is a BOUNDS guard only -- false means "pos does
// not index a relationship", never "this relationship has no type" -- which
// is why it mirrors RelEndpoints rather than the semantically-fallible
// RelProp. O(1): a direct index into the outgoing-CSR type array, then the
// atom table resolves the id to its interned name.
func ( *Snapshot) ( uint32) (string, bool) {
	if int() >= len(.outTypes) {
		return "", false
	}
	return .atoms.Resolve(uint32(.outTypes[]))
}

// Col resolves a reader for the node property key; ok is false when no such
// column exists. Narrow with I64/F64/Bool/Str and hoist out of a hot loop
// instead of calling Prop per node.
func ( *Snapshot) ( string) (Col, bool) {
	,  := .PropertyKey()
	if ! {
		return Col{}, false
	}
	,  := .columns[]
	if ! {
		return Col{}, false
	}
	return Col{col: }, true
}

// ColIndexed is Col with O(1) reads at scattered node ids even when the
// column is stored sparse: a position -> slot index is built once (lazily,
// cached on the snapshot) and shared by every reader. Use in hot kernels
// reading a sparse column at many positions; dense columns are already O(1)
// and ignore the index.
func ( *Snapshot) ( string) (Col, bool) {
	,  := .PropertyKey()
	if ! {
		return Col{}, false
	}
	,  := .columns[]
	if ! {
		return Col{}, false
	}
	return Col{col: , idx: .posIndexFor(, , &.colPosMu, .colPosIndex)}, true
}

// RelCol resolves a reader for the relationship property key, indexed by
// outgoing-CSR position (see RelRef.Pos); ok is false when absent.
func ( *Snapshot) ( string) (Col, bool) {
	,  := .PropertyKey()
	if ! {
		return Col{}, false
	}
	,  := .relColumns[]
	if ! {
		return Col{}, false
	}
	return Col{col: }, true
}

// RelColIndexed is RelCol with O(1) sparse reads -- ideal for per-rel
// weight reads inside a search.
func ( *Snapshot) ( string) (Col, bool) {
	,  := .PropertyKey()
	if ! {
		return Col{}, false
	}
	,  := .relColumns[]
	if ! {
		return Col{}, false
	}
	return Col{col: , idx: .posIndexFor(, , &.relColPosMu, .relColPosIndex)}, true
}

// posIndexFor lazily builds (once) and returns the position -> slot index
// for a sparse column; dense columns are already O(1) and get nil. The
// table is insert-only and an entry, once built, is never replaced, so the
// returned map reads safely off-lock.
func ( *Snapshot) ( PropertyKey,  Column,  *sync.Mutex,  map[PropertyKey]posIndex) posIndex {
	if !isSparse() {
		return nil
	}
	.Lock()
	,  := []
	.Unlock()
	if  {
		return 
	}
	 := buildPosIndex()
	.Lock()
	defer .Unlock()
	if ,  := [];  {
		return 
	}
	[] = 
	return 
}

// NodePropertyKeys lists the property keys node carries a value for, in
// ascending key order -- the reverse of Prop (backs keys(n)/properties(n)).
// Sorted so the enumeration is deterministic.
func ( *Snapshot) ( NodeID) []string {
	var  []string
	for ,  := range .columns {
		if ,  := .Get();  {
			if ,  := .atoms.Resolve();  {
				 = append(, )
			}
		}
	}
	sort.Strings()
	return 
}

// Version is the snapshot's version string; ok is false when none was set.
func ( *Snapshot) () (string, bool) {
	if .version == nil {
		return "", false
	}
	return *.version, true
}

// Atoms is the snapshot's interned-string table (atom <-> string in both
// directions).
func ( *Snapshot) () *Atoms {
	return .atoms
}

// ResolveString resolves an atom id to its string; ok is false when out of
// range.
func ( *Snapshot) ( uint32) (string, bool) {
	return .atoms.Resolve()
}

// PropertyKey resolves a property-key name to its atom; ok is false when
// the key was never interned.
func ( *Snapshot) ( string) (PropertyKey, bool) {
	return .atoms.ID()
}

// Label resolves a label name to its atom; ok is false when unknown.
func ( *Snapshot) ( string) (Label, bool) {
	,  := .atoms.ID()
	return Label(), 
}

// RelType resolves a relationship-type name to its atom; ok is false when
// unknown. Resolve once and pass to MatchType in a hot loop to skip the
// per-call string lookup.
func ( *Snapshot) ( string) (RelType, bool) {
	,  := .atoms.ID()
	return RelType(), 
}

// ValueFromString resolves a string property value to its interned Value;
// ok is false when the string was never interned (so no property equals
// it).
func ( *Snapshot) ( string) (Value, bool) {
	,  := .atoms.ID()
	return StrValue(), 
}