/// <summary> /// Initializes a new instance of the <see cref="GeneratedConfiguration"/> class. /// </summary> /// <param name="currentConfiguration">The configuration that was extended.</param> /// <param name="newObject">The new object with which this configuration was extended.</param> /// <param name="iterationIndex">The index of the iteration on which the output was produced, starting with 0.</param> public GeneratedConfiguration(GeneratedConfiguration currentConfiguration, ConstructedConfigurationObject newObject, int iterationIndex) : base(currentConfiguration.LooseObjectsHolder, currentConfiguration.ConstructedObjects.Concat(newObject).ToList()) { PreviousConfiguration = currentConfiguration; IterationIndex = iterationIndex; }
/// <summary> /// Creates the enumerator that will enumerate all possible ways to extended a given configuration. /// </summary> /// <param name="currentConfiguration">The current configuration being extended.</param> /// <param name="input">The input for the generator.</param> /// <param name="filter">The filter of generated configurations that should ensure that we generate distinct configurations.</param> /// <returns>The enumerator of generated configurations.</returns> private static IEnumerator <GeneratedConfiguration> ExtendConfiguration(GeneratedConfiguration currentConfiguration, ConfigurationGeneratorInput input, IConfigurationFilter filter) { // For a given configuration we create all possible objects using the constructions from the input return(input.Constructions.SelectMany(construction => { // First we check if we have enough object to match the signature if (!construction.Signature.CanBeMatched(currentConfiguration.ObjectMap)) { return Enumerable.Empty <ConstructedConfigurationObject>(); } // Now we check whether adding an object of the current type wouldn't exceed the maximal number // Find the number of added objects of this type as all objects of this type var numberOfAddedObjectsOfTheCurrentType = currentConfiguration.ObjectMap.GetObjectsForKeys(construction.OutputType).Count() // Minus initial objects of this type - input.InitialConfiguration.ObjectMap.GetObjectsForKeys(construction.OutputType).Count(); // If adding of a new object would exceed the maximal requested count, we're done if (numberOfAddedObjectsOfTheCurrentType + 1 > input.MaximalNumbersOfObjectsToAdd[construction.OutputType]) { return Enumerable.Empty <ConstructedConfigurationObject>(); } // Now we're sure we can generate some objects // First we take all the available pairs [object type, objects] return currentConfiguration.ObjectMap // Those are wrapped in a dictionary mapping object types to particular objects // We take only those types that are required in our signature .Where(keyValue => construction.Signature.ObjectTypesToNeededCount.ContainsKey(keyValue.Key)) // We cast each type to the IEnumerable of all possible variations of those objects // having the needed number of elements. Each of these variations represents a way to // use some objects of their type in our arguments .Select(keyValue => keyValue.Value.Variations(construction.Signature.ObjectTypesToNeededCount[keyValue.Key])) // We need to combine all these options for particular object types in every possible way // For example, if we need 2 points and 2 lines, and we have 4 pair of points and 5 pairs of lines // then there are 20 ways of combining these pairs. The result of this call will be an enumerable // where each element is an array of options (each array representing an option for some particular type) .Combine() // We need to create a single array representing an input for the signature.Match method .Select(arrays => { // Create a helper dictionary mapping types to an array of objects of these types var typeToObjects = arrays.ToDictionary(objects => objects[0].ObjectType, objects => objects); // Create a helper dictionary mapping types to the current index (pointer) on the next object // of this type that should be returned var typeToIndex = arrays.ToDictionary(objects => objects[0].ObjectType, objects => 0); // We go through the requested types and for each we take the object of the given type // and at the same moment increase the index so that it points out to the next object of this type return construction.Signature.ObjectTypes.Select(type => typeToObjects[type][typeToIndex[type]++]).ToArray(); }) // After we have objects for the signature, we can match it to create arguments .Select(construction.Signature.Match) // And finally a constructed object using them .Select(arguments => new ConstructedConfigurationObject(construction, arguments)) // We want distinct objects .Distinct(); }) // Don't take the objects that would make duplicate objects in the configuration .Where(newObject => !currentConfiguration.ConstructedObjectsSet.Contains(newObject)) // Add the object to the current configuration .Select(newObject => new GeneratedConfiguration(currentConfiguration, newObject, currentConfiguration.IterationIndex + 1)) // Check the configuration by the filter .Where(configuration => !filter.ShouldBeExcluded(configuration)) // Apply the custom configuration filter .Where(input.ConfigurationFilter.Invoke) // Finally get the enumerator .GetEnumerator()); }