--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <vector>
+#include <map>
+#include <algorithm>
+
+#define NUM_DISTRO 5
+#define SWITCHES_PER_ROW 6
+#define TRUNCATE_METRIC 1
+#define TENPLUSTEN 250
+#define THIRTYPLUSTEN 450
+#define FIFTYPLUSTEN 650
+
+struct sw {
+ unsigned char row, num;
+
+ sw(unsigned char row, unsigned char num) : row(row), num(num) {}
+};
+int distro_placements[NUM_DISTRO] = {
+ 5, 12, 19, 26, 33
+};
+unsigned horiz_cost[SWITCHES_PER_ROW] = {
+ 346, 250, 154, 104, 200, 296
+};
+
+// memoization table
+struct key {
+ unsigned char sw_ind, distro_bound, distro_bound_hard;
+ unsigned char ports_left[NUM_DISTRO];
+
+ bool operator< (const key &other) const
+ {
+ if (sw_ind != other.sw_ind)
+ return (sw_ind < other.sw_ind);
+ if (distro_bound != other.distro_bound)
+ return (distro_bound < other.distro_bound);
+ if (distro_bound_hard != other.distro_bound_hard)
+ return (distro_bound_hard < other.distro_bound_hard);
+ for (unsigned i = distro_bound_hard; i < NUM_DISTRO - 1; ++i)
+ if (ports_left[i] != other.ports_left[i])
+ return (ports_left[i] < other.ports_left[i]);
+ return (ports_left[NUM_DISTRO - 1] < other.ports_left[NUM_DISTRO - 1]);
+ }
+};
+struct value {
+ unsigned char distro;
+ unsigned cost, this_cost;
+};
+std::map<key, value> cache;
+unsigned cache_hits = 0;
+std::vector<sw> switches;
+
+unsigned distance(sw from_where, unsigned distro)
+{
+ // FIXME: calculate gaps as well
+ unsigned base_cost = 41 * abs(from_where.row - distro_placements[distro]) + horiz_cost[from_where.num];
+
+ if ((from_where.row <= 9) == (distro_placements[distro] >= 10))
+ base_cost += 25;
+ if ((from_where.row <= 17) == (distro_placements[distro] >= 18))
+ base_cost += 25;
+ if ((from_where.row <= 25) == (distro_placements[distro] >= 26))
+ base_cost += 25;
+ if ((from_where.row <= 34) == (distro_placements[distro] >= 35))
+ base_cost += 25;
+
+#if TRUNCATE_METRIC
+ if (base_cost > 600)
+ return 1000;
+ if (base_cost > 500)
+ return FIFTYPLUSTEN;
+ if (base_cost > 400)
+ return 500;
+ if (base_cost > 300)
+ return THIRTYPLUSTEN;
+ if (base_cost > 200)
+ return 300;
+ if (base_cost > 100)
+ return TENPLUSTEN;
+ return 100;
+#else
+ return base_cost;
+// return ((base_cost + 90) / 100) * 100;
+#endif
+}
+
+unsigned find_optimal_cost(key &k)
+{
+ unsigned best_cost = 999999990, best_this_cost = 0;
+ int best = -1;
+ unsigned char db = k.distro_bound;
+ unsigned char dbh = k.distro_bound_hard;
+
+ if (k.sw_ind == switches.size())
+ return 0;
+ if (cache.count(k)) {
+ ++cache_hits;
+ return cache[k].cost;
+ }
+
+ bool next_changes_row = (k.sw_ind + 1U < switches.size() && switches[k.sw_ind].row != switches[k.sw_ind + 1].row);
+
+ for (unsigned char i = dbh; i < dbh + 2 && i < NUM_DISTRO; ++i) {
+ if (k.ports_left[i] == 0)
+ continue;
+
+ unsigned cost = distance(switches[k.sw_ind], i);
+
+ --(k.ports_left[i]);
+ ++(k.sw_ind);
+
+ k.distro_bound = std::max(i, db);
+ if (next_changes_row) {
+ k.distro_bound_hard = k.distro_bound;
+ }
+
+ cost += find_optimal_cost(k);
+ --(k.sw_ind);
+ ++(k.ports_left[i]);
+
+ if (best == -1 || cost < best_cost) {
+ best = i;
+ best_cost = cost;
+ best_this_cost = distance(switches[k.sw_ind], i);
+ }
+ }
+ k.distro_bound = db;
+ k.distro_bound_hard = dbh;
+
+ value v = { best, best_cost, best_this_cost };
+ cache[k] = v;
+
+ return best_cost;
+}
+
+int main()
+{
+ struct key start;
+#if TRUNCATE_METRIC
+ std::map<unsigned, unsigned> cable_count;
+#endif
+
+ switches.push_back(sw(1, 3));
+ switches.push_back(sw(1, 4));
+ switches.push_back(sw(1, 5));
+ switches.push_back(sw(2, 3));
+ switches.push_back(sw(2, 4));
+ switches.push_back(sw(2, 5));
+
+ for (unsigned i = 3; i < 35; ++i)
+ for (unsigned j = 0; j < SWITCHES_PER_ROW; ++j)
+ switches.push_back(sw(i, j));
+
+ switches.push_back(sw(35, 1));
+ switches.push_back(sw(35, 2));
+ switches.push_back(sw(35, 3));
+ switches.push_back(sw(35, 4));
+
+ switches.push_back(sw(36, 1));
+ switches.push_back(sw(36, 2));
+ switches.push_back(sw(36, 3));
+ switches.push_back(sw(36, 4));
+
+ switches.push_back(sw(37, 1));
+ switches.push_back(sw(37, 2));
+ switches.push_back(sw(37, 3));
+ switches.push_back(sw(37, 4));
+
+ start.sw_ind = 0;
+ start.distro_bound = 0;
+ start.distro_bound_hard = 0;
+ start.ports_left[0] = 40;
+ start.ports_left[1] = 40;
+ start.ports_left[2] = 50;
+ start.ports_left[3] = 40;
+ start.ports_left[4] = 40;
+
+ printf("Finding optimal layout for %u switches\n", switches.size());
+ find_optimal_cost(start);
+ printf("%u cache nodes, %u cache hits.\n", cache.size(), cache_hits);
+
+ key k = start;
+ int last_row = 0, last_num = -1;
+ for (unsigned i = 0; i < switches.size(); ++i) {
+ value v = cache[k];
+ bool next_changes_row = (k.sw_ind + 1U < switches.size() && switches[k.sw_ind].row != switches[k.sw_ind + 1].row);
+
+ if (cache.count(k) == 0) {
+ fprintf(stderr, "FATAL: no solutions!\n");
+ exit(1);
+ }
+
+ k.distro_bound = std::max(k.distro_bound, v.distro);
+ if (next_changes_row)
+ k.distro_bound_hard = k.distro_bound;
+
+ if (last_row != switches[k.sw_ind].row) {
+ printf("\n");
+ last_num = -1;
+ }
+ for (int j = last_num; j + 1 < switches[k.sw_ind].num; ++j) {
+ printf("%11s", "");
+ }
+
+
+ printf("\e[%um%u ", v.distro + 33, v.distro);
+#if TRUNCATE_METRIC
+ if (v.this_cost == FIFTYPLUSTEN)
+ printf("(50+10) ");
+ else if (v.this_cost == THIRTYPLUSTEN)
+ printf("(30+10) ");
+ else if (v.this_cost == TENPLUSTEN)
+ printf("(10+10) ");
+ else
+ printf("(%-3u ) ", v.this_cost / 10);
+ cable_count[v.this_cost]++;
+#else
+ printf("(%3.1f) ", v.this_cost / 10.0);
+#endif
+
+ last_row = switches[k.sw_ind].row;
+ last_num = switches[k.sw_ind].num;
+
+ ++(k.sw_ind);
+ --k.ports_left[v.distro];
+ }
+ printf("\n");
+
+ for (std::map<unsigned,unsigned>::iterator i = cable_count.begin(); i != cable_count.end(); ++i)
+ printf("%u => %u\n", i->first, i->second);
+}