/// <summary> /// Gets the string definition of a passed configuration object. Loose objects have only /// names as their definition, whereas constructed objects have the construction and arguments. /// </summary> /// <param name="configurationObject">The object that we're formatting.</param> /// <returns>The string representing the object.</returns> public string FormatConfigurationObject(ConfigurationObject configurationObject) { // Switch based on the object return(configurationObject switch { // Loose objects have their names as the definition LooseConfigurationObject _ => _objectNames[configurationObject], // For constructed objects we include the construction and arguments ConstructedConfigurationObject constructedObject => $"{constructedObject.Construction.Name}({constructedObject.PassedArguments.Select(FormatArgument).ToJoinedString()})", // Unhandled cases _ => throw new GeoGenException($"Unhandled type of {nameof(ConfigurationObject)}: {configurationObject.GetType()}") });
/// <summary> /// Finds the objects that define these objects, in an order in which they can be constructed, including these ones. /// </summary> /// <param name="objects">The enumerable of objects.</param> /// <returns>The collection of the defining objects.</returns> public static IReadOnlyList <ConfigurationObject> GetDefiningObjects(this IEnumerable <ConfigurationObject> objects) { #region Building graph // Prepare a set of objects that are already examined var examinedObjects = new HashSet <ConfigurationObject>(); // Prepare the dictionary mapping each object to the set of the ones that are used directly in its construction var objectToWhatUses = new Dictionary <ConfigurationObject, HashSet <ConfigurationObject> >(); // Prepare a function that recursively find uses of an object + uses of its internal objects void FindUsesAllDefiningObjects(ConfigurationObject configurationObject) { // If the object has been examined, do nothing if (examinedObjects.Contains(configurationObject)) { return; } // Get the set holding the objects directly used in the definition based on the object type var usedObjects = configurationObject switch { // If we have a constructed object, then we look at its flattened arguments ConstructedConfigurationObject constructedObject => constructedObject.PassedArguments.FlattenedList.ToHashSet(), // If we have a loose object, we have no internal objects LooseConfigurationObject _ => new HashSet <ConfigurationObject>(), // Unhandled cases _ => throw new GeoGenException($"Unhandled type of {nameof(ConfigurationObject)}: {configurationObject.GetType()}") }; // Mark found object objectToWhatUses.Add(configurationObject, usedObjects); // Mark that it's been examined examinedObjects.Add(configurationObject); // Recursively find uses for the internal objects usedObjects.ForEach(FindUsesAllDefiningObjects); } // Find uses for all the objects objects.ForEach(FindUsesAllDefiningObjects); #endregion #region DFS search // Prepare a list holding the final result, initializes with the loose objects, // that will definitely be the first ones that define everything var result = new List <ConfigurationObject>(objectToWhatUses.Keys.OfType <LooseConfigurationObject>()); // Prepare a set of already visited objects, initializes with the loose objects // already added in the current result var visitedObjects = new HashSet <ConfigurationObject>(result); // Local function that performs a visit of an object // It will recursively visit its dependent objects and after // that add itself to the result. Clearly the objects that // depend on some other one will be added to the result later void Visit(ConfigurationObject configurationObject) { // If the object has been visited, do nothing if (visitedObjects.Contains(configurationObject)) { return; } // Recursively visit all the dependent objects objectToWhatUses[configurationObject].ForEach(Visit); // Add the object to the resulting list result.Add(configurationObject); // Mark the object as visited visitedObjects.Add(configurationObject); } // Visit all the objects objectToWhatUses.Keys.ForEach(Visit); #endregion // Now the result is prepared return(result); }