vpr: Filter netlist pack pattern matches

This completes splitting the pattern matching and pattern
selection/filtering into separate steps, which is clearer and simplifies
the code.

It should also ensure there is no netlist order-dependence to
determining pack pattern molecules.
diff --git a/vpr/src/pack/pack_patterns.cpp b/vpr/src/pack/pack_patterns.cpp
index 3beb834..dfd5d9d 100644
--- a/vpr/src/pack/pack_patterns.cpp
+++ b/vpr/src/pack/pack_patterns.cpp
@@ -64,6 +64,7 @@
 static bool is_wildcard_node(const t_netlist_pack_pattern_node& pattern_node);
 static bool is_wildcard_pin(const t_netlist_pack_pattern_pin& pattern_pin);
 static bool matches_pattern_pin(const AtomPinId from_pin, const t_netlist_pack_pattern_pin& pattern_pin, const AtomNetlist& netlist);
+static bool match_covered(const NetlistPatternMatch& match, const std::map<std::string,std::set<AtomBlockId>>& pattern_covered_blocks);
 
 /*
  * Global Function Implementations
@@ -146,19 +147,27 @@
 std::vector<NetlistPatternMatch> filter_netlist_pattern_matches(std::vector<NetlistPatternMatch> unfiltered_matches) {
     std::vector<NetlistPatternMatch> filtered_matches;
 
-    //Sort the matches by descending size
-    auto by_match_size = [](const NetlistPatternMatch& lhs , NetlistPatternMatch& rhs) {
-        return lhs.internal_blocks.size() > rhs.internal_blocks.size();
+    //Sort the matches in descending order based on the following criteria:
+    //   1) Size
+    //   2) Base cost
+    //Note that we use a stable sort to maintain the topological order in which the matches were found
+    auto by_preference = [&](const NetlistPatternMatch& lhs, const NetlistPatternMatch& rhs) {
+        return std::make_tuple(lhs.internal_blocks.size(), lhs.base_cost) > 
+               std::make_tuple(rhs.internal_blocks.size(), rhs.base_cost);
     };
-    std::sort(unfiltered_matches.begin(), unfiltered_matches.end(), by_match_size);
+    std::stable_sort(unfiltered_matches.begin(), unfiltered_matches.end(), by_preference);
 
-    //Remove matches with no internal blocks
-    auto non_empty_internal_blocks = [](const NetlistPatternMatch& lhs) {
-        //return lhs.internal_blocks.size() > 0;
-        return true;
-    };
-    std::copy_if(unfiltered_matches.begin(), unfiltered_matches.end(), std::back_inserter(filtered_matches),
-                 non_empty_internal_blocks);
+
+    std::map<std::string,std::set<AtomBlockId>> pattern_covered_blocks;
+    for (auto& match : unfiltered_matches) {
+        if (match.internal_blocks.empty()) continue; //Don't keep empty matches
+
+        if (match_covered(match, pattern_covered_blocks)) continue; //Each atom should appear in at most one molecule per pack pattern type
+
+        pattern_covered_blocks[match.pattern_name].insert(match.internal_blocks.begin(), match.internal_blocks.end());
+
+        filtered_matches.push_back(match);
+    }
 
     return filtered_matches;
 }
@@ -834,3 +843,11 @@
     return true;
 }
 
+static bool match_covered(const NetlistPatternMatch& match, const std::map<std::string,std::set<AtomBlockId>>& pattern_covered_blocks) {
+    for (auto blk : match.internal_blocks) {
+        auto itr = pattern_covered_blocks.find(match.pattern_name);
+        if (itr != pattern_covered_blocks.end() && itr->second.count(blk)) return true; 
+    }
+    return false;
+}
+