internal static bool CalculateEventEarliestFinishTimes <T, TResourceId, TActivity, TEvent> (this ArrowGraphBuilderBase <T, TResourceId, TActivity, TEvent> arrowGraphBuilder) where TActivity : IActivity <T, TResourceId> where TEvent : IEvent <T> where T : struct, IComparable <T>, IEquatable <T> where TResourceId : struct, IComparable <TResourceId>, IEquatable <TResourceId> { if (arrowGraphBuilder is null) { throw new ArgumentNullException(nameof(arrowGraphBuilder)); } if (!arrowGraphBuilder.AllDependenciesSatisfied) { return(false); } if (arrowGraphBuilder.FindInvalidConstraints().Any()) { return(false); } var completedNodeIds = new HashSet <T>(); var remainingNodeIds = new HashSet <T>(arrowGraphBuilder.NodeIds); // Make sure the remainingNodeIds contain the Start node. if (!remainingNodeIds.Contains(arrowGraphBuilder.StartNode.Id)) { return(false); } // Complete the Start node first to ensure the completed node IDs // contains something. Node <T, TEvent> startNode = arrowGraphBuilder.StartNode; // Earliest Start Time. startNode.Content.EarliestFinishTime = 0; completedNodeIds.Add(startNode.Id); remainingNodeIds.Remove(startNode.Id); // Forward flow algorithm. while (remainingNodeIds.Any()) { bool progress = false; foreach (T nodeId in remainingNodeIds.ToList()) { Node <T, TEvent> node = arrowGraphBuilder.Node(nodeId); // Get the incoming edges and the dependency nodes IDs. HashSet <T> incomingEdges = node.IncomingEdges; var dependencyNodeIds = new HashSet <T>(incomingEdges.Select(arrowGraphBuilder.EdgeTailNode).Select(x => x.Id)); // If calculations for all the dependency nodes have been completed, then use them // to complete the calculations for this node. if (dependencyNodeIds.IsSubsetOf(completedNodeIds)) { int earliestFinishTime = 0; foreach (T incomingEdgeId in incomingEdges) { Edge <T, TActivity> incomingEdge = arrowGraphBuilder.Edge(incomingEdgeId); Node <T, TEvent> incomingEdgeTailNode = arrowGraphBuilder.EdgeTailNode(incomingEdgeId); if (incomingEdgeTailNode.Content.EarliestFinishTime.HasValue) { int proposedEarliestFinishTime = incomingEdgeTailNode.Content.EarliestFinishTime.Value + incomingEdge.Content.Duration; proposedEarliestFinishTime += incomingEdge.Content.MinimumFreeSlack.GetValueOrDefault(); // Augment the earliest finish time artificially (if required). if (proposedEarliestFinishTime > earliestFinishTime) { earliestFinishTime = proposedEarliestFinishTime; } } if (incomingEdge.Content.MinimumEarliestStartTime.HasValue) { int proposedEarliestFinishTime = incomingEdge.Content.MinimumEarliestStartTime.Value + incomingEdge.Content.Duration; // Augment the earliest finish time artificially (if required). if (proposedEarliestFinishTime > earliestFinishTime) { earliestFinishTime = proposedEarliestFinishTime; } } if (incomingEdge.Content.MaximumLatestFinishTime.HasValue) { int proposedLatestFinishTime = incomingEdge.Content.MaximumLatestFinishTime.Value; // Diminish the earliest finish time artificially (if required). if (proposedLatestFinishTime < earliestFinishTime) { earliestFinishTime = proposedLatestFinishTime; } } } node.Content.EarliestFinishTime = earliestFinishTime; completedNodeIds.Add(nodeId); remainingNodeIds.Remove(nodeId); // Note we are making progress. progress = true; } } // If we have not made any progress then a cycle must exist in // the graph and we will not be able to calculate the earliest // finish times. if (!progress) { throw new InvalidOperationException(Properties.Resources.CannotCalculateEarliestFinishTimesDueToCyclicDependency); } } return(true); }
internal static bool CalculateEventLatestFinishTimes <T, TResourceId, TActivity, TEvent> (this ArrowGraphBuilderBase <T, TResourceId, TActivity, TEvent> arrowGraphBuilder) where TActivity : IActivity <T, TResourceId> where TEvent : IEvent <T> where T : struct, IComparable <T>, IEquatable <T> where TResourceId : struct, IComparable <TResourceId>, IEquatable <TResourceId> { if (arrowGraphBuilder is null) { throw new ArgumentNullException(nameof(arrowGraphBuilder)); } if (!arrowGraphBuilder.AllDependenciesSatisfied) { return(false); } if (arrowGraphBuilder.FindInvalidConstraints().Any()) { return(false); } // Only perform if all events a have earliest finish times. if (!arrowGraphBuilder.Nodes.All(x => x.Content.EarliestFinishTime.HasValue)) { return(false); } var completedNodeIds = new HashSet <T>(); var remainingNodeIds = new HashSet <T>(arrowGraphBuilder.NodeIds); Node <T, TEvent> endNode = arrowGraphBuilder.EndNode; // Make sure the remainingNodeIds contain the End node. if (!remainingNodeIds.Contains(endNode.Id)) { return(false); } endNode.Content.LatestFinishTime = endNode.Content.EarliestFinishTime; if (!endNode.Content.LatestFinishTime.HasValue) { return(false); } int endNodeLatestFinishTime = endNode.Content.LatestFinishTime.Value; // Complete the End node first to ensure the completed node IDs contains something. completedNodeIds.Add(endNode.Id); remainingNodeIds.Remove(endNode.Id); // Backward flow algorithm. while (remainingNodeIds.Any()) { bool progress = false; foreach (T nodeId in remainingNodeIds.ToList()) { Node <T, TEvent> node = arrowGraphBuilder.Node(nodeId); // Get the outgoing edges and the successor nodes IDs. HashSet <T> outgoingEdges = node.OutgoingEdges; var successorNodeIds = new HashSet <T>(outgoingEdges.Select(arrowGraphBuilder.EdgeHeadNode).Select(x => x.Id)); if (successorNodeIds.IsSubsetOf(completedNodeIds)) { int latestFinishTime = endNodeLatestFinishTime; foreach (T outgoingEdgeId in outgoingEdges) { Edge <T, TActivity> outgoingEdge = arrowGraphBuilder.Edge(outgoingEdgeId); Node <T, TEvent> outgoingEdgeHeadnode = arrowGraphBuilder.EdgeHeadNode(outgoingEdgeId); if (outgoingEdgeHeadnode.Content.LatestFinishTime.HasValue) { int proposedLatestFinishTime = outgoingEdgeHeadnode.Content.LatestFinishTime.Value - outgoingEdge.Content.Duration; if (proposedLatestFinishTime < latestFinishTime) { latestFinishTime = proposedLatestFinishTime; } } if (outgoingEdge.Content.MaximumLatestFinishTime.HasValue) { int proposedLatestFinishTime = outgoingEdge.Content.MaximumLatestFinishTime.Value - outgoingEdge.Content.Duration; // Diminish the latest finish time artificially (if required). if (proposedLatestFinishTime < latestFinishTime) { latestFinishTime = proposedLatestFinishTime; } } } node.Content.LatestFinishTime = latestFinishTime; completedNodeIds.Add(nodeId); remainingNodeIds.Remove(nodeId); // Note we are making progress. progress = true; } } // If we have not made any progress then a cycle must exist in // the graph and we will not be able to calculate the latest // finish times. if (!progress) { throw new InvalidOperationException(Properties.Resources.CannotCalculateLatestFinishTimesDueToCyclicDependency); } } return(true); }