use crate::tree::Tree;
use crate::point::Point;
use super::wirelength_vector::*;
use super::position_sequence::PositionSequence;
use super::hanan_grid::HananGridWithCoords;
use num_traits::{Num, Zero, PrimInt};
use std::iter::Sum;
use std::ops::Add;
use super::HananCoord;
use super::marker_types::Canonical;
#[derive(Clone, Debug)]
pub struct LookupTable {
pub(crate) sub_lut_by_degree: Vec<SubLookupTable>
}
impl LookupTable {
pub fn rsmt_low_degree<T>(&self, mut pins: Vec<Point<T>>) -> (Vec<(Point<T>, Point<T>)>, T)
where T: Ord + Copy + Num + Zero + Sum + std::fmt::Debug {
pins.sort();
pins.dedup();
let num_pins = pins.len();
if num_pins <= 1 {
return (vec![], Zero::zero());
}
assert!(pins.len() <= self.max_degree(), "number of pins exceeds the supported maximal net degree");
let position_sequence = PositionSequence::from_points(&pins);
let group_index = position_sequence.group_index();
let sub_lut = self.get_sub_lut(num_pins);
debug_assert_eq!(sub_lut.values.len(), (1..num_pins + 1).product());
let (powvs, transform) = sub_lut.resolve_group_index(group_index);
pins.iter_mut()
.for_each(|p| {
*p = transform.transform_point(*p);
});
let xs = {
let mut xs: Vec<_> = pins.iter().map(|p| p.x).collect();
xs.sort();
xs
};
let ys = {
let mut ys: Vec<_> = pins.iter().map(|p| p.y).collect();
ys.sort();
ys
};
let (edge_widths_horizontal, edge_widths_vertical) = {
let mut w = xs.clone();
let mut h = ys.clone();
for i in 0..xs.len() - 1 {
w[i] = w[i + 1] - w[i];
h[i] = h[i + 1] - h[i];
}
w.pop();
h.pop();
(w, h)
};
let best_powv_and_post = {
let compute_wire_length = |powv: &WirelenghtVector| -> T {
let (h, v) = powv.hv_vectors();
debug_assert_eq!(h.len(), edge_widths_horizontal.len());
debug_assert_eq!(v.len(), edge_widths_vertical.len());
let wl_horiz: T = h.into_iter()
.zip(&edge_widths_horizontal)
.map(|(count, w)| (0..*count).map(|_| *w).sum())
.sum();
let wl_vert: T = v.into_iter()
.zip(&edge_widths_vertical)
.map(|(count, w)| (0..*count).map(|_| *w).sum())
.sum();
wl_horiz + wl_vert
};
powvs.into_iter()
.map(|powv| (powv, compute_wire_length(&powv.potential_optimal_wirelength_vector)))
.min_by_key(|(_powv, wire_len)| *wire_len)
};
let (best_powv_and_post, wire_length) = best_powv_and_post.expect("no steiner tree found");
let transform_point_to_original_grid = |p: Point<HananCoord>| -> Point<T> {
Point::new(xs[p.x as usize], ys[p.y as usize])
};
let tree_edges = best_powv_and_post.potential_optimal_steiner_tree
.all_edges()
.map(|e| (e.start(), e.end())) .map(|(a, b)| (transform_point_to_original_grid(a), transform_point_to_original_grid(b)));
let inverse_transform = transform.inverse();
let tree = tree_edges
.map(|(a, b)|
(inverse_transform.transform_point(a), inverse_transform.transform_point(b))
)
.filter(|(a, b)| a != b)
.collect();
(tree, wire_length)
}
pub fn max_degree(&self) -> usize {
self.sub_lut_by_degree.len() + 1
}
pub fn get_sub_lut(&self, num_pins: usize) -> &SubLookupTable {
assert!(num_pins >=2, "no LUT for less than 2 pins");
&self.sub_lut_by_degree[num_pins-2]
}
pub fn print_statistics(&self) {
for (degree, sub_lut) in self.sub_lut_by_degree.iter().enumerate() {
let degree = degree + 2;
let max_num_powvs = sub_lut.values.iter()
.filter_map(|v| match v {
ValuesOrRedirect::Values(v) => Some(v.len()),
ValuesOrRedirect::Redirect { .. } => None
})
.max()
.unwrap_or(0);
let total_num_powvs: usize = sub_lut.values.iter()
.map(|v| match v {
ValuesOrRedirect::Values(v) => v.len(),
ValuesOrRedirect::Redirect { group_index, .. } => {
match &sub_lut.values[*group_index] {
ValuesOrRedirect::Values(v) => v.len(),
_ => panic!("invalid double-redirect")
}
}
})
.sum();
let average_num_pows = (total_num_powvs as f64) / (sub_lut.values.len() as f64);
println!("degree={}, max. number of POWVs = {}, avg. num. POWVs = {:.3}", degree, max_num_powvs, average_num_pows);
}
}
}
#[test]
fn test_rsmt_low_degree() {
let lut = super::gen_lut::gen_full_lut(5);
let points = vec![(5,0).into(), (0, 10).into(), (10, 10).into()];
let (rsmt, wl) = lut.rsmt_low_degree(points);
assert_eq!(rsmt.len(), 3);
assert_eq!(wl, 20);
let points = vec![(0,0).into(), (2, 1).into(), (2, 4).into(), (4, 2).into()];
let (rsmt, wl) = lut.rsmt_low_degree(points);
assert_eq!(rsmt.len(), 5);
assert_eq!(wl, 8);
let points = vec![(10,5).into(), (5, 0).into(), (5, 5).into(), (0, 5).into(), (5, 10).into()];
let (rsmt, wl) = lut.rsmt_low_degree(points);
assert_eq!(rsmt.len(), 4);
assert_eq!(wl, 20);
}
#[derive(Clone, Debug)]
pub struct SubLookupTable {
pub(crate) values: Vec<ValuesOrRedirect>
}
impl SubLookupTable {
fn resolve_group_index(&self, group_index: usize) -> (&Vec<LutValue>, Transform){
let lut_value = &self.values[group_index];
match lut_value {
ValuesOrRedirect::Values(values) => (values, Transform::identity()),
&ValuesOrRedirect::Redirect { group_index, transform } => {
let values = match &self.values[group_index] {
ValuesOrRedirect::Values(values) => values,
_ => panic!("lookup-table contains nested redirects")
};
(values, transform)
}
}
}
}
#[derive(Clone, Debug)]
pub struct LutValue {
pub(crate) potential_optimal_wirelength_vector: WirelenghtVector,
pub(crate) potential_optimal_steiner_tree: Tree<Canonical>,
}
#[derive(Debug, Clone)]
pub enum ValuesOrRedirect {
Values(Vec<LutValue>),
Redirect {
group_index: usize,
transform: Transform,
},
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Transform {
mirror_x: bool,
rotate: u8,
}
impl Transform {
pub fn new(rotate: u8, mirror_x: bool) -> Self {
Self {
rotate: rotate % 4,
mirror_x,
}
}
pub fn identity() -> Self {
Self::new(0, false)
}
pub fn inverse(&self) -> Self {
let r = if self.mirror_x {
self.rotate
} else {
4 - self.rotate
};
Self::new(r, self.mirror_x)
}
pub fn transform_point<T>(&self, mut p: Point<T>) -> Point<T>
where T: Copy + Num + Zero {
for _ in 0..self.rotate {
p.rotate_ccw90();
}
if self.mirror_x {
p.x = T::zero()-p.x;
}
p
}
}