private function run_discussion_order_once($cflt, $plist)
 {
     $m = new MinCostMaxFlow();
     $m->add_progressf(array($this, "mcmf_progress"));
     $this->set_progress("Preparing assignment optimizer");
     // paper nodes
     // set p->po edge cost so low that traversing that edge will
     // definitely lower total cost; all positive costs are <=
     // count($this->pcm), so this edge should have cost:
     $pocost = -(count($this->pcm) + 1);
     $this->mcmf_max_cost = $pocost * count($plist) * 0.75;
     $m->add_node(".s", "source");
     $m->add_edge(".source", ".s", 1, 0);
     foreach ($plist as $i => $pids) {
         $m->add_node("p{$i}", "p");
         $m->add_node("po{$i}", "po");
         $m->add_edge(".s", "p{$i}", 1, 0);
         $m->add_edge("p{$i}", "po{$i}", 1, $pocost);
         $m->add_edge("po{$i}", ".sink", 1, 0);
     }
     // conflict edges
     $plist2 = $plist;
     // need copy for different iteration ptr
     foreach ($plist as $i => $pid1) {
         foreach ($plist2 as $j => $pid2) {
             if ($i != $j) {
                 $pid1 = is_array($pid1) ? $pid1[count($pid1) - 1] : $pid1;
                 $pid2 = is_array($pid2) ? $pid2[0] : $pid2;
                 // cost of edge is number of different conflicts
                 $cost = count($cflt[$pid1] + $cflt[$pid2]) - count(array_intersect($cflt[$pid1], $cflt[$pid2]));
                 $m->add_edge("po{$i}", "p{$j}", 1, $cost);
             }
         }
     }
     // run MCMF
     $this->mcmf = $m;
     $m->shuffle();
     $m->run();
     // extract next roots
     $roots = array_keys($plist);
     $result = array();
     while (count($roots)) {
         $source = ".source";
         if (count($roots) !== count($plist)) {
             $source = "p" . $roots[mt_rand(0, count($roots) - 1)];
         }
         $pgroup = $igroup = array();
         foreach ($m->topological_sort($source, "p") as $v) {
             $pidx = (int) substr($v->name, 1);
             $igroup[] = $pidx;
             if (is_array($plist[$pidx])) {
                 $pgroup = array_merge($pgroup, $plist[$pidx]);
             } else {
                 $pgroup[] = $plist[$pidx];
             }
         }
         $result[] = $pgroup;
         $roots = array_values(array_diff($roots, $igroup));
     }
     // done
     $m->clear();
     // break circular refs
     $this->mcmf = null;
     $this->profile["maxflow"] += $m->maxflow_end_at - $m->maxflow_start_at;
     if ($m->mincost_start_at) {
         $this->profile["mincost"] += $m->mincost_end_at - $m->mincost_start_at;
     }
     return $result;
 }
    srand($seed);
    // the shuffle() uses this seed
    $m->shuffle();
    $m->run();
    $assignments[mcmf_assignment_text($m)] = true;
}
$assignments = array_keys($assignments);
sort($assignments);
xassert_eqq(count($assignments), 2);
xassert_eqq($assignments[0], "u0 p0\nu1 p1\nu2 p2\n");
xassert_eqq($assignments[1], "u0 p1\nu1 p0\nu2 p2\n");
fwrite(STDERR, "- Phase 3 complete.\n");
// (4) all zero preferences => all possible results; this uses push-relabel
$m = new MinCostMaxFlow();
foreach (array("u0", "u1", "u2") as $x) {
    $m->add_node($x, "u");
    $m->add_edge(".source", $x, 1);
}
foreach (array("p0", "p1", "p2") as $x) {
    $m->add_node($x, "p");
    $m->add_edge($x, ".sink", 1);
}
foreach (array("u0", "u1", "u2") as $x) {
    foreach (array("p0", "p1", "p2") as $y) {
        $m->add_edge($x, $y, 1);
    }
}
$assignments = array();
foreach (range(100, 921384, 1247) as $seed) {
    $m->reset();
    srand($seed);