Skip to content
This repository was archived by the owner on Feb 6, 2023. It is now read-only.

Commit f6bdf2c

Browse files
committed
Init commit
1 parent 9730581 commit f6bdf2c

11 files changed

+695
-0
lines changed

.clang-format

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
Language: Cpp
3+
# BasedOnStyle: Chromium
4+
AccessModifierOffset: -4
5+
AlignAfterOpenBracket: Align
6+
AlignConsecutiveAssignments: false
7+
AlignEscapedNewlinesLeft: true
8+
AlignOperands: true
9+
AlignTrailingComments: true
10+
AllowAllParametersOfDeclarationOnNextLine: false
11+
AllowShortBlocksOnASingleLine: false
12+
AllowShortCaseLabelsOnASingleLine: false
13+
AllowShortFunctionsOnASingleLine: Inline
14+
AllowShortIfStatementsOnASingleLine: false
15+
AllowShortLoopsOnASingleLine: false
16+
AlwaysBreakAfterDefinitionReturnType: None
17+
AlwaysBreakBeforeMultilineStrings: true
18+
AlwaysBreakTemplateDeclarations: true
19+
BinPackArguments: false
20+
BinPackParameters: false
21+
BreakBeforeBinaryOperators: None
22+
BreakBeforeBraces: Stroustrup
23+
BreakBeforeTernaryOperators: true
24+
BreakConstructorInitializersBeforeComma: true
25+
ColumnLimit: 80
26+
CommentPragmas: '^ IWYU pragma:'
27+
ConstructorInitializerAllOnOneLineOrOnePerLine: true
28+
ConstructorInitializerIndentWidth: 4
29+
ContinuationIndentWidth: 4
30+
Cpp11BracedListStyle: true
31+
DerivePointerAlignment: false
32+
DisableFormat: false
33+
ExperimentalAutoDetectBinPacking: false
34+
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
35+
IndentCaseLabels: true
36+
IndentWidth: 4
37+
IndentWrappedFunctionNames: false
38+
KeepEmptyLinesAtTheStartOfBlocks: false
39+
MacroBlockBegin: '^IPC_END_MESSAGE_MAP$'
40+
MacroBlockEnd: ''
41+
MaxEmptyLinesToKeep: 1
42+
NamespaceIndentation: None
43+
ObjCBlockIndentWidth: 4
44+
ObjCSpaceAfterProperty: false
45+
ObjCSpaceBeforeProtocolList: false
46+
PenaltyBreakBeforeFirstCallParameter: 1
47+
PenaltyBreakComment: 300
48+
PenaltyBreakFirstLessLess: 120
49+
PenaltyBreakString: 1000
50+
PenaltyExcessCharacter: 1000000
51+
PenaltyReturnTypeOnItsOwnLine: 200
52+
PointerAlignment: Left
53+
SpaceAfterCStyleCast: false
54+
SpaceBeforeAssignmentOperators: true
55+
SpaceBeforeParens: ControlStatements
56+
SpaceInEmptyParentheses: false
57+
SpacesBeforeTrailingComments: 2
58+
SpacesInAngles: false
59+
SpacesInContainerLiterals: true
60+
SpacesInCStyleCastParentheses: false
61+
SpacesInParentheses: false
62+
SpacesInSquareBrackets: false
63+
Standard: Auto
64+
TabWidth: 8
65+
UseTab: Never
66+
...
67+

detail/common.hpp

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Andrew Naplavkov
2+
3+
#ifndef STEP_COMMON_HPP
4+
#define STEP_COMMON_HPP
5+
6+
#include <array>
7+
#include <utility>
8+
#include <vector>
9+
10+
namespace step {
11+
12+
struct make_pair {
13+
template <typename Lhs, typename Rhs>
14+
auto operator()(Lhs&& lhs, Rhs&& rhs)
15+
{
16+
return std::make_pair(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs));
17+
}
18+
};
19+
20+
struct make_reverse_pair {
21+
template <typename Lhs, typename Rhs>
22+
auto operator()(Lhs&& lhs, Rhs&& rhs)
23+
{
24+
return std::make_pair(std::forward<Rhs>(rhs), std::forward<Lhs>(lhs));
25+
}
26+
};
27+
28+
template <typename T, size_t N>
29+
class ring_table {
30+
std::array<std::vector<T>, N> rows_;
31+
32+
public:
33+
explicit ring_table(size_t cols)
34+
{
35+
for (auto& row : rows_)
36+
row.resize(cols);
37+
}
38+
39+
auto& operator[](size_t row) { return rows_[row % N]; }
40+
};
41+
42+
} // namespace step
43+
44+
#endif // STEP_COMMON_HPP

