# Tests that repeated references to the same type in a struct (e.g. #ref twice,
# _ | {} twice) do not cause exponential blowup in conjuncts/disjuncts.
# https://cuelang.org/issue/758
# https://cuelang.org/issue/1044

// TODO(perf): reduce number of conjunctions and disjunctions.
-- in.cue --
A: #Task
B: #steps: #Script & {mount: [A]}
C: #steps: #Script & {mount: [B]}

#Script: {mount: [...#Task]} @test(eq, {mount: []})

#Task: {
	// Without the proper constructs, repeating the below results
	// in exponential runtime.
	#ref
	#ref
	_ | {}
	_ | {}
	#steps: #Script
	...
}

#ref: {a: 1} | {b: 2} @test(eq, {a: 1} | {b: 2})

@test(eq, {
	A: {#steps: {mount: []}, a: 1} | {#steps: {mount: []}, b: 2}
	B: {
		#steps: {
			mount: [{#steps: {mount: []}, a: 1} | {#steps: {mount: []}, b: 2}]
		}
	}
	C: {
		#steps: {
			mount: [{#steps: {
	mount: [{#steps: {mount: []}, a: 1} | {#steps: {mount: []}, b: 2}]
}, a: 1} | {#steps: {
	mount: [{#steps: {mount: []}, a: 1} | {#steps: {mount: []}, b: 2}]
}, b: 2}]
		}
	}
	#Script: {mount: []}
	#Task: {#steps: {mount: []}, a: 1} | {#steps: {mount: []}, b: 2}
	#ref: {a: 1} | {b: 2}
})
// TODO(inline): replace @test(debug) with @test(shareID=...) once the
// framework supports asserting sharing inside disjunction members. All
// ~(#Script) occurrences here are inside disjunct arms (A, #Task,
// B.#steps.mount[0]) — the CUE API creates a fresh vertex when projecting
// a field through a disjunction boundary, so pointer-identity checks fail.
@test(debug, """
	(struct){
	  A: (#struct){ |((#struct){
	      #steps: ~(#Script)
	      a: (int){ 1 }
	    }, (#struct){
	      #steps: ~(#Script)
	      b: (int){ 2 }
	    }) }
	  B: (struct){
	    #steps: (#struct){
	      mount: (#list){
	        0: (#struct){ |((#struct){
	            #steps: ~(#Script)
	            a: (int){ 1 }
	          }, (#struct){
	            #steps: ~(#Script)
	            b: (int){ 2 }
	          }) }
	      }
	    }
	  }
	  C: (struct){
	    #steps: (#struct){
	      mount: (#list){
	        0: (#struct){ |((#struct){
	            #steps: (#struct){
	              mount: (#list){
	                0: (#struct){ |((#struct){
	                    #steps: ~(#Script)
	                    a: (int){ 1 }
	                  }, (#struct){
	                    #steps: ~(#Script)
	                    b: (int){ 2 }
	                  }) }
	              }
	            }
	            a: (int){ 1 }
	          }, (#struct){
	            #steps: (#struct){
	              mount: (#list){
	                0: (#struct){ |((#struct){
	                    #steps: ~(#Script)
	                    a: (int){ 1 }
	                  }, (#struct){
	                    #steps: ~(#Script)
	                    b: (int){ 2 }
	                  }) }
	              }
	            }
	            b: (int){ 2 }
	          }) }
	      }
	    }
	  }
	  #Script: (#struct){
	    mount: (list){
	    }
	  }
	  #Task: (#struct){ |((#struct){
	      #steps: ~(#Script)
	      a: (int){ 1 }
	    }, (#struct){
	      #steps: ~(#Script)
	      b: (int){ 2 }
	    }) }
	  #ref: (#struct){ |((#struct){
	      a: (int){ 1 }
	    }, (#struct){
	      b: (int){ 2 }
	    }) }
	}
	""")
-- out/todo.txt --
v2 expanded #Task and A into 3-disjunct forms with independent copies of #Script;
v3 uses structure sharing (#steps: ~(#Script)) and reduces to 2 disjuncts.
-- out/eval/stats --
Leaks:  0
Freed:  89
Reused: 51
Allocs: 38
Retain: 0

Unifications: 33
Conjuncts:    91
Disjuncts:    24

NumCloseIDs: 22

ConjunctInfos:       70
MaxConjunctInfos:    6
MaxReqSets:          9
MaxRedirect:         2
