use crate::constraint::{Revision, VarId};
use crate::domain::Domain;
use crate::variable::Variable;
const NONE: u32 = u32::MAX;
fn hopcroft_karp(
n_u: usize,
n_v: usize,
adj: &[Vec<u32>],
) -> (Vec<u32>, Vec<u32>) {
let mut match_u = vec![NONE; n_u];
let mut match_v = vec![NONE; n_v];
let mut dist = vec![0u32; n_u];
let inf = (n_u + n_v + 1) as u32;
loop {
let mut queue: Vec<u32> = Vec::new();
for u in 0..n_u {
if match_u[u] == NONE {
dist[u] = 0;
queue.push(u as u32);
} else {
dist[u] = inf;
}
}
let mut found = false;
let mut head = 0;
while head < queue.len() {
let u = queue[head] as usize;
head += 1;
for &v in &adj[u] {
let mu = match_v[v as usize];
if mu == NONE {
found = true;
} else if dist[mu as usize] == inf {
dist[mu as usize] = dist[u] + 1;
queue.push(mu);
}
}
}
if !found {
break;
}
for u in 0..n_u {
if match_u[u] == NONE {
dfs_augment(u, adj, &mut match_u, &mut match_v, &mut dist, inf);
}
}
}
(match_u, match_v)
}
fn dfs_augment(
u: usize,
adj: &[Vec<u32>],
match_u: &mut [u32],
match_v: &mut [u32],
dist: &mut [u32],
inf: u32,
) -> bool {
for &v in &adj[u] {
let mu = match_v[v as usize];
if mu == NONE || (dist[mu as usize] == dist[u] + 1
&& dfs_augment(mu as usize, adj, match_u, match_v, dist, inf))
{
match_u[u] = v;
match_v[v as usize] = u as u32;
return true;
}
}
dist[u] = inf;
false
}
fn tarjan_scc_iterative(n_nodes: usize, adj: &[Vec<u32>]) -> Vec<u32> {
let mut index = vec![NONE; n_nodes];
let mut lowlink = vec![0u32; n_nodes];
let mut on_stack = vec![false; n_nodes];
let mut scc_id = vec![NONE; n_nodes];
let mut stack: Vec<u32> = Vec::new();
let mut call_stack: Vec<(u32, u32)> = Vec::new();
let mut counter: u32 = 0;
let mut scc_counter: u32 = 0;
for start in 0..n_nodes {
if index[start] != NONE {
continue;
}
let start = start as u32;
index[start as usize] = counter;
lowlink[start as usize] = counter;
counter += 1;
stack.push(start);
on_stack[start as usize] = true;
call_stack.push((start, 0));
while let Some(&mut (v, ref mut ni)) = call_stack.last_mut() {
let v_usize = v as usize;
let neighbors = &adj[v_usize];
if (*ni as usize) < neighbors.len() {
let w = neighbors[*ni as usize];
*ni += 1;
let w_usize = w as usize;
if index[w_usize] == NONE {
index[w_usize] = counter;
lowlink[w_usize] = counter;
counter += 1;
stack.push(w);
on_stack[w_usize] = true;
call_stack.push((w, 0));
} else if on_stack[w_usize] {
if index[w_usize] < lowlink[v_usize] {
lowlink[v_usize] = index[w_usize];
}
}
} else {
if lowlink[v_usize] == index[v_usize] {
loop {
let w = stack.pop().unwrap();
on_stack[w as usize] = false;
scc_id[w as usize] = scc_counter;
if w == v {
break;
}
}
scc_counter += 1;
}
let v_lowlink = lowlink[v_usize];
call_stack.pop();
if let Some(&(parent, _)) = call_stack.last() {
let p = parent as usize;
if v_lowlink < lowlink[p] {
lowlink[p] = v_lowlink;
}
}
}
}
}
scc_id
}
pub fn propagate_gac_alldiff<D: Domain>(
scope: &[VarId],
variables: &mut [Variable<D>],
depth: usize,
) -> Revision {
let unassigned: Vec<usize> = scope
.iter()
.enumerate()
.filter(|&(_, &v)| {
let dom = &variables[v as usize].domain;
!dom.is_singleton() && !dom.is_empty()
})
.map(|(i, _)| i)
.collect();
let n_vars = unassigned.len();
if n_vars <= 2 {
return Revision::Unchanged;
}
let assigned_vals: Vec<D::Value> = scope
.iter()
.filter_map(|&v| variables[v as usize].domain.singleton_value())
.collect();
let mut all_vals: Vec<D::Value> = Vec::new();
let mut var_avail_raw: Vec<Vec<D::Value>> = Vec::with_capacity(n_vars);
for &ui in &unassigned {
let var_id = scope[ui] as usize;
let dom_vals = variables[var_id].domain.values();
let mut avail: Vec<D::Value> = Vec::new();
for v in dom_vals {
if !assigned_vals.contains(&v) {
if !all_vals.contains(&v) {
all_vals.push(v.clone());
}
avail.push(v);
}
}
var_avail_raw.push(avail);
}
if all_vals.is_empty() {
return Revision::Unsatisfiable;
}
let n_vals = all_vals.len();
let val_to_idx = |v: &D::Value| -> u32 {
all_vals.iter().position(|x| x == v).unwrap() as u32
};
let adj: Vec<Vec<u32>> = var_avail_raw
.iter()
.map(|avail| avail.iter().map(&val_to_idx).collect())
.collect();
let (match_u, match_v) = hopcroft_karp(n_vars, n_vals, &adj);
for &mu in &match_u[..n_vars] {
if mu == NONE {
return Revision::Unsatisfiable;
}
}
let total_nodes = n_vars + n_vals;
let mut res_adj: Vec<Vec<u32>> = vec![Vec::new(); total_nodes];
for u in 0..n_vars {
let matched_vi = match_u[u];
for &vi in &adj[u] {
let val_node = (n_vars as u32) + vi;
if vi == matched_vi {
res_adj[val_node as usize].push(u as u32);
} else {
res_adj[u].push(val_node);
}
}
}
let mut reachable = vec![false; total_nodes];
{
let mut bfs_queue: Vec<u32> = Vec::new();
for (vi, &mv) in match_v.iter().enumerate().take(n_vals) {
if mv == NONE {
let node = (n_vars + vi) as u32;
reachable[node as usize] = true;
bfs_queue.push(node);
}
}
let mut head = 0;
while head < bfs_queue.len() {
let node = bfs_queue[head];
head += 1;
for &next in &res_adj[node as usize] {
if !reachable[next as usize] {
reachable[next as usize] = true;
bfs_queue.push(next);
}
}
}
}
let scc_id = tarjan_scc_iterative(total_nodes, &res_adj);
let mut changed = false;
for u in 0..n_vars {
let var_id = scope[unassigned[u]] as usize;
let matched_vi = match_u[u];
for &vi in &adj[u] {
if vi == matched_vi {
continue; }
let val_node = n_vars + vi as usize;
if scc_id[u] == scc_id[val_node] || reachable[val_node] {
continue;
}
let val = &all_vals[vi as usize];
if variables[var_id].prune(val, depth) {
changed = true;
}
}
if variables[var_id].domain.is_empty() {
return Revision::Unsatisfiable;
}
}
if changed { Revision::Changed } else { Revision::Unchanged }
}