use std::collections::HashMap;
use crate::algs::lattice::AdjacencyOpts;
use crate::topology::point::PointId;
use crate::topology::sieve::Sieve;
#[derive(Debug, Clone)]
pub struct DualGraph {
pub xadj: Vec<usize>,
pub adjncy: Vec<usize>,
pub vwgt: Vec<i32>, }
#[derive(Clone, Copy, Debug)]
pub struct DualGraphOpts {
pub boundary: AdjacencyOpts,
pub symmetrize: bool,
}
impl Default for DualGraphOpts {
fn default() -> Self {
Self {
boundary: AdjacencyOpts::default(),
symmetrize: true,
}
}
}
pub type WeightFn = Option<fn(PointId) -> i32>;
pub fn build_dual_with_opts<S>(
sieve: &S,
cells: impl IntoIterator<Item = PointId>,
opts: DualGraphOpts,
w: WeightFn,
) -> DualGraph
where
S: Sieve<Point = PointId>,
{
build_dual_inner(sieve, cells, opts, w).0
}
pub fn build_dual_with_order_and_opts<S>(
sieve: &S,
cells: impl IntoIterator<Item = PointId>,
opts: DualGraphOpts,
w: WeightFn,
) -> (DualGraph, Vec<PointId>)
where
S: Sieve<Point = PointId>,
{
build_dual_inner(sieve, cells, opts, w)
}
pub fn build_dual<S>(sieve: &S, cells: impl IntoIterator<Item = PointId>) -> DualGraph
where
S: Sieve<Point = PointId>,
{
build_dual_with_opts(sieve, cells, DualGraphOpts::default(), None)
}
pub fn build_dual_with_order<S>(
sieve: &S,
cells: impl IntoIterator<Item = PointId>,
) -> (DualGraph, Vec<PointId>)
where
S: Sieve<Point = PointId>,
{
build_dual_with_order_and_opts(sieve, cells, DualGraphOpts::default(), None)
}
fn build_dual_inner<S>(
sieve: &S,
cells_iter: impl IntoIterator<Item = PointId>,
opts: DualGraphOpts,
w: WeightFn,
) -> (DualGraph, Vec<PointId>)
where
S: Sieve<Point = PointId>,
{
let cells: Vec<PointId> = cells_iter.into_iter().collect();
let n = cells.len();
if n == 0 {
return (
DualGraph {
xadj: vec![0],
adjncy: Vec::new(),
vwgt: Vec::new(),
},
cells,
);
}
use std::collections::hash_map::Entry;
fn boundary_points<S2: Sieve<Point = PointId>>(
s: &S2,
p: PointId,
max_down_depth: Option<u32>,
) -> Vec<PointId> {
use std::collections::{HashSet, VecDeque};
match max_down_depth {
None => {
let mut out: Vec<PointId> = s.cone_points(p).collect();
let mut seen: HashSet<PointId> = out.iter().copied().collect();
let mut q: VecDeque<(PointId, u32)> = out.iter().copied().map(|x| (x, 1)).collect();
while let Some((r, _d)) = q.pop_front() {
for qn in s.cone_points(r) {
if seen.insert(qn) {
out.push(qn);
q.push_back((qn, 0));
}
}
}
out.sort_unstable();
out.dedup();
out
}
Some(k) if k == 0 => Vec::new(),
Some(k) => {
let mut out = Vec::new();
let mut seen = HashSet::new();
let mut q = VecDeque::from_iter(s.cone_points(p).map(|x| (x, 1)));
while let Some((r, d)) = q.pop_front() {
if seen.insert(r) {
out.push(r);
}
if d < k {
for qn in s.cone_points(r) {
q.push_back((qn, d + 1));
}
}
}
out.sort_unstable();
out.dedup();
out
}
}
}
let mut incident: HashMap<PointId, Vec<usize>> = HashMap::new();
incident.reserve(n * 4);
for (ci, &cell) in cells.iter().enumerate() {
let bnd = boundary_points(sieve, cell, opts.boundary.max_down_depth);
for b in bnd {
match incident.entry(b) {
Entry::Occupied(mut e) => e.get_mut().push(ci),
Entry::Vacant(e) => {
e.insert(vec![ci]);
}
}
}
}
let mut neigh: Vec<Vec<usize>> = vec![Vec::new(); n];
for (_b, mut cells_on_b) in incident {
if cells_on_b.len() < 2 {
continue;
}
cells_on_b.sort_unstable();
cells_on_b.dedup();
for i in 0..cells_on_b.len() {
let ci = cells_on_b[i];
for &cj in &cells_on_b[(i + 1)..] {
neigh[ci].push(cj);
if opts.symmetrize {
neigh[cj].push(ci);
}
}
}
}
let mut total_edges = 0usize;
for (i, list) in neigh.iter_mut().enumerate() {
list.sort_unstable();
list.dedup();
if let Ok(pos) = list.binary_search(&i) {
list.remove(pos);
}
total_edges += list.len();
}
let mut xadj = Vec::with_capacity(n + 1);
let mut adjncy = Vec::with_capacity(total_edges);
xadj.push(0);
for list in &neigh {
adjncy.extend(list.iter().copied());
xadj.push(adjncy.len());
}
let vwgt = if let Some(f) = w {
cells.iter().map(|&c| f(c)).collect()
} else {
vec![1; n]
};
(DualGraph { xadj, adjncy, vwgt }, cells)
}