detail/hirschberg.hpp

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Andrew Naplavkov
2+
3+
#ifndef STEP_HIRSCHBERG_HPP
4+
#define STEP_HIRSCHBERG_HPP
5+
6+
#include <algorithm>
7+
#include <iterator>
8+
#include <utility>
9+
10+
namespace step {
11+
namespace hirschberg {
12+
namespace detail {
13+
14+
template <typename RandomIt1,
15+
typename RandomIt2,
16+
typename DynamicProg,
17+
typename BinaryOp>
18+
auto partition_point(RandomIt1 first1,
19+
RandomIt1 last1,
20+
RandomIt2 first2,
21+
RandomIt2 last2,
22+
DynamicProg dp,
23+
BinaryOp op)
24+
{
25+
auto split1 = first1 + std::distance(first1, last1) / 2;
26+
auto top = dp.make_last_row(first1, split1, first2, last2);
27+
auto bottom = dp.make_last_row(std::make_reverse_iterator(last1),
28+
std::make_reverse_iterator(split1),
29+
std::make_reverse_iterator(last2),
30+
std::make_reverse_iterator(first2));
31+
std::transform(
32+
top.begin(), top.end(), bottom.rbegin(), top.begin(), std::plus{});
33+
auto split2 =
34+
first2 + std::distance(top.begin(),
35+
std::min_element(top.begin(), top.end(), dp));
36+
return op(split1, split2);
37+
}
38+
39+
} // namespace detail
40+
41+
/// @see https://en.wikipedia.org/wiki/Hirschberg's_algorithm
42+
template <typename RandomIt1,
43+
typename RandomIt2,
44+
typename OutputIt,
45+
typename DynamicProg>
46+
OutputIt align(RandomIt1 first1,
47+
RandomIt1 last1,
48+
RandomIt2 first2,
49+
RandomIt2 last2,
50+
OutputIt result,
51+
DynamicProg dp)
52+
{
53+
auto size1 = std::distance(first1, last1);
54+
auto size2 = std::distance(first2, last2);
55+
if (size1 < 2 || size2 < 2)
56+
return dp.trivial_align(first1, last1, first2, last2, result);
57+
58+
auto[split1, split2] =
59+
size2 < size1
60+
? detail::partition_point(
61+
first1, last1, first2, last2, dp, make_pair{})
62+
: detail::partition_point(
63+
first2, last2, first1, last1, dp, make_reverse_pair{});
64+
65+
result = hirschberg::align(first1, split1, first2, split2, result, dp);
66+
result = hirschberg::align(split1, last1, split2, last2, result, dp);
67+
return result;
68+
}
69+
70+
} // namespace hirschberg
71+
} // namespace step
72+
73+
#endif // STEP_HIRSCHBERG_HPP

edit_distance.hpp

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Andrew Naplavkov
2+
3+
#ifndef STEP_EDIT_DISTANCE_HPP
4+
#define STEP_EDIT_DISTANCE_HPP
5+
6+
#include <algorithm>
7+
#include <optional>
8+
#include <step/detail/common.hpp>
9+
#include <step/detail/hirschberg.hpp>
10+
#include <utility>
11+
12+
namespace step {
13+
namespace edit_distance {
14+
namespace detail {
15+
16+
template <typename ForwardIt, typename T, typename OutputIt, typename BinaryOp>
17+
auto align(ForwardIt first,
18+
ForwardIt last,
19+
const T& val,
20+
OutputIt result,
21+
BinaryOp op)
22+
{
23+
auto it = std::find(first, last, val);
24+
if (it == last)
25+
it = first;
26+
return std::transform(first, last, result, [&](const auto& item) {
27+
return op(item, item == *it ? std::optional{val} : std::nullopt);
28+
});
29+
}
30+
31+
struct dynamic_programming {
32+
/// @see https://en.wikipedia.org/wiki/Wagner–Fischer_algorithm
33+
template <typename RandomIt1, typename RandomIt2>
34+
auto make_last_row(RandomIt1 first1,
35+
RandomIt1 last1,
36+
RandomIt2 first2,
37+
RandomIt2 last2) const
38+
{
39+
auto size1 = std::distance(first1, last1);
40+
auto size2 = std::distance(first2, last2);
41+
ring_table<size_t, 2> tbl(size2 + 1);
42+
for (size_t l = 0; l <= size1; ++l)
43+
for (size_t r = 0; r <= size2; ++r) {
44+
if (l == 0)
45+
tbl[l][r] = r;
46+
else if (r == 0)
47+
tbl[l][r] = l;
48+
else if (first1[l - 1] == first2[r - 1])
49+
tbl[l][r] = tbl[l - 1][r - 1];
50+
else
51+
tbl[l][r] = 1 + std::min({tbl[l][r - 1], // insert
52+
tbl[l - 1][r], // remove
53+
tbl[l - 1][r - 1]}); // replace
54+
}
55+
return std::move(tbl[size1]);
56+
}
57+
58+
bool operator()(size_t lhs, size_t rhs) const { return lhs < rhs; }
59+
60+
template <typename RandomIt1, typename RandomIt2, typename OutputIt>
61+
OutputIt trivial_align(RandomIt1 first1,
62+
RandomIt1 last1,
63+
RandomIt2 first2,
64+
RandomIt2 last2,
65+
OutputIt result) const
66+
{
67+
if (first1 == last1)
68+
return std::transform(first2, last2, result, [&](const auto& item) {
69+
return std::make_pair(std::nullopt, item);
70+
});
71+
else if (first2 == last2)
72+
return std::transform(first1, last1, result, [&](const auto& item) {
73+
return std::make_pair(item, std::nullopt);
74+
});
75+
else if (std::next(first1) == last1)
76+
return align(first2, last2, *first1, result, make_reverse_pair{});
77+
else // std::next(first2) == last2
78+
return align(first1, last1, *first2, result, make_pair{});
79+
}
80+
};
81+
82+
} // namespace detail
83+
84+
/**
85+
* Find the optimal sequence alignment between two strings. Optimality is
86+
* measured with the Levenshtein distance, defined to be the sum of the costs of
87+
* insertions, replacements, deletions, and null actions needed to change one
88+
* string into the other.
89+
*
90+
* Time complexity: O(N*M), space complexity O(min(N,M)), where:
91+
* N = std::distance(first1, last1), M = std::distance(first2, last2).
92+
*/
93+
template <typename RandomIt1, typename RandomIt2, typename OutputIt>
94+
OutputIt align(RandomIt1 first1,
95+
RandomIt1 last1,
96+
RandomIt2 first2,
97+
RandomIt2 last2,
98+
OutputIt result)
99+
{
100+
return hirschberg::align(
101+
first1, last1, first2, last2, result, detail::dynamic_programming{});
102+
}
103+
104+
template <typename RandomRng1, typename RandomRng2, typename OutputIt>
105+
OutputIt align(const RandomRng1& rng1, const RandomRng2& rng2, OutputIt result)
106+
{
107+
return edit_distance::align(std::begin(rng1),
108+
std::end(rng1),
109+
std::begin(rng2),
110+
std::end(rng2),
111+
result);
112+
}
113+
114+
} // namespace edit_distance
115+
} // namespace step
116+
117+
#endif // STEP_EDIT_DISTANCE_HPP

