From 3d2635f379e1daf0fbb1707e70253b0c55b2c729 Mon Sep 17 00:00:00 2001 From: SpaceCat-Chan <49094338+SpaceCat-Chan@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:38:01 +0100 Subject: [PATCH] use approximate partitioning for binary search --- common/route/awooter/rust/src/partition.rs | 153 +++++++++++++++++---- 1 file changed, 128 insertions(+), 25 deletions(-) diff --git a/common/route/awooter/rust/src/partition.rs b/common/route/awooter/rust/src/partition.rs index 6df99731..d20602dd 100644 --- a/common/route/awooter/rust/src/partition.rs +++ b/common/route/awooter/rust/src/partition.rs @@ -124,37 +124,23 @@ pub fn find_partition_point( let mut x_diff = (x_finish - x_start) / 4; let mut y_diff = (y_finish - y_start) / 4; - let mut ne; - let mut se; - let mut sw; - let mut nw; - while x_diff != 0 { - (ne, se, sw, nw) = partition( - ctx, - nets, - arcs, - pips, - x, - y, - (x_start, x_finish), - (y_start, y_finish), - ); - let north = ne.len() + nw.len(); - let south = se.len() + sw.len(); + let (ne, se, sw, nw) = approximate_partition_results(arcs, (x, y)); + let north = ne + nw; + let south = se + sw; let nets = (north + south) as f64; - let ne_dist = f64::abs(((ne.len() as f64) / nets) - 0.25); - let se_dist = f64::abs(((se.len() as f64) / nets) - 0.25); - let sw_dist = f64::abs(((sw.len() as f64) / nets) - 0.25); - let nw_dist = f64::abs(((nw.len() as f64) / nets) - 0.25); + let ne_dist = f64::abs(((ne as f64) / nets) - 0.25); + let se_dist = f64::abs(((se as f64) / nets) - 0.25); + let sw_dist = f64::abs(((sw as f64) / nets) - 0.25); + let nw_dist = f64::abs(((nw as f64) / nets) - 0.25); let distortion = 100.0 * (ne_dist + se_dist + sw_dist + nw_dist); // Stop early if Good Enough. if distortion <= 5.0 { - return (x, y, ne, se, sw, nw); + break; } x += match north.cmp(&south) { @@ -163,8 +149,8 @@ pub fn find_partition_point( std::cmp::Ordering::Greater => -x_diff, }; - let east = ne.len() + se.len(); - let west = nw.len() + sw.len(); + let east = ne + se; + let west = nw + sw; y += match east.cmp(&west) { std::cmp::Ordering::Less => y_diff, std::cmp::Ordering::Equal => 0, @@ -175,7 +161,7 @@ pub fn find_partition_point( y_diff >>= 1; } - (ne, se, sw, nw) = partition( + let (ne, se, sw, nw) = partition( ctx, nets, arcs, @@ -203,6 +189,123 @@ pub fn find_partition_point( (x, y, ne, se, sw, nw) } +fn approximate_partition_results( + arcs: &[Arc], + partition_point: (i32, i32), +) -> (usize, usize, usize, usize) { + let mut count_ne = 0; + let mut count_se = 0; + let mut count_sw = 0; + let mut count_nw = 0; + for arc in arcs { + // TODO(SpaceCat~Chan): stop being lazy and merge Loc and Coord already + let source_is_north = arc.get_source_loc().x < partition_point.0; + let source_is_east = arc.get_source_loc().y < partition_point.1; + let sink_is_north = arc.get_sink_loc().x < partition_point.0; + let sink_is_east = arc.get_sink_loc().y < partition_point.1; + if source_is_north == sink_is_north && source_is_east == sink_is_east { + match (source_is_north, source_is_east) { + (true, true) => count_ne += 1, + (false, true) => count_se += 1, + (false, false) => count_sw += 1, + (true, false) => count_nw += 1, + } + } else if source_is_north != sink_is_north && source_is_east == sink_is_east { + if source_is_east { + count_ne += 1; + count_se += 1; + } else { + count_nw += 1; + count_sw += 1; + } + } else if source_is_north == sink_is_north { + if source_is_north { + count_ne += 1; + count_nw += 1; + } else { + count_se += 1; + count_sw += 1; + } + } else { + // all of this calculation is not be needed and an approximation would be good enough + // but i can't be bothered (yes this is all copy-pasted from the actual partitioner) + let mut middle_horiz = ( + partition_point.0, + split_line_over_x( + (arc.get_source_loc(), arc.get_sink_loc()), + partition_point.0, + ), + ); + + let mut middle_vert = ( + split_line_over_y( + (arc.get_source_loc(), arc.get_sink_loc()), + partition_point.1, + ), + partition_point.1, + ); + + // need to avoid the partition point + if middle_horiz.1 == partition_point.1 || middle_vert.0 == partition_point.0 { + if source_is_east != sink_is_north { + middle_horiz.1 = partition_point.1 + 1; + middle_vert.0 = partition_point.0 - 1; + } else { + middle_horiz.1 = partition_point.1 + 1; + middle_vert.0 = partition_point.0 + 1; + } + } + let horiz_happens_first = (middle_horiz.1 < partition_point.1) == source_is_east; + + // note: if you invert all the bools it adds to the same things, not sure how make less redundant + match (source_is_north, source_is_east, horiz_happens_first) { + (true, true, true) => { + count_ne += 1; + count_se += 1; + count_sw += 1; + } + (true, false, true) => { + count_nw += 1; + count_se += 1; + count_sw += 1; + } + (false, true, true) => { + count_ne += 1; + count_nw += 1; + count_se += 1; + } + (false, false, true) => { + count_ne += 1; + count_nw += 1; + count_sw += 1; + } + (true, true, false) => { + count_ne += 1; + count_nw += 1; + count_sw += 1; + } + (true, false, false) => { + count_ne += 1; + count_nw += 1; + count_se += 1; + } + (false, true, false) => { + count_nw += 1; + count_se += 1; + count_sw += 1; + } + (false, false, false) => { + count_ne += 1; + count_se += 1; + count_sw += 1; + } + } + } + } + + (count_ne, count_se, count_sw, count_nw) +} + /// finds the y location a line would be split at if you split it at a certain x location /// /// the function assumes the line goes on forever in both directions, and it truncates the actual coordinate