Skip to content

Commit

Permalink
Added Myers diff algorithm
Browse files Browse the repository at this point in the history
For now just one test case was updated as an example for initial
feedback
  • Loading branch information
Omar Awile committed Feb 21, 2024
1 parent 768cb81 commit 2649abf
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 1 deletion.
103 changes: 103 additions & 0 deletions test/unit/utils/test_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "utils/logger.hpp"
#include "utils/string_utils.hpp"

#include "fmt/core.h"

#include <cassert>
#include <filesystem>
#include <fstream>
Expand Down Expand Up @@ -99,6 +101,107 @@ TempFile::~TempFile() {
logger->error("Cannot delete temporary file {}", path_.string());
}
}
bool MyersDiff::do_diff() {
edits = std::deque<Edit>();
auto x = a.size();
auto y = b.size();

bool identical = true;

auto trace = shortest_edit();
std::size_t d = trace.size();

for (auto v_it = trace.rbegin(); v_it != trace.rend(); v_it++, d--) {

auto v = *v_it;
int k = x - y;
int prev_k{};
if ((k == -d) || ((k != d) && (v[idx(k - 1)] < v[idx(k + 1)]))) {
prev_k = k + 1;
} else {
prev_k = k - 1;
}
std::size_t prev_x = v[idx(prev_k)];
std::size_t prev_y = prev_x - prev_k;

while ((x > prev_x) && (y > prev_y)) {
edits.emplace_front(Edit::etype::eql, &a[x - 1], &b[y - 1]);
x--; y--;
}
if (x == prev_x) {
edits.emplace_front(Edit::etype::ins, nullptr, &b[y - 1]);
identical = false;
} else if (y == prev_y) {
edits.emplace_front(Edit::etype::del, &a[x - 1], nullptr);
identical = false;
}
x = prev_x;
y = prev_y;
}
return identical;
}

std::deque<MyersDiff::Edit> MyersDiff::get_edits() {
return edits;
}

std::vector<std::vector<int>> MyersDiff::shortest_edit() {
auto n = a.size();
auto m = b.size();
int x{}, y{};

auto trace = std::vector<std::vector<int>>();
auto v = std::vector<int>(2 * max + 1, 0);
for (int d = 0; d <= max; ++d) {
trace.push_back(v);
for (int k = -d; k <= d; k += 2) {
if ((k == -d) || (k != d && (v[idx(k - 1)] < v[idx(k + 1)]))) {
x = v[idx(k + 1)];
} else {
x = v[idx(k - 1)] + 1;
}
y = x - k;
while ((x < n) && (y < m) && (a[x].second == b[y].second)) {
x++; y++;
}
v[idx(k)] = x;

if ((x >= n) && (y >= m)) {
trace.push_back(v);
return trace;
}
}
}
return trace;
}

string_lines MyersDiff::split_lines(const std::string& txt) {
std::size_t pos = 0, ppos = 0;
std::size_t lineno = 1;
string_lines lines;
while ((pos = txt.find('\n', ppos)) != std::string::npos) {
lines.emplace_back(lineno++, txt.substr(ppos, pos - ppos));
ppos = pos + 1;
}
if (ppos < txt.length()) {
lines.emplace_back(lineno, txt.substr(ppos));
}

return lines;
}
std::ostringstream& operator<<(std::ostringstream& oss, MyersDiff::Edit& edit) {

if (edit.edit == MyersDiff::Edit::etype::ins) {
oss << fmt::format("{} {:>3s} {:>3d} {}", '+', "", edit.new_line->first, edit.new_line->second);
} else if (edit.edit == MyersDiff::Edit::etype::del) {
oss << fmt::format("{} {:>3d} {:>3s} {}", '-', edit.old_line->first, "", edit.old_line->second);
} else {
oss << fmt::format("{} {:>3d} {:>3d} {}", ' ', edit.old_line->first, edit.new_line->first, edit.old_line->second);
}
return oss;
}


} // namespace test_utils
} // namespace nmodl

52 changes: 52 additions & 0 deletions test/unit/utils/test_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@

#pragma once

#include <cstdlib>
#include <deque>
#include <filesystem>
#include <string>
#include <utility>
#include <vector>

namespace nmodl {
namespace test_utils {
Expand All @@ -26,5 +30,53 @@ struct TempFile {
std::filesystem::path path_;
};

using line = std::pair<std::size_t, std::string>;
using string_lines = std::vector<line>;

class MyersDiff {

public:

struct Edit {
enum etype {ins, del, eql};
etype edit;
const line* old_line = nullptr;
const line* new_line = nullptr;
Edit(etype e, const line* o, const line* n) : edit(e), old_line(o), new_line(n) {};

friend std::ostringstream& operator<<(std::ostringstream& out, Edit&);
};

MyersDiff(const std::string& str_a, const std::string& str_b) {
a = split_lines(str_a);
b = split_lines(str_b);
max = a.size() + b.size();
}

bool do_diff();
std::deque<Edit> get_edits();

private:

struct TraceTuple {
std::size_t prev_x = 0;
std::size_t prev_y = 0;
std::size_t x = 0;
std::size_t y = 0;
};

string_lines a, b;
std::size_t max{};
std::deque<MyersDiff::Edit> edits;

inline std::size_t idx(int i) const {
return (i + max) % max;
}

string_lines split_lines(const std::string& txt);
std::vector<std::vector<int>> shortest_edit();

};

} // namespace test_utils
} // namespace nmodl
12 changes: 11 additions & 1 deletion test/unit/visitor/loop_unroll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <sstream>
#include <catch2/catch_test_macros.hpp>

#include "ast/program.hpp"
Expand Down Expand Up @@ -88,7 +89,16 @@ SCENARIO("Perform loop unrolling of FROM construct", "[visitor][unroll]") {
)";
THEN("Loop body gets correctly unrolled") {
auto result = run_loop_unroll_visitor(input_nmodl);
REQUIRE(reindent_text(output_nmodl) == reindent_text(result));
if (reindent_text(output_nmodl) != reindent_text(result)) {
auto diff = MyersDiff(reindent_text(output_nmodl), reindent_text(result));
diff.do_diff();
std::ostringstream edits;
for (auto& edit : diff.get_edits()) {
edits << edit << "\n";
}
FAIL(edits.str());
}
//REQUIRE(reindent_text(output_nmodl) == reindent_text(result));
}
}

Expand Down

0 comments on commit 2649abf

Please sign in to comment.