Example #1
0
        /// <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()}")
            });
Example #2
0
        /// <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);
        }