#Issue: 1684

// TODO: significantly reduce the number of counts in this evaluation.
-- stats.txt --
Leaks:  0
Freed:  995025
Reused: 994976
Allocs: 49
Retain: 0

Unifications: 740771
Conjuncts:    3143640
Disjuncts:    995025

-- in.cue --
#Secret: {
	$secret: id: string
}
#secrets: #Secret | {[string]: #secrets}

out: #secrets & {
	FOO: $secret: id: "100"
	ONE: TWO: THREE: $secret: id: "123"
}

#Secret: {
	$secret: _id: string
}
#secrets: #Secret | {[string]: #secrets}

out: #secrets & {
	FOO: $secret: _id: "100"
	ONE: TWO: THREE: $secret: _id: "123"
}

-- perf.cue --
nestedCross: closed: {
	#D: {id: string} | {[string]: #D}
	#D: {id: string} | {[string]: #D}
	#D: {id: string} | {[string]: #D}
}

nestedCross: open: {
	D: {id: string} | {[string]: D}
	D: {id: string} | {[string]: D}
	D: {id: string} | {[string]: D}
}

// TODO(perf): support this case.  This is currently highly exponential.
// The old evaluator solved this performance issue by simply ignoring equality
// for patterns. This led to duplicate disjunctions and incorrect results.
//
// The new evaluator computes the correct result, but is highly exponential.
// The solution is probably to limit structural cycle depth.
//
// An alternative would be to stop processing disjunctions if we see there are
// more than two solutions. This might still result in exponential behavior,
// but will limit it considerably.
nestedClosed: passing: {
	D: {id: {}} | {[string]: D}
	D: {id: {}} | {[string]: D}
	D: {id: {}} | {[string]: D}
}
-- out/evalalpha/stats --
Leaks:  27399
Freed:  0
Reused: 0
Allocs: 27399
Retain: 0

Unifications: 5541
Conjuncts:    25871
Disjuncts:    6534

CloseIDElems: 85837
NumCloseIDs: 4115
-- out/evalalpha --
(struct){
  #Secret: (#struct){
    $secret: (#struct){
      id: (string){ string }
      _id: (string){ string }
    }
  }
  #secrets: (#struct){ |((#struct){
      $secret: (#struct){
        id: (string){ string }
        _id: (string){ string }
      }
    }, (#struct){
    }) }
  out: (#struct){
    FOO: (#struct){
      $secret: (#struct){
        id: (string){ "100" }
        _id: (string){ "100" }
      }
    }
    ONE: (#struct){
      TWO: (#struct){
        THREE: (#struct){
          $secret: (#struct){
            id: (string){ "123" }
            _id: (string){ "123" }
          }
        }
      }
    }
  }
  nestedCross: (struct){
    closed: (struct){
      #D: (#struct){ |((#struct){
          id: (string){ string }
        }, (#struct){
        }) }
    }
    open: (struct){
      D: (struct){ |((struct){
          id: (string){ string }
        }, (struct){
        }) }
    }
  }
  nestedClosed: (struct){
    passing: (struct){
      D: (struct){ |((struct){
          id: (struct){
          }
        }, (struct){
          id: (struct){ |((struct){
              id: (struct){
              }
            }, (struct){
            }) }
        }, (struct){
        }) }
    }
  }
}
-- diff/-out/evalalpha/stats<==>+out/eval/stats --
diff old new
--- old
+++ new
@@ -1,9 +1,12 @@
-Leaks:  0
-Freed:  1064333
-Reused: 1064282
-Allocs: 51
+Leaks:  27399
+Freed:  0
+Reused: 0
+Allocs: 27399
 Retain: 0
 
-Unifications: 792123
-Conjuncts:    2480117
-Disjuncts:    1064333
+Unifications: 5541
+Conjuncts:    25871
+Disjuncts:    6534
+
+CloseIDElems: 85837
+NumCloseIDs: 4115
-- diff/-out/evalalpha<==>+out/eval --
diff old new
--- old
+++ new
@@ -56,36 +56,6 @@
             }, (struct){
             }) }
         }, (struct){
-          id: (struct){ |((struct){
-              id: (struct){
-              }
-            }, (struct){
-            }) }
-        }, (struct){
-          id: (struct){ |((struct){
-              id: (struct){
-              }
-            }, (struct){
-            }) }
-        }, (struct){
-          id: (struct){ |((struct){
-              id: (struct){
-              }
-            }, (struct){
-            }) }
-        }, (struct){
-          id: (struct){ |((struct){
-              id: (struct){
-              }
-            }, (struct){
-            }) }
-        }, (struct){
-          id: (struct){ |((struct){
-              id: (struct){
-              }
-            }, (struct){
-            }) }
-        }, (struct){
         }) }
     }
   }
