/// <inheritdoc/>
        public override IFact CalculateFact <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
        {
            var fact = base.CalculateFact(node, context);

            WriteLog(() => $"FactFactory | FactRule\n{node.Info.Rule}\nInput facts: {string.Join(", ", GetRequireFacts(node.Info.Rule, context))}\nResult: {fact}");
            return(fact);
        }
        /// <summary>
        /// Delete current node.
        /// </summary>
        /// <typeparam name="TFactRule">FatcRule type.</typeparam>
        /// <typeparam name="TWantAction">WantAction type.</typeparam>
        /// <typeparam name="TFactContainer">FactContainer type.</typeparam>
        /// <param name="node">Node to be removed.</param>
        /// <param name="treeByFactRule">Rule tree.</param>
        /// <param name="level">Level in tree.</param>
        /// <returns>True - remove root node.</returns>
        /// <remarks>
        /// Recursively delete parent nodes
        /// if they do not have other nodes calculating the fact from the child node.
        /// </remarks>
        private bool TryRemoveRootNode <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, TreeByFactRule <TFactRule, TWantAction, TFactContainer> treeByFactRule, int level)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            treeByFactRule.Levels[level].Remove(node);

            if (level == 0)
            {
                return(true);
            }

            NodeByFactRule <TFactRule> parent = node.Parent;

            parent.Childs.Remove(node);

            // If the node has a child node that can calculate this fact
            if (parent.Childs.Any(n => n.Info.Rule.OutputFactType.EqualsFactType(node.Info.Rule.OutputFactType)))
            {
                return(false);
            }
            else
            {
                return(TryRemoveRootNode(parent, treeByFactRule, level - 1));
            }
        }
Пример #3
0
        /// <summary>
        /// Calculates the fact and adds the priority fact to the parameters.
        /// </summary>
        /// <typeparam name="TFactRule">Type rule.</typeparam>
        /// <typeparam name="TWantAction">Type wantAction.</typeparam>
        /// <typeparam name="TFactContainer">Type fact container.</typeparam>
        /// <param name="node">Node containing information about the calculation rule.</param>
        /// <param name="context">Context</param>
        /// <returns>Fact.</returns>
        public override IFact CalculateFact <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
        {
            IPriorityFact priority = node.Info.Rule.GetPriorityFact(context);

            return(base
                   .CalculateFact(node, context)
                   .AddPriorityParameter(priority));
        }
 internal static NodeByFactRule <TFactRule> Copy <TFactRule>(this NodeByFactRule <TFactRule> node, NodeByFactRule <TFactRule> newParent)
     where TFactRule : IFactRule
 {
     return(new NodeByFactRule <TFactRule>
     {
         Childs = node.Childs,
         Info = node.Info,
         Parent = newParent,
     });
 }
        private static void FillUniqueRulesFromTree <TFactRule>(NodeByFactRule <TFactRule> node, HashSet <TFactRule> eniqueRules)
            where TFactRule : IFactRule
        {
            foreach (var child in node.Childs)
            {
                FillUniqueRulesFromTree(child, eniqueRules);
            }

            if (!eniqueRules.Contains(node.Info.Rule))
            {
                eniqueRules.Add(node.Info.Rule);
            }
        }
        /// <summary>
        /// Synchronize the tree level with years ready for calculation.
        /// </summary>
        /// <typeparam name="TFactRule"></typeparam>
        /// <typeparam name="TWantAction"></typeparam>
        /// <typeparam name="TFactContainer"></typeparam>
        /// <param name="treeLevel">Tree level.</param>
        /// <param name="finishedNodes">Nodes by which the rule can already be calculated. Key - node info, value - node</param>
        /// <param name="context">Context.</param>
        private void SyncTreeLevelAndFinishedNodes <TFactRule, TWantAction, TFactContainer>(List <NodeByFactRule <TFactRule> > treeLevel, Dictionary <NodeByFactRuleInfo <TFactRule>, NodeByFactRule <TFactRule> > finishedNodes, IWantActionContext <TWantAction, TFactContainer> context)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            foreach (var finishedNode in finishedNodes)
            {
                List <NodeByFactRule <TFactRule> > parentNodes = treeLevel
                                                                 .Where(node => node.Info.Rule.EqualsWork(finishedNode.Key.Rule, context.WantAction, context.Container))
                                                                 .Select(node => node.Parent)
                                                                 .Distinct()
                                                                 .ToList();

                foreach (NodeByFactRule <TFactRule> parentNode in parentNodes)
                {
                    if (parentNode == null)
                    {
                        continue;
                    }

                    for (int i = parentNode.Childs.Count - 1; i >= 0; i--)
                    {
                        NodeByFactRule <TFactRule> childNode = parentNode.Childs[i];
                        if (!childNode.Info.Rule.OutputFactType.EqualsFactType(finishedNode.Key.Rule.OutputFactType))
                        {
                            continue;
                        }

                        parentNode.Childs.Remove(childNode);
                        int indexNodeInTreeLevel = treeLevel.IndexOf(childNode);
                        if (indexNodeInTreeLevel != -1)
                        {
                            treeLevel.RemoveAt(indexNodeInTreeLevel);
                        }
                    }

                    if (parentNode == finishedNode.Value.Parent)
                    {
                        parentNode.Childs.Add(finishedNode.Value);
                    }
                    else
                    {
                        parentNode.Childs.Add(finishedNode.Value.Copy(parentNode));
                    }
                }
            }
        }
