public static NodeSet <T> Merge(NodeSet <T> s1, NodeSet <T> s2)
        {
            MultiNode <T> ms1 = s1 as MultiNode <T>;
            MultiNode <T> ms2 = s2 as MultiNode <T>;

            if (ms1 != null)
            {
                foreach (T x in s2.Nodes)
                {
                    ms1.Add(x);
                }
                return(ms1);
            }
            if (ms2 != null)
            {
                foreach (T x in s1.Nodes)
                {
                    ms2.Add(x);
                }
                return(ms2);
            }
            return(new MultiNode <T>(
                       s1.Nodes.Union(s2.Nodes).Distinct()));
        }
        public long Schedule <T>(ISchedulingAdapter <T> a, IList <T> nodes, IList <T> end, SchedulingConstraints constraints)
        {
            ASAPScheduler.CheckInput(a, nodes);

            long endTime = constraints.EndTime;

            foreach (T x in nodes)
            {
                a.CStep[x] = long.MaxValue;
            }

            var pq = new PriorityQueue <NodeSet <T> >()
            {
                Resolve = NodeSet <T> .Merge
            };

            foreach (T x in end)
            {
                pq.Enqueue(-endTime, NodeSet <T> .From(x));
            }
            long startTime = endTime - 1;

            while (!pq.IsEmpty)
            {
                var  cur     = pq.Dequeue();
                long curTime = -cur.Key;
                long reqTime = long.MaxValue;
                var  curSet  = cur.Value;
                var  order   = PriorizeNodes(curSet.Nodes);
                foreach (T x in order)
                {
                    bool ready = true;
                    foreach (var dy in a.Succs[x])
                    {
                        T y = dy.Task;
                        if (a.CStep[y] == long.MaxValue)
                        {
                            // at least one successor is not yet scheduled
                            // -> task not ready
                            ready   = false;
                            reqTime = long.MaxValue;
                            break;
                        }
                        else if (a.CStep[y] < curTime - a.Latency[x] + dy.MinDelay)
                        {
                            // at least one successor starts before current task could
                            // complete -> task not ready
                            ready = false;
                            if (reqTime < long.MaxValue)
                            {
                                reqTime = Math.Min(reqTime, a.CStep[y] + a.Latency[x] - dy.MinDelay);
                            }
                        }
                    }
                    if (ready)
                    {
                        // Check for deadline violations in second pass
                        foreach (var dy in a.Succs[x])
                        {
                            T y = dy.Task;
                            if (a.CStep[y] > curTime - a.Latency[x] + dy.MaxDelay)
                            {
                                // deadline exceeded
                                throw new NotSchedulableException();
                            }
                        }

                        long lat      = a.Latency[x];
                        long execTime = curTime - lat;
                        // If some operation is combinatorial (latency == 0) and is scheduled as
                        // last instruction, it must be moved one step back to fit inside the schedule's
                        // time frame.
                        if (execTime == endTime)
                        {
                            --execTime;
                        }
                        long preHint, postHint;
                        if (!ConstrainedResources || a.TryPin(x, execTime, out preHint, out postHint))
                        {
                            a.CStep[x] = execTime;
                            long nextTime = execTime;
                            startTime = Math.Min(startTime, execTime);
                            // requeue predecessors
                            foreach (var dw in a.Preds[x])
                            {
                                T w = dw.Task;
                                pq.Enqueue(-(execTime - dw.MinDelay + a.Latency[w]), NodeSet <T> .From(w));
                            }
                        }
                        else
                        {
                            if (preHint < 0)
                            {
                                throw new NotSchedulableException();
                            }

                            pq.Enqueue(-(preHint + lat), NodeSet <T> .From(x));
                        }
                    }
                    else if (reqTime < long.MaxValue)
                    {
                        pq.Enqueue(-reqTime, NodeSet <T> .From(x));
                    }
                }
            }
            foreach (T x in nodes)
            {
                if (a.CStep[x] == long.MaxValue)
                {
                    throw new NotSchedulableException();
                }
            }
            return(startTime);
        }
        public long Schedule <T>(ISchedulingAdapter <T> a, IList <T> nodes, IList <T> startNodes,
                                 SchedulingConstraints constraints)
        {
            CheckInput(a, nodes);

            long startTime = constraints.StartTime;

            foreach (T x in nodes)
            {
                a.CStep[x] = long.MinValue;
            }

            var pq = new PriorityQueue <NodeSet <T> >()
            {
                Resolve = NodeSet <T> .Merge
            };

            foreach (T x in startNodes)
            {
                pq.Enqueue(startTime, NodeSet <T> .From(x));
            }
            long endTime = startTime + 1;

            while (!pq.IsEmpty)
            {
                var  cur     = pq.Dequeue();
                long curTime = cur.Key;
                var  curSet  = cur.Value;
                foreach (T x in curSet.Nodes)
                {
                    bool ready   = true;
                    long reqTime = curTime;
                    foreach (var dw in a.Preds[x])
                    {
                        T w = dw.Task;
                        if (a.CStep[w] == long.MinValue)
                        {
                            // at least one predecessor is not yet scheduled
                            // -> we cannot tell whether it is ok to schedule current task.
                            ready   = false;
                            reqTime = long.MinValue;
                            break;
                        }
                        else if (a.CStep[w] + dw.MinDelay > curTime)
                        {
                            // at least one predecessor did not yet complete.
                            ready = false;
                            if (reqTime > long.MinValue)
                            {
                                reqTime = Math.Max(reqTime, a.CStep[w] + dw.MinDelay);
                            }
                        }
                    }
                    if (ready)
                    {
                        // Check for deadline violations in second pass
                        foreach (var dw in a.Preds[x])
                        {
                            T w = dw.Task;
                            if (a.CStep[w] + dw.MaxDelay < curTime)
                            {
                                // deadline exceeded
                                throw new NotSchedulableException();
                            }
                        }

                        if (a.CStep[x] == long.MinValue)
                        {
                            long preHint, postHint;
                            if (!ConstrainedResources || a.TryPin(x, curTime, out preHint, out postHint))
                            {
                                a.CStep[x] = curTime;
                                long lat      = a.Latency[x];
                                long nextTime = curTime + lat;
                                if (lat > 0)
                                {
                                    endTime = Math.Max(endTime, nextTime);
                                }
                                else
                                {
                                    endTime = Math.Max(endTime, nextTime + 1);
                                }

                                // enqueue successor tasks
                                foreach (var dy in a.Succs[x])
                                {
                                    T y = dy.Task;
                                    pq.Enqueue(curTime + dy.MinDelay, NodeSet <T> .From(y));
                                }
                            }
                            else
                            {
                                pq.Enqueue(postHint, NodeSet <T> .From(x));
                            }
                        }
                    }
                    else if (reqTime > long.MinValue)
                    {
                        pq.Enqueue(reqTime, NodeSet <T> .From(x));
                    }
                }
            }
            foreach (T x in nodes)
            {
                if (a.CStep[x] == long.MinValue)
                {
                    throw new NotSchedulableException();
                }
            }
            return(endTime);
        }