use crate::heuristic::{reachability_layers, Scratch};
use crate::packed::PackedTask;
pub fn goal_landmarks(task: &PackedTask) -> Vec<u32> {
let init = task.initial();
landmarks_for(task, &init, &task.goal_pos)
}
pub fn landmarks_for(
task: &PackedTask,
start: &crate::packed::State,
goal_pos: &[u32],
) -> Vec<u32> {
let mut sc = Scratch::new(task);
let (fact_layer, op_layer) =
reachability_layers(task, &mut sc, &start.bits, &start.fv, &start.fdef);
let mut is_lm = vec![false; task.n_facts];
let mut queue: Vec<u32> = Vec::new();
for &g in goal_pos {
if fact_layer[g as usize] != u32::MAX && !is_lm[g as usize] {
is_lm[g as usize] = true;
queue.push(g);
}
}
let mut head = 0;
while head < queue.len() {
let f = queue[head] as usize;
head += 1;
let fl = fact_layer[f];
if fl == 0 {
continue; }
let mut common: Option<Vec<u32>> = None;
for &oi in task.add_by_fact.slice(f) {
let oi = oi as usize;
if op_layer[oi] >= fl {
continue;
}
let pre: Vec<u32> = task.pre_pos.slice(oi).to_vec();
common = Some(match common {
None => pre,
Some(prev) => prev.into_iter().filter(|p| pre.contains(p)).collect(),
});
if common.as_ref().is_some_and(|c| c.is_empty()) {
break;
}
}
for p in common.unwrap_or_default() {
if !is_lm[p as usize] {
is_lm[p as usize] = true;
queue.push(p);
}
}
}
let mut out: Vec<u32> = (0..task.n_facts as u32)
.filter(|&f| is_lm[f as usize] && !crate::bitset::test(&start.bits, f as usize))
.collect();
out.sort_unstable();
out
}