example/diff/main.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Andrew Naplavkov
2+
3+
#include <fstream>
4+
#include <iostream>
5+
#include <step/edit_distance.hpp>
6+
#include <string>
7+
#include <string_view>
8+
9+
auto read_file(const char* file_name)
10+
{
11+
using iterator_t = std::istreambuf_iterator<char>;
12+
return std::string{(iterator_t(std::ifstream{file_name})), iterator_t()};
13+
}
14+
15+
auto split(const std::string& str)
16+
{
17+
std::vector<std::string_view> result;
18+
auto first = str.c_str();
19+
auto last = first + str.size();
20+
while (first != last) {
21+
auto it = std::find(first, last, '\n');
22+
result.emplace_back(first, std::distance(first, it));
23+
first = it == last ? it : std::next(it);
24+
}
25+
return result;
26+
}
27+
28+
int main(int argc, char* argv[])
29+
{
30+
using option_t = std::optional<std::string_view>;
31+
auto file1 = read_file(argv[1]);
32+
auto file2 = read_file(argv[2]);
33+
auto rng1 = split(file1);
34+
auto rng2 = split(file2);
35+
std::vector<std::pair<option_t, option_t>> pairs;
36+
37+
step::edit_distance::align(rng1, rng2, std::back_inserter(pairs));
38+
39+
size_t row = 0;
40+
for (const auto& pair : pairs) {
41+
if (pair.second)
42+
++row;
43+
if (pair.first != pair.second) {
44+
if (pair.first)
45+
std::cout << "--- " << row + (pair.second ? 0 : 1) << " ---\n"
46+
<< pair.first.value() << "\n";
47+
if (pair.second)
48+
std::cout << "+++ " << row << " +++\n"
49+
<< pair.second.value() << "\n";
50+
}
51+
}
52+
}

example/diff/makefile.windows

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CXXFLAGS=/EHsc /bigobj /std:c++17 /D_HAS_AUTO_PTR_ETC=1 $(CXXFLAGS)
2+
EXECUTABLE=diff.exe
3+
INCLUDE=..\..\;$(INCLUDE)
4+
OBJECTS=$(SOURCES:.cpp=.obj)
5+
SOURCES=main.cpp
6+
7+
all: $(SOURCES) $(EXECUTABLE)
8+
9+
$(EXECUTABLE): $(OBJECTS)
10+
$(CXX) /Fe:$(EXECUTABLE) $(OBJECTS) $(LIBS)
11+
12+
.cpp.obj:
13+
$(CXX) -c $(CXXFLAGS) $<
14+
15+
clean:
16+
del *.obj *.exp *.lib $(EXECUTABLE)

0 commit comments

Comments
 (0)