Пример #7
0
        /// <inheritdoc/>
        public virtual IFact CalculateFact <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            (var rule, var buildSuccessConditions, var runtimeConditions) =
                (node.Info.Rule, node.Info.BuildSuccessConditions, node.Info.RuntimeConditions);

            foreach (IRuntimeConditionFact condition in runtimeConditions)
            {
                (bool calculated, IFact result) = TryCalculateFactByRuntimeCondition(rule, condition, context);

                if (calculated)
                {
                    return(result);
                }
            }

            using (var writer = context.Container.GetWriter())
            {
                buildSuccessConditions.ForEach(writer.Add);
                runtimeConditions.ForEach(writer.Add);
            }

            var requiredFacts = GetRequireFacts(rule, context);

            if (!CanInvokeWork(requiredFacts, rule, context.Cache))
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.InvalidOperation, $"Can't calculate the '{rule}' rule.", context.WantAction, context.Container);
            }

            var fact = Factory.CreateObject(
                facts => rule.Calculate(facts),
                requiredFacts);

            fact.SetCalculateByRule();
            context.WantAction.AddUsedRule(rule);

            using (var writer = context.Container.GetWriter())
            {
                buildSuccessConditions.ForEach(writer.Remove);
                runtimeConditions.ForEach(writer.Remove);
            }

            return(fact);
        }
        /// <summary>
        /// Whether the <paramref name="rule"/> is contained in a branch with <paramref name="nodeFromBranch"/>.
        /// </summary>
        /// <typeparam name="TFactRule"></typeparam>
        /// <param name="rule"></param>
        /// <param name="nodeFromBranch"></param>
        /// <returns></returns>
        internal static bool RuleContainBranch <TFactRule>(this TFactRule rule, NodeByFactRule <TFactRule> nodeFromBranch)
            where TFactRule : IFactRule
        {
            if (rule == null && nodeFromBranch.Info.Rule == null)
            {
                return(true);
            }
            else if (rule == null || nodeFromBranch.Info.Rule == null)
            {
                return(false);
            }
            else if (rule.Equals(nodeFromBranch.Info.Rule))
            {
                return(true);
            }

            else if (nodeFromBranch.Parent != null)
            {
                return(rule.RuleContainBranch(nodeFromBranch.Parent));
            }

            return(false);
        }
Пример #9
0
 public void Initialize()
 {
     Rule     = GetFactRule((Priority1 p, Fact1 f) => new FactResult(f.Value + p));
     NodeInfo = new NodeByFactRuleInfo <FactRule>
     {
         BuildFailedConditions = new List <IBuildConditionFact>(),
         Rule = Rule,
         BuildSuccessConditions = new List <IBuildConditionFact>(),
         RuntimeConditions      = new List <IRuntimeConditionFact>(),
     };
     Node = new NodeByFactRule <FactRule>
     {
         Childs = new List <NodeByFactRule <FactRule> >(),
         Info   = NodeInfo,
         Parent = null,
     };
     Context = GetWantActionContext(
         GetWantAction((FactResult result) => { }),
         new FactContainer
     {
         new Priority1(),
         new Priority2(),
     });
 }
