// Whole-graph analytics: PageRank, weakly-connected components, community
// detection by label propagation (CDLP), the local clustering coefficient
// (LCC), and single-source shortest paths (SSSP) -- the LDBC Graphalytics
// kernel set. Outputs are node-indexed slices over the CSR id space (index
// == node id; sparse-id safe). directed selects the forward direction:
// true follows Outgoing (a directed graph), false Both (an undirected one
// whose rels are stored once).

package chickpeas

import (
	
	

	
)

func fwd( bool) Direction {
	if  {
		return Outgoing
	}
	return Both
}

func ind( bool) Direction {
	if  {
		return Incoming
	}
	return Both
}

// SSSP is single-source shortest paths over forward rels with additive
// weights from the weightKey rel property ("" = unit weights); unreachable
// nodes get +Inf. Unit weights make every distance the hop count, so that
// case runs the BFS -- a unit-weight Dijkstra pays a heap push and pop
// plus map probes per relationship to rediscover the ordering a BFS queue
// gives for free (the same dispatch the COST-constant path search takes).
// A weight key that resolves no column also means unit weights.
func ( *Snapshot) ( NodeID,  bool,  string) []float64 {
	 := make([]float64, .CSRIDSpace())
	var  WeightFn
	if  != "" {
		if ,  := .RelCol();  {
			 := .F64()
			 = func( NodeID,  RelRef) float64 {
				if ,  := .Get(.Pos);  {
					return 
				}
				return 1
			}
		}
	}
	if  == nil {
		 := .BFSDistances(, fwd(), MatchAll(), -1)
		for  := range  {
			if ,  := [NodeID()];  {
				[] = float64()
			} else {
				[] = math.Inf(1)
			}
		}
		return 
	}
	 := .Dijkstra(, fwd(), MatchAll(), )
	for  := range  {
		if ,  := .Distance(NodeID());  {
			[] = 
		} else {
			[] = math.Inf(1)
		}
	}
	return 
}

// WCC labels each node with the smallest node id in its weakly-connected
// component, flooding undirected (Both) rels in ascending id sweep order.
func ( *Snapshot) () []uint32 {
	return .wccFlood(MatchAll(), Both)
}

// WCCVia is connected components over only the m-matched rels, flooding in
// dir (pass Both for the usual weakly-connected sense over a
// stored-directed rel set, e.g. a reply forest). Nodes with no matching
// rel are their own singleton component.
func ( *Snapshot) ( RelMatch,  Direction) []uint32 {
	return .wccFlood(, )
}

func ( *Snapshot) ( RelMatch,  Direction) []uint32 {
	 := uint32(.CSRIDSpace())
	const  = ^uint32(0)
	 := make([]uint32, )
	for  := range  {
		[] = 
	}
	var  []uint32
	for  := uint32(0);  < ; ++ {
		if [] !=  {
			continue
		}
		[] = 
		 = append([:0], )
		for len() > 0 {
			 := [len()-1]
			 = [:len()-1]
			for  := range .NeighborsMatch(, , ) {
				if [] ==  {
					[] = 
					 = append(, )
				}
			}
		}
	}
	return 
}

// PageRank runs iterations of synchronous pull updates with the given
// damping: PR0(v) = 1/|V|, then PRi(v) = (1-d)/|V| + d*(sum over in-
// neighbors of PRi-1(u)/outdeg(u) + the uniformly redistributed rank of
// sinks).
func ( *Snapshot) ( bool,  float64,  int) []float64 {
	 := int(.CSRIDSpace())
	if  == 0 {
		return nil
	}
	// N is the number of REAL nodes: on a sparse id space the arrays
	// stay id-space-sized (raw-id indexing in bounds), but init, the
	// teleport/dangling base, and the dangling sum are confined to
	// existing nodes -- broadcasting base over gap ids both contaminates
	// real ranks and breaks mass conservation (the padded-space form
	// conserves exactly, but over the wrong N). Gap entries stay 0 and
	// the emission layer filters them anyway. Dense graphs are
	// arithmetically identical (existence covers the whole space).
	 := float64(.nNodes)
	if  == 0 {
		return make([]float64, )
	}
	 := func( int) bool { return true }
	if .existence != nil && int(.existence.Len()) <  {
		 = func( int) bool { return .existence.Contains(uint32()) }
	}
	,  := fwd(), ind()
	 := MatchAll()
	 := make([]uint32, )
	parallel.For(, func(,  int) {
		for  := ;  < ; ++ {
			 := uint32(0)
			for range .NeighborsMatch(NodeID(), , ) {
				++
			}
			[] = 
		}
	})
	 := make([]float64, )
	 := make([]float64, )
	for  := range  {
		if () {
			[] = 1 / 
		}
	}
	for range  {
		 := 0.0
		for  := range  {
			if [] == 0 && () {
				 += []
			}
		}
		 := (1-)/ + */
		parallel.For(, func(,  int) {
			for  := ;  < ; ++ {
				if !() {
					[] = 0
					continue
				}
				 := 0.0
				for  := range .NeighborsMatch(NodeID(), , ) {
					if  := [];  > 0 {
						 += [] / float64()
					}
				}
				[] =  + *
			}
		})
		,  = , 
	}
	return 
}

