#include "transform.hpp" #include "parts.hpp" #include "pin_name.hpp" #include "pins.hpp" #include "transform_vpx.hpp" #include #include #include #include #include Transform::Transform(std::string name) : name(std::move(name)) {} std::string CheckIdentityCompatible(const Part *a, const Part *b, std::string *info) { if (!a || !b) return "missing part"; // Compare on canonical names so that A1 ↔ A001 etc. count as the same pin. std::set a_pins, b_pins; std::unordered_map a_orig, b_orig; for (auto &kv : *a) { std::string c = canonical_pin_name(kv.first); a_pins.insert(c); a_orig.emplace(c, kv.first); } for (auto &kv : *b) { std::string c = canonical_pin_name(kv.first); b_pins.insert(c); b_orig.emplace(c, kv.first); } if (a_pins == b_pins) return ""; std::vector only_a, only_b; for (const auto &n : a_pins) if (!b_pins.count(n)) only_a.push_back(a_orig[n]); for (const auto &n : b_pins) if (!a_pins.count(n)) only_b.push_back(b_orig[n]); // True bidirectional mismatch — refuse. if (!only_a.empty() && !only_b.empty()) { std::string msg = "identity wiring requires the pin sets to be related"; msg += "; only on '" + a->name + "': " + std::to_string(only_a.size()) + " (e.g. " + only_a.front() + ")"; msg += "; only on '" + b->name + "': " + std::to_string(only_b.size()) + " (e.g. " + only_b.front() + ")"; return msg; } // One side is a (strict) subset of the other — accept, surface as info. if (info) { const auto &orphans = only_a.empty() ? only_b : only_a; const std::string &side = only_a.empty() ? b->name : a->name; *info = std::to_string(orphans.size()) + " pin(s) only on '" + side + "' (e.g. " + orphans.front() + ") — wiring intersection"; } return ""; } int FillIdentityNCs(Part *a, Part *b) { if (!a || !b) return 0; std::unordered_map a_canon, b_canon; for (auto &kv : *a) a_canon.emplace(canonical_pin_name(kv.first), kv.first); for (auto &kv : *b) b_canon.emplace(canonical_pin_name(kv.first), kv.first); auto fill = [](Part *dst, const std::unordered_map &src, const std::unordered_map &dst_canon) { int n = 0; for (const auto &kv : src) { if (dst_canon.count(kv.first)) continue; // already there canonically if (dst->exists(kv.second)) continue; // safety net for exotic clashes dst->add(new Pin(kv.second)); ++n; } return n; }; int added = 0; added += fill(a, b_canon, a_canon); added += fill(b, a_canon, b_canon); return added; } IdentityTransform::IdentityTransform() : Transform("identity") {} std::vector> IdentityTransform::apply(Part *a, Part *b) const { // Match pins on canonical name so A1 (one card) wires to A001 (the other). std::vector> out; std::unordered_map b_canon; for (auto &kv : *b) b_canon.emplace(canonical_pin_name(kv.first), kv.second); for (auto &kv : *a) { auto it = b_canon.find(canonical_pin_name(kv.first)); if (it != b_canon.end()) out.emplace_back(kv.second, it->second); } return out; } TransformRegistry::TransformRegistry() : identity_(new IdentityTransform()) { RegisterVpxTransforms(*this); } TransformRegistry::~TransformRegistry() { for (auto &kv : entries) delete kv.second; delete identity_; } TransformRegistry &TransformRegistry::get() { static TransformRegistry instance; return instance; } void TransformRegistry::add(const std::string &kA, const std::string &kB, Transform *t) { auto key = std::make_pair(kA, kB); auto it = entries.find(key); if (it != entries.end()) { delete it->second; it->second = t; } else { entries.emplace(key, t); } } Transform *TransformRegistry::lookup(const std::string &kA, const std::string &kB) const { auto it = entries.find({kA, kB}); if (it != entries.end()) return it->second; auto it2 = entries.find({kB, kA}); if (it2 != entries.end()) return it2->second; return identity_; } Transform *TransformRegistry::identity() const { return identity_; }