Пример #10
0
 /// <inheritdoc/>
 public virtual bool NeedCalculateFact <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
     where TFactRule : IFactRule
     where TWantAction : IWantAction
     where TFactContainer : IFactContainer
 {
     return(node.Parent != null
         ? !CanExtractFact(node.Info.Rule.OutputFactType, node.Parent.Info.Rule, context)
         : !CanExtractFact(node.Info.Rule.OutputFactType, context.WantAction, context));
 }
        /// <summary>
        /// Determines you can no longer consider the <paramref name="factType"/> necessary.
        /// </summary>
        /// <typeparam name="TFactRule">FactRole type.</typeparam>
        /// <typeparam name="TWantAction">WantAction type.</typeparam>
        /// <typeparam name="TFactContainer">FactContainer type.</typeparam>
        /// <param name="factType">Fact type info.</param>
        /// <param name="node"></param>
        /// <param name="context"></param>
        /// <param name="copatibleAllFinishedNodes"></param>
        /// <returns>True - may not be considered necessary</returns>
        private bool CanRemoveFromNeedFactTypes <TFactRule, TWantAction, TFactContainer>(IFactType factType, NodeByFactRule <TFactRule> node, IFactRulesContext <TFactRule, TWantAction, TFactContainer> context, Dictionary <NodeByFactRuleInfo <TFactRule>, NodeByFactRule <TFactRule> > copatibleAllFinishedNodes)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            // Exclude condition special facts
            if (factType.IsFactType <IBuildConditionFact>())
            {
                var nodeInfo = node.Info;

                if (nodeInfo.BuildSuccessConditions.Exists(fact => context.Cache.GetFactType(fact).EqualsFactType(factType)))
                {
                    return(true);
                }
                else if (nodeInfo.BuildFailedConditions.Exists(fact => context.Cache.GetFactType(fact).EqualsFactType(factType)))
                {
                    return(false);
                }

                var condition = Factory.CreateObject(
                    type => type.CreateBuildConditionFact <IBuildConditionFact>(),
                    factType
                    );

                if (condition.Condition(nodeInfo.Rule, context, _ => nodeInfo.CompatibleRules))
                {
                    nodeInfo.BuildSuccessConditions.Add(condition);
                    return(true);
                }
                else
                {
                    nodeInfo.BuildFailedConditions.Add(condition);
                    return(false);
                }
            }
            else if (factType.IsFactType <IRuntimeConditionFact>())
            {
                var nodeInfo = node.Info;

                if (nodeInfo.RuntimeConditions.Exists(fact => context.Cache.GetFactType(fact).EqualsFactType(factType)))
                {
                    return(true);
                }

                var condition = Factory.CreateObject(
                    type => type.CreateRuntimeConditionFact <IRuntimeConditionFact>(),
                    factType
                    );

                condition.SetGetRelatedRulesFunc <TFactRule, TWantAction, TFactContainer>(GetRelatedRules, node.Info.Rule, context.FactRules);

                nodeInfo.RuntimeConditions.Add(condition);

                return(true);
            }
            else
            {
                // Exclude facts for which a solution has already been found.
                List <KeyValuePair <NodeByFactRuleInfo <TFactRule>, NodeByFactRule <TFactRule> > > finishedNodesForCurrentFact = copatibleAllFinishedNodes
                                                                                                                                 .Where(finishedNode => finishedNode.Key.Rule.OutputFactType.EqualsFactType(factType))
                                                                                                                                 .ToList();

                if (finishedNodesForCurrentFact.Count != 0)
                {
                    node.Childs.Insert(0, finishedNodesForCurrentFact[0].Value);
                    return(true);
                }
            }

            return(false);
        }
        /// <inheritdoc/>
        public virtual bool TryBuildTreeForFactInfo <TFactRule, TWantAction, TFactContainer>(BuildTreeForFactInfoRequest <TFactRule, TWantAction, TFactContainer> request, out TreeByFactRule <TFactRule, TWantAction, TFactContainer> treeResult, out List <DeriveFactErrorDetail> deriveFactErrorDetails)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            var context = request.Context;

            treeResult             = null;
            deriveFactErrorDetails = null;

            // find the rules that can calculate the fact
            List <TreeByFactRule <TFactRule, TWantAction, TFactContainer> > treesByWantFactType = request.GetTreesByRequest();
            var treeReady = treesByWantFactType.FirstOrDefault(tree => tree.Status == TreeStatus.Built);

            if (treeReady != null)
            {
                treeResult = treeReady;
                return(true);
            }

            List <List <IFactType> > notFoundFactSet = treesByWantFactType.ConvertAll(item => new List <IFactType>());
            var allFinichedNodes = new Dictionary <NodeByFactRuleInfo <TFactRule>, NodeByFactRule <TFactRule> >();

            while (true)
            {
                for (int i = treesByWantFactType.Count - 1; i >= 0; i--)
                {
                    var treeByWantFactType = treesByWantFactType[i];

                    if (treeByWantFactType.Status != TreeStatus.BeingBuilt)
                    {
                        continue;
                    }

                    int lastlevelNumber = treeByWantFactType.Levels.Count - 1;

                    // If after synchronization we can calculate the tree.
                    if (TrySyncTreeLevelsAndFinishedNodes(treeByWantFactType, lastlevelNumber, allFinichedNodes))
                    {
                        treeByWantFactType.Built();
                        continue;
                    }

                    List <NodeByFactRule <TFactRule> > lastTreeLevel = treeByWantFactType.Levels[lastlevelNumber];

                    // If in the last level there are no nodes for calculation, then the tree can be calculated.
                    if (lastTreeLevel.Count == 0)
                    {
                        treeByWantFactType.Built();
                        continue;
                    }

                    // Next level nodes.
                    var  nextTreeLevel             = new List <NodeByFactRule <TFactRule> >();
                    var  currentLevelFinishedNodes = new Dictionary <NodeByFactRuleInfo <TFactRule>, NodeByFactRule <TFactRule> >();
                    bool cannotDerived             = false;

                    for (int j = 0; j < lastTreeLevel.Count; j++)
                    {
                        NodeByFactRule <TFactRule>     node     = lastTreeLevel[j];
                        NodeByFactRuleInfo <TFactRule> nodeInfo = node.Info;
                        Dictionary <NodeByFactRuleInfo <TFactRule>, NodeByFactRule <TFactRule> > copatibleAllFinishedNodes = nodeInfo.GetCompatibleFinishedNodes(allFinichedNodes, context);
                        List <IFactType> needFacts = nodeInfo
                                                     .RequiredFactTypes
                                                     .FindAll(needFactType => !CanRemoveFromNeedFactTypes(needFactType, node, context, copatibleAllFinishedNodes));

                        // If the rule can be calculated from the parameters in the container, then add the node to the list of complete.
                        if (needFacts.IsNullOrEmpty())
                        {
                            allFinichedNodes.Add(node.Info, node);
                            currentLevelFinishedNodes.Add(node.Info, node);
                            continue;
                        }

                        bool canTryRemoveNode = false;
                        foreach (var needFact in needFacts)
                        {
                            if (needFact.IsFactType <ISpecialFact>())
                            {
                                if (!canTryRemoveNode)
                                {
                                    canTryRemoveNode = true;
                                }

                                notFoundFactSet[i].Add(needFact);
                                continue;
                            }

                            var needRules = nodeInfo
                                            .CompatibleRules
                                            .FindAll(rule => rule.OutputFactType.EqualsFactType(needFact) && !rule.RuleContainBranch(node));

                            if (needRules.Count > 0)
                            {
                                List <NodeByFactRule <TFactRule> > nodes = needRules.GetNodesByRules(node, treeByWantFactType);
                                nextTreeLevel.AddRange(nodes);
                                node.Childs.AddRange(nodes);
                            }
                            else
                            {
                                if (!canTryRemoveNode)
                                {
                                    canTryRemoveNode = true;
                                }

                                notFoundFactSet[i].Add(needFact);
                            }
                        }

                        if (canTryRemoveNode)
                        {
                            // Is there a neighboring node capable of deriving this fact.
                            cannotDerived = TryRemoveRootNode(node, treeByWantFactType, lastlevelNumber);
                            j--;
                        }
                    }

                    if (cannotDerived)
                    {
                        treeByWantFactType.Cencel();
                    }
                    else if (currentLevelFinishedNodes.Count > 0)
                    {
                        if (TrySyncTreeLevelsAndFinishedNodes(treeByWantFactType, lastlevelNumber, currentLevelFinishedNodes))
                        {
                            treeByWantFactType.Built();
                        }
                        else if (nextTreeLevel.Count > 0)
                        {
                            SyncTreeLevelAndFinishedNodes(nextTreeLevel, currentLevelFinishedNodes, context);
                            treeByWantFactType.Levels.Add(nextTreeLevel);
                        }
                    }
                    else if (nextTreeLevel.Count > 0)
                    {
                        treeByWantFactType.Levels.Add(nextTreeLevel);
                    }
                    else
                    {
                        treeByWantFactType.Built();
                    }
                }

                List <TreeByFactRule <TFactRule, TWantAction, TFactContainer> > builtTrees = treesByWantFactType
                                                                                             .FindAll(tree => tree.Status == TreeStatus.Built);

                if (builtTrees.Count != 0)
                {
                    var countRuleByBuiltTrees = builtTrees.ToDictionary(tree => tree, tree => tree.GetUniqueRulesFromTree().Count);
                    int minRuleCount          = countRuleByBuiltTrees.Min(item => item.Value);
                    var suitableTree          = countRuleByBuiltTrees.First(item => item.Value == minRuleCount).Key;

                    foreach (var tree in treesByWantFactType)
                    {
                        if (tree != suitableTree && tree.Status != TreeStatus.Cencel && tree.GetUniqueRulesFromTree().Count >= minRuleCount)
                        {
                            tree.Cencel();
                        }
                    }

                    if (treesByWantFactType.All(tree => tree.Status != TreeStatus.BeingBuilt))
                    {
                        treeResult = suitableTree;
                        return(true);
                    }
                }

                if (treesByWantFactType.All(tree => tree.Status == TreeStatus.Cencel))
                {
                    break;
                }
            }

            deriveFactErrorDetails = new List <DeriveFactErrorDetail>();

            foreach (var factSet in notFoundFactSet)
            {
                if (factSet.Count != 0)
                {
                    deriveFactErrorDetails.Add(new DeriveFactErrorDetail(request.WantFactType, factSet.AsReadOnly()));
                }
            }

            return(false);
        }