// CDLP is community detection by synchronous label propagation: L0(v) = v,
// then each node adopts the most frequent label among its neighbors
// (in+out tallied separately for directed graphs, so a mutual rel counts
// twice), smallest label breaking ties; a node with no neighbors keeps its
// label.
func ( *Snapshot) ( bool,  int) []uint32 {
	 := make([]uint32, .CSRIDSpace())
	for  := range  {
		[] = uint32()
	}
	return .CDLPSeeded(, , )
}

// CDLPSeeded is CDLP with explicit initial labels (init[node] = L0(node));
// seed with original vertex ids to match a vertex-id-keyed reference.
// Missing slots (a short init) default to the node's own id.
func ( *Snapshot) ( bool,  int,  []uint32) []uint32 {
	 := int(.CSRIDSpace())
	 := make([]uint32, )
	for  := range  {
		if  < len() {
			[] = []
		} else {
			[] = uint32()
		}
	}
	 := make([]uint32, )
	 := MatchAll()
	for range  {
		parallel.For(, func(,  int) {
			// One label-tally scratch per chunk, reused across its nodes.
			var  []uint32
			for  := ;  < ; ++ {
				 = [:0]
				if  {
					for  := range .NeighborsMatch(NodeID(), Outgoing, ) {
						 = append(, [])
					}
					for  := range .NeighborsMatch(NodeID(), Incoming, ) {
						 = append(, [])
					}
				} else {
					for  := range .NeighborsMatch(NodeID(), Both, ) {
						 = append(, [])
					}
				}
				[] = mostFrequentLabel(, [])
			}
		})
		,  = , 
	}
	return 
}

// mostFrequentLabel is the most frequent value of buf (smallest on a tie,
// via sort + run scan), or fallback when buf is empty.
func mostFrequentLabel( []uint32,  uint32) uint32 {
	if len() == 0 {
		return 
	}
	sort.Slice(, func(,  int) bool { return [] < [] })
	,  := , 0
	for  := 0;  < len(); {
		 :=  + 1
		for  < len() && [] == [] {
			++
		}
		if - >  {
			 =  - 
			 = []
		}
		 = 
	}
	return 
}

// LCC is the local clustering coefficient: for each node v with undirected
// neighbor set N(v) (each neighbor once, self excluded), 0 if |N(v)| <= 1,
// else the number of forward rels between members of N(v) over
// |N(v)|*(|N(v)|-1).
func ( *Snapshot) ( bool) []float64 {
	 := int(.CSRIDSpace())
	 := fwd()
	 := MatchAll()

	// Sorted forward adjacency, built once, so the triangle count probes
	// the smaller side of each intersection.
	 := make([]uint32, +1)
	for  := range  {
		 := uint32(0)
		for range .NeighborsMatch(NodeID(), , ) {
			++
		}
		[+1] = [] + 
	}
	 := make([]uint32, [])
	for  := range  {
		 := []
		for  := range .NeighborsMatch(NodeID(), , ) {
			[] = 
			++
		}
		 := [[]:]
		sort.Slice(, func(,  int) bool { return [] < [] })
	}

	 := make([]float64, )
	parallel.For(, func(,  int) {
		 := make([]uint64, (+63)/64)
		var  []uint32
		for  := ;  < ; ++ {
			 = [:0]
			for  := range .NeighborsMatch(NodeID(), Both, ) {
				 := [>>6]>>(&63)&1 != 0
				if  != NodeID() && ! {
					[>>6] |= 1 << ( & 63)
					 = append(, )
				}
			}
			 := len()
			if  >= 2 {
				 := false
				for ,  := range  {
					if int([+1]-[]) >  {
						 = true
						break
					}
				}
				if  {
					sort.Slice(, func(,  int) bool { return [] < [] })
				}
				 := uint64(0)
				for ,  := range  {
					 := [[]:[+1]]
					if len() <=  {
						for ,  := range  {
							if  !=  && [>>6]>>(&63)&1 != 0 {
								++
							}
						}
					} else {
						 := 0
						for ,  := range  {
							if  ==  {
								continue
							}
							,  := gallop(, , )
							 = 
							if  {
								++
							}
						}
					}
				}
				[] = float64() / (float64() * float64(-1))
			}
			for ,  := range  {
				[>>6] &^= 1 << ( & 63)
			}
		}
	})
	return 
}

// gallop is exponential search for target in uo[from:]: whether present
// and the index where it is/would be (always >= from); the cursor only
// advances.
func gallop( []uint32,  int,  uint32) (bool, int) {
	 := len()
	if  >=  {
		return false, 
	}
	 := 1
	for + <  && [+] <  {
		 *= 2
	}
	 :=  + /2
	 := min(++1, )
	 := sort.Search(-, func( int) bool { return [+] >=  })
	if + <  && [+] ==  {
		return true,  + 
	}
	return false,  + 
}