using System; using System.Collections.Generic; namespace Amb { public interface IValue { T Value { get; } string ToString(); } public sealed class Amb { public IValue Choose(params T[] choices) { var array = new ChoiceArray { Values = choices }; _choices.Add(array); return array; } public void Require(Func predicate) => _constraints.Add(new Constraint { Predicate = predicate, AppliesForItems = _choices.Count }); public bool Disambiguate() => Disambiguate(0, 0); interface IChoices { int Length { get; } int Index { get; set; } } interface IConstraint { int AppliesForItems { get; } bool Invoke(); } readonly List _choices = new(); readonly List _constraints = new(); bool Disambiguate(int choicesTracked, int constraintIdx) { while (constraintIdx < _constraints.Count && _constraints[constraintIdx].AppliesForItems <= choicesTracked) { if (!_constraints[constraintIdx].Invoke()) return false; constraintIdx++; } if (choicesTracked == _choices.Count) return true; for (var i = 0; i < _choices[choicesTracked].Length; i++) { _choices[choicesTracked].Index = i; if (Disambiguate(choicesTracked + 1, constraintIdx)) return true; } return false; } class Constraint : IConstraint { internal Func Predicate; public int AppliesForItems { get; set; } public bool Invoke() => Predicate?.Invoke() ?? default; } class ChoiceArray : IChoices, IValue { internal T[] Values; public int Index { get; set; } public T Value => Values[Index]; public int Length => Values.Length; public override string ToString() => Value.ToString(); } } }