// Copyright 2016 // Author: Patrick Brosi // #include #include "lest.h" #include "util/Nullable.h" #include "util/String.h" #include "util/geo/Geo.h" #include "util/geo/GeoNew.h" #include "util/graph/DirGraph.h" #include "util/graph/UndirGraph.h" #include "util/graph/Dijkstra.h" #include "util/graph/EDijkstra.h" #include "util/geo/Grid.h" #include "util/Misc.h" using lest::approx; using namespace util; using namespace util::geo; using namespace util::graph; // define LEST cases const lest::test specification[] = { // ___________________________________________________________________________ CASE("atof") { EXPECT(util::atof("45.534215") == approx(45.534215)); EXPECT(util::atof("5.534") == approx(5.534)); EXPECT(util::atof("534") == approx(534)); EXPECT(util::atof("-534") == approx(-534)); EXPECT(util::atof("-45.534215") == approx(-45.534215)); EXPECT(util::atof("-45.534215", 2) == approx(-45.53)); // TODO: more test cases }, // ___________________________________________________________________________ CASE("dirgraph") { DirGraph g; DirNode* a = new DirNode(0); DirNode* b = new DirNode(0); g.addNd(a); EXPECT(g.getNds()->size() == (size_t)1); g.addNd(b); EXPECT(g.getNds()->size() == (size_t)2); g.addEdg(a, b); EXPECT(a->getDeg() == (size_t)1); EXPECT(b->getDeg() == (size_t)0); auto c = g.addNd(); g.addEdg(a, c); g.addEdg(c, b); EXPECT(a->getDeg() == (size_t)2); EXPECT(b->getDeg() == (size_t)0); EXPECT(c->getDeg() == (size_t)1); g.delEdg(a, c); EXPECT(a->getDeg() == (size_t)1); EXPECT(b->getDeg() == (size_t)0); EXPECT(c->getDeg() == (size_t)1); g.addEdg(a, a); EXPECT(a->getDeg() == (size_t)2); g.delEdg(a, a); EXPECT(a->getDeg() == (size_t)1); g.delEdg(a, a); EXPECT(a->getDeg() == (size_t)1); // TODO: more test cases }, // ___________________________________________________________________________ CASE("unddirgraph") { UndirGraph g; UndirNode* a = new UndirNode(0); UndirNode* b = new UndirNode(0); g.addNd(a); EXPECT(g.getNds()->size() == (size_t)1); g.addNd(b); EXPECT(g.getNds()->size() == (size_t)2); g.addEdg(a, b); EXPECT(a->getDeg() == (size_t)1); EXPECT(b->getDeg() == (size_t)1); auto c = g.addNd(); g.addEdg(a, c); g.addEdg(c, b); EXPECT(a->getDeg() == (size_t)2); EXPECT(b->getDeg() == (size_t)2); EXPECT(c->getDeg() == (size_t)2); g.delEdg(a, c); EXPECT(a->getDeg() == (size_t)1); EXPECT(b->getDeg() == (size_t)2); EXPECT(c->getDeg() == (size_t)1); g.delNd(b); EXPECT(a->getDeg() == (size_t)0); EXPECT(c->getDeg() == (size_t)0); g.addEdg(a, a); EXPECT(a->getDeg() == (size_t)1); g.delEdg(a, a); EXPECT(a->getDeg() == (size_t)0); // TODO: more test cases }, // ___________________________________________________________________________ CASE("grid") { Grid g(.5, .5, Box(Point(0, 0), Point(3, 3))); Line l; l.push_back(Point(0, 0)); l.push_back(Point(1.5, 2)); Line l2; l.push_back(Point(2.5, 1)); l.push_back(Point(2.5, 2)); g.add(l, 1); g.add(l2, 2); std::set ret; Box req(Point(.5, 1), Point(1, 1.5)); g.get(req, &ret); EXPECT(ret.size() == (size_t)1); ret.clear(); g.getNeighbors(1, 0, &ret); // TODO! //EXPECT(ret.size() == 1); ret.clear(); g.getNeighbors(1, 0.55, &ret); EXPECT(ret.size() == (size_t)2); // TODO: more test cases }, // ___________________________________________________________________________ CASE("densify") { Line a; a.push_back(Point(1, 1)); a.push_back(Point(10, 1)); auto dense = util::geo::densify(a, 1); EXPECT(dense.size() == (size_t)10); for (int i = 0; i < 10; i++) { EXPECT(dense[i].get<0>() == approx(i + 1.0)); } dense = util::geo::simplify(dense, 0.1); EXPECT(dense.size() == (size_t)2); Line b; b.push_back(Point(1, 1)); b.push_back(Point(5, 7)); b.push_back(Point(10, 3)); dense = util::geo::densify(b, 1); dense = util::geo::simplify(dense, 0.1); EXPECT(dense.size() == (size_t)3); }, // ___________________________________________________________________________ CASE("summed frechet distance") { Line a; a.push_back(Point(1, 1)); a.push_back(Point(2, 1)); a.push_back(Point(3, 1)); a.push_back(Point(3, 2)); a.push_back(Point(4, 2)); a.push_back(Point(4, 1)); a.push_back(Point(5, 1)); a.push_back(Point(6, 1)); Line b; b.push_back(Point(1, 1)); b.push_back(Point(2, 1)); b.push_back(Point(3, 1)); b.push_back(Point(4, 1)); b.push_back(Point(5, 1)); b.push_back(Point(6, 1)); double fd = util::geo::accFrechetDistC(a, b, 0.1); EXPECT(fd == approx(2)); }, // ___________________________________________________________________________ CASE("frechet distance") { Line e; e.push_back(Point(1, 1)); e.push_back(Point(1, 2)); Line f; f.push_back(Point(1, 1)); f.push_back(Point(1, 2)); double fd = util::geo::frechetDist(e, f, 0.1); EXPECT(fd == approx(0)); Line a; a.push_back(Point(1, 1)); a.push_back(Point(2, 1)); a.push_back(Point(3, 2)); a.push_back(Point(4, 2)); a.push_back(Point(5, 1)); a.push_back(Point(6, 1)); Line b; b.push_back(Point(1, 1)); b.push_back(Point(2, 1)); b.push_back(Point(3, 1)); b.push_back(Point(4, 1)); b.push_back(Point(5, 1)); b.push_back(Point(6, 1)); auto adense = util::geo::densify(a, 0.1); auto bdense = util::geo::densify(b, 0.1); fd = util::geo::frechetDist(a, b, 0.1); EXPECT(fd == approx(1)); Line c; c.push_back(Point(1, 1)); c.push_back(Point(2, 1)); Line d; d.push_back(Point(3, 1)); d.push_back(Point(4, 1)); fd = util::geo::frechetDist(c, d, 0.1); EXPECT(fd == approx(2)); Line g; g.push_back(Point(1, 1)); g.push_back(Point(10, 1)); Line h; h.push_back(Point(1, 1)); h.push_back(Point(3, 2)); h.push_back(Point(3, 1)); h.push_back(Point(10, 1)); fd = util::geo::frechetDist(g, h, 0.1); EXPECT(fd == approx(1)); }, // ___________________________________________________________________________ CASE("geo box alignment") { Line a; a.push_back(Point(1, 1)); a.push_back(Point(1, 2)); Line b; b.push_back(Point(1, 2)); b.push_back(Point(2, 2)); Line c; c.push_back(Point(2, 2)); c.push_back(Point(2, 1)); Line d; d.push_back(Point(2, 1)); d.push_back(Point(1, 1)); Box box(Point(2, 3), Point(5, 4)); MultiLine ml; ml.push_back(a); ml.push_back(b); ml.push_back(c); ml.push_back(d); EXPECT(parallelity(box, ml) == approx(1)); ml = rotate(ml, 45); EXPECT(parallelity(box, ml) == approx(0)); ml = rotate(ml, 45); EXPECT(parallelity(box, ml) == approx(1)); ml = rotate(ml, 45); EXPECT(parallelity(box, ml) == approx(0)); ml = rotate(ml, 45); EXPECT(parallelity(box, ml) == approx(1)); }, // ___________________________________________________________________________ CASE("url decode") { EXPECT("zürich" == util::urlDecode("z%C3%BCrich")); EXPECT("!@$%^*()" == util::urlDecode("!%40%24%25%5E*()")); EXPECT("Løkken" == util::urlDecode("L%C3%B8kken")); EXPECT("á é" == util::urlDecode("%C3%A1%20%C3%A9")); EXPECT("á é" == util::urlDecode("%C3%A1+%C3%A9")); }, // ___________________________________________________________________________ CASE("json escape") { EXPECT("Hello\\\\Goodbye!" == util::jsonStringEscape("Hello\\Goodbye!")); EXPECT("\\\"Hello\\\"" == util::jsonStringEscape("\"Hello\"")); }, // ___________________________________________________________________________ CASE("split") { EXPECT(util::split("hello,again", ',').size() == (size_t)2); EXPECT(util::split("hello,,again", ',').size() == (size_t)3); EXPECT(util::split("hello", ',').size() == (size_t)1); EXPECT(util::split("", ',').size() == (size_t)0); }, // ___________________________________________________________________________ CASE("editdist") { EXPECT(util::editDist("hello", "mello") == (size_t)1); EXPECT(util::editDist("mello", "hello") == (size_t)1); EXPECT(util::editDist("abcde", "abfde") == (size_t)1); EXPECT(util::editDist("abcd", "abcde") == (size_t)1); EXPECT(util::editDist("xabcd", "abcde") == (size_t)2); EXPECT(util::editDist("abcd", "abcdes") == (size_t)2); EXPECT(util::editDist("hello", "hello") == (size_t)0); }, // ___________________________________________________________________________ CASE("toString") { EXPECT(util::toString(34) == "34"); EXPECT(util::toString("34") == "34"); }, // ___________________________________________________________________________ CASE("replace") { std::string a("lorem ipsum ipsum lorem"); EXPECT(util::replace(a, "ips", "aa")); EXPECT(a == "lorem aaum ipsum lorem"); EXPECT(!util::replace(a, "blablabla", "")); EXPECT(a == "lorem aaum ipsum lorem"); EXPECT(util::replace(a, "m", "")); EXPECT(a == "lore aaum ipsum lorem"); EXPECT(!util::replace(a, "", "")); EXPECT(a == "lore aaum ipsum lorem"); std::string b("lorem ipsum ipsum lorem"); EXPECT(util::replaceAll(b, "ips", "aa")); EXPECT(b == "lorem aaum aaum lorem"); EXPECT(util::replaceAll(b, "m", "")); EXPECT(b == "lore aau aau lore"); EXPECT(util::replaceAll(b, "a", "aa")); EXPECT(b == "lore aaaau aaaau lore"); EXPECT(util::replaceAll(b, "e", "e")); EXPECT(b == "lore aaaau aaaau lore"); EXPECT(util::replaceAll(b, "e", "ee")); EXPECT(b == "loree aaaau aaaau loree"); EXPECT(!util::replaceAll(b, "", "ee")); EXPECT(b == "loree aaaau aaaau loree"); }, // ___________________________________________________________________________ CASE("Edge-based Dijkstra directed, 1 to all") { DirGraph g; auto a = g.addNd("A"); auto b = g.addNd("B"); auto c = g.addNd("C"); auto d = g.addNd("D"); auto e = g.addNd("E"); auto eAC = g.addEdg(a, c, 1); auto eAB = g.addEdg(a, b, 5); auto eDC = g.addEdg(d, c, 1); auto eDB = g.addEdg(d, b, 3); auto eED = g.addEdg(e, d, 1); auto eEB = g.addEdg(e, b, 1); UNUSED(eAC); UNUSED(eDC); UNUSED(eDB); UNUSED(eED); UNUSED(eEB); struct CostFunc : public EDijkstra::CostFunc { int operator()(const Edge* from, const Node* n, const Edge* to) const { UNUSED(from); // dont count cost of start edge if (n) return to->pl(); return 0; }; int inf() const { return 999; }; }; CostFunc cFunc; auto cost = EDijkstra::shortestPath(eAB, cFunc); for (auto u : cost) { int single = EDijkstra::shortestPath(eAB, u.first, cFunc); EXPECT(single == u.second); } // all to 1 auto eBC = g.addEdg(b, c, 10); auto costb = EDijkstra::shortestPathRev(eBC, cFunc); for (auto u : costb) { int single = EDijkstra::shortestPath(u.first, eBC, cFunc); EXPECT(single == u.second); } }, // ___________________________________________________________________________ CASE("Edge-based Dijkstra undirected, edge 1 to 1") { UndirGraph g; auto a = g.addNd("A"); auto b = g.addNd("B"); auto c = g.addNd("C"); auto d = g.addNd("D"); auto e = g.addNd("E"); auto eAC = g.addEdg(a, c, 1); auto eAB = g.addEdg(a, b, 5); auto eDC = g.addEdg(d, c, 1); auto eDB = g.addEdg(d, b, 3); auto eED = g.addEdg(e, d, 1); auto eEB = g.addEdg(e, b, 1); UNUSED(eAC); UNUSED(eDC); UNUSED(eDB); UNUSED(eED); UNUSED(eEB); struct CostFunc : public EDijkstra::CostFunc { int operator()(const Edge* from, const Node* n, const Edge* to) const { UNUSED(from); // dont count cost of start edge if (n) return to->pl(); return 0; }; int inf() const { return 999; }; }; CostFunc cFunc; EDijkstra::NList res; EDijkstra::EList resE; int cost = EDijkstra::shortestPath(eAB, d, cFunc, &resE, &res); EXPECT(cost == 2); EXPECT(resE.size() == (size_t)3); EXPECT(res.size() == (size_t)3); EXPECT((*(res.rbegin()))->pl() == "A"); EXPECT((*(++res.rbegin()))->pl() == "C"); EXPECT((*(++++res.rbegin()))->pl() == "D"); EXPECT((*(resE.rbegin())) == eAB); EXPECT((*(++resE.rbegin())) == eAC); EXPECT((*(++++resE.rbegin())) == eDC); cost = EDijkstra::shortestPath(eAB, b, cFunc, &resE, &res); EXPECT(cost == 0); }, // ___________________________________________________________________________ CASE("Edge-based Dijkstra undirected, edge 1 to n") { UndirGraph g; auto a = g.addNd("A"); auto b = g.addNd("B"); auto c = g.addNd("C"); auto d = g.addNd("D"); auto e = g.addNd("E"); auto eAC = g.addEdg(a, c, 1); auto eAB = g.addEdg(a, b, 5); auto eDC = g.addEdg(d, c, 1); auto eDB = g.addEdg(d, b, 3); auto eED = g.addEdg(e, d, 1); auto eEB = g.addEdg(e, b, 1); UNUSED(eAC); UNUSED(eDC); UNUSED(eDB); UNUSED(eED); UNUSED(eEB); struct CostFunc : public EDijkstra::CostFunc { int operator()(const Edge* from, const Node* n, const Edge* to) const { UNUSED(from); // dont count cost of start edge if (n) return to->pl(); return 0; }; int inf() const { return 999; }; }; CostFunc cFunc; std::set*> tos; tos.insert(d); tos.insert(b); tos.insert(b); EDijkstra::NList res; EDijkstra::EList resE; int cost = EDijkstra::shortestPath(eAB, tos, cFunc, &resE, &res); EXPECT(cost == 0); }, // ___________________________________________________________________________ CASE("Edge-based Dijkstra undirected, 1 to n") { UndirGraph g; auto a = g.addNd("A"); auto b = g.addNd("B"); auto c = g.addNd("C"); auto d = g.addNd("D"); auto e = g.addNd("E"); g.addEdg(a, c, 1); auto eAB = g.addEdg(a, b, 5); auto eDC = g.addEdg(d, c, 1); g.addEdg(d, b, 3); auto eED = g.addEdg(e, d, 1); g.addEdg(e, b, 1); struct CostFunc : public EDijkstra::CostFunc { int operator()(const Edge* from, const Node* n, const Edge* to) const { UNUSED(from); // dont count cost of start edge if (n) return to->pl(); return 0; }; int inf() const { return 999; }; }; CostFunc cFunc; std::set*> tos; tos.insert(eDC); tos.insert(eED); std::unordered_map*, EDijkstra::EList*> resE; resE[eDC] = new EDijkstra::EList(); resE[eED] = new EDijkstra::EList(); std::unordered_map*, EDijkstra::NList*> res; res[eDC] = new EDijkstra::NList(); res[eED] = new EDijkstra::NList(); auto hFunc = EDijkstra::ZeroHeurFunc(); std::unordered_map*, int> cost = EDijkstra::shortestPath(eAB, tos, cFunc, hFunc, resE, res); EXPECT(cost[eDC] == 2); EXPECT(cost[eED] == 2); EXPECT(resE[eDC]->size() == (size_t)3); EXPECT(res[eED]->size() == (size_t)3); EXPECT(resE[eDC]->size() == (size_t)3); EXPECT(res[eED]->size() == (size_t)3); }, // ___________________________________________________________________________ CASE("Edge-based Dijkstra undirected") { UndirGraph g; auto a = g.addNd("A"); auto b = g.addNd("B"); auto c = g.addNd("C"); auto d = g.addNd("D"); auto e = g.addNd("E"); g.addEdg(a, c, 1); g.addEdg(a, b, 5); g.addEdg(d, c, 1); g.addEdg(d, b, 3); g.addEdg(e, d, 1); g.addEdg(e, b, 1); struct CostFunc : public EDijkstra::CostFunc { int operator()(const Edge* fr, const Node* n, const Edge* to) const { UNUSED(fr); UNUSED(n); return to->pl(); }; int inf() const { return 999; }; }; CostFunc cFunc; EDijkstra::NList res; EDijkstra::EList resE; int cost = EDijkstra::shortestPath(a, b, cFunc, &resE, &res); EXPECT(res.size() == (size_t)5); EXPECT((*(res.rbegin()))->pl() == "A"); EXPECT((*(++res.rbegin()))->pl() == "C"); EXPECT((*(++++res.rbegin()))->pl() == "D"); EXPECT((*(++++++res.rbegin()))->pl() == "E"); EXPECT((*(++++++++res.rbegin()))->pl() == "B"); EXPECT(cost == 4); EXPECT((*(resE.rbegin()))->getFrom()->pl() == "A"); EXPECT((*(++resE.rbegin()))->getFrom()->pl() == "D"); EXPECT((*(++++resE.rbegin()))->getFrom()->pl() == "E"); EXPECT((*(++++++resE.rbegin()))->getTo()->pl() == "B"); EXPECT(resE.size() == (size_t)4); cost = EDijkstra::shortestPath(d, b, cFunc, &res); EXPECT(cost == 2); cost = EDijkstra::shortestPath(b, d, cFunc, &res); EXPECT(cost == 2); cost = EDijkstra::shortestPath(e, b, cFunc, &res); EXPECT(cost == 1); cost = EDijkstra::shortestPath(b, e, cFunc, &res); EXPECT(cost == 1); cost = EDijkstra::shortestPath(b, a, cFunc, &res); EXPECT(cost == 4); cost = EDijkstra::shortestPath(c, a, cFunc, &res); EXPECT(cost == 1); cost = EDijkstra::shortestPath(a, c, cFunc, &res); EXPECT(cost == 1); cost = EDijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 2); }, // ___________________________________________________________________________ CASE("Edge-based Dijkstra") { DirGraph g; DirNode* a = new DirNode(1); DirNode* b = new DirNode(4); g.addNd(a); g.addNd(b); auto c = g.addNd(2); auto d = g.addNd(3); auto x = g.addNd(); g.addEdg(a, d, 4); g.addEdg(a, c, 1); g.addEdg(c, b, 1); g.addEdg(b, d, 1); struct CostFunc : public EDijkstra::CostFunc { int operator()(const Edge* fr, const Node* n, const Edge* to) const { UNUSED(fr); UNUSED(n); return to->pl(); }; int inf() const { return 999; }; }; CostFunc cFunc; EDijkstra::NList res; int cost = EDijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 3); g.addEdg(c, d, 3); cost = EDijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 3); g.addEdg(a, b, 1); g.addEdg(x, a, 1); cost = EDijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 2); }, // ___________________________________________________________________________ CASE("Dijkstra") { DirGraph g; DirNode* a = new DirNode(1); DirNode* b = new DirNode(0); g.addNd(a); g.addNd(b); auto c = g.addNd(); auto d = g.addNd(4); auto x = g.addNd(); g.addEdg(a, d, 4); g.addEdg(a, c, 1); g.addEdg(c, b, 1); g.addEdg(b, d, 1); struct CostFunc : public Dijkstra::CostFunc { int operator()(const Node* fr, const Edge* e, const Node* to) const { UNUSED(fr); UNUSED(to); return e->pl(); }; int inf() const { return 999; }; }; CostFunc cFunc; Dijkstra::NList res; int cost = Dijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 3); EXPECT(res.size() == (size_t)4); g.addEdg(c, d, 3); cost = Dijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 3); g.addEdg(a, b, 1); g.addEdg(x, a, 1); cost = Dijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 2); const std::set*> to{b, c, d, x}; std::unordered_map*, Dijkstra::EList*> resEdges; std::unordered_map*, Dijkstra::NList*> resNodes; for (auto n : to) { resEdges[n] = new Dijkstra::EList(); resNodes[n] = new Dijkstra::NList(); } auto costs = Dijkstra::shortestPath(a, to, cFunc, resEdges, resNodes); EXPECT(costs[b] == 1); EXPECT(costs[c] == 1); EXPECT(costs[d] == 2); EXPECT(costs[x] == 999); }, // ___________________________________________________________________________ CASE("nullable") { { util::Nullable nullable; EXPECT(nullable.isNull()); } { util::Nullable nullable(0); EXPECT(nullable.isNull()); } { std::string str = "aa"; util::Nullable nullable(&str); EXPECT(!nullable.isNull()); EXPECT(nullable == "aa"); EXPECT(!(nullable == "aaa")); EXPECT(!(nullable != "aa")); EXPECT(nullable == "aa"); EXPECT(nullable.get() == "aa"); EXPECT(std::string(nullable) == "aa"); } { int a = 23; util::Nullable nullable(a); util::Nullable nullable2(24); EXPECT(!nullable.isNull()); EXPECT(nullable == 23); EXPECT(nullable >= 23); EXPECT(nullable <= 23); EXPECT(nullable < 24); EXPECT(nullable < 24); EXPECT(!(nullable < 22)); EXPECT(nullable != nullable2); EXPECT(nullable < nullable2); EXPECT(nullable2 > nullable); util::Nullable nullable3(nullable); EXPECT(nullable == nullable3); nullable3 = nullable2; EXPECT(nullable2 == nullable3); EXPECT(nullable3 == 24); EXPECT(nullable2 == 24); EXPECT(nullable2 == nullable2.get()); EXPECT(int(nullable2) == nullable2.get()); EXPECT(!nullable3.isNull()); EXPECT(!nullable2.isNull()); util::Nullable voidnull; EXPECT(voidnull.isNull()); EXPECT_THROWS(nullable == voidnull); } }, // ___________________________________________________________________________ CASE("geometry") { } }; // _____________________________________________________________________________ int main(int argc, char** argv) { return(lest::run(specification, argc, argv)); }