Пример #13
0
        /// <summary>
        /// Calculates the fact asynchronously and adds the priority fact to the parameters.
        /// </summary>
        /// <typeparam name="TFactRule">Type rule.</typeparam>
        /// <typeparam name="TWantAction">Type wantAction.</typeparam>
        /// <typeparam name="TFactContainer">Type fact container.</typeparam>
        /// <param name="node">Node containing information about the calculation rule.</param>
        /// <param name="context">Context</param>
        /// <returns>Fact.</returns>
        public override async ValueTask <IFact> CalculateFactAsync <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
        {
            IPriorityFact priority = node.Info.Rule.GetPriorityFact(context);

            return((await base.CalculateFactAsync(node, context).ConfigureAwait(false)).AddPriorityParameter(priority));
        }
        /// <inheritdoc/>
        /// <remarks>Adds a versioned fact to the parameters of the calculated fact.</remarks>
        public override IFact CalculateFact <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
        {
            var version = node.Info.Rule.InputFactTypes.GetVersionFact(context);

            return(base.CalculateFact(node, context).AddVerionParameter(version));
        }
        /// <summary>
        /// Get <see cref="TreeByFactRule{TFactRule, TWantAction, TFactContainer}"/> by <paramref name="request"/>.
        /// </summary>
        /// <typeparam name="TFactRule"></typeparam>
        /// <typeparam name="TWantAction"></typeparam>
        /// <typeparam name="TFactContainer"></typeparam>
        /// <param name="request"></param>
        /// <returns></returns>
        internal static List <TreeByFactRule <TFactRule, TWantAction, TFactContainer> > GetTreesByRequest <TFactRule, TWantAction, TFactContainer>(this BuildTreeForFactInfoRequest <TFactRule, TWantAction, TFactContainer> request)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            var context   = request.Context;
            var factRules = request.Context.FactRules;

            if (factRules.IsNullOrEmpty())
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.EmptyRuleCollection, "Rules cannot be null.");
            }

            var needRules = factRules
                            .Where(rule => rule.OutputFactType.EqualsFactType(request.WantFactType))
                            .ToList();

            if (needRules.IsNullOrEmpty())
            {
                throw CommonHelper.CreateDeriveException(ErrorCode.RuleNotFound, $"No rules found able to calculate fact {request.WantFactType.FactName}.");
            }

            var nodeInfos = needRules.ConvertAll(rule => new NodeByFactRuleInfo <TFactRule>
            {
                BuildSuccessConditions = new List <IBuildConditionFact>(rule.InputFactTypes.Count(type => type.IsFactType <IBuildConditionFact>())),
                BuildFailedConditions  = new List <IBuildConditionFact>(),
                RuntimeConditions      = new List <IRuntimeConditionFact>(rule.InputFactTypes.Count(type => type.IsFactType <IRuntimeConditionFact>())),
                Rule = rule,
                RequiredFactTypes = context.SingleEntity.GetRequiredTypesOfFacts(rule, context).ToList(),
                CompatibleRules   = rule.GetCompatibleRulesEx(context.FactRules, context),
            });

            return(nodeInfos.ConvertAll(info =>
            {
                var node = new NodeByFactRule <TFactRule>
                {
                    Childs = new List <NodeByFactRule <TFactRule> >(),
                    Info = info,
                };

                var tree = new TreeByFactRule <TFactRule, TWantAction, TFactContainer>
                {
                    Levels = new List <List <NodeByFactRule <TFactRule> > >
                    {
                        new List <NodeByFactRule <TFactRule> > {
                            node
                        },
                    },
                    NodeInfos = nodeInfos,
                    Root = node,
                    Context = context,
                };

                if (info.RequiredFactTypes.Count == 0)
                {
                    tree.Built();
                }

                return tree;
            }));
        }
        /// <inheritdoc/>
        /// <remarks>Adds a <see cref="Interfaces.IVersionFact"/> to the parameters of the calculated fact.</remarks>
        public override async ValueTask <IFact> CalculateFactAsync <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
        {
            var version = node.Info.Rule.InputFactTypes.GetVersionFact(context);

            return((await base.CalculateFactAsync(node, context).ConfigureAwait(false)).AddVerionParameter(version));
        }
        /// <summary>
        /// Returns nodes by rules.
        /// </summary>
        /// <typeparam name="TFactRule">FatcRule type.</typeparam>
        /// <typeparam name="TWantAction">WantAction type.</typeparam>
        /// <typeparam name="TFactContainer">FactContainer type.</typeparam>
        /// <param name="rules">List of rule.</param>
        /// <param name="treeByFactRule">Rule tree.</param>
        /// <param name="parentNode">Parent node.</param>
        /// <returns>Node list.</returns>
        public static List <NodeByFactRule <TFactRule> > GetNodesByRules <TFactRule, TWantAction, TFactContainer>(this IEnumerable <TFactRule> rules, NodeByFactRule <TFactRule> parentNode, TreeByFactRule <TFactRule, TWantAction, TFactContainer> treeByFactRule)
            where TFactRule : IFactRule
            where TWantAction : IWantAction
            where TFactContainer : IFactContainer
        {
            var context = treeByFactRule.Context;
            var result  = new List <NodeByFactRule <TFactRule> >();

            foreach (var rule in rules)
            {
                NodeByFactRuleInfo <TFactRule> nodeInfo = treeByFactRule.NodeInfos.FirstOrDefault(nInfo => nInfo.Rule.EqualsWork(rule, context.WantAction, context.Container));

                if (nodeInfo == null)
                {
                    nodeInfo = new NodeByFactRuleInfo <TFactRule>
                    {
                        Rule = rule,
                        BuildSuccessConditions = new List <IBuildConditionFact>(rule.InputFactTypes.Count(type => type.IsFactType <IBuildConditionFact>())),
                        BuildFailedConditions  = new List <IBuildConditionFact>(),
                        RuntimeConditions      = new List <IRuntimeConditionFact>(rule.InputFactTypes.Count(type => type.IsFactType <IRuntimeConditionFact>())),
                        RequiredFactTypes      = context.SingleEntity.GetRequiredTypesOfFacts(rule, context).ToList(),
                        CompatibleRules        = rule.GetCompatibleRulesEx(context.FactRules, context),
                    }
                }
                ;

                result.Add(new NodeByFactRule <TFactRule>
                {
                    Childs = new List <NodeByFactRule <TFactRule> >(),
                    Info   = nodeInfo,
                    Parent = parentNode,
                });
            }

            return(result);
        }
        /// <inheritdoc/>
        public override async ValueTask <IFact> CalculateFactAsync <TFactRule, TWantAction, TFactContainer>(NodeByFactRule <TFactRule> node, IWantActionContext <TWantAction, TFactContainer> context)
        {
            var fact = await base.CalculateFactAsync(node, context).ConfigureAwait(false);

            WriteLog(() => $"FactFactory | FactRule\n{node.Info.Rule}\nInput facts: {string.Join(", ", GetRequireFacts(node.Info.Rule, context))}\nResult: {fact}");
            return(fact);
        }