-- diff/explanation --
New algorithm is better at trimming recursive disjunctions.
Note that the old closedness implementation of V3 was able to reduce this to
445 unification ops. This could be due to the fact that the old closedness could
in some cases eliminate earlier than with a flat structure. So this may not be
attainable with the current implementation.
TODO(perf): investigate nonetheless.
-- out/eval/stats --
Leaks:  0
Freed:  1064333
Reused: 1064282
Allocs: 51
Retain: 0

Unifications: 792123
Conjuncts:    2480117
Disjuncts:    1064333
-- out/eval --
(struct){
  #Secret: (#struct){
    $secret: (#struct){
      id: (string){ string }
      _id: (string){ string }
    }
  }
  #secrets: (#struct){ |((#struct){
      $secret: (#struct){
        id: (string){ string }
        _id: (string){ string }
      }
    }, (#struct){
    }) }
  out: (#struct){
    FOO: (#struct){
      $secret: (#struct){
        id: (string){ "100" }
        _id: (string){ "100" }
      }
    }
    ONE: (#struct){
      TWO: (#struct){
        THREE: (#struct){
          $secret: (#struct){
            id: (string){ "123" }
            _id: (string){ "123" }
          }
        }
      }
    }
  }
  nestedCross: (struct){
    closed: (struct){
      #D: (#struct){ |((#struct){
          id: (string){ string }
        }, (#struct){
        }) }
    }
    open: (struct){
      D: (struct){ |((struct){
          id: (string){ string }
        }, (struct){
        }) }
    }
  }
  nestedClosed: (struct){
    passing: (struct){
      D: (struct){ |((struct){
          id: (struct){
          }
        }, (struct){
          id: (struct){ |((struct){
              id: (struct){
              }
            }, (struct){
            }) }
        }, (struct){
          id: (struct){ |((struct){
              id: (struct){
              }
            }, (struct){
            }) }
        }, (struct){
          id: (struct){ |((struct){
              id: (struct){
              }
            }, (struct){
            }) }
        }, (struct){
          id: (struct){ |((struct){
              id: (struct){
              }
            }, (struct){
            }) }
        }, (struct){
          id: (struct){ |((struct){
              id: (struct){
              }
            }, (struct){
            }) }
        }, (struct){
          id: (struct){ |((struct){
              id: (struct){
              }
            }, (struct){
            }) }
        }, (struct){
        }) }
    }
  }
}
-- out/compile --
--- in.cue
{
  #Secret: {
    $secret: {
      id: string
    }
  }
  #secrets: (〈0;#Secret〉|{
    [string]: 〈1;#secrets〉
  })
  out: (〈0;#secrets〉 & {
    FOO: {
      $secret: {
        id: "100"
      }
    }
    ONE: {
      TWO: {
        THREE: {
          $secret: {
            id: "123"
          }
        }
      }
    }
  })
  #Secret: {
    $secret: {
      _id: string
    }
  }
  #secrets: (〈0;#Secret〉|{
    [string]: 〈1;#secrets〉
  })
  out: (〈0;#secrets〉 & {
    FOO: {
      $secret: {
        _id: "100"
      }
    }
    ONE: {
      TWO: {
        THREE: {
          $secret: {
            _id: "123"
          }
        }
      }
    }
  })
}
--- perf.cue
{
  nestedCross: {
    closed: {
      #D: ({
        id: string
      }|{
        [string]: 〈1;#D〉
      })
      #D: ({
        id: string
      }|{
        [string]: 〈1;#D〉
      })
      #D: ({
        id: string
      }|{
        [string]: 〈1;#D〉
      })
    }
  }
  nestedCross: {
    open: {
      D: ({
        id: string
      }|{
        [string]: 〈1;D〉
      })
      D: ({
        id: string
      }|{
        [string]: 〈1;D〉
      })
      D: ({
        id: string
      }|{
        [string]: 〈1;D〉
      })
    }
  }
  nestedClosed: {
    passing: {
      D: ({
        id: {}
      }|{
        [string]: 〈1;D〉
      })
      D: ({
        id: {}
      }|{
        [string]: 〈1;D〉
      })
      D: ({
        id: {}
      }|{
        [string]: 〈1;D〉
      })
    }
  }
}
