# Tests that the evaluator resolves various forms of self-referential cycles
# without errors or hangs. Covers arithmetic cycles (math), tautological list
# cycles (list.t0), list cycles through external bindings (list.t1), and
# mutual struct cycles with two (nodes.t2) and three (nodes.t3) participants.
-- in.cue --
math: {
	@test(permute, count=6)
	a: b - 100
	b: a + 100
	b: 200
	@test(eq, {a: 100, b: 200})
}

a: 100 @test(eq, 100)

// list.t0: tautological cycle — c[0] = c[0] adds no constraint,
// so c resolves to a list with one unconstrained element.
list: t0: c: [c[0]] @test(eq, [_])

// list.t1: cycles where an external binding (a=100) breaks the cycle.
// p1 and p2 test different element orderings; both resolve to [100, 100].
list: t1: p1: c: [c[1], a] @test(eq, [100, 100])
list: t1: p2: c: [a, c[0]] @test(eq, [100, 100])

// nodes.t1: self-referential struct; tests commutativity of &.
nodes: t1: p1: s1: s1 & {a: 1} @test(eq, {a: 1})
nodes: t1: p2: s1: {a: 1} & s1 @test(eq, {a: 1})
// nodes.t2: two-node mutual cycle; field order must not affect the result.
nodes: t2: {
	@test(permute, count=2)
	s1: s2 & {a: 1}
	s2: s1 & {b: 2}
	@test(eq, {s1: {a: 1, b: 2}, s2: {b: 2, a: 1}})
}
// nodes.t3: three-node mutual cycle; all 6 orderings must yield equal results.
nodes: t3: {
	@test(permute, count=6)
	s1: s2 & {a: 1}
	s2: s3 & {b: 2}
	s3: s1 & {c: 3}
	@test(eq, {s1: {a: 1, b: 2, c: 3}, s2: {b: 2, c: 3, a: 1}, s3: {c: 3, a: 1, b: 2}})
}

-- out/eval/stats --
Leaks:  0
Freed:  62
Reused: 55
Allocs: 7
Retain: 10

Unifications: 62
Conjuncts:    105
Disjuncts:    70
