From 83ec364ca35f8343732d57130f3ba93e1221e31b Mon Sep 17 00:00:00 2001
From: xinyangli <lixinyang411@gmail.com>
Date: Sun, 4 Feb 2024 12:25:22 +0800
Subject: [PATCH] fix: mark broken for ci

---
 pkgs/abstract-machine/default.nix |   42 +
 pkgs/ieda/default.nix             |   77 +
 pkgs/ieda/fix_header.patch        |   12 +
 pkgs/ieda/fixtcl.patch            |   13 +
 pkgs/ieda/remove_format.patch     | 3077 +++++++++++++++++++++++++++++
 5 files changed, 3221 insertions(+)
 create mode 100644 pkgs/abstract-machine/default.nix
 create mode 100644 pkgs/ieda/default.nix
 create mode 100644 pkgs/ieda/fix_header.patch
 create mode 100644 pkgs/ieda/fixtcl.patch
 create mode 100644 pkgs/ieda/remove_format.patch

diff --git a/pkgs/abstract-machine/default.nix b/pkgs/abstract-machine/default.nix
new file mode 100644
index 0000000..fce6c82
--- /dev/null
+++ b/pkgs/abstract-machine/default.nix
@@ -0,0 +1,42 @@
+{ lib
+, stdenvNoCC
+, fetchFromGitHub
+, pkgsCross
+, gnumake
+, isa ? "riscv32e"
+, platform ? "nemu"
+}:
+
+let 
+  isa_mapping = {
+    riscv32e = "riscv64";
+  };
+in stdenvNoCC.mkDerivation rec {
+  pname = "abstract-machine";
+  version = "2023-08-29";
+
+  src = fetchFromGitHub {
+    owner = "NJU-ProjectN";
+    repo = "abstract-machine";
+    rev = "3348db971fd860be5cb28e21c18f9d0e65d0c96a";
+    hash = "sha256-jZVivawwHXw5WCrHm5vOb9nDBqK6BFVer8Zec0/oZvc=";
+  };
+
+  nativeBuildInputs = [
+    gnumake
+    pkgsCross.${isa_mapping.${isa}}.gcc
+  ];
+
+  SRCS = src;
+  AM_HOME = src;
+  ARCH = "${isa}-${platform}";
+  MAKECMDGOALS = "image";
+
+  meta = with lib; {
+    description = "A minimal, modularized, and machine-independent hardware abstraction layer";
+    homepage = "https://github.com/NJU-ProjectN/abstract-machine.git";
+    license = licenses.mit;
+    maintainers = with maintainers; [ ];
+    broken = true;
+  };
+}
diff --git a/pkgs/ieda/default.nix b/pkgs/ieda/default.nix
new file mode 100644
index 0000000..044576b
--- /dev/null
+++ b/pkgs/ieda/default.nix
@@ -0,0 +1,77 @@
+{ stdenvNoCC
+, lib
+, cargo
+, fetchgit
+, pkgsStatic
+, gcc11
+, cmake
+, ninja
+, flex
+, bison
+, zlib
+, tcl
+, gflags
+, glog
+, boost180
+, eigen
+, yaml-cpp
+, libunwind
+, metis
+, gmp
+, python3
+}:
+
+stdenvNoCC.mkDerivation rec {
+  name = "iEDA";
+  version = "0.3";
+  # src = fetchFromGitHub {
+  #   owner = "OSCC-Project";
+  #   repo = "iEDA";
+  #   rev = "a7df35fb4667cc099ab76d10da113f09060ac0ea";
+  #   hash = "sha256-ShMZ20EoFM34JUjmBh2enHTarM0qQbYb5A5eoC9D/VA=";
+  # };
+  src = fetchgit {
+    url = "https://gitee.com/oscc-project/iEDA.git";
+    name = "iEDA";
+    rev = "4de9f6c2dc602852fc1b668a866ffee0d96e2dcb";
+    hash = "sha256-BBVLVa2vuKdhz0Eb5aRrlkAMXlLcrTKWW63G3L3XgyE="; 
+  };
+  patches = [ ./fixtcl.patch ./fix_header.patch ];
+
+  nativeBuildInputs = [
+    gcc11
+    cargo
+    cmake
+    ninja
+    flex
+    bison
+    zlib
+  ];
+
+  buildInputs = [
+    tcl
+    pkgsStatic.gtest
+    gflags
+    glog
+    boost180
+    eigen
+    yaml-cpp
+    libunwind
+    metis
+    gmp
+    python3
+  ];
+
+  cmakeFlags = [
+    "-DBUILD_STATIC_LIB=off"
+  ];
+
+  CXXFLAGS = "-fpermissive";
+  meta = with lib; {
+    description = "An open-source EDA infracstrucutre and tools from netlist to GDS for ASIC design.";
+    homepage = "https://gitee.com/oscc-project/iEDA";
+    license = licenses.mulan-psl2;
+    maintainers = with maintainers; [ ];
+    broken = true;
+  };
+}
diff --git a/pkgs/ieda/fix_header.patch b/pkgs/ieda/fix_header.patch
new file mode 100644
index 0000000..8afb206
--- /dev/null
+++ b/pkgs/ieda/fix_header.patch
@@ -0,0 +1,12 @@
+diff --git a/src/operation/iCTS/api/CTSAPI.hh b/src/operation/iCTS/api/CTSAPI.hh
+index 86ff3565..bef443a0 100644
+--- a/src/operation/iCTS/api/CTSAPI.hh
++++ b/src/operation/iCTS/api/CTSAPI.hh
+@@ -27,6 +27,7 @@
+ #include <string_view>
+ #include <type_traits>
+ #include <vector>
++#include <optional>
+ 
+ #include "../../../database/interaction/ids.hpp"
+ 
diff --git a/pkgs/ieda/fixtcl.patch b/pkgs/ieda/fixtcl.patch
new file mode 100644
index 0000000..e830834
--- /dev/null
+++ b/pkgs/ieda/fixtcl.patch
@@ -0,0 +1,13 @@
+diff --git a/src/utility/tcl/ScriptEngine.hh b/src/utility/tcl/ScriptEngine.hh
+index 522e9878..cdee8af9 100644
+--- a/src/utility/tcl/ScriptEngine.hh
++++ b/src/utility/tcl/ScriptEngine.hh
+@@ -24,7 +24,7 @@
+ 
+ #pragma once
+ 
+-#include <tcl8.6/tcl.h>
++#include <tcl.h>
+ 
+ #include <memory>
+ #include <mutex>
diff --git a/pkgs/ieda/remove_format.patch b/pkgs/ieda/remove_format.patch
new file mode 100644
index 0000000..f97fa13
--- /dev/null
+++ b/pkgs/ieda/remove_format.patch
@@ -0,0 +1,3077 @@
+diff --git a/src/third_party/salt/CHANGELOG.md b/src/third_party/salt/CHANGELOG.md
+index 5408be85..33992341 100644
+--- a/src/third_party/salt/CHANGELOG.md
++++ b/src/third_party/salt/CHANGELOG.md
+@@ -149,8 +149,4 @@ diff -r ./base/net.h {ORIGIN REPOSITORY}/src/salt/base/net.h
+ >     void Write(const string& prefix, bool withNetInfo = true) const;
+ > 
+ >     friend ostream& operator<<(ostream& os, const Net& net) { net.Write(os); return os; }
+-```
+-
+-### Rename variable
+-
+-Changing some variable names and function names for the uniform naming convention does not change the code logic
+\ No newline at end of file
++```
+\ No newline at end of file
+diff --git a/src/third_party/salt/base/eval.cpp b/src/third_party/salt/base/eval.cpp
+index 1cf554b4..b1f8cc5e 100644
+--- a/src/third_party/salt/base/eval.cpp
++++ b/src/third_party/salt/base/eval.cpp
+@@ -7,7 +7,7 @@ namespace salt {
+ 
+ void WireLengthEvalBase::Update(const Tree& tree) {
+     wireLength = 0;
+-    tree.postOrder([&](const shared_ptr<TreeNode>& node) {
++    tree.PostOrder([&](const shared_ptr<TreeNode>& node) {
+         if (node->parent) {
+             wireLength += node->WireToParent();
+         }
+@@ -60,7 +60,7 @@ void ElmoreDelayEval::Update(double rd, Tree& tree, bool normalize) {
+     maxDelay = avgDelay = maxNorDelay = avgNorDelay = 0;
+ 
+     auto delay = GetDelay(rd, tree, numNodes);  // delay for all tree nodes
+-    tree.preOrder([&](const shared_ptr<TreeNode>& node) {
++    tree.PreOrder([&](const shared_ptr<TreeNode>& node) {
+         if (!node->pin || node == tree.source) return;
+         maxDelay = max(maxDelay, delay[node->id]);
+         avgDelay += delay[node->id];
+@@ -70,7 +70,7 @@ void ElmoreDelayEval::Update(double rd, Tree& tree, bool normalize) {
+     if (!normalize) return;
+ 
+     auto lb = GetDelayLB(rd, tree);  // delay lb for all pins, 0 is source
+-    // tree.preOrder([&](const shared_ptr<TreeNode>& node){
++    // tree.PreOrder([&](const shared_ptr<TreeNode>& node){
+     // 	if(!node->pin || node == tree.source) return;
+     // 	double norDelay = delay[node->id] / lb[node->id];
+     // 	maxNorDelay = max(maxNorDelay, norDelay);
+@@ -85,7 +85,7 @@ void ElmoreDelayEval::Update(double rd, Tree& tree, bool normalize) {
+ vector<double> ElmoreDelayEval::GetDelay(double rd, const Tree& tree, int numNode) {
+     // get node cap by post-order traversal
+     vector<double> cap(numNode, 0);
+-    tree.postOrder([&](const shared_ptr<TreeNode>& node) {
++    tree.PostOrder([&](const shared_ptr<TreeNode>& node) {
+         if (node->pin && node != tree.source) cap[node->id] = node->pin->cap;
+         for (auto c : node->children) {
+             cap[node->id] += cap[c->id];
+@@ -95,7 +95,7 @@ vector<double> ElmoreDelayEval::GetDelay(double rd, const Tree& tree, int numNod
+ 
+     // get delay by post-order traversal
+     vector<double> delay(numNode, 0);
+-    tree.preOrder([&](const shared_ptr<TreeNode>& node) {
++    tree.PreOrder([&](const shared_ptr<TreeNode>& node) {
+         if (node == tree.source)
+             delay[node->id] = rd * cap[node->id];
+         else {
+diff --git a/src/third_party/salt/base/flute.cpp b/src/third_party/salt/base/flute.cpp
+index 31672f46..f83f637f 100644
+--- a/src/third_party/salt/base/flute.cpp
++++ b/src/third_party/salt/base/flute.cpp
+@@ -14,8 +14,8 @@ void salt::FluteBuilder::Run(const salt::Net& net, salt::Tree& saltTree) {
+     }
+ 
+     // Obtain flute tree
+-    Flute::Tree flute_tree;
+-    flute_tree.branch = nullptr;
++    Flute::Tree fluteTree;
++    fluteTree.branch = nullptr;
+     int d = net.pins.size();
+     assert(d <= MAXD);
+     int x[MAXD], y[MAXD];
+@@ -23,17 +23,17 @@ void salt::FluteBuilder::Run(const salt::Net& net, salt::Tree& saltTree) {
+         x[i] = net.pins[i]->loc.x;
+         y[i] = net.pins[i]->loc.y;
+     }
+-    if (flute_tree.branch) free(flute_tree.branch);  // is it complete for mem leak?
+-    flute_tree = Flute::flute(d, x, y, FLUTE_ACCURACY);
++    if (fluteTree.branch) free(fluteTree.branch);  // is it complete for mem leak?
++    fluteTree = Flute::flute(d, x, y, FLUTE_ACCURACY);
+ 
+     // Build adjacency list
+     unordered_map<pair<DTYPE, DTYPE>, shared_ptr<salt::TreeNode>, boost::hash<pair<DTYPE, DTYPE>>> key2node;
+     for (auto p : net.pins) {
+         key2node[{p->loc.x, p->loc.y}] = make_shared<salt::TreeNode>(p);
+     }
+-    auto& t = flute_tree;
++    auto& t = fluteTree;
+ 
+-    auto find_or_create = [&](DTYPE x, DTYPE y) {
++    auto FindOrCreate = [&](DTYPE x, DTYPE y) {
+         auto it = key2node.find({x, y});
+         if (it == key2node.end()) {
+             shared_ptr<salt::TreeNode> node = make_shared<salt::TreeNode>(x, y);
+@@ -47,8 +47,8 @@ void salt::FluteBuilder::Run(const salt::Net& net, salt::Tree& saltTree) {
+         int j = t.branch[i].n;
+         if (t.branch[i].x == t.branch[j].x && t.branch[i].y == t.branch[j].y) continue;
+         // any more duplicate?
+-        shared_ptr<salt::TreeNode> n1 = find_or_create(t.branch[i].x, t.branch[i].y);
+-        shared_ptr<salt::TreeNode> n2 = find_or_create(t.branch[j].x, t.branch[j].y);
++        shared_ptr<salt::TreeNode> n1 = FindOrCreate(t.branch[i].x, t.branch[i].y);
++        shared_ptr<salt::TreeNode> n2 = FindOrCreate(t.branch[j].x, t.branch[j].y);
+         // printlog(LOG_INFO, "%d - %d\n", n1->pin?n1->pin->id:-1, n2->pin?n2->pin->id:-1);
+         n1->children.push_back(n2);
+         n2->children.push_back(n1);
+@@ -59,5 +59,5 @@ void salt::FluteBuilder::Run(const salt::Net& net, salt::Tree& saltTree) {
+     saltTree.SetParentFromUndirectedAdjList();
+     saltTree.net = &net;
+ 
+-    free(flute_tree.branch);
++    free(fluteTree.branch);
+ }
+\ No newline at end of file
+diff --git a/src/third_party/salt/base/mst.cpp b/src/third_party/salt/base/mst.cpp
+index 8a2ac098..48fe9352 100644
+--- a/src/third_party/salt/base/mst.cpp
++++ b/src/third_party/salt/base/mst.cpp
+@@ -68,7 +68,7 @@ void MstBuilder::RunPrimAlg(const Net& net, const vector<vector<int>>& adjLists,
+     }
+     for (int i = 0; i < numPins; ++i) {
+         if (prefixes[i] >= 0) {
+-            TreeNode::setParent(nodes[i], nodes[prefixes[i]]);
++            TreeNode::SetParent(nodes[i], nodes[prefixes[i]]);
+         }
+     }
+     tree.source = nodes[0];
+diff --git a/src/third_party/salt/base/net.cpp b/src/third_party/salt/base/net.cpp
+index ce852e22..246d2f23 100644
+--- a/src/third_party/salt/base/net.cpp
++++ b/src/third_party/salt/base/net.cpp
+@@ -39,7 +39,7 @@ bool Net::Read(istream& is) {
+     istringstream iss(buf);
+     iss >> id >> name >> numPin >> option;
+     assert(numPin > 0);
+-    with_cap = (option == "-cap");
++    withCap = (option == "-cap");
+ 
+     // pins
+     int i;
+@@ -48,7 +48,7 @@ bool Net::Read(istream& is) {
+     pins.resize(numPin);
+     for (auto& pin : pins) {
+         is >> i >> x >> y;
+-        if (with_cap) is >> c;
++        if (withCap) is >> c;
+         pin = make_shared<Pin>(x, y, i, c);
+     }
+ 
+@@ -67,7 +67,7 @@ void Net::Read(const string& fileName) {
+ string Net::GetHeader() const {
+     string header = to_string(id) + " " + name + " " + to_string(pins.size());
+     // string header = name + " " + to_string(pins.size());
+-    if (with_cap) header += " -cap";
++    if (withCap) header += " -cap";
+     return header;
+ }
+ 
+@@ -78,7 +78,7 @@ void Net::Write(ostream& os) const {
+     // pins
+     for (const auto& pin : pins) {
+         os << pin->id << " " << pin->loc.x << " " << pin->loc.y;
+-        if (with_cap) os << " " << pin->cap;
++        if (withCap) os << " " << pin->cap;
+         os << endl;
+     }
+ }
+diff --git a/src/third_party/salt/base/net.h b/src/third_party/salt/base/net.h
+index 3869359e..968eb8e2 100644
+--- a/src/third_party/salt/base/net.h
++++ b/src/third_party/salt/base/net.h
+@@ -6,19 +6,21 @@
+ 
+ #include "salt/utils/utils.h"
+ 
++using namespace std;
++
+ // #define DTYPE int  // same as flute.h, will overwrite it
+ typedef int DTYPE;
+ 
+ namespace salt {
+-using namespace std;
++
+ using Point = utils::PointT<DTYPE>;
+ using Box = utils::BoxT<DTYPE>;
+ 
+ class Pin
+ {
+  public:
+-  Point loc;
+   int id;  // 0 for source, should be in range [0, pin_num) for a net
++  Point loc;
+   double cap;
+ 
+   Pin(const Point& l, int i = -1, double c = 0.0) : loc(l), id(i), cap(c) {}
+@@ -33,7 +35,7 @@ class Net
+  public:
+   int id;
+   string name;
+-  bool with_cap = false;
++  bool withCap = false;
+ 
+   vector<shared_ptr<Pin>> pins;  // source is always the first one
+ 
+@@ -42,7 +44,7 @@ class Net
+     this->id = id;
+     this->name = name;
+     this->pins = pins;
+-    this->with_cap = true;
++    this->withCap = true;
+   }
+ 
+   void RanInit(int i, int numPin, DTYPE width = 100, DTYPE height = 100);  // random initialization
+diff --git a/src/third_party/salt/base/rsa.cpp b/src/third_party/salt/base/rsa.cpp
+index 8461cbbe..b76f4345 100644
+--- a/src/third_party/salt/base/rsa.cpp
++++ b/src/third_party/salt/base/rsa.cpp
+@@ -5,255 +5,227 @@
+ 
+ namespace salt {
+ 
+-constexpr double kPI = 3.14159265358979323846; /* pi */
+-
+-void RSABase::ReplaceRootChildren(Tree& tree)
+-{
+-  const Net* old_net = tree.net;
+-
+-  // create tmp_net and fake_pins
+-  Net tmp_net = *old_net;
+-  tmp_net.pins.clear();
+-  unordered_map<shared_ptr<Pin>, shared_ptr<TreeNode>> pin_to_old_node;
+-  tmp_net.pins.push_back(tree.source->pin);
+-  unordered_set<shared_ptr<Pin>> fake_pins;
+-  pin_to_old_node[tree.source->pin] = tree.source;
+-  for (auto c : tree.source->children) {  // only contains the direct children of tree.source
+-    shared_ptr<Pin> pin = c->pin;
+-    if (!pin) {
+-      pin = make_shared<Pin>(c->loc);
+-      fake_pins.insert(pin);
+-    }
+-    tmp_net.pins.push_back(pin);
+-    pin_to_old_node[pin] = c;
+-  }
+-  tree.source->children.clear();  // free them...
+-
+-  // get rsa and graft the old subtrees to it
+-  Run(tmp_net, tree);
+-  tree.postOrder([&](const shared_ptr<TreeNode>& node) {
+-    if (node->pin) {
+-      auto old_node = pin_to_old_node[node->pin];
+-      if (fake_pins.find(node->pin) != fake_pins.end())
+-        node->pin = nullptr;
+-      if (node->parent)
+-        for (auto c : old_node->children)
+-          TreeNode::setParent(c, node);
++constexpr double PI_VALUE = 3.14159265358979323846; /* pi */
++
++void RsaBase::ReplaceRootChildren(Tree& tree) {
++    const Net* oldNet = tree.net;
++
++    // create tmpNet and fakePins
++    Net tmpNet = *oldNet;
++    tmpNet.pins.clear();
++    unordered_map<shared_ptr<Pin>, shared_ptr<TreeNode>> pinToOldNode;
++    tmpNet.pins.push_back(tree.source->pin);
++    unordered_set<shared_ptr<Pin>> fakePins;
++    pinToOldNode[tree.source->pin] = tree.source;
++    for (auto c : tree.source->children) {  // only contains the direct children of tree.source
++        shared_ptr<Pin> pin = c->pin;
++        if (!pin) {
++            pin = make_shared<Pin>(c->loc);
++            fakePins.insert(pin);
++        }
++        tmpNet.pins.push_back(pin);
++        pinToOldNode[pin] = c;
+     }
+-  });
+-  tree.net = old_net;
+-  tree.RemoveTopoRedundantSteiner();
++    tree.source->children.clear();  // free them...
++
++    // get rsa and graft the old subtrees to it
++    Run(tmpNet, tree);
++    tree.PostOrder([&](const shared_ptr<TreeNode>& node) {
++        if (node->pin) {
++            auto oldNode = pinToOldNode[node->pin];
++            if (fakePins.find(node->pin) != fakePins.end()) node->pin = nullptr;
++            if (node->parent)
++                for (auto c : oldNode->children) TreeNode::SetParent(c, node);
++        }
++    });
++    tree.net = oldNet;
++    tree.RemoveTopoRedundantSteiner();
+ }
+ 
+-DTYPE RSABase::MaxOvlp(DTYPE z1, DTYPE z2)
+-{
+-  if (z1 >= 0 && z2 >= 0)
+-    return min(z1, z2);
+-  else if (z1 <= 0 && z2 <= 0)
+-    return max(z1, z2);
+-  else
+-    return 0;
++DTYPE RsaBase::MaxOvlp(DTYPE z1, DTYPE z2) {
++    if (z1 >= 0 && z2 >= 0)
++        return min(z1, z2);
++    else if (z1 <= 0 && z2 <= 0)
++        return max(z1, z2);
++    else
++        return 0;
+ }
+ 
+-void RsaBuilder::Run(const Net& net, Tree& tree)
+-{
+-  // Shift all pins to make source (0,0)
+-  auto ori_src_loc = net.source()->loc;
+-  for (auto& p : net.pins)
+-    p->loc -= ori_src_loc;
+-
+-  // Init inner_nodes with all sinks
+-  for (auto p : net.pins)
+-    if (p->IsSink())
+-      inner_nodes.insert(new InnerNode(make_shared<TreeNode>(p)));
+-
+-  // Process a inner node in each iteration
+-  while (!inner_nodes.empty()) {
+-    if ((*inner_nodes.begin())->dist == 0)
+-      break;  // TODO: clear
+-    shared_ptr<TreeNode> node = (*inner_nodes.begin())->tn;
+-    auto for_delete = *inner_nodes.begin();
+-    inner_nodes.erase(inner_nodes.begin());
+-    if (!node->pin) {  // steiner node
+-      assert(node->children.size() == 2);
+-      if (node->children[0])
+-        RemoveAnOuterNode(node->children[0], true, false);
+-      if (node->children[1])
+-        RemoveAnOuterNode(node->children[1], false, true);
+-      node->children[0]->parent = node;
+-      node->children[1]->parent = node;
+-    } else {  // pin node
+-      TryDominating(node);
++void RsaBuilder::Run(const Net& net, Tree& tree) {
++    // Shift all pins to make source (0,0)
++    auto oriSrcLoc = net.source()->loc;
++    for (auto& p : net.pins) p->loc -= oriSrcLoc;
++
++    // Init innerNodes with all sinks
++    for (auto p : net.pins)
++        if (p->IsSink()) innerNodes.insert(new InnerNode(make_shared<TreeNode>(p)));
++
++    // Process a inner node in each iteration
++    while (!innerNodes.empty()) {
++        if ((*innerNodes.begin())->dist == 0) break;  // TODO: clear
++        shared_ptr<TreeNode> node = (*innerNodes.begin())->tn;
++        auto forDelete = *innerNodes.begin();
++        innerNodes.erase(innerNodes.begin());
++        if (!node->pin) {  // steiner node
++            assert(node->children.size() == 2);
++            if (node->children[0]) RemoveAnOuterNode(node->children[0], true, false);
++            if (node->children[1]) RemoveAnOuterNode(node->children[1], false, true);
++            node->children[0]->parent = node;
++            node->children[1]->parent = node;
++        } else {  // pin node
++            TryDominating(node);
++        }
++        delete forDelete;
++        AddAnOuterNode(node);
+     }
+-    delete for_delete;
+-    AddAnOuterNode(node);
+-  }
+ 
+-  // connet the remaining outer_nodes to the source
+-  tree.source = make_shared<TreeNode>(net.source());
+-  for (const auto& on : outer_nodes)
+-    TreeNode::setParent(on.second.cur, tree.source);
+-  tree.net = &net;
++    // connet the remaining outerNodes to the source
++    tree.source = make_shared<TreeNode>(net.source());
++    for (const auto& on : outerNodes) TreeNode::SetParent(on.second.cur, tree.source);
++    tree.net = &net;
+ 
+-  // shift all pins back
+-  for (auto& p : net.pins)
+-    p->loc += ori_src_loc;
+-  tree.preOrder([&](const shared_ptr<TreeNode>& node) { node->loc += ori_src_loc; });
++    // shift all pins back
++    for (auto& p : net.pins) p->loc += oriSrcLoc;
++    tree.PreOrder([&](const shared_ptr<TreeNode>& node) { node->loc += oriSrcLoc; });
+ 
+-  // clear
+-  for (auto in : inner_nodes)
+-    delete in;
+-  inner_nodes.clear();
+-  outer_nodes.clear();
++    // clear
++    for (auto in : innerNodes) delete in;
++    innerNodes.clear();
++    outerNodes.clear();
+ }
+ 
+-map<double, OuterNode>::iterator RsaBuilder::NextOuterNode(const map<double, OuterNode>::iterator& it)
+-{
+-  auto res = next(it);
+-  if (res != outer_nodes.end())
+-    return res;
+-  else
+-    return outer_nodes.begin();
++map<double, OuterNode>::iterator RsaBuilder::NextOuterNode(const map<double, OuterNode>::iterator& it) {
++    auto res = next(it);
++    if (res != outerNodes.end())
++        return res;
++    else
++        return outerNodes.begin();
+ }
+ 
+-map<double, OuterNode>::iterator RsaBuilder::PrevOuterNode(const map<double, OuterNode>::iterator& it)
+-{
+-  if (it != outer_nodes.begin())
+-    return prev(it);
+-  else
+-    return prev(outer_nodes.end());
++map<double, OuterNode>::iterator RsaBuilder::PrevOuterNode(const map<double, OuterNode>::iterator& it) {
++    if (it != outerNodes.begin())
++        return prev(it);
++    else
++        return prev(outerNodes.end());
+ }
+ 
+-bool RsaBuilder::TryMaxOvlpSteinerNode(OuterNode& left, OuterNode& right)
+-{
+-  double rl_ang = atan2(right.cur->loc.y, right.cur->loc.x) - atan2(left.cur->loc.y, left.cur->loc.x);
+-  // if (rl_ang>-kPI && rl_ang<0) return false; // there is smaller arc
+-  DTYPE new_x, new_y;
+-  if ((rl_ang > -kPI && rl_ang < 0) || rl_ang > kPI) {  // large arc
+-    new_x = 0;
+-    new_y = 0;
+-  } else {
+-    new_x = MaxOvlp(left.cur->loc.x, right.cur->loc.x);
+-    new_y = MaxOvlp(left.cur->loc.y, right.cur->loc.y);
+-  }
+-  // if (new_x==0 && new_y==0) return false; // non-neighboring quadrant
+-  auto tn = make_shared<TreeNode>(new_x, new_y);
+-  tn->children = {left.cur, right.cur};
+-  auto in = new InnerNode(tn);
+-  left.right_parent = in;
+-  right.left_parent = in;
+-  inner_nodes.insert(in);
+-  // cout << "add a tmp steiner point" << endl;
+-  // tn->Print(0,true);
+-  return true;
++bool RsaBuilder::TryMaxOvlpSteinerNode(OuterNode& left, OuterNode& right) {
++    double rlAng = atan2(right.cur->loc.y, right.cur->loc.x) - atan2(left.cur->loc.y, left.cur->loc.x);
++    // if (rlAng>-PI_VALUE && rlAng<0) return false; // there is smaller arc
++    DTYPE newX, newY;
++    if ((rlAng > -PI_VALUE && rlAng < 0) || rlAng > PI_VALUE) {  // large arc
++        newX = 0;
++        newY = 0;
++    } else {
++        newX = MaxOvlp(left.cur->loc.x, right.cur->loc.x);
++        newY = MaxOvlp(left.cur->loc.y, right.cur->loc.y);
++    }
++    // if (newX==0 && newY==0) return false; // non-neighboring quadrant
++    auto tn = make_shared<TreeNode>(newX, newY);
++    tn->children = {left.cur, right.cur};
++    auto in = new InnerNode(tn);
++    left.rightP = in;
++    right.leftP = in;
++    innerNodes.insert(in);
++    // cout << "add a tmp steiner point" << endl;
++    // tn->Print(0,true);
++    return true;
+ }
+ 
+-void RsaBuilder::RemoveAnOuterNode(const shared_ptr<TreeNode>& node, bool del_left, bool del_right)
+-{
+-  auto outer_cur = outer_nodes.find(OuterNodeKey(node));
+-  assert(outer_cur != outer_nodes.end());
+-  InnerNode *inner_left = outer_cur->second.left_parent, *inner_right = outer_cur->second.right_parent;
+-  auto outer_left = outer_nodes.end(), outer_right = outer_nodes.end();
+-  if (inner_left != nullptr) {
+-    outer_left = PrevOuterNode(outer_cur);
+-    assert(outer_left->second.cur == inner_left->tn->children[0]);
+-    assert(outer_cur->second.cur == inner_left->tn->children[1]);
+-    inner_nodes.erase(inner_left);
+-    if (del_left)
+-      delete inner_left;  // inner parent become invalid now
+-    outer_left->second.right_parent = nullptr;
+-  }
+-  if (inner_right != nullptr) {
+-    outer_right = NextOuterNode(outer_cur);
+-    assert(outer_cur->second.cur == inner_right->tn->children[0]);
+-    assert(outer_right->second.cur == inner_right->tn->children[1]);
+-    inner_nodes.erase(inner_right);
+-    if (del_right)
+-      delete inner_right;  // inner parent become invalid now
+-    outer_right->second.left_parent = nullptr;
+-  }
+-  // delete outer_cur->second.first; //  outer child should be kept
+-  outer_nodes.erase(outer_cur);
+-  if (del_left && del_right && outer_right != outer_nodes.end() && outer_left != outer_nodes.end() && outer_left != outer_right) {
+-    TryMaxOvlpSteinerNode(outer_left->second, outer_right->second);
+-  }
++void RsaBuilder::RemoveAnOuterNode(const shared_ptr<TreeNode>& node, bool delL, bool delR) {
++    auto outerCur = outerNodes.find(OuterNodeKey(node));
++    assert(outerCur != outerNodes.end());
++    InnerNode *innerL = outerCur->second.leftP, *innerR = outerCur->second.rightP;
++    auto outerL = outerNodes.end(), outerR = outerNodes.end();
++    if (innerL != nullptr) {
++        outerL = PrevOuterNode(outerCur);
++        assert(outerL->second.cur == innerL->tn->children[0]);
++        assert(outerCur->second.cur == innerL->tn->children[1]);
++        innerNodes.erase(innerL);
++        if (delL) delete innerL;  // inner parent become invalid now
++        outerL->second.rightP = nullptr;
++    }
++    if (innerR != nullptr) {
++        outerR = NextOuterNode(outerCur);
++        assert(outerCur->second.cur == innerR->tn->children[0]);
++        assert(outerR->second.cur == innerR->tn->children[1]);
++        innerNodes.erase(innerR);
++        if (delR) delete innerR;  // inner parent become invalid now
++        outerR->second.leftP = nullptr;
++    }
++    // delete outerCur->second.first; //  outer child should be kept
++    outerNodes.erase(outerCur);
++    if (delL && delR && outerR != outerNodes.end() && outerL != outerNodes.end() && outerL != outerR) {
++        TryMaxOvlpSteinerNode(outerL->second, outerR->second);
++    }
+ }
+ 
+ // p dominates c
+-inline bool Dominate(const Point& p, const Point& c)
+-{
+-  return ((p.x >= 0 && c.x >= 0 && p.x <= c.x) || (p.x <= 0 && c.x <= 0 && p.x >= c.x))      // x
+-         && ((p.y >= 0 && c.y >= 0 && p.y <= c.y) || (p.y <= 0 && c.y <= 0 && p.y >= c.y));  // y
++inline bool Dominate(const Point& p, const Point& c) {
++    return ((p.x >= 0 && c.x >= 0 && p.x <= c.x) || (p.x <= 0 && c.x <= 0 && p.x >= c.x))      // x
++           && ((p.y >= 0 && c.y >= 0 && p.y <= c.y) || (p.y <= 0 && c.y <= 0 && p.y >= c.y));  // y
+ }
+ 
+-bool RsaBuilder::TryDominatingOneSide(OuterNode& p, OuterNode& c)
+-{
+-  if (!Dominate(p.cur->loc, c.cur->loc))
+-    return false;
+-  TreeNode::setParent(c.cur, p.cur);
+-  RemoveAnOuterNode(c.cur);
+-  return true;
++bool RsaBuilder::TryDominatingOneSide(OuterNode& p, OuterNode& c) {
++    if (!Dominate(p.cur->loc, c.cur->loc)) return false;
++    TreeNode::SetParent(c.cur, p.cur);
++    RemoveAnOuterNode(c.cur);
++    return true;
+ }
+ 
+-void RsaBuilder::TryDominating(const shared_ptr<TreeNode>& node)
+-{
+-  OuterNode outer_cur(node);
+-  if (outer_nodes.empty())
+-    return;
+-  else if (outer_nodes.size() == 1) {
+-    TryDominatingOneSide(outer_cur, outer_nodes.begin()->second);
+-    return;
+-  }
+-  // get outer_right & outer_left
+-  auto outer_right = outer_nodes.upper_bound(OuterNodeKey(node));
+-  if (outer_right == outer_nodes.end())
+-    outer_right = outer_nodes.begin();
+-  auto outer_left = PrevOuterNode(outer_right);
+-  assert(outer_left != outer_nodes.end() && outer_right != outer_nodes.end());
+-  assert(outer_left->second.right_parent == outer_right->second.left_parent);
+-  // try dominating twice
+-  TryDominatingOneSide(outer_cur, outer_left->second);
+-  TryDominatingOneSide(outer_cur, outer_right->second);
++void RsaBuilder::TryDominating(const shared_ptr<TreeNode>& node) {
++    OuterNode outerCur(node);
++    if (outerNodes.empty())
++        return;
++    else if (outerNodes.size() == 1) {
++        TryDominatingOneSide(outerCur, outerNodes.begin()->second);
++        return;
++    }
++    // get outerR & outerL
++    auto outerR = outerNodes.upper_bound(OuterNodeKey(node));
++    if (outerR == outerNodes.end()) outerR = outerNodes.begin();
++    auto outerL = PrevOuterNode(outerR);
++    assert(outerL != outerNodes.end() && outerR != outerNodes.end());
++    assert(outerL->second.rightP == outerR->second.leftP);
++    // try dominating twice
++    TryDominatingOneSide(outerCur, outerL->second);
++    TryDominatingOneSide(outerCur, outerR->second);
+ }
+ 
+ // suppose no case of [node = min(node, an outer node)]
+-void RsaBuilder::AddAnOuterNode(const shared_ptr<TreeNode>& node)
+-{
+-  OuterNode outer_cur(node);
+-  if (!outer_nodes.empty()) {
+-    // get outer_right & outer_left
+-    auto outer_right = outer_nodes.upper_bound(OuterNodeKey(node));
+-    if (outer_right == outer_nodes.end())
+-      outer_right = outer_nodes.begin();
+-    auto outer_left = PrevOuterNode(outer_right);
+-    assert(outer_left != outer_nodes.end() && outer_right != outer_nodes.end());
+-    assert(outer_left->second.right_parent == outer_right->second.left_parent);
+-    // delete parent(outer_right, outer_left)
+-    if (outer_left->second.right_parent) {
+-      inner_nodes.erase(outer_left->second.right_parent);
+-      delete outer_left->second.right_parent;  // inner parent become invalid now
++void RsaBuilder::AddAnOuterNode(const shared_ptr<TreeNode>& node) {
++    OuterNode outerCur(node);
++    if (!outerNodes.empty()) {
++        // get outerR & outerL
++        auto outerR = outerNodes.upper_bound(OuterNodeKey(node));
++        if (outerR == outerNodes.end()) outerR = outerNodes.begin();
++        auto outerL = PrevOuterNode(outerR);
++        assert(outerL != outerNodes.end() && outerR != outerNodes.end());
++        assert(outerL->second.rightP == outerR->second.leftP);
++        // delete parent(outerR, outerL)
++        if (outerL->second.rightP) {
++            innerNodes.erase(outerL->second.rightP);
++            delete outerL->second.rightP;  // inner parent become invalid now
++        }
++        // add two parents
++        TryMaxOvlpSteinerNode(outerL->second, outerCur);
++        TryMaxOvlpSteinerNode(outerCur, outerR->second);
+     }
+-    // add two parents
+-    TryMaxOvlpSteinerNode(outer_left->second, outer_cur);
+-    TryMaxOvlpSteinerNode(outer_cur, outer_right->second);
+-  }
+-  outer_nodes.insert({OuterNodeKey(node), outer_cur});
++    outerNodes.insert({OuterNodeKey(node), outerCur});
+ }
+ 
+-void RsaBuilder::PrintInnerNodes()
+-{
+-  cout << "Inner nodes (# = " << inner_nodes.size() << ")" << endl;
+-  for (auto in : inner_nodes) {
+-    cout << in->tn;
+-  }
++void RsaBuilder::PrintInnerNodes() {
++    cout << "Inner nodes (# = " << innerNodes.size() << ")" << endl;
++    for (auto in : innerNodes) {
++        cout << in->tn;
++    }
+ }
+ 
+-void RsaBuilder::PrintOuterNodes()
+-{
+-  cout << "Outer nodes (# = " << outer_nodes.size() << ")" << endl;
+-  for (auto on : outer_nodes) {
+-    cout << on.second.cur;
+-  }
++void RsaBuilder::PrintOuterNodes() {
++    cout << "Outer nodes (# = " << outerNodes.size() << ")" << endl;
++    for (auto on : outerNodes) {
++        cout << on.second.cur;
++    }
+ }
+ 
+ }  // namespace salt
+\ No newline at end of file
+diff --git a/src/third_party/salt/base/rsa.h b/src/third_party/salt/base/rsa.h
+index 172829fa..2266ae0a 100644
+--- a/src/third_party/salt/base/rsa.h
++++ b/src/third_party/salt/base/rsa.h
+@@ -1,91 +1,85 @@
+ #pragma once
+ 
++#include "tree.h"
++
+ #include <cmath>
+ #include <map>
+ #include <set>
+ 
+-#include "tree.h"
+-
+ namespace salt {
+ 
+-class RSABase
+-{
+- public:
+-  void ReplaceRootChildren(Tree& tree);
+-  virtual void Run(const Net& net, Tree& tree) = 0;
++class RsaBase {
++public:
++    void ReplaceRootChildren(Tree& tree);
++    virtual void Run(const Net& net, Tree& tree) = 0;
+ 
+- protected:
+-  DTYPE MaxOvlp(DTYPE z1, DTYPE z2);
++protected:
++    DTYPE MaxOvlp(DTYPE z1, DTYPE z2);
+ };
+ 
+ // Inner Node: an inner node alway has two child (outer) nodes
+ // tn->children[0]: smaller angle, clockwise, left
+ // tn->children[1]: larger angle, counter clockwise, right
+-class InnerNode
+-{
+- public:
+-  unsigned id;
+-  shared_ptr<TreeNode> tn;
+-  DTYPE dist;
+-  double angle;  // [-pi, pi]
+-  InnerNode(const shared_ptr<TreeNode>& treeNode) : tn(treeNode), dist(abs(tn->loc.x) + abs(tn->loc.y)), angle(atan2(tn->loc.y, tn->loc.x))
+-  {
+-    static unsigned gid = 0;
+-    id = gid++;
+-  }  // use id instead of pointer to make it deterministic
++class InnerNode {
++public:
++    unsigned id;
++    shared_ptr<TreeNode> tn;
++    DTYPE dist;
++    double angle;  // [-pi, pi]
++    InnerNode(const shared_ptr<TreeNode>& treeNode)
++        : tn(treeNode), dist(abs(tn->loc.x) + abs(tn->loc.y)), angle(atan2(tn->loc.y, tn->loc.x)) {
++        static unsigned gid = 0;
++        id = gid++;
++    }  // use id instead of pointer to make it deterministic
+ };
+-class CompInnerNode
+-{
+- public:
+-  bool operator()(const InnerNode* a, const InnerNode* b)
+-  {
+-    return a->dist > b->dist ||  // prefer fathest one
+-           (a->dist == b->dist
+-            && (a->angle > b->angle ||                      // prefer single direction
+-                (a->angle == b->angle && a->id > b->id)));  // distinguish two nodes with same loc (not touched normally)
+-  }
++class CompInnerNode {
++public:
++    bool operator()(const InnerNode* a, const InnerNode* b) {
++        return a->dist > b->dist ||                            // prefer fathest one
++               (a->dist == b->dist && (a->angle > b->angle ||  // prefer single direction
++                                       (a->angle == b->angle &&
++                                        a->id > b->id)));  // distinguish two nodes with same loc (not touched normally)
++    }
+ };
+ 
+ // Outer Node: an outer node has one or two parent (inner) nodes
+-class OuterNode
+-{
+- public:
+-  shared_ptr<TreeNode> cur;               // itself
+-  InnerNode *left_parent, *right_parent;  // left parent, right parent
+-  OuterNode(const shared_ptr<TreeNode>& c, InnerNode* l = nullptr, InnerNode* r = nullptr) : cur(c), left_parent(l), right_parent(r) {}
++class OuterNode {
++public:
++    shared_ptr<TreeNode> cur;   // itself
++    InnerNode *leftP, *rightP;  // left parent, right parent
++    OuterNode(const shared_ptr<TreeNode>& c, InnerNode* l = nullptr, InnerNode* r = nullptr) : cur(c), leftP(l), rightP(r) {}
+ };
+ 
+ // RSA Builder
+-class RsaBuilder : public RSABase
+-{
+- public:
+-  void Run(const Net& net, Tree& tree);
++class RsaBuilder : public RsaBase {
++public:
++    void Run(const Net& net, Tree& tree);
+ 
+- private:
+-  // inner nodes
+-  set<InnerNode*, CompInnerNode> inner_nodes;
++private:
++    // inner nodes
++    set<InnerNode*, CompInnerNode> innerNodes;
+ 
+-  // outer nodes
+-  map<double, OuterNode> outer_nodes;
+-  // the unique key is always guaranteed, better for query/find by key
+-  inline double OuterNodeKey(const shared_ptr<TreeNode>& tn) { return atan2(tn->loc.y, tn->loc.x); }
+-  // larger angle, counter clockwise, right
+-  map<double, OuterNode>::iterator NextOuterNode(const map<double, OuterNode>::iterator& it);
+-  // smaller angle, clockwise, left
+-  map<double, OuterNode>::iterator PrevOuterNode(const map<double, OuterNode>::iterator& it);
++    // outer nodes
++    map<double, OuterNode> outerNodes;
++    // the unique key is always guaranteed, better for query/find by key
++    inline double OuterNodeKey(const shared_ptr<TreeNode>& tn) { return atan2(tn->loc.y, tn->loc.x); }
++    // larger angle, counter clockwise, right
++    map<double, OuterNode>::iterator NextOuterNode(const map<double, OuterNode>::iterator& it);
++    // smaller angle, clockwise, left
++    map<double, OuterNode>::iterator PrevOuterNode(const map<double, OuterNode>::iterator& it);
+ 
+-  // remove an outer node
+-  bool TryMaxOvlpSteinerNode(OuterNode& left, OuterNode& right);  // maximize the overlapping
+-  void RemoveAnOuterNode(const shared_ptr<TreeNode>& node, bool del_left = true, bool del_right = true);
++    // remove an outer node
++    bool TryMaxOvlpSteinerNode(OuterNode& left, OuterNode& right);  // maximize the overlapping
++    void RemoveAnOuterNode(const shared_ptr<TreeNode>& node, bool delL = true, bool delR = true);
+ 
+-  // add an outer node
+-  bool TryDominatingOneSide(OuterNode& p, OuterNode& c);
+-  void TryDominating(const shared_ptr<TreeNode>& node);
+-  void AddAnOuterNode(const shared_ptr<TreeNode>& node);
++    // add an outer node
++    bool TryDominatingOneSide(OuterNode& p, OuterNode& c);
++    void TryDominating(const shared_ptr<TreeNode>& node);
++    void AddAnOuterNode(const shared_ptr<TreeNode>& node);
+ 
+-  // for debug
+-  void PrintInnerNodes();
+-  void PrintOuterNodes();
++    // for debug
++    void PrintInnerNodes();
++    void PrintOuterNodes();
+ };
+ 
+ }  // namespace salt
+\ No newline at end of file
+diff --git a/src/third_party/salt/base/tree.cpp b/src/third_party/salt/base/tree.cpp
+index a8648297..0fa5f8a3 100644
+--- a/src/third_party/salt/base/tree.cpp
++++ b/src/third_party/salt/base/tree.cpp
+@@ -6,349 +6,310 @@
+ 
+ namespace salt {
+ 
+-void TreeNode::PrintSingle(ostream& os) const
+-{
+-  os << "Node " << id << ": " << loc << (pin ? ", pin" : "") << ", " << children.size() << " children";
++void TreeNode::PrintSingle(ostream& os) const {
++    os << "Node " << id << ": " << loc << (pin ? ", pin" : "") << ", " << children.size() << " children";
+ }
+ 
+-void TreeNode::PrintRecursiveHelp(ostream& os, vector<bool>& prefix) const
+-{
+-  for (auto pre : prefix)
+-    os << (pre ? "  |" : "   ");
+-  if (!prefix.empty())
+-    os << "-> ";
+-  PrintSingle(os);
+-  os << endl;
+-  if (children.size() > 0) {
+-    prefix.push_back(true);
+-    for (size_t i = 0; i < children.size() - 1; ++i) {
+-      if (children[i])
+-        children[i]->PrintRecursiveHelp(os, prefix);
+-      else
+-        os << "<null>" << endl;
++void TreeNode::PrintRecursiveHelp(ostream& os, vector<bool>& prefix) const {
++    for (auto pre : prefix) os << (pre ? "  |" : "   ");
++    if (!prefix.empty()) os << "-> ";
++    PrintSingle(os);
++    os << endl;
++    if (children.size() > 0) {
++        prefix.push_back(true);
++        for (size_t i = 0; i < children.size() - 1; ++i) {
++            if (children[i])
++                children[i]->PrintRecursiveHelp(os, prefix);
++            else
++                os << "<null>" << endl;
++        }
++        prefix.back() = false;
++        children.back()->PrintRecursiveHelp(os, prefix);
++        prefix.pop_back();
+     }
+-    prefix.back() = false;
+-    children.back()->PrintRecursiveHelp(os, prefix);
+-    prefix.pop_back();
+-  }
+ }
+ 
+-void TreeNode::PrintRecursive(ostream& os) const
+-{
+-  vector<bool> prefix;  // prefix indicates whether an ancestor is a last child or not
+-  PrintRecursiveHelp(os, prefix);
++void TreeNode::PrintRecursive(ostream& os) const {
++    vector<bool> prefix;  // prefix indicates whether an ancestor is a last child or not
++    PrintRecursiveHelp(os, prefix);
+ }
+ 
+-void TreeNode::setParent(const shared_ptr<TreeNode>& childNode, const shared_ptr<TreeNode>& parentNode)
+-{
+-  childNode->parent = parentNode;
+-  parentNode->children.push_back(childNode);
++void TreeNode::SetParent(const shared_ptr<TreeNode>& childNode, const shared_ptr<TreeNode>& parentNode) {
++    childNode->parent = parentNode;
++    parentNode->children.push_back(childNode);
+ }
+ 
+-void TreeNode::resetParent(const shared_ptr<TreeNode>& node)
+-{
+-  assert(node->parent);
++void TreeNode::ResetParent(const shared_ptr<TreeNode>& node) {
++    assert(node->parent);
+ 
+-  auto& n = node->parent->children;
+-  auto it = find(n.begin(), n.end(), node);
+-  assert(it != n.end());
+-  *it = n.back();
+-  n.pop_back();
++    auto& n = node->parent->children;
++    auto it = find(n.begin(), n.end(), node);
++    assert(it != n.end());
++    *it = n.back();
++    n.pop_back();
+ 
+-  node->parent.reset();
++    node->parent.reset();
+ }
+ 
+-void TreeNode::reroot(const shared_ptr<TreeNode>& node)
+-{
+-  if (node->parent) {
+-    reroot(node->parent);
+-    auto old_parent = node->parent;
+-    TreeNode::resetParent(node);
+-    TreeNode::setParent(old_parent, node);
+-  }
++void TreeNode::Reroot(const shared_ptr<TreeNode>& node) {
++    if (node->parent) {
++        Reroot(node->parent);
++        auto oldParent = node->parent;
++        TreeNode::ResetParent(node);
++        TreeNode::SetParent(oldParent, node);
++    }
+ }
+ 
+-bool TreeNode::isAncestor(const shared_ptr<TreeNode>& ancestor, const shared_ptr<TreeNode>& descendant)
+-{
+-  auto tmp = descendant;
+-  do {
+-    if (tmp == ancestor) {
+-      return true;
+-    }
+-    tmp = tmp->parent;
+-  } while (tmp);
+-  return false;
++bool TreeNode::IsAncestor(const shared_ptr<TreeNode>& ancestor, const shared_ptr<TreeNode>& descendant) {
++    auto tmp = descendant;
++    do {
++        if (tmp == ancestor) {
++            return true;
++        }
++        tmp = tmp->parent;
++    } while (tmp);
++    return false;
+ }
+ 
+-void TreeNode::preOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit)
+-{
+-  visit(node);
+-  for (auto c : node->children)
+-    preOrder(c, visit);
++void TreeNode::PreOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit) {
++    visit(node);
++    for (auto c : node->children) PreOrder(c, visit);
+ }
+ 
+-void TreeNode::postOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit)
+-{
+-  for (auto c : node->children)
+-    postOrder(c, visit);
+-  visit(node);
++void TreeNode::PostOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit) {
++    for (auto c : node->children) PostOrder(c, visit);
++    visit(node);
+ }
+ 
+-void TreeNode::postOrderCopy(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit)
+-{
+-  auto tmp = node->children;
+-  for (auto c : tmp)
+-    postOrderCopy(c, visit);
+-  visit(node);
++void TreeNode::PostOrderCopy(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit) {
++    auto tmp = node->children;
++    for (auto c : tmp) PostOrderCopy(c, visit);
++    visit(node);
+ }
+ 
+-void Tree::Reset(bool freeTreeNodes)
+-{
+-  if (freeTreeNodes) {
+-    postOrder([](const shared_ptr<TreeNode>& node) { node->children.clear(); });
+-  }
+-  source.reset();
+-  net = nullptr;
++void Tree::Reset(bool freeTreeNodes) {
++    if (freeTreeNodes) {
++        PostOrder([](const shared_ptr<TreeNode>& node) { node->children.clear(); });
++    }
++    source.reset();
++    net = nullptr;
+ }
+ 
+-void Tree::Read(istream& is)
+-{
+-  Net* p_net = new Net;
+-
+-  // header
+-  string buf, option;
+-  int num_pin = 0;
+-  while (is >> buf && buf != "Tree")
+-    ;
+-  assert(buf == "Tree");
+-  getline(is, buf);
+-  istringstream iss(buf);
+-  iss >> p_net->id >> p_net->name >> num_pin >> option;
+-  assert(num_pin > 0);
+-  p_net->with_cap = (option == "-cap");
+-
+-  // pins
+-  int i, parent_idx;
+-  DTYPE x, y;
+-  double c = 0.0;
+-  p_net->pins.resize(num_pin);
+-  vector<int> parent_idxs;
+-  vector<shared_ptr<TreeNode>> tree_nodes;
+-  for (auto& pin : p_net->pins) {
+-    is >> i >> x >> y >> parent_idx;
+-    assert(i == tree_nodes.size());
+-    if (p_net->with_cap)
+-      is >> c;
+-    pin = make_shared<Pin>(x, y, i, c);
+-    tree_nodes.push_back(make_shared<TreeNode>(x, y, pin, i));
+-    parent_idxs.push_back(parent_idx);
+-  }
+-  assert(tree_nodes.size() == num_pin);
+-
+-  // non-pin nodes
+-  getline(is, buf);  // consume eol
+-  streampos pos;
+-  while (true) {
+-    pos = is.tellg();
++void Tree::Read(istream& is) {
++    Net* pNet = new Net;
++
++    // header
++    string buf, option;
++    int numPin = 0;
++    while (is >> buf && buf != "Tree")
++        ;
++    assert(buf == "Tree");
+     getline(is, buf);
+-    istringstream iss2(buf);
+-    iss2 >> i >> x >> y >> parent_idx;
+-    if (iss2.fail())
+-      break;
+-    assert(i == tree_nodes.size());
+-    tree_nodes.push_back(make_shared<TreeNode>(x, y, nullptr, i));
+-    parent_idxs.push_back(parent_idx);
+-  }
+-  is.seekg(pos);  // go back
+-
+-  // parents
+-  for (unsigned i = 0; i < tree_nodes.size(); ++i) {
+-    parent_idx = parent_idxs[i];
+-    if (parent_idx >= 0) {
+-      assert(parent_idx < tree_nodes.size());
+-      salt::TreeNode::setParent(tree_nodes[i], tree_nodes[parent_idx]);
+-    } else {
+-      assert(parent_idx == -1);
+-      source = tree_nodes[i];
++    istringstream iss(buf);
++    iss >> pNet->id >> pNet->name >> numPin >> option;
++    assert(numPin > 0);
++    pNet->withCap = (option == "-cap");
++
++    // pins
++    int i, parentIdx;
++    DTYPE x, y;
++    double c = 0.0;
++    pNet->pins.resize(numPin);
++    vector<int> parentIdxs;
++    vector<shared_ptr<TreeNode>> treeNodes;
++    for (auto& pin : pNet->pins) {
++        is >> i >> x >> y >> parentIdx;
++        assert(i == treeNodes.size());
++        if (pNet->withCap) is >> c;
++        pin = make_shared<Pin>(x, y, i, c);
++        treeNodes.push_back(make_shared<TreeNode>(x, y, pin, i));
++        parentIdxs.push_back(parentIdx);
++    }
++    assert(treeNodes.size() == numPin);
++
++    // non-pin nodes
++    getline(is, buf);  // consume eol
++    streampos pos;
++    while (true) {
++        pos = is.tellg();
++        getline(is, buf);
++        istringstream iss2(buf);
++        iss2 >> i >> x >> y >> parentIdx;
++        if (iss2.fail()) break;
++        assert(i == treeNodes.size());
++        treeNodes.push_back(make_shared<TreeNode>(x, y, nullptr, i));
++        parentIdxs.push_back(parentIdx);
++    }
++    is.seekg(pos);  // go back
++
++    // parents
++    for (unsigned i = 0; i < treeNodes.size(); ++i) {
++        parentIdx = parentIdxs[i];
++        if (parentIdx >= 0) {
++            assert(parentIdx < treeNodes.size());
++            salt::TreeNode::SetParent(treeNodes[i], treeNodes[parentIdx]);
++        } else {
++            assert(parentIdx == -1);
++            source = treeNodes[i];
++        }
+     }
+-  }
+ 
+-  net = p_net;
+-  // TODO: check dangling nodes
++    net = pNet;
++    // TODO: check dangling nodes
+ }
+ 
+-void Tree::Read(const string& file_name)
+-{
+-  ifstream is(file_name);
+-  if (is.fail()) {
+-    cout << "ERROR: Cannot open file " << file_name << endl;
+-    exit(1);
+-  }
+-  Read(is);
++void Tree::Read(const string& fileName) {
++    ifstream is(fileName);
++    if (is.fail()) {
++        cout << "ERROR: Cannot open file " << fileName << endl;
++        exit(1);
++    }
++    Read(is);
+ }
+ 
+-void Tree::Write(ostream& os)
+-{
+-  // header
+-  os << "Tree " << net->GetHeader() << endl;
+-
+-  // nodes
+-  // Note: source pin may not be covered in some intermediate state
+-  int num_nodes = UpdateId();
+-  auto nodes = ObtainNodes();
+-  vector<shared_ptr<TreeNode>> sorted_nodes(num_nodes, nullptr);  // bucket sort
+-  for (auto node : nodes) {
+-    sorted_nodes[node->id] = node;
+-  }
+-  for (auto node : sorted_nodes) {
+-    if (!node)
+-      continue;
+-    int parentId = node->parent ? node->parent->id : -1;
+-    os << node->id << " " << node->loc.x << " " << node->loc.y << " " << parentId;
+-    if (net->with_cap && node->pin)
+-      os << " " << node->pin->cap;
+-    os << endl;
+-  }
++void Tree::Write(ostream& os) {
++    // header
++    os << "Tree " << net->GetHeader() << endl;
++
++    // nodes
++    // Note: source pin may not be covered in some intermediate state
++    int numNodes = UpdateId();
++    auto nodes = ObtainNodes();
++    vector<shared_ptr<TreeNode>> sortedNodes(numNodes, nullptr);  // bucket sort
++    for (auto node : nodes) {
++        sortedNodes[node->id] = node;
++    }
++    for (auto node : sortedNodes) {
++        if (!node) continue;
++        int parentId = node->parent ? node->parent->id : -1;
++        os << node->id << " " << node->loc.x << " " << node->loc.y << " " << parentId;
++        if (net->withCap && node->pin) os << " " << node->pin->cap;
++        os << endl;
++    }
+ }
+ 
+-void Tree::Write(const string& prefix, bool with_net_info)
+-{
+-  ofstream ofs(prefix + (with_net_info ? ("_" + net->name) : "") + ".tree");
+-  Write(ofs);
+-  ofs.close();
++void Tree::Write(const string& prefix, bool withNetInfo) {
++    ofstream ofs(prefix + (withNetInfo ? ("_" + net->name) : "") + ".tree");
++    Write(ofs);
++    ofs.close();
+ }
+ 
+-void Tree::Print(ostream& os) const
+-{
+-  os << "Tree ";
+-  if (net)
+-    os << net->id << ": #pins=" << net->pins.size() << endl;
+-  else
+-    os << "<no_net_associated>" << endl;
+-  if (source)
+-    source->PrintRecursive(os);
+-  else
+-    os << "<null>" << endl;
++void Tree::Print(ostream& os) const {
++    os << "Tree ";
++    if (net)
++        os << net->id << ": #pins=" << net->pins.size() << endl;
++    else
++        os << "<no_net_associated>" << endl;
++    if (source)
++        source->PrintRecursive(os);
++    else
++        os << "<null>" << endl;
+ }
+ 
+-int Tree::UpdateId()
+-{
+-  int num_node = net->pins.size();
+-  preOrder([&](const shared_ptr<TreeNode>& node) {
+-    if (node->pin) {
+-      assert(node->pin->id < net->pins.size());
+-      node->id = node->pin->id;
+-    } else
+-      node->id = num_node++;
+-  });
+-  return num_node;
++int Tree::UpdateId() {
++    int numNode = net->pins.size();
++    PreOrder([&](const shared_ptr<TreeNode>& node) {
++        if (node->pin) {
++            assert(node->pin->id < net->pins.size());
++            node->id = node->pin->id;
++        } else
++            node->id = numNode++;
++    });
++    return numNode;
+ }
+ 
+-vector<shared_ptr<TreeNode>> Tree::ObtainNodes() const
+-{
+-  vector<shared_ptr<TreeNode>> nodes;
+-  preOrder([&](const shared_ptr<TreeNode>& node) { nodes.push_back(node); });
+-  return nodes;
++vector<shared_ptr<TreeNode>> Tree::ObtainNodes() const {
++    vector<shared_ptr<TreeNode>> nodes;
++    PreOrder([&](const shared_ptr<TreeNode>& node) { nodes.push_back(node); });
++    return nodes;
+ }
+ 
+-void Tree::SetParentFromChildren()
+-{
+-  preOrder([](const shared_ptr<TreeNode>& node) {
+-    for (auto& c : node->children) {
+-      c->parent = node;
+-    }
+-  });
++void Tree::SetParentFromChildren() {
++    PreOrder([](const shared_ptr<TreeNode>& node) {
++        for (auto& c : node->children) {
++            c->parent = node;
++        }
++    });
+ }
+ 
+-void Tree::SetParentFromUndirectedAdjList()
+-{
+-  preOrder([](const shared_ptr<TreeNode>& node) {
+-    for (auto& c : node->children) {
+-      auto& n = c->children;
+-      auto it = find(n.begin(), n.end(), node);
+-      assert(it != n.end());
+-      *it = n.back();
+-      n.pop_back();
+-      c->parent = node;
+-    }
+-  });
++void Tree::SetParentFromUndirectedAdjList() {
++    PreOrder([](const shared_ptr<TreeNode>& node) {
++        for (auto& c : node->children) {
++            auto& n = c->children;
++            auto it = find(n.begin(), n.end(), node);
++            assert(it != n.end());
++            *it = n.back();
++            n.pop_back();
++            c->parent = node;
++        }
++    });
+ }
+ 
+-void Tree::reroot()
+-{
+-  TreeNode::reroot(source);
++void Tree::Reroot() {
++    TreeNode::Reroot(source);
+ }
+ 
+-void Tree::QuickCheck()
+-{
+-  int num_pin = net->pins.size(), num_checked = 0;
+-  vector<bool> pin_exist(num_pin, false);
+-  preOrder([&](const shared_ptr<TreeNode>& node) {
+-    if (!node) {
+-      cerr << "Error: empty node" << endl;
++void Tree::QuickCheck() {
++    int numPin = net->pins.size(), numChecked = 0;
++    vector<bool> pinExist(numPin, false);
++    PreOrder([&](const shared_ptr<TreeNode>& node) {
++        if (!node) {
++            cerr << "Error: empty node" << endl;
++        }
++        if (node->pin) {
++            auto id = node->pin->id;
++            if (!(id >= 0 && id < numPin && pinExist[id] == false)) {
++                cerr << "Error: Steiner node with incorrect id" << endl;
++            }
++            pinExist[id] = true;
++            ++numChecked;
++        }
++        for (auto& c : node->children) {
++            if (!c->parent || c->parent != node) {
++                cerr << "Error: inconsistent parent-child relationship" << endl;
++            }
++        }
++    });
++    if (numChecked != numPin) {
++        cerr << "Error: pin not covered" << endl;
+     }
+-    if (node->pin) {
+-      auto id = node->pin->id;
+-      if (!(id >= 0 && id < num_pin && pin_exist[id] == false)) {
+-        cerr << "Error: Steiner node with incorrect id" << endl;
+-      }
+-      pin_exist[id] = true;
+-      ++num_checked;
+-    }
+-    for (auto& c : node->children) {
+-      if (!c->parent || c->parent != node) {
+-        cerr << "Error: inconsistent parent-child relationship" << endl;
+-      }
+-    }
+-  });
+-  if (num_checked != num_pin) {
+-    cerr << "Error: pin not covered" << endl;
+-  }
+ }
+ 
+-void Tree::RemovePhyRedundantSteiner()
+-{
+-  postOrderCopy([](const shared_ptr<TreeNode>& node) {
+-    if (!node->parent || node->loc != node->parent->loc)
+-      return;
+-    if (node->pin) {
+-      if (node->parent->pin && node->parent->pin != node->pin)
+-        return;
+-      node->parent->pin = node->pin;
+-    }
+-    for (auto c : node->children)
+-      TreeNode::setParent(c, node->parent);
+-    TreeNode::resetParent(node);
+-  });
++void Tree::RemovePhyRedundantSteiner() {
++    PostOrderCopy([](const shared_ptr<TreeNode>& node) {
++        if (!node->parent || node->loc != node->parent->loc) return;
++        if (node->pin) {
++            if (node->parent->pin && node->parent->pin != node->pin) return;
++            node->parent->pin = node->pin;
++        }
++        for (auto c : node->children) TreeNode::SetParent(c, node->parent);
++        TreeNode::ResetParent(node);
++    });
+ }
+ 
+-void Tree::RemoveTopoRedundantSteiner()
+-{
+-  postOrderCopy([](const shared_ptr<TreeNode>& node) {
+-    // degree may change after post-order traversal of its children
+-    if (node->pin)
+-      return;
+-    if (node->children.empty()) {
+-      TreeNode::resetParent(node);
+-    } else if (node->children.size() == 1) {
+-      auto old_parent = node->parent, oldChild = node->children[0];
+-      TreeNode::resetParent(node);
+-      TreeNode::resetParent(oldChild);
+-      TreeNode::setParent(oldChild, old_parent);
+-    }
+-  });
++void Tree::RemoveTopoRedundantSteiner() {
++    PostOrderCopy([](const shared_ptr<TreeNode>& node) {
++        // degree may change after post-order traversal of its children
++        if (node->pin) return;
++        if (node->children.empty()) {
++            TreeNode::ResetParent(node);
++        } else if (node->children.size() == 1) {
++            auto oldParent = node->parent, oldChild = node->children[0];
++            TreeNode::ResetParent(node);
++            TreeNode::ResetParent(oldChild);
++            TreeNode::SetParent(oldChild, oldParent);
++        }
++    });
+ }
+ 
+-void Tree::RemoveEmptyChildren()
+-{
+-  preOrder([](const shared_ptr<TreeNode>& node) {
+-    int size = 0;
+-    for (int i = 0; i < node->children.size(); ++i) {
+-      if (node->children[i])
+-        node->children[size++] = node->children[i];
+-    }
+-    node->children.resize(size);
+-  });
++void Tree::RemoveEmptyChildren() {
++    PreOrder([](const shared_ptr<TreeNode>& node) {
++        int size = 0;
++        for (int i = 0; i < node->children.size(); ++i) {
++            if (node->children[i]) node->children[size++] = node->children[i];
++        }
++        node->children.resize(size);
++    });
+ }
+ 
+ }  // namespace salt
+\ No newline at end of file
+diff --git a/src/third_party/salt/base/tree.h b/src/third_party/salt/base/tree.h
+index 8fde2af2..f66880e7 100644
+--- a/src/third_party/salt/base/tree.h
++++ b/src/third_party/salt/base/tree.h
+@@ -1,132 +1,102 @@
+ #pragma once
+ 
+-#include <functional>
+-
+ #include "net.h"
+ 
++#include <functional>
++
+ namespace salt {
+ 
+-class TreeNode
+-{
+- public:
+-  Point loc;
+-  shared_ptr<Pin> pin;  // nullptr if it is not a pin
+-  int id;
+-  vector<shared_ptr<TreeNode>> children;  // empty for leaf
+-  shared_ptr<TreeNode> parent;            // nullptr for source
+-
+-  // Constructors
+-  TreeNode(const Point& l = Point(), shared_ptr<Pin> p = nullptr, int i = -1) : loc(l), pin(p), id(i) {}
+-  TreeNode(DTYPE x, DTYPE y, shared_ptr<Pin> p = nullptr, int i = -1) : loc(x, y), pin(p), id(i) {}
+-  TreeNode(shared_ptr<Pin> p) : loc(p->loc), pin(p), id(p->id) {}
+-
+-  // Accessors
+-  DTYPE WireToParent() const { return Dist(loc, parent->loc); }
+-  DTYPE WireToParentChecked() const { return parent ? WireToParent() : 0.0; }
+-
+-  // Hunman-readable print
+-  void PrintSingle(ostream& os = cout) const;
+-  void PrintRecursiveHelp(ostream& os, vector<bool>& prefix) const;  // hunman-readable print
+-  void PrintRecursive(ostream& os = cout) const;
+-  friend ostream& operator<<(ostream& os, const TreeNode& node)
+-  {
+-    node.PrintRecursive(os);
+-    return os;
+-  }
+-
+-  // Set/reset/check parent/ancestor
+-  static void setParent(const shared_ptr<TreeNode>& childNode, const shared_ptr<TreeNode>& parentNode);
+-  static void resetParent(const shared_ptr<TreeNode>& node);
+-  static void reroot(const shared_ptr<TreeNode>& node);
+-  static bool isAncestor(const shared_ptr<TreeNode>& ancestor, const shared_ptr<TreeNode>& descendant);
+-
+-  // Traverse
+-  static void preOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit);
+-  static void postOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit);
+-  static void postOrderCopy(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit);
++class TreeNode {
++public:
++    int id;
++    shared_ptr<Pin> pin;  // nullptr if it is not a pin
++    Point loc;
++    vector<shared_ptr<TreeNode>> children;  // empty for leaf
++    shared_ptr<TreeNode> parent;  // nullptr for source
++
++    // Constructors
++    TreeNode(const Point& l = Point(), shared_ptr<Pin> p = nullptr, int i = -1) : loc(l), pin(p), id(i) {}
++    TreeNode(DTYPE x, DTYPE y, shared_ptr<Pin> p = nullptr, int i = -1) : loc(x, y), pin(p), id(i) {}
++    TreeNode(shared_ptr<Pin> p) : loc(p->loc), pin(p), id(p->id) {}
++
++    // Accessors
++    DTYPE WireToParent() const { return Dist(loc, parent->loc); }
++    DTYPE WireToParentChecked() const { return parent ? WireToParent() : 0.0; }
++
++    // Hunman-readable print
++    void PrintSingle(ostream& os = cout) const; 
++    void PrintRecursiveHelp(ostream& os, vector<bool>& prefix) const; // hunman-readable print
++    void PrintRecursive(ostream& os = cout) const; 
++    friend ostream& operator<<(ostream& os, const TreeNode& node) { node.PrintRecursive(os); return os; }
++
++    // Set/reset/check parent/ancestor
++    static void SetParent(const shared_ptr<TreeNode>& childNode, const shared_ptr<TreeNode>& parentNode);
++    static void ResetParent(const shared_ptr<TreeNode>& node);
++    static void Reroot(const shared_ptr<TreeNode>& node);
++    static bool IsAncestor(const shared_ptr<TreeNode>& ancestor, const shared_ptr<TreeNode>& descendant);
++
++    // Traverse
++    static void PreOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit);
++    static void PostOrder(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit);
++    static void PostOrderCopy(const shared_ptr<TreeNode>& node, const function<void(const shared_ptr<TreeNode>&)>& visit);
+ };
+ 
+-class Tree
+-{
+- public:
+-  shared_ptr<TreeNode> source;
+-  const Net* net;
+-
+-  // Note: take care when using copy assign operator and copy constructor. use swap().
+-  Tree(const shared_ptr<TreeNode>& sourceNode = nullptr, const Net* associatedNet = nullptr) : source(sourceNode), net(associatedNet) {}
+-  void Reset(bool freeTreeNodes = true);
+-  ~Tree() { Reset(); }
+-
+-  // File read/write
+-  // ------
+-  // Format:
+-  // Tree <net_id> <net_name> <pin_num> [-cap]
+-  // 0 x0 y0 -1 [cap0]
+-  // 1 x1 y1 parent_idx1 [cap1]
+-  // 2 x2 y2 parent_idx2 [cap2]
+-  // ...
+-  // k xk yk parent_idxk
+-  // ...
+-  // ------
+-  // Notes:
+-  // 1. Nodes with indexes [0, pin_num) are pins, others are Steiner
+-  // 2. Steiner nodes have no cap
+-  void Read(istream& is);
+-  void Read(const string& file_name);
+-  void Write(ostream& os);  // TODO: const? but UpdateId
+-  void Write(const string& prefix, bool with_net_info = true);
+-
+-  // Hunman-readable print
+-  void Print(ostream& os = cout) const;
+-  friend ostream& operator<<(ostream& os, const Tree& tree)
+-  {
+-    tree.Print(os);
+-    return os;
+-  }
+-
+-  // Traverse
+-  void preOrder(const function<void(const shared_ptr<TreeNode>&)>& visit)
+-  {
+-    if (source)
+-      TreeNode::preOrder(source, visit);
+-  }
+-  void postOrder(const function<void(const shared_ptr<TreeNode>&)>& visit)
+-  {
+-    if (source)
+-      TreeNode::postOrder(source, visit);
+-  }
+-  void postOrderCopy(const function<void(const shared_ptr<TreeNode>&)>& visit)
+-  {
+-    if (source)
+-      TreeNode::postOrderCopy(source, visit);
+-  }
+-  void preOrder(const function<void(const shared_ptr<TreeNode>&)>& visit) const
+-  {
+-    if (source)
+-      TreeNode::preOrder(source, visit);
+-  }
+-  void postOrder(const function<void(const shared_ptr<TreeNode>&)>& visit) const
+-  {
+-    if (source)
+-      TreeNode::postOrder(source, visit);
+-  }
+-
+-  // Flatten
+-  int UpdateId();  // update node ids to [0, nodeNum), return nodeNum
+-  vector<shared_ptr<TreeNode>> ObtainNodes() const;
+-
+-  // Legalize parent-child relationship
+-  void SetParentFromChildren();
+-  void SetParentFromUndirectedAdjList();
+-  void reroot();
+-  void QuickCheck();  // check parent-child and pin coverage
+-
+-  // Remove redundant Steiner nodes
+-  void RemovePhyRedundantSteiner();   // remove physically redudant ones (i.e., with the same locatoin of a pin)
+-  void RemoveTopoRedundantSteiner();  // remove topologically redudant ones (i.e., with 0/1 children)
+-
+-  // Remove empty children
+-  void RemoveEmptyChildren();
++class Tree {
++public:
++    shared_ptr<TreeNode> source;
++    const Net* net;
++
++    // Note: take care when using copy assign operator and copy constructor. use swap().
++    Tree(const shared_ptr<TreeNode>& sourceNode = nullptr, const Net* associatedNet = nullptr) : source(sourceNode), net(associatedNet) {}
++    void Reset(bool freeTreeNodes = true);
++    ~Tree() { Reset(); }
++
++    // File read/write
++    // ------
++    // Format:
++    // Tree <net_id> <net_name> <pin_num> [-cap]
++    // 0 x0 y0 -1 [cap0]
++    // 1 x1 y1 parent_idx1 [cap1]
++    // 2 x2 y2 parent_idx2 [cap2]
++    // ...
++    // k xk yk parent_idxk
++    // ...
++    // ------
++    // Notes:
++    // 1. Nodes with indexes [0, pin_num) are pins, others are Steiner
++    // 2. Steiner nodes have no cap
++    void Read(istream& is);
++    void Read(const string& fileName);
++    void Write(ostream& os);  // TODO: const? but UpdateId
++    void Write(const string& prefix, bool withNetInfo = true);
++
++    // Hunman-readable print
++    void Print(ostream& os = cout) const;
++    friend ostream& operator<<(ostream& os, const Tree& tree) { tree.Print(os); return os; }
++
++    // Traverse
++    void PreOrder(const function<void(const shared_ptr<TreeNode>&)>& visit) { if (source) TreeNode::PreOrder(source, visit); }
++    void PostOrder(const function<void(const shared_ptr<TreeNode>&)>& visit) { if (source) TreeNode::PostOrder(source, visit); }
++    void PostOrderCopy(const function<void(const shared_ptr<TreeNode>&)>& visit) { if (source) TreeNode::PostOrderCopy(source, visit); }
++    void PreOrder(const function<void(const shared_ptr<TreeNode>&)>& visit) const { if (source) TreeNode::PreOrder(source, visit); }
++    void PostOrder(const function<void(const shared_ptr<TreeNode>&)>& visit) const { if (source) TreeNode::PostOrder(source, visit); }
++
++    // Flatten
++    int UpdateId();  // update node ids to [0, nodeNum), return nodeNum
++    vector<shared_ptr<TreeNode>> ObtainNodes() const;
++
++    // Legalize parent-child relationship
++    void SetParentFromChildren();
++    void SetParentFromUndirectedAdjList();
++    void Reroot();
++    void QuickCheck();  // check parent-child and pin coverage
++
++    // Remove redundant Steiner nodes
++    void RemovePhyRedundantSteiner();   // remove physically redudant ones (i.e., with the same locatoin of a pin)
++    void RemoveTopoRedundantSteiner();  // remove topologically redudant ones (i.e., with 0/1 children)
++
++    // Remove empty children
++    void RemoveEmptyChildren();
+ };
+ 
+ }  // namespace salt
+\ No newline at end of file
+diff --git a/src/third_party/salt/refine/cancel_intersect.cpp b/src/third_party/salt/refine/cancel_intersect.cpp
+index f79b7b0e..1e17da44 100644
+--- a/src/third_party/salt/refine/cancel_intersect.cpp
++++ b/src/third_party/salt/refine/cancel_intersect.cpp
+@@ -96,16 +96,16 @@ void Cancel(array<shared_ptr<TreeNode>, 2> oric) {
+     auto rootN = oric[1 - minD]->parent;
+     shared_ptr<TreeNode> mergeN = nullptr;
+     for (int d = 0; d < 2; ++d) {
+-        TreeNode::resetParent(oric[d]);
++        TreeNode::ResetParent(oric[d]);
+         if (oric[d]->loc == cclosest[d]) mergeN = oric[d];
+     }
+     if (!mergeN) mergeN = make_shared<TreeNode>(cclosest[minD]);
+-    TreeNode::setParent(mergeN, rootN);
++    TreeNode::SetParent(mergeN, rootN);
+     for (int d = 0; d < 2; ++d)
+-        if (oric[d] != mergeN) TreeNode::setParent(oric[d], mergeN);
++        if (oric[d] != mergeN) TreeNode::SetParent(oric[d], mergeN);
+ }
+ 
+-void Refine::cancelIntersect(Tree& tree) {
++void Refine::CancelIntersect(Tree& tree) {
+     bgi::rtree<RNode, bgi::linear<16>, bgi::indexable<RNode>, RNodeComp> rtree;
+     auto nodes = tree.ObtainNodes();
+     for (int i = 0; i < nodes.size(); ++i) {
+diff --git a/src/third_party/salt/refine/flip.cpp b/src/third_party/salt/refine/flip.cpp
+index 5dad3766..fca59b4c 100644
+--- a/src/third_party/salt/refine/flip.cpp
++++ b/src/third_party/salt/refine/flip.cpp
+@@ -27,10 +27,10 @@ void UpdateNode(const shared_ptr<TreeNode> node,       // central node
+                 const vector<array<DTYPE, 2>>& gains,  // the gain of each child
+                 int i,                                 // current child
+                 array<vector<DTYPE>, 4>& segs,         // segments in each direction
+-                DTYPE acc_gain,                         // gain accumulated by now
++                DTYPE accGain,                         // gain accumulated by now
+                 DTYPE& bestGain,                       // best gain
+-                vector<bool>& low_or_up,                 // each child edge is low or up
+-                vector<bool>& best_low_or_up              // each child edge is low or up for best gain
++                vector<bool>& lowOrUp,                 // each child edge is low or up
++                vector<bool>& bestLowOrUp              // each child edge is low or up for best gain
+ ) {
+     if (i == node->children.size()) {  // get to the end of enumeration
+         for (int j = 0; j < 4; ++j) {
+@@ -38,49 +38,49 @@ void UpdateNode(const shared_ptr<TreeNode> node,       // central node
+             DTYPE max = 0;
+             for (auto len : segs[j]) {
+                 if (len > max) {
+-                    acc_gain += max;
++                    accGain += max;
+                     max = len;
+                 } else
+-                    acc_gain += len;
++                    accGain += len;
+             }
+         }
+-        if (acc_gain > bestGain) {
+-            bestGain = acc_gain;
+-            for (auto c : node->children) best_low_or_up[c->id] = low_or_up[c->id];
++        if (accGain > bestGain) {
++            bestGain = accGain;
++            for (auto c : node->children) bestLowOrUp[c->id] = lowOrUp[c->id];
+         }
+     } else if (align[i]) {  // already consider, pass this child
+-        UpdateNode(node, align, gains, i + 1, segs, acc_gain, bestGain, low_or_up, best_low_or_up);
++        UpdateNode(node, align, gains, i + 1, segs, accGain, bestGain, lowOrUp, bestLowOrUp);
+     } else {  // make a branch because this node-child edge can be L/U
+         int dir;
+         DTYPE dist;
+         bool low = true;  // L/U
+         for (int j = 0; j < 2; ++j) {
+             GetDirId(node->children[i]->loc, node->loc, low, dir, dist);
+-            acc_gain += gains[i][j];
++            accGain += gains[i][j];
+             segs[dir].push_back(dist);
+-            low_or_up[node->children[i]->id] = low;
+-            UpdateNode(node, align, gains, i + 1, segs, acc_gain, bestGain, low_or_up, best_low_or_up);
+-            acc_gain -= gains[i][j];
++            lowOrUp[node->children[i]->id] = low;
++            UpdateNode(node, align, gains, i + 1, segs, accGain, bestGain, lowOrUp, bestLowOrUp);
++            accGain -= gains[i][j];
+             segs[dir].pop_back();
+             low = false;  // L/U
+         }
+     }
+ }
+ 
+-void UpdateSubTree(const shared_ptr<TreeNode> node, array<DTYPE, 2>& res, array<vector<bool>, 2>& best_low_or_up) {
++void UpdateSubTree(const shared_ptr<TreeNode> node, array<DTYPE, 2>& res, array<vector<bool>, 2>& bestLowOrUp) {
+     res = {0, 0};  // low, up
+     if (node->children.empty()) return;
+ 
+     // Calc gains of children
+     unsigned nc = node->children.size();
+     vector<array<DTYPE, 2>> gains(nc);
+-    for (unsigned i = 0; i < nc; ++i) UpdateSubTree(node->children[i], gains[i], best_low_or_up);
++    for (unsigned i = 0; i < nc; ++i) UpdateSubTree(node->children[i], gains[i], bestLowOrUp);
+ 
+     // Process aligned children
+     vector<bool> align(nc, false);
+     array<vector<DTYPE>, 4> segs;
+-    DTYPE acc_gain = 0;
+-    vector<bool> low_or_up(best_low_or_up[0].size());
++    DTYPE accGain = 0;
++    vector<bool> lowOrUp(bestLowOrUp[0].size());
+     for (unsigned i = 0; i < nc; ++i) {
+         Point &a = node->children[i]->loc, b = node->loc;
+         if (a.x == b.x || a.y == b.y) {
+@@ -89,7 +89,7 @@ void UpdateSubTree(const shared_ptr<TreeNode> node, array<DTYPE, 2>& res, array<
+             GetDirId(a, node->loc, true, dir, dist);
+             segs[dir].push_back(dist);
+             align[i] = true;  // no need for further processing
+-            acc_gain += gains[i][0];
++            accGain += gains[i][0];
+         }
+     }
+ 
+@@ -101,12 +101,12 @@ void UpdateSubTree(const shared_ptr<TreeNode> node, array<DTYPE, 2>& res, array<
+         for (int j = 0; j < 2; ++j) {
+             GetDirId(node->parent->loc, node->loc, low, dir, dist);
+             segs[dir].push_back(dist);
+-            UpdateNode(node, align, gains, 0, segs, acc_gain, res[j], low_or_up, best_low_or_up[j]);
++            UpdateNode(node, align, gains, 0, segs, accGain, res[j], lowOrUp, bestLowOrUp[j]);
+             segs[dir].pop_back();
+             low = false;  // L/U
+         }
+     } else  // for source node, update res[0] only
+-        UpdateNode(node, align, gains, 0, segs, acc_gain, res[0], low_or_up, best_low_or_up[0]);
++        UpdateNode(node, align, gains, 0, segs, accGain, res[0], lowOrUp, bestLowOrUp[0]);
+ 
+     // cout<<*node->pin<<", low="<<res[0]<<", up="<<res[1]<<endl;
+ }
+@@ -124,16 +124,16 @@ void AddNode(shared_ptr<TreeNode> c1, shared_ptr<TreeNode> c2, shared_ptr<TreeNo
+         if (c1->parent == c2) swap(c1, c2);  // make sure (c1>=c2)
+         assert(c2->parent == c1 && c1->parent == p);
+     }
+-    TreeNode::resetParent(c1);
+-    TreeNode::setParent(c1, steiner);
+-    TreeNode::resetParent(c2);
+-    TreeNode::setParent(c2, steiner);
+-    TreeNode::setParent(steiner, p);
++    TreeNode::ResetParent(c1);
++    TreeNode::SetParent(c1, steiner);
++    TreeNode::ResetParent(c2);
++    TreeNode::SetParent(c2, steiner);
++    TreeNode::SetParent(steiner, p);
+ }
+ 
+-int gDir[4] = {0, 0, 1, 1};  // down, up, left, right
++int dir2xy[4] = {0, 0, 1, 1};  // down, up, left, right
+ 
+-void TraverseAndAddSteiner(const shared_ptr<TreeNode>& node, bool low, array<vector<bool>, 2>& best_low_or_up) {
++void TraverseAndAddSteiner(const shared_ptr<TreeNode>& node, bool low, array<vector<bool>, 2>& bestLowOrUp) {
+     // cout<<node->id<<(low?" low":"  up")<<endl;
+     // node->Print(0,true);
+     if (node->children.empty()) return;
+@@ -141,7 +141,7 @@ void TraverseAndAddSteiner(const shared_ptr<TreeNode>& node, bool low, array<vec
+     // go down first
+     int luid = low ? 0 : 1;
+     auto children_cpy = node->children;  // it may be chagned later
+-    for (auto c : children_cpy) TraverseAndAddSteiner(c, best_low_or_up[luid][c->id], best_low_or_up);
++    for (auto c : children_cpy) TraverseAndAddSteiner(c, bestLowOrUp[luid][c->id], bestLowOrUp);
+ 
+     // get overlap info
+     array<vector<pair<int, shared_ptr<TreeNode>>>, 4> dist2node;  // for four directions
+@@ -153,12 +153,12 @@ void TraverseAndAddSteiner(const shared_ptr<TreeNode>& node, bool low, array<vec
+     }
+     for (auto c : node->children) {
+         if (c->id == -1) continue;
+-        GetDirId(c->loc, node->loc, best_low_or_up[luid][c->id], dir, dist);
++        GetDirId(c->loc, node->loc, bestLowOrUp[luid][c->id], dir, dist);
+         dist2node[dir].emplace_back(dist, c);
+     }
+ 
+     // add steiner point
+-    auto ori_parent = node->parent;
++    auto oriParent = node->parent;
+     for (dir = 0; dir < 4; ++dir) {
+         auto& cands = dist2node[dir];
+         if (cands.size() <= 1) continue;
+@@ -171,7 +171,7 @@ void TraverseAndAddSteiner(const shared_ptr<TreeNode>& node, bool low, array<vec
+ 
+         auto out = cands[0].second;
+         for (int cid = 1; cid < cands.size(); ++cid) {
+-            int xy = gDir[dir];
++            int xy = dir2xy[dir];
+             Point p;
+             p[xy] = node->loc[xy];
+             p[1 - xy] = cands[cid].second->loc[1 - xy];
+@@ -182,16 +182,16 @@ void TraverseAndAddSteiner(const shared_ptr<TreeNode>& node, bool low, array<vec
+     }
+ }
+ 
+-void Refine::flip(Tree& tree) {
++void Refine::Flip(Tree& tree) {
+     WireLengthEvalBase cur(tree), pre;
+ 
+     do {
+         pre = cur;
+         array<DTYPE, 2> g;  // low, up
+-        int num_node = tree.UpdateId();
+-        array<vector<bool>, 2> best_low_or_up = {vector<bool>(num_node), vector<bool>(num_node)};
+-        UpdateSubTree(tree.source, g, best_low_or_up);
+-        TraverseAndAddSteiner(tree.source, true, best_low_or_up);
++        int numNode = tree.UpdateId();
++        array<vector<bool>, 2> bestLowOrUp = {vector<bool>(numNode), vector<bool>(numNode)};
++        UpdateSubTree(tree.source, g, bestLowOrUp);
++        TraverseAndAddSteiner(tree.source, true, bestLowOrUp);
+         tree.RemovePhyRedundantSteiner();
+         cur.Update(tree);
+     } while (cur.wireLength < pre.wireLength);
+diff --git a/src/third_party/salt/refine/refine.h b/src/third_party/salt/refine/refine.h
+index 370e2cf9..c7a37e42 100644
+--- a/src/third_party/salt/refine/refine.h
++++ b/src/third_party/salt/refine/refine.h
+@@ -4,13 +4,12 @@
+ 
+ namespace salt {
+ 
+-class Refine
+-{
+- public:
+-  static void cancelIntersect(Tree& tree);
+-  static void flip(Tree& tree);
+-  static void uShift(Tree& tree);  // should be after flip to achieve good quality
+-  static void substitute(Tree& tree, double eps, bool useRTree = true);
++class Refine {
++public:
++    static void CancelIntersect(Tree& tree);
++    static void Flip(Tree& tree);
++    static void UShift(Tree& tree);  // should be after Flip to achieve good quality
++    static void Substitute(Tree& tree, double eps, bool useRTree = true);
+ };
+ 
+ }  // namespace salt
+\ No newline at end of file
+diff --git a/src/third_party/salt/refine/substitute.cpp b/src/third_party/salt/refine/substitute.cpp
+index bb90fead..02083af4 100644
+--- a/src/third_party/salt/refine/substitute.cpp
++++ b/src/third_party/salt/refine/substitute.cpp
+@@ -1,12 +1,13 @@
++#include "refine.h"
++
++#include "salt/base/eval.h"
++#include "salt/base/mst.h"
++
+ #include <boost/geometry.hpp>
+ #include <boost/geometry/geometries/point.hpp>
+ #include <boost/geometry/geometries/segment.hpp>
+ #include <boost/geometry/index/rtree.hpp>
+ 
+-#include "refine.h"
+-#include "salt/base/eval.h"
+-#include "salt/base/mst.h"
+-
+ namespace salt {
+ 
+ namespace bg = boost::geometry;
+@@ -17,216 +18,216 @@ using BSegment = bg::model::segment<BPoint>;
+ using BBox = bg::model::box<BPoint>;
+ using BPolygon = bg::model::polygon<BPoint>;
+ using RNode = pair<BBox, shared_ptr<TreeNode>>;  // R-Tree node
+-struct RNodeComp
+-{
+-  bool operator()(const RNode& l, const RNode& r) const { return bg::equals(l.first, r.first) && l.second == r.second; }
++struct RNodeComp {
++    bool operator()(const RNode& l, const RNode& r) const {
++        return bg::equals(l.first, r.first) && l.second == r.second;
++    }
+ };
+ 
+-void Refine::substitute(Tree& tree, double eps, bool useRTree)
+-{
+-  bgi::rtree<RNode, bgi::rstar<8>, bgi::indexable<RNode>, RNodeComp> rtree;
+-  if (useRTree) {
+-    tree.postOrder([&](const shared_ptr<TreeNode>& n) {
+-      if (n->parent) {
+-        BBox s;
+-        bg::envelope(BSegment(BPoint(n->loc.x, n->loc.y), BPoint(n->parent->loc.x, n->parent->loc.y)), s);
+-        rtree.insert({s, n});
+-      }
+-    });
+-  }
+-  auto disconnect = [&](const shared_ptr<TreeNode>& n) {
++void Refine::Substitute(Tree& tree, double eps, bool useRTree) {
++    bgi::rtree<RNode, bgi::rstar<8>, bgi::indexable<RNode>, RNodeComp> rtree;
+     if (useRTree) {
+-      BBox s;
+-      bg::envelope(BSegment(BPoint(n->loc.x, n->loc.y), BPoint(n->parent->loc.x, n->parent->loc.y)), s);
+-      rtree.remove({s, n});
++        tree.PostOrder([&](const shared_ptr<TreeNode>& n) {
++            if (n->parent) {
++                BBox s;
++                bg::envelope(BSegment(BPoint(n->loc.x, n->loc.y), BPoint(n->parent->loc.x, n->parent->loc.y)), s);
++                rtree.insert({s, n});
++            }
++        });
+     }
+-    TreeNode::resetParent(n);
+-  };
+-  auto connect = [&](const shared_ptr<TreeNode>& n, const shared_ptr<TreeNode>& parent) {
+-    TreeNode::setParent(n, parent);
+-    if (useRTree) {
+-      BBox s;
+-      bg::envelope(BSegment(BPoint(n->loc.x, n->loc.y), BPoint(parent->loc.x, parent->loc.y)), s);
+-      rtree.insert({s, n});
+-    }
+-  };
+-  while (true) {
+-    // Get nearest neighbors
+-    tree.UpdateId();
+-    vector<shared_ptr<TreeNode>> nodes = tree.ObtainNodes(),
+-                                 ordered_nodes(nodes.size());  // note: all pins should be covered
+-    vector<Point> points(nodes.size());
+-    for (int i = 0; i < nodes.size(); ++i) {
+-      ordered_nodes[nodes[i]->id] = nodes[i];
+-      points[nodes[i]->id] = nodes[i]->loc;  // TODO: move within bracket
+-    }
+-    nodes = ordered_nodes;
+-    vector<vector<int>> nearest_neighbors;
+-    if (!useRTree) {
+-      MstBuilder mst_builder;
+-      mst_builder.GetAllNearestNeighbors(points, nearest_neighbors);
+-    } else {
+-      nearest_neighbors.resize(nodes.size());
+-      for (auto n : nodes) {
+-        if (n->parent) {
+-          Point c = n->loc;  // center
+-          DTYPE radius = n->WireToParent();
+-          // diamond is too slow...
+-          // BPolygon diamond;
+-          // diamond.outer().emplace_back(c.x - radius, c.y);
+-          // diamond.outer().emplace_back(c.x, c.y + radius);
+-          // diamond.outer().emplace_back(c.x + radius, c.y);
+-          // diamond.outer().emplace_back(c.x, c.y - radius);
+-          // diamond.outer().emplace_back(c.x - radius, c.y);
+-          BBox query_box{{c.x - radius, c.y - radius}, {c.x + radius, c.y + radius}};
+-          vector<RNode> cands;
+-          rtree.query(bgi::intersects(query_box), back_inserter(cands));  // TODO: change back_inserter
+-          for (const auto& cand : cands) {
+-            nearest_neighbors[n->id].push_back(cand.second->id);
+-          }
++    auto Disconnect = [&](const shared_ptr<TreeNode>& n) {
++        if (useRTree) {
++            BBox s;
++            bg::envelope(BSegment(BPoint(n->loc.x, n->loc.y), BPoint(n->parent->loc.x, n->parent->loc.y)), s);
++            rtree.remove({s, n});
+         }
+-      }
+-    }
+-
+-    // Prune descendants in nearest neighbors
+-    vector<int> pre_order_idxes(nodes.size(), -1);
+-    int global_pre_order_idx = 0;
+-    function<void(const shared_ptr<TreeNode>&)> remove_descendants = [&](const shared_ptr<TreeNode>& node) {
+-      pre_order_idxes[node->id] = global_pre_order_idx++;
+-      for (auto child : node->children) {
+-        remove_descendants(child);
+-      }
+-      for (auto& neigh_idx : nearest_neighbors[node->id]) {
+-        int neigh_pre_order_idx = pre_order_idxes[neigh_idx];
+-        if (neigh_pre_order_idx != -1 && neigh_pre_order_idx >= pre_order_idxes[node->id]) {
+-          neigh_idx = -1;  // -1 stands for "descendant"
++        TreeNode::ResetParent(n);
++    };
++    auto Connect = [&](const shared_ptr<TreeNode>& n, const shared_ptr<TreeNode>& parent) {
++        TreeNode::SetParent(n, parent);
++        if (useRTree) {
++            BBox s;
++            bg::envelope(BSegment(BPoint(n->loc.x, n->loc.y), BPoint(parent->loc.x, parent->loc.y)), s);
++            rtree.insert({s, n});
+         }
+-      }
+     };
+-    remove_descendants(tree.source);
++    while (true) {
++        // Get nearest neighbors
++        int num = tree.UpdateId();
++        vector<shared_ptr<TreeNode>> nodes = tree.ObtainNodes(),
++                                     orderedNodes(nodes.size());  // note: all pins should be covered
++        vector<Point> points(nodes.size());
++        for (int i = 0; i < nodes.size(); ++i) {
++            orderedNodes[nodes[i]->id] = nodes[i];
++            points[nodes[i]->id] = nodes[i]->loc;  // TODO: move within bracket
++        }
++        nodes = orderedNodes;
++        vector<vector<int>> nearestNeighbors;
++        if (!useRTree) {
++            MstBuilder mstB;
++            mstB.GetAllNearestNeighbors(points, nearestNeighbors);
++        } else {
++            nearestNeighbors.resize(nodes.size());
++            for (auto n : nodes) {
++                if (n->parent) {
++                    Point c = n->loc;  // center
++                    DTYPE radius = n->WireToParent();
++                    // diamond is too slow...
++                    // BPolygon diamond;
++                    // diamond.outer().emplace_back(c.x - radius, c.y);
++                    // diamond.outer().emplace_back(c.x, c.y + radius);
++                    // diamond.outer().emplace_back(c.x + radius, c.y);
++                    // diamond.outer().emplace_back(c.x, c.y - radius);
++                    // diamond.outer().emplace_back(c.x - radius, c.y);
++                    BBox queryBox{{c.x - radius, c.y - radius}, {c.x + radius, c.y + radius}};
++                    vector<RNode> cands;
++                    rtree.query(bgi::intersects(queryBox), back_inserter(cands));  // TODO: change back_inserter
++                    for (const auto& cand : cands) {
++                        nearestNeighbors[n->id].push_back(cand.second->id);
++                    }
++                }
++            }
++        }
+ 
+-    // Init path lengths and subtree slacks
+-    vector<DTYPE> path_lengths(nodes.size());
+-    vector<DTYPE> slacks(nodes.size());
+-    auto update_path_lengths = [&](const shared_ptr<TreeNode>& node) {
+-      if (node->parent) {
+-        path_lengths[node->id] = path_lengths[node->parent->id] + node->WireToParent();
+-      } else {
+-        path_lengths[node->id] = 0;
+-      }
+-    };
+-    auto update_slacks = [&](const shared_ptr<TreeNode>& node) {
+-      if (node->children.empty()) {
+-        slacks[node->id] = Dist(node->loc, tree.source->loc) * (1 + eps) - path_lengths[node->id];  // floor here...
+-      } else {
+-        DTYPE min_slack = Dist(node->loc, tree.source->loc) * (1 + eps) - path_lengths[node->id];
+-        for (auto child : node->children) {
+-          min_slack = min(min_slack, slacks[child->id]);
++        // Prune descendants in nearest neighbors
++        vector<int> preOrderIdxes(nodes.size(), -1);
++        int globalPreOrderIdx = 0;
++        function<void(const shared_ptr<TreeNode>&)> removeDescendants = [&](const shared_ptr<TreeNode>& node) {
++            preOrderIdxes[node->id] = globalPreOrderIdx++;
++            for (auto child : node->children) {
++                removeDescendants(child);
++            }
++            for (auto& neighIdx : nearestNeighbors[node->id]) {
++                int neighPreOrderIdx = preOrderIdxes[neighIdx];
++                if (neighPreOrderIdx != -1 && neighPreOrderIdx >= preOrderIdxes[node->id]) {
++                    neighIdx = -1;  // -1 stands for "descendant"
++                }
++            }
++        };
++        removeDescendants(tree.source);
++
++        // Init path lengths and subtree slacks
++        vector<DTYPE> pathLengths(nodes.size());
++        vector<DTYPE> slacks(nodes.size());
++        auto UpdatePathLengths = [&](const shared_ptr<TreeNode>& node) {
++            if (node->parent) {
++                pathLengths[node->id] = pathLengths[node->parent->id] + node->WireToParent();
++            } else {
++                pathLengths[node->id] = 0;
++            }
++        };
++        auto UpdateSlacks = [&](const shared_ptr<TreeNode>& node) {
++            if (node->children.empty()) {
++                slacks[node->id] =
++                    Dist(node->loc, tree.source->loc) * (1 + eps) - pathLengths[node->id];  // floor here...
++            } else {
++                DTYPE minSlack = Dist(node->loc, tree.source->loc) * (1 + eps) - pathLengths[node->id];
++                for (auto child : node->children) {
++                    minSlack = min(minSlack, slacks[child->id]);
++                }
++                slacks[node->id] = minSlack;
++            }
++        };
++        tree.PreOrder(UpdatePathLengths);
++        tree.PostOrder(UpdateSlacks);
++
++        // Find legal candidate moves
++        using MoveT = tuple<DTYPE, shared_ptr<TreeNode>, shared_ptr<TreeNode>>;
++        vector<MoveT> candidateMoves;  // <wireLengthDelta, node, newParent>
++        auto GetNearestPoint = [](const shared_ptr<TreeNode>& target, const shared_ptr<TreeNode>& neigh) {
++            Box box(neigh->loc, neigh->parent->loc);
++            box.Legalize();
++            return box.GetNearestPointTo(target->loc);
++        };
++        for (auto node : nodes) {
++            if (!(node->parent)) {
++                continue;
++            }
++            DTYPE bestWireLengthDelta = 0;  // the negative, the better
++            shared_ptr<TreeNode> bestNewParent;
++            for (int neighIdx : nearestNeighbors[node->id]) {
++                if (neighIdx == -1 || !nodes[neighIdx]->parent) continue;
++                auto neigh = nodes[neighIdx];
++                auto neighParent = neigh->parent;
++                auto steinerPt = GetNearestPoint(node, neigh);
++                DTYPE wireLengthDelta = Dist(node->loc, steinerPt) - node->WireToParent();
++                if (wireLengthDelta < bestWireLengthDelta) {  // has wire length improvement
++                    DTYPE pathLengthDelta =
++                        pathLengths[neighParent->id] + Dist(node->loc, neighParent->loc) - pathLengths[node->id];
++                    if (pathLengthDelta <= slacks[node->id]) {  // make path length under control
++                        bestWireLengthDelta = wireLengthDelta;
++                        bestNewParent = neigh;
++                    }
++                }
++            }
++            if (bestNewParent) {
++                candidateMoves.emplace_back(bestWireLengthDelta, node, bestNewParent);
++            }
++        }
++        if (candidateMoves.empty()) {
++            break;
+         }
+-        slacks[node->id] = min_slack;
+-      }
+-    };
+-    tree.preOrder(update_path_lengths);
+-    tree.postOrder(update_slacks);
+ 
+-    // Find legal candidate moves
+-    using MoveT = tuple<DTYPE, shared_ptr<TreeNode>, shared_ptr<TreeNode>>;
+-    vector<MoveT> candidate_moves;  // <wire_length_delta, node, newParent>
+-    auto get_nearest_point = [](const shared_ptr<TreeNode>& target, const shared_ptr<TreeNode>& neigh) {
+-      Box box(neigh->loc, neigh->parent->loc);
+-      box.Legalize();
+-      return box.GetNearestPointTo(target->loc);
+-    };
+-    for (auto node : nodes) {
+-      if (!(node->parent)) {
+-        continue;
+-      }
+-      DTYPE best_wire_length_delta = 0;  // the negative, the better
+-      shared_ptr<TreeNode> best_new_parent;
+-      for (int neigh_idx : nearest_neighbors[node->id]) {
+-        if (neigh_idx == -1 || !nodes[neigh_idx]->parent)
+-          continue;
+-        auto neigh = nodes[neigh_idx];
+-        auto neigh_parent = neigh->parent;
+-        auto steiner_pt = get_nearest_point(node, neigh);
+-        DTYPE wire_length_delta = Dist(node->loc, steiner_pt) - node->WireToParent();
+-        if (wire_length_delta < best_wire_length_delta) {  // has wire length improvement
+-          DTYPE path_length_delta = path_lengths[neigh_parent->id] + Dist(node->loc, neigh_parent->loc) - path_lengths[node->id];
+-          if (path_length_delta <= slacks[node->id]) {  // make path length under control
+-            best_wire_length_delta = wire_length_delta;
+-            best_new_parent = neigh;
+-          }
++        // Try candidate moves in the order of descending wire length savings
++        // Note that earlier moves may influence the legality of later one
++        sort(candidateMoves.begin(), candidateMoves.end(), [](const MoveT& lhs, const MoveT& rhs){
++            return get<0>(lhs) < get<0>(rhs);
++        });
++        for (const auto& move : candidateMoves) {
++            auto node = get<1>(move), neigh = get<2>(move);
++            auto neighParent = neigh->parent;
++            // check due to earlier moves
++            if (TreeNode::IsAncestor(node, neighParent)) continue;
++            DTYPE pathLengthDelta =
++                pathLengths[neighParent->id] + Dist(node->loc, neighParent->loc) - pathLengths[node->id];
++            if (pathLengthDelta > slacks[node->id]) continue;
++            auto steinerPt = GetNearestPoint(node, neigh);
++            DTYPE wireLengthDelta = Dist(node->loc, steinerPt) - node->WireToParent();
++            if (wireLengthDelta >= 0) continue;
++            // break
++            Disconnect(node);
++            // reroot
++            if (steinerPt == neigh->loc) {
++                Connect(node, neigh);
++            } else if (steinerPt == neighParent->loc) {
++                Connect(node, neighParent);
++            } else {
++                auto steinerNode = make_shared<TreeNode>(steinerPt);
++                Connect(steinerNode, neighParent);
++                Disconnect(neigh);
++                Connect(neigh, steinerNode);
++                Connect(node, steinerNode);
++                // for later moves
++                steinerNode->id = nodes.size();
++                nodes.push_back(steinerNode);
++                pathLengths.push_back(pathLengths[neighParent->id] + steinerNode->WireToParent());
++                slacks.push_back(Dist(steinerNode->loc, tree.source->loc) * (1 + eps) - pathLengths.back());
++            }
++            // update slack for later moves: first subtree, then path to source
++            TreeNode::PreOrder(neighParent, UpdatePathLengths);
++            TreeNode::PostOrder(neighParent, UpdateSlacks);
++            auto tmp = neighParent;
++            while (tmp->parent) {
++                slacks[tmp->parent->id] = min(slacks[tmp->parent->id], slacks[tmp->id]);
++                tmp = tmp->parent;
++            }
+         }
+-      }
+-      if (best_new_parent) {
+-        candidate_moves.emplace_back(best_wire_length_delta, node, best_new_parent);
+-      }
+-    }
+-    if (candidate_moves.empty()) {
+-      break;
+-    }
+ 
+-    // Try candidate moves in the order of descending wire length savings
+-    // Note that earlier moves may influence the legality of later one
+-    sort(candidate_moves.begin(), candidate_moves.end(), [](const MoveT& lhs, const MoveT& rhs) { return get<0>(lhs) < get<0>(rhs); });
+-    for (const auto& move : candidate_moves) {
+-      auto node = get<1>(move), neigh = get<2>(move);
+-      auto neigh_parent = neigh->parent;
+-      // check due to earlier moves
+-      if (TreeNode::isAncestor(node, neigh_parent))
+-        continue;
+-      DTYPE path_length_delta = path_lengths[neigh_parent->id] + Dist(node->loc, neigh_parent->loc) - path_lengths[node->id];
+-      if (path_length_delta > slacks[node->id])
+-        continue;
+-      auto steiner_pt = get_nearest_point(node, neigh);
+-      DTYPE wire_length_delta = Dist(node->loc, steiner_pt) - node->WireToParent();
+-      if (wire_length_delta >= 0)
+-        continue;
+-      // break
+-      disconnect(node);
+-      // reroot
+-      if (steiner_pt == neigh->loc) {
+-        connect(node, neigh);
+-      } else if (steiner_pt == neigh_parent->loc) {
+-        connect(node, neigh_parent);
+-      } else {
+-        auto steiner_node = make_shared<TreeNode>(steiner_pt);
+-        connect(steiner_node, neigh_parent);
+-        disconnect(neigh);
+-        connect(neigh, steiner_node);
+-        connect(node, steiner_node);
+-        // for later moves
+-        steiner_node->id = nodes.size();
+-        nodes.push_back(steiner_node);
+-        path_lengths.push_back(path_lengths[neigh_parent->id] + steiner_node->WireToParent());
+-        slacks.push_back(Dist(steiner_node->loc, tree.source->loc) * (1 + eps) - path_lengths.back());
+-      }
+-      // update slack for later moves: first subtree, then path to source
+-      TreeNode::preOrder(neigh_parent, update_path_lengths);
+-      TreeNode::postOrder(neigh_parent, update_slacks);
+-      auto tmp = neigh_parent;
+-      while (tmp->parent) {
+-        slacks[tmp->parent->id] = min(slacks[tmp->parent->id], slacks[tmp->id]);
+-        tmp = tmp->parent;
+-      }
++        // Finalize
++        // tree.RemoveTopoRedundantSteiner();
++        tree.PostOrderCopy([&](const shared_ptr<TreeNode>& node) {
++            // degree may change after post-order traversal of its children
++            if (node->pin) return;
++            if (node->children.empty()) {
++                Disconnect(node);
++            } else if (node->children.size() == 1) {
++                auto oldParent = node->parent, oldChild = node->children[0];
++                Disconnect(node);
++                Disconnect(oldChild);
++                Connect(oldChild, oldParent);
++            }
++        });
+     }
+-
+-    // Finalize
+-    // tree.RemoveTopoRedundantSteiner();
+-    tree.postOrderCopy([&](const shared_ptr<TreeNode>& node) {
+-      // degree may change after post-order traversal of its children
+-      if (node->pin)
+-        return;
+-      if (node->children.empty()) {
+-        disconnect(node);
+-      } else if (node->children.size() == 1) {
+-        auto old_parent = node->parent, old_child = node->children[0];
+-        disconnect(node);
+-        disconnect(old_child);
+-        connect(old_child, old_parent);
+-      }
+-    });
+-  }
+ }
+ 
+ }  // namespace salt
+\ No newline at end of file
+diff --git a/src/third_party/salt/refine/u_shift.cpp b/src/third_party/salt/refine/u_shift.cpp
+index b923661c..80f662ff 100644
+--- a/src/third_party/salt/refine/u_shift.cpp
++++ b/src/third_party/salt/refine/u_shift.cpp
+@@ -7,7 +7,7 @@ namespace salt {
+ //  c    pp -> root
+ // four Nodes:      c       - cur   - p     - pp    -> root
+ // four points:     out[0]  - in[0] - in[1] - out[1]-> root
+-void Refine::uShift(Tree& tree) {
++void Refine::UShift(Tree& tree) {
+     auto nodes = tree.ObtainNodes();  // fix the nodes considered (no deletion will happen)
+     for (auto cur : nodes) {
+         Point in[2], out[2];
+@@ -35,9 +35,9 @@ void Refine::uShift(Tree& tree) {
+             for (int i = 0; i < 2; ++i) newP[i][d] = closer;            // set pri dir
+             for (int i = 0; i < 2; ++i) newP[i][1 - d] = in[i][1 - d];  // set sec dir
+ 
+-            TreeNode::resetParent(c);
+-            TreeNode::resetParent(cur);
+-            TreeNode::resetParent(p);
++            TreeNode::ResetParent(c);
++            TreeNode::ResetParent(cur);
++            TreeNode::ResetParent(p);
+             //           inS[0]    inS[1]
+             //              |         |
+             // outS[0] - newS[0] - newS[1] - outS[1] -> root
+@@ -50,13 +50,13 @@ void Refine::uShift(Tree& tree) {
+                 else {
+                     newS[i] = make_shared<TreeNode>(newP[i]);
+                     if (i == 0)
+-                        TreeNode::setParent(outS[i], newS[i]);
++                        TreeNode::SetParent(outS[i], newS[i]);
+                     else
+-                        TreeNode::setParent(newS[i], outS[i]);
++                        TreeNode::SetParent(newS[i], outS[i]);
+                 }
+-                TreeNode::setParent(inS[i], newS[i]);
++                TreeNode::SetParent(inS[i], newS[i]);
+             }
+-            TreeNode::setParent(newS[0], newS[1]);
++            TreeNode::SetParent(newS[0], newS[1]);
+         }
+     }
+ 
+diff --git a/src/third_party/salt/salt.cpp b/src/third_party/salt/salt.cpp
+index 6e87682f..34d36d6e 100644
+--- a/src/third_party/salt/salt.cpp
++++ b/src/third_party/salt/salt.cpp
+@@ -6,92 +6,86 @@
+ 
+ namespace salt {
+ 
+-void SaltInterface::Init(Tree& min_tree, shared_ptr<Pin> src_pin)
+-{
+-  min_tree.UpdateId();
+-  auto mt_nodes = min_tree.ObtainNodes();
+-  sl_nodes.resize(mt_nodes.size());
+-  shortest_dists.resize(mt_nodes.size());
+-  cur_dists.resize(mt_nodes.size());
+-  for (auto mt_node : mt_nodes) {
+-    sl_nodes[mt_node->id] = make_shared<TreeNode>(mt_node->loc, mt_node->pin, mt_node->id);
+-    shortest_dists[mt_node->id] = Dist(mt_node->loc, src_pin->loc);
+-    cur_dists[mt_node->id] = numeric_limits<DTYPE>::max();
+-  }
+-  cur_dists[src_pin->id] = 0;
+-  sl_src = sl_nodes[src_pin->id];
++void SaltBase::Init(Tree& minTree, shared_ptr<Pin> srcP) {
++    minTree.UpdateId();
++    auto mtNodes = minTree.ObtainNodes();
++    slNodes.resize(mtNodes.size());
++    shortestDists.resize(mtNodes.size());
++    curDists.resize(mtNodes.size());
++    for (auto mtN : mtNodes) {
++        slNodes[mtN->id] = make_shared<TreeNode>(mtN->loc, mtN->pin, mtN->id);
++        shortestDists[mtN->id] = Dist(mtN->loc, srcP->loc);
++        curDists[mtN->id] = numeric_limits<DTYPE>::max();
++    }
++    curDists[srcP->id] = 0;
++    slSrc = slNodes[srcP->id];
+ }
+ 
+-void SaltInterface::Finalize(const Net& net, Tree& tree)
+-{
+-  for (auto n : sl_nodes)
+-    if (n->parent)
+-      sl_nodes[n->parent->id]->children.push_back(n);
+-  tree.source = sl_src;
+-  tree.net = &net;
++void SaltBase::Finalize(const Net& net, Tree& tree) {
++    for (auto n : slNodes)
++        if (n->parent) slNodes[n->parent->id]->children.push_back(n);
++    tree.source = slSrc;
++    tree.net = &net;
+ }
+ 
+-void SaltBuilder::Run(const Net& net, Tree& tree, double eps, int refineLevel)
+-{
+-  // SMT
+-  Tree smt;
+-  FluteBuilder flute_builder;
+-  flute_builder.Run(net, smt);
++void SaltBuilder::Run(const Net& net, Tree& tree, double eps, int refineLevel) {
++    // SMT
++    Tree smt;
++    FluteBuilder fluteB;
++    fluteB.Run(net, smt);
+ 
+-  // Refine SMT
+-  if (refineLevel >= 1) {
+-    Refine::flip(smt);
+-    Refine::uShift(smt);
+-  }
++    // Refine SMT
++    if (refineLevel >= 1) {
++        Refine::Flip(smt);
++        Refine::UShift(smt);
++    }
+ 
+-  // Init
+-  Init(smt, net.source());
++    // Init
++    Init(smt, net.source());
+ 
+-  // DFS
+-  DFS(smt.source, sl_src, eps);
+-  Finalize(net, tree);
+-  tree.RemoveTopoRedundantSteiner();
++    // DFS
++    DFS(smt.source, slSrc, eps);
++    Finalize(net, tree);
++    tree.RemoveTopoRedundantSteiner();
+ 
+-  // Connect breakpoints to source by RSA
+-  salt::RsaBuilder rsa_builder;
+-  rsa_builder.ReplaceRootChildren(tree);
++    // Connect breakpoints to source by RSA
++    salt::RsaBuilder rsaB;
++    rsaB.ReplaceRootChildren(tree);
+ 
+-  // Refine SALT
+-  if (refineLevel >= 1) {
+-    Refine::cancelIntersect(tree);
+-    Refine::flip(tree);
+-    Refine::uShift(tree);
+-    if (refineLevel >= 2) {
+-      Refine::substitute(tree, eps, refineLevel == 3);
++    // Refine SALT
++    if (refineLevel >= 1) {
++        Refine::CancelIntersect(tree);
++        Refine::Flip(tree);
++        Refine::UShift(tree);
++        if (refineLevel >= 2) {
++            Refine::Substitute(tree, eps, refineLevel == 3);
++        }
+     }
+-  }
+ }
+ 
+-bool SaltBuilder::Relax(const shared_ptr<TreeNode>& u, const shared_ptr<TreeNode>& v)
+-{
+-  DTYPE new_dist = cur_dists[u->id] + Dist(u->loc, v->loc);
+-  if (cur_dists[v->id] > new_dist) {
+-    cur_dists[v->id] = new_dist;
+-    v->parent = u;
+-    return true;
+-  } else if (cur_dists[v->id] == new_dist && Dist(u->loc, v->loc) < v->WireToParentChecked()) {
+-    v->parent = u;
+-    return true;
+-  } else
+-    return false;
++bool SaltBuilder::Relax(const shared_ptr<TreeNode>& u, const shared_ptr<TreeNode>& v) {
++    DTYPE newDist = curDists[u->id] + Dist(u->loc, v->loc);
++    if (curDists[v->id] > newDist) {
++        curDists[v->id] = newDist;
++        v->parent = u;
++        return true;
++    } else if (curDists[v->id] == newDist && Dist(u->loc, v->loc) < v->WireToParentChecked()) {
++        v->parent = u;
++        return true;
++    } else
++        return false;
+ }
+ 
+-void SaltBuilder::DFS(const shared_ptr<TreeNode>& mst_node, const shared_ptr<TreeNode>& sl_node, double eps)
+-{
+-  if (mst_node->pin && cur_dists[sl_node->id] > (1 + eps) * shortest_dists[sl_node->id]) {
+-    sl_node->parent = sl_src;
+-    cur_dists[sl_node->id] = shortest_dists[sl_node->id];
+-  }
+-  for (auto c : mst_node->children) {
+-    Relax(sl_node, sl_nodes[c->id]);
+-    DFS(c, sl_nodes[c->id], eps);
+-    Relax(sl_nodes[c->id], sl_node);
+-  }
++void SaltBuilder::DFS(const shared_ptr<TreeNode>& smtNode, const shared_ptr<TreeNode>& slNode, double eps) {
++    if (smtNode->pin && curDists[slNode->id] > (1 + eps) * shortestDists[slNode->id]) {
++        slNode->parent = slSrc;
++        curDists[slNode->id] = shortestDists[slNode->id];
++    }
++    for (auto c : smtNode->children) {
++        Relax(slNode, slNodes[c->id]);
++        DFS(c, slNodes[c->id], eps);
++        Relax(slNodes[c->id], slNode);
++    }
+ }
+ 
+ }  // namespace salt
+diff --git a/src/third_party/salt/salt.h b/src/third_party/salt/salt.h
+index c4f38769..640e0401 100644
+--- a/src/third_party/salt/salt.h
++++ b/src/third_party/salt/salt.h
+@@ -4,28 +4,26 @@
+ 
+ namespace salt {
+ 
+-class SaltInterface
+-{
+- protected:
+-  vector<shared_ptr<TreeNode>> sl_nodes;  // nodes of the shallow-light tree
+-  vector<DTYPE> shortest_dists;
+-  vector<DTYPE> cur_dists;
+-  shared_ptr<TreeNode> sl_src;  // source node of the shallow-light tree
++class SaltBase {
++protected:
++    vector<shared_ptr<TreeNode>> slNodes; // nodes of the shallow-light tree
++    vector<DTYPE> shortestDists;
++    vector<DTYPE> curDists;
++    shared_ptr<TreeNode> slSrc;  // source node of the shallow-light tree
+ 
+-  void Init(Tree& min_tree, shared_ptr<Pin> src_pin);  // tree of minimum weight
+-  void Finalize(const Net& net, Tree& tree);
+-  virtual bool Relax(const shared_ptr<TreeNode>& u, const shared_ptr<TreeNode>& v) = 0;  // from u to v
+-  virtual void DFS(const shared_ptr<TreeNode>& mst_node, const shared_ptr<TreeNode>& sl_node, double eps) = 0;
++    void Init(Tree& minTree, shared_ptr<Pin> srcP);   // tree of minimum weight
++    void Finalize(const Net& net, Tree& tree);
++    virtual bool Relax(const shared_ptr<TreeNode>& u, const shared_ptr<TreeNode>& v) = 0;  // from u to v
++    virtual void DFS(const shared_ptr<TreeNode>& mstNode, const shared_ptr<TreeNode>& slNode, double eps) = 0;
+ };
+ 
+-class SaltBuilder : public SaltInterface
+-{
+- public:
+-  void Run(const Net& net, Tree& tree, double eps, int refineLevel = 3);
++class SaltBuilder : public SaltBase {
++public:
++    void Run(const Net& net, Tree& tree, double eps, int refineLevel = 3);
+ 
+- protected:
+-  bool Relax(const shared_ptr<TreeNode>& u, const shared_ptr<TreeNode>& v);  // from u to v
+-  void DFS(const shared_ptr<TreeNode>& mst_node, const shared_ptr<TreeNode>& sl_node, double eps);
++protected:
++    bool Relax(const shared_ptr<TreeNode>& u, const shared_ptr<TreeNode>& v);  // from u to v
++    void DFS(const shared_ptr<TreeNode>& mstNode, const shared_ptr<TreeNode>& slNode, double eps);
+ };
+ 
+ }  // namespace salt
+\ No newline at end of file
+diff --git a/src/third_party/salt/utils/geo.h b/src/third_party/salt/utils/geo.h
+index 19c5181f..fc0445a2 100644
+--- a/src/third_party/salt/utils/geo.h
++++ b/src/third_party/salt/utils/geo.h
+@@ -314,24 +314,24 @@ inline double L2Dist(const BoxT<T>& box1, const BoxT<T>& box2) {
+ // use BoxT instead of T & BoxT<T> to make it more general
+ template <typename BoxT>
+ void MergeRects(std::vector<BoxT>& boxes, int mergeDir) {
+-    int boundary_dir = 1 - mergeDir;
++    int boundaryDir = 1 - mergeDir;
+     std::sort(boxes.begin(), boxes.end(), [&](const BoxT& lhs, const BoxT& rhs) {
+-        return lhs[boundary_dir].low < rhs[boundary_dir].low ||
+-               (lhs[boundary_dir].low == rhs[boundary_dir].low && lhs[mergeDir].low < rhs[mergeDir].low);
++        return lhs[boundaryDir].low < rhs[boundaryDir].low ||
++               (lhs[boundaryDir].low == rhs[boundaryDir].low && lhs[mergeDir].low < rhs[mergeDir].low);
+     });
+-    std::vector<BoxT> merged_boxes;
+-    merged_boxes.push_back(boxes.front());
++    std::vector<BoxT> mergedBoxes;
++    mergedBoxes.push_back(boxes.front());
+     for (int i = 1; i < boxes.size(); ++i) {
+-        auto& last_box = merged_boxes.back();
+-        auto& sliced_box = boxes[i];
+-        if (sliced_box[boundary_dir] == last_box[boundary_dir] &&
+-            sliced_box[mergeDir].low <= last_box[mergeDir].high) {  // aligned and intersected
+-            last_box[mergeDir] = last_box[mergeDir].UnionWith(sliced_box[mergeDir]);
++        auto& lastBox = mergedBoxes.back();
++        auto& slicedBox = boxes[i];
++        if (slicedBox[boundaryDir] == lastBox[boundaryDir] &&
++            slicedBox[mergeDir].low <= lastBox[mergeDir].high) {  // aligned and intersected
++            lastBox[mergeDir] = lastBox[mergeDir].UnionWith(slicedBox[mergeDir]);
+         } else {  // neither misaligned not seperated
+-            merged_boxes.push_back(sliced_box);
++            mergedBoxes.push_back(slicedBox);
+         }
+     }
+-    boxes = move(merged_boxes);
++    boxes = move(mergedBoxes);
+ }
+ 
+ // Slice polygons along sliceDir
+@@ -339,40 +339,40 @@ void MergeRects(std::vector<BoxT>& boxes, int mergeDir) {
+ // assume no degenerated case
+ template <typename T>
+ void SlicePolygons(std::vector<BoxT<T>>& boxes, int sliceDir) {
+-    // Line sweep in sweep_dir = 1 - sliceDir
+-    // Suppose sliceDir = y and sweep_dir = x (sweep from left to right)
++    // Line sweep in sweepDir = 1 - sliceDir
++    // Suppose sliceDir = y and sweepDir = x (sweep from left to right)
+     // Not scalable impl (brute force interval query) but fast for small case
+     if (boxes.size() <= 1) return;
+ 
+-    // sort slice lines in sweep_dir
+-    int sweep_dir = 1 - sliceDir;
++    // sort slice lines in sweepDir
++    int sweepDir = 1 - sliceDir;
+     std::vector<T> locs;
+     for (const auto& box : boxes) {
+-        locs.push_back(box[sweep_dir].low);
+-        locs.push_back(box[sweep_dir].high);
++        locs.push_back(box[sweepDir].low);
++        locs.push_back(box[sweepDir].high);
+     }
+     std::sort(locs.begin(), locs.end());
+     locs.erase(std::unique(locs.begin(), locs.end()), locs.end());
+ 
+     // slice each box
+-    std::vector<BoxT<T>> sliced_boxes;
++    std::vector<BoxT<T>> slicedBoxes;
+     for (const auto& box : boxes) {
+-        BoxT<T> sliced_box = box;
+-        auto it_loc = std::lower_bound(locs.begin(), locs.end(), box[sweep_dir].low);
+-        auto it_end = std::upper_bound(it_loc, locs.end(), box[sweep_dir].high);
+-        while ((it_loc + 1) != it_end) {
+-            sliced_box[sweep_dir].Set(*it_loc, *(it_loc + 1));
+-            sliced_boxes.push_back(sliced_box);
+-            ++it_loc;
++        BoxT<T> slicedBox = box;
++        auto itLoc = std::lower_bound(locs.begin(), locs.end(), box[sweepDir].low);
++        auto itEnd = std::upper_bound(itLoc, locs.end(), box[sweepDir].high);
++        while ((itLoc + 1) != itEnd) {
++            slicedBox[sweepDir].Set(*itLoc, *(itLoc + 1));
++            slicedBoxes.push_back(slicedBox);
++            ++itLoc;
+         }
+     }
+-    boxes = move(sliced_boxes);
++    boxes = move(slicedBoxes);
+ 
+     // merge overlaped boxes along slice dir
+     MergeRects(boxes, sliceDir);
+ 
+     // stitch boxes along sweep dir
+-    MergeRects(boxes, sweep_dir);
++    MergeRects(boxes, sweepDir);
+ }
+ 
+ template <typename T>
+diff --git a/src/third_party/salt/utils/prettyprint.h b/src/third_party/salt/utils/prettyprint.h
+index 8acff997..191792f3 100644
+--- a/src/third_party/salt/utils/prettyprint.h
++++ b/src/third_party/salt/utils/prettyprint.h
+@@ -15,251 +15,219 @@
+ 
+ namespace utils {
+ 
+-// SFINAE HasBeginEnd
++// SFINAE has_begin_end
+ 
+ template <typename T, typename = void>
+-struct HasBeginEnd : std::false_type
+-{
+-};
++struct has_begin_end : std::false_type {};
+ template <typename T>
+-struct HasBeginEnd<T, decltype((void) std::begin(std::declval<T>()), (void) std::end(std::declval<T>()))> : std::true_type
+-{
+-};
++struct has_begin_end<T, decltype((void)std::begin(std::declval<T>()), (void)std::end(std::declval<T>()))>
++    : std::true_type {};
+ 
+ // Holds the delimiter values for a specific character type
+ 
+ template <typename TChar>
+-struct DelimitersValues
+-{
+-  using char_type = TChar;
+-  const char_type* prefix;
+-  const char_type* delimiter;
+-  const char_type* postfix;
++struct delimiters_values {
++    using char_type = TChar;
++    const char_type *prefix;
++    const char_type *delimiter;
++    const char_type *postfix;
+ };
+ 
+ // Defines the delimiter values for a specific container and character type
+ 
+ template <typename T, typename TChar = char>
+-struct Delimiters
+-{
+-  using type = DelimitersValues<TChar>;
+-  static const type kValues;
++struct delimiters {
++    using type = delimiters_values<TChar>;
++    static const type values;
+ };
+ 
+ // Functor to print containers. You can use this directly if you want
+-// to specificy a non-default Delimiters type. The printing logic can
++// to specificy a non-default delimiters type. The printing logic can
+ // be customized by specializing the nested template.
+ 
+-template <typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar>, typename TDelimiters = Delimiters<T, TChar>>
+-struct PrintContainerHelper
+-{
+-  using delimiters_type = TDelimiters;
+-  using ostream_type = std::basic_ostream<TChar, TCharTraits>;
+-
+-  template <typename U>
+-  struct Printer
+-  {
+-    static void printBody(const U& c, ostream_type& stream)
+-    {
+-      auto it = std::begin(c);
+-      const auto the_end = std::end(c);
+-
+-      if (it != the_end) {
+-        for (;;) {
+-          stream << *it;
+-
+-          if (++it == the_end)
+-            break;
+-
+-          if (delimiters_type::values.delimiter != NULL)
+-            stream << delimiters_type::values.delimiter;
++template <typename T,
++          typename TChar = char,
++          typename TCharTraits = std::char_traits<TChar>,
++          typename TDelimiters = delimiters<T, TChar>>
++struct print_container_helper {
++    using delimiters_type = TDelimiters;
++    using ostream_type = std::basic_ostream<TChar, TCharTraits>;
++
++    template <typename U>
++    struct printer {
++        static void print_body(const U &c, ostream_type &stream) {
++            auto it = std::begin(c);
++            const auto the_end = std::end(c);
++
++            if (it != the_end) {
++                for (;;) {
++                    stream << *it;
++
++                    if (++it == the_end) break;
++
++                    if (delimiters_type::values.delimiter != NULL) stream << delimiters_type::values.delimiter;
++                }
++            }
+         }
+-      }
+-    }
+-  };
++    };
+ 
+-  PrintContainerHelper(const T& container) : container_(container) {}
++    print_container_helper(const T &container) : container_(container) {}
+ 
+-  inline void operator()(ostream_type& stream) const
+-  {
+-    if (delimiters_type::values.prefix != NULL)
+-      stream << delimiters_type::values.prefix;
++    inline void operator()(ostream_type &stream) const {
++        if (delimiters_type::values.prefix != NULL) stream << delimiters_type::values.prefix;
+ 
+-    Printer<T>::printBody(container_, stream);
++        printer<T>::print_body(container_, stream);
+ 
+-    if (delimiters_type::values.postfix != NULL)
+-      stream << delimiters_type::values.postfix;
+-  }
++        if (delimiters_type::values.postfix != NULL) stream << delimiters_type::values.postfix;
++    }
+ 
+- private:
+-  const T& container_;
++private:
++    const T &container_;
+ };
+ 
+ // Specialization for pairs
+ 
+ template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+ template <typename T1, typename T2>
+-struct PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::Printer<std::pair<T1, T2>>
+-{
+-  using ostream_type = typename PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
+-
+-  static void printBody(const std::pair<T1, T2>& c, ostream_type& stream)
+-  {
+-    stream << c.first;
+-    if (PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
+-      stream << PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
+-    stream << c.second;
+-  }
++struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::pair<T1, T2>> {
++    using ostream_type = typename print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
++
++    static void print_body(const std::pair<T1, T2> &c, ostream_type &stream) {
++        stream << c.first;
++        if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
++            stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
++        stream << c.second;
++    }
+ };
+ 
+ // Specialization for tuples
+ 
+ template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+ template <typename... Args>
+-struct PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::Printer<std::tuple<Args...>>
+-{
+-  using ostream_type = typename PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
+-  using element_type = std::tuple<Args...>;
++struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::tuple<Args...>> {
++    using ostream_type = typename print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
++    using element_type = std::tuple<Args...>;
+ 
+-  template <std::size_t I>
+-  struct Int
+-  {
+-  };
++    template <std::size_t I>
++    struct Int {};
+ 
+-  static void printBody(const element_type& c, ostream_type& stream) { tuplePrint(c, stream, Int<0>()); }
++    static void print_body(const element_type &c, ostream_type &stream) { tuple_print(c, stream, Int<0>()); }
+ 
+-  static void tuplePrint(const element_type&, ostream_type&, Int<sizeof...(Args)>) {}
++    static void tuple_print(const element_type &, ostream_type &, Int<sizeof...(Args)>) {}
+ 
+-  static void tuplePrint(const element_type& c, ostream_type& stream,
+-                         typename std::conditional<sizeof...(Args) != 0, Int<0>, std::nullptr_t>::type)
+-  {
+-    stream << std::get<0>(c);
+-    tuplePrint(c, stream, Int<1>());
+-  }
++    static void tuple_print(const element_type &c,
++                            ostream_type &stream,
++                            typename std::conditional<sizeof...(Args) != 0, Int<0>, std::nullptr_t>::type) {
++        stream << std::get<0>(c);
++        tuple_print(c, stream, Int<1>());
++    }
+ 
+-  template <std::size_t N>
+-  static void tuplePrint(const element_type& c, ostream_type& stream, Int<N>)
+-  {
+-    if (PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
+-      stream << PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
++    template <std::size_t N>
++    static void tuple_print(const element_type &c, ostream_type &stream, Int<N>) {
++        if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
++            stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
+ 
+-    stream << std::get<N>(c);
++        stream << std::get<N>(c);
+ 
+-    tuplePrint(c, stream, Int<N + 1>());
+-  }
++        tuple_print(c, stream, Int<N + 1>());
++    }
+ };
+ 
+-// Prints a PrintContainerHelper to the specified stream.
++// Prints a print_container_helper to the specified stream.
+ 
+ template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+-inline std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits>& stream,
+-                                                          const PrintContainerHelper<T, TChar, TCharTraits, TDelimiters>& helper)
+-{
+-  helper(stream);
+-  return stream;
++inline std::basic_ostream<TChar, TCharTraits> &operator<<(
++    std::basic_ostream<TChar, TCharTraits> &stream,
++    const print_container_helper<T, TChar, TCharTraits, TDelimiters> &helper) {
++    helper(stream);
++    return stream;
+ }
+ 
+-// Basic IsContainer template; specialize to derive from std::true_type for all desired container types
++// Basic is_container template; specialize to derive from std::true_type for all desired container types
+ 
+ template <typename T>
+-struct IsContainer : std::integral_constant<bool, HasBeginEnd<T>::value>
+-{
+-};
++struct is_container : std::integral_constant<bool, has_begin_end<T>::value> {};
+ 
+ template <typename... T>
+-struct IsContainer<std::pair<T...>> : std::true_type
+-{
+-};
++struct is_container<std::pair<T...>> : std::true_type {};
+ 
+ template <typename... T>
+-struct IsContainer<std::tuple<T...>> : std::true_type
+-{
+-};
++struct is_container<std::tuple<T...>> : std::true_type {};
+ 
+-// Default Delimiters
++// Default delimiters
+ 
+ template <typename T>
+-struct Delimiters<T, char>
+-{
+-  static constexpr DelimitersValues<char> kValues = {"[", ", ", "]"};
++struct delimiters<T, char> {
++    static constexpr delimiters_values<char> values = {"[", ", ", "]"};
+ };
+ 
+ // Delimiters for (unordered_)(multi)set
+ 
+ template <typename... T>
+-struct Delimiters<std::set<T...>>
+-{
+-  static constexpr DelimitersValues<char> kValues = {"{", ", ", "}"};
++struct delimiters<std::set<T...>> {
++    static constexpr delimiters_values<char> values = {"{", ", ", "}"};
+ };
+ 
+ template <typename... T>
+-struct Delimiters<std::multiset<T...>>
+-{
+-  static constexpr DelimitersValues<char> kValues = {"{", ", ", "}"};
++struct delimiters<std::multiset<T...>> {
++    static constexpr delimiters_values<char> values = {"{", ", ", "}"};
+ };
+ 
+ template <typename... T>
+-struct Delimiters<std::unordered_set<T...>>
+-{
+-  static constexpr DelimitersValues<char> kValues = {"{", ", ", "}"};
++struct delimiters<std::unordered_set<T...>> {
++    static constexpr delimiters_values<char> values = {"{", ", ", "}"};
+ };
+ 
+ template <typename... T>
+-struct Delimiters<std::unordered_multiset<T...>>
+-{
+-  static constexpr DelimitersValues<char> kValues = {"{", ", ", "}"};
++struct delimiters<std::unordered_multiset<T...>> {
++    static constexpr delimiters_values<char> values = {"{", ", ", "}"};
+ };
+ 
+ // Delimiters for pair and tuple
+ 
+ template <typename... T>
+-struct Delimiters<std::pair<T...>>
+-{
+-  static constexpr DelimitersValues<char> kValues = {"(", ", ", ")"};
++struct delimiters<std::pair<T...>> {
++    static constexpr delimiters_values<char> values = {"(", ", ", ")"};
+ };
+ 
+ template <typename... T>
+-struct Delimiters<std::tuple<T...>>
+-{
+-  static constexpr DelimitersValues<char> kValues = {"(", ", ", ")"};
++struct delimiters<std::tuple<T...>> {
++    static constexpr delimiters_values<char> values = {"(", ", ", ")"};
+ };
+ 
+-// Type-erasing helper class for easy use of custom Delimiters.
++// Type-erasing helper class for easy use of custom delimiters.
+ // Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t, and MyDelims needs to be defined for
+-// TChar. Usage: "cout << pretty_print::CustomDelims<MyDelims>(x)".
++// TChar. Usage: "cout << pretty_print::custom_delims<MyDelims>(x)".
+ 
+-struct CustomDelimsInterface
+-{
+-  virtual ~CustomDelimsInterface() {}
+-  virtual std::ostream& stream(std::ostream&) = 0;
++struct custom_delims_base {
++    virtual ~custom_delims_base() {}
++    virtual std::ostream &stream(std::ostream &) = 0;
+ };
+ 
+ template <typename T, typename Delims>
+-struct CustomDelimsWrapper : CustomDelimsInterface
+-{
+-  CustomDelimsWrapper(const T& t_) : t(t_) {}
++struct custom_delims_wrapper : custom_delims_base {
++    custom_delims_wrapper(const T &t_) : t(t_) {}
+ 
+-  std::ostream& stream(std::ostream& s) { return s << PrintContainerHelper<T, char, std::char_traits<char>, Delims>(t); }
++    std::ostream &stream(std::ostream &s) {
++        return s << print_container_helper<T, char, std::char_traits<char>, Delims>(t);
++    }
+ 
+- private:
+-  const T& t;
++private:
++    const T &t;
+ };
+ 
+ template <typename Delims>
+-struct CustomDelims
+-{
+-  template <typename Container>
+-  CustomDelims(const Container& c) : base(new CustomDelimsWrapper<Container, Delims>(c))
+-  {
+-  }
+-
+-  std::unique_ptr<CustomDelimsInterface> base;
++struct custom_delims {
++    template <typename Container>
++    custom_delims(const Container &c) : base(new custom_delims_wrapper<Container, Delims>(c)) {}
++
++    std::unique_ptr<custom_delims_base> base;
+ };
+ 
+ template <typename TChar, typename TCharTraits, typename Delims>
+-inline std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits>& s, const CustomDelims<Delims>& p)
+-{
+-  return p.base->stream(s);
++inline std::basic_ostream<TChar, TCharTraits> &operator<<(std::basic_ostream<TChar, TCharTraits> &s,
++                                                          const custom_delims<Delims> &p) {
++    return p.base->stream(s);
+ }
+ 
+ }  // namespace utils
+@@ -268,13 +236,12 @@ inline std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TCh
+ // Can we do better?
+ 
+ namespace std {
+-// Prints a container to the stream using default Delimiters
++// Prints a container to the stream using default delimiters
+ 
+ template <typename T, typename TChar, typename TCharTraits>
+-inline typename enable_if<::utils::IsContainer<T>::value, basic_ostream<TChar, TCharTraits>&>::type operator<<(
+-    basic_ostream<TChar, TCharTraits>& stream, const T& container)
+-{
+-  return stream << ::utils::PrintContainerHelper<T, TChar, TCharTraits>(container);
++inline typename enable_if<::utils::is_container<T>::value, basic_ostream<TChar, TCharTraits> &>::type operator<<(
++    basic_ostream<TChar, TCharTraits> &stream, const T &container) {
++    return stream << ::utils::print_container_helper<T, TChar, TCharTraits>(container);
+ }
+ 
+ }  // namespace std
+\ No newline at end of file