/// <summary> /// One level of GenericJoin, taking functions acting on the stream of candidates as if they were the relations. /// </summary> /// <typeparam name="V">The new value type</typeparam> /// <typeparam name="T">The current tuple type</typeparam> /// <param name="Candidates">A stream of candidates of type T</param> /// <param name="Relations">Representatives of each relation</param> /// <returns>A stream of Pair(T, V[]) corresponding to extensions of Candidates validated by all relations.</returns> public static Stream <Pair <T, V[]>, Epoch> GenericJoinLayer <V, T>(this Stream <T, Epoch> Candidates, PrefixExtender <T, V>[] Relations) where V : IComparable <V> { // count the extensions in each relation, maintain the smallest. var Smallest = Relations[0].CountExtensions(Candidates, 0); foreach (var index in Enumerable.Range(1, Relations.Length - 1)) { Smallest = Relations[index].ImproveExtensions(Smallest, index); } // propose extensions for each relation, filter by other relations. var Proposals = new Stream <Pair <T, V[]>, Epoch> [Relations.Length]; foreach (var index in Enumerable.Range(0, Relations.Length)) { Proposals[index] = Relations[index].ExtendPrefixes(Smallest, index); foreach (var otherIndex in Enumerable.Range(0, Relations.Length)) { if (otherIndex != index) { Proposals[index] = Relations[otherIndex].ValidateExtensions(Proposals[index]); } } } // return all proposals which passed all filtering steps. return(Proposals.Aggregate((x, y) => x.Concat(y))); }