libpitust 0.1.0

libpitust implements random utility functions I tend to use a lot
Documentation
//! Module `dag_sched` computes a DAG schedule based on a terrible heuristicy
//! algorithm.

use crate::graph::{node_map::NodeMap, postorder::postorder_all, GraphPred, GraphSucc};

struct NodeInfo {
    last_use_count: u32,
    priority: u32,
    num_incomplete_deps: u32,
    outstanding_users: u32,
    max_height: u32,
    peer_max_height: u32,
    issued: bool,
}

pub fn dag_schedule<G: GraphPred + GraphSucc>(graph: G) -> Vec<G::Node> {
    let mut nodes = NodeMap::new_with(graph, |_| NodeInfo {
        last_use_count: 0,
        priority: 0,
        num_incomplete_deps: 0,
        outstanding_users: 0,
        max_height: u32::MAX,
        peer_max_height: u32::MAX,
        issued: false,
    });
    for id in graph.nodes() {
        for depid in graph.pred_of(id) {
            nodes[depid].outstanding_users += 1;
            nodes[id].num_incomplete_deps += 1;
        }
    }

    let order = postorder_all(graph);
    for &id in &order {
        let mut prio = 0;
        let mut extra = 0;
        for succ in graph.succ_of(id) {
            let pprio = nodes[succ].priority;
            if prio == pprio {
                extra += 1;
            } else if prio < pprio {
                extra = 0;
                prio = pprio;
            }
        }
        nodes[id].priority = prio + extra + 1;
    }

    let mut ready = vec![];
    for id in graph.nodes() {
        if nodes[id].num_incomplete_deps == 0 {
            ready.push(id);
        }
    }

    let mut schedule = vec![];
    while ready.len() != 0 {
        // FIXME: compute all of this shit incrementally lmao
        // recompute max height
        for id in graph.nodes() {
            let mut height = 0;
            if !nodes[id].issued {
                for dep in graph.pred_of(id) {
                    let node = &mut nodes[dep];
                    if node.issued {
                        height = height.max(node.max_height);
                    }
                }
            }
            nodes[id].max_height = height + 1;
        }
        // recompute the maximum height of the adjacent nodes
        for id in graph.nodes() {
            let mut peer_max_height = 0;
            if !nodes[id].issued {
                for s in graph.succ_of(id) {
                    if nodes[s].max_height > peer_max_height {
                        peer_max_height = nodes[s].max_height;
                    }
                }
            }
            nodes[id].peer_max_height = peer_max_height + 1;
        }

        ready.sort_by_cached_key(|&id| {
            let last_use_count = nodes[id].last_use_count;
            let priority = nodes[id].priority;
            let max_height = nodes[id].peer_max_height;

            (last_use_count, priority, !max_height)
        });

        let id = ready.pop().unwrap();
        schedule.push(id);
        nodes[id].issued = true;

        for succ in graph.succ_of(id) {
            nodes[succ].num_incomplete_deps -= 1;
            if nodes[succ].num_incomplete_deps == 0 {
                ready.push(succ);
            }
        }
        for dep in graph.pred_of(id) {
            nodes[dep].outstanding_users -= 1;

            if nodes[dep].outstanding_users == 1 {
                for ns in graph.succ_of(dep) {
                    nodes[ns].last_use_count += 1;
                }
            }
        }
        if nodes[id].outstanding_users == 1 {
            for ns in graph.succ_of(id) {
                nodes[ns].last_use_count += 1;
            }
        }
    }

    schedule
}