ArKanjo 0.5.1
A tool for find code duplicated functions in codebases
similarity_table.cpp
Go to the documentation of this file.
5
7
9 if (paths[id2] < paths[id1])
10 std::swap(id1, id2);
11}
12
13PathId Similarity_Table::find_id_path(const Path& path) {
14 auto [it, inserted] = path_id.try_emplace(path, paths.size());
15
16 if (inserted) {
17 paths.push_back(path);
18 similarity_graph.emplace_back();
19 }
20
21 return it->second;
22}
23
24void Similarity_Table::read_comparation(std::ifstream& table_file) {
25 std::string string_path1, string_path2;
26 double similarity;
27 table_file >> string_path1 >> string_path2 >> similarity;
28
29 PathId id1 = find_id_path(Path(string_path1));
30 PathId id2 = find_id_path(Path(string_path2));
31
32 normalize_pair(id1, id2);
33
34 similarity_graph[id1].push_back(std::make_pair(id2, similarity));
35 similarity_graph[id2].push_back(std::make_pair(id1, similarity));
36 similarity_table[std::make_pair(id1, id2)] = similarity;
37}
38
39void Similarity_Table::read_file_table(std::ifstream& table_file) {
40 int number_comparations;
41 table_file >> number_comparations;
42 for (int i = 0; i < number_comparations; i++) {
43 read_comparation(table_file);
44 }
45}
46
47Similarity_Table::Cache_Compatibility Similarity_Table::init_similarity_table() {
49
51
52 if (!Preprocess_State::is_cache_compatible(params.version)) {
54 }
55
56 std::ifstream table_file;
57 const fs::path similarity_table_file_name = FunctionStorage{}.build_similarity_path();
58 table_file.open(similarity_table_file_name);
59 Utils::ensure_file_is_open(table_file, similarity_table_file_name);
60
61 read_file_table(table_file);
62
63 table_file.close();
64 return res;
65}
66
67Similarity_Table::Similarity_Table(double _similarity_threshold)
68 : similarity_threshold{_similarity_threshold} { }
69
71 : similarity_threshold{DEFAULT_SIMILARITY} { }
72
74 return init_similarity_table();
75}
76
77void Similarity_Table::update_similarity(double new_similarity_threshold) {
78 similarity_threshold = new_similarity_threshold;
79}
80
81double Similarity_Table::get_similarity(const Path& path1, const Path& path2) {
82 PathId id1 = find_id_path(path1);
83 PathId id2 = find_id_path(path2);
84
85 if (id1 == id2) {
86 return MAXIMUM_SIMILARITY;
87 }
88 normalize_pair(id1, id2);
89 std::pair<PathId, PathId> aux = std::make_pair(id1, id2);
90 auto it = similarity_table.find(aux);
91 if (it != similarity_table.end())
92 return it->second;
93 return MINIMUM_SIMILARITY;
94}
95
97 return paths[id];
98}
99
100bool Similarity_Table::is_above_threshold(double similarity) const {
101 return similarity_threshold <= similarity + EPS_ERROR_MARGIN;
102}
103
105 return similarity_threshold;
106}
107
108bool Similarity_Table::is_similar(const Path& path1, const Path& path2) {
109 double similarity = get_similarity(path1, path2);
110 return is_above_threshold(similarity);
111}
112
113const std::vector<Path>& Similarity_Table::get_path_list() const {
114 return paths;
115}
116
118 int reference_id = find_id_path(reference);
119 std::vector<Path> ret;
120 for (auto [neighbor_id, similarity] : similarity_graph[reference_id]) {
121 if (is_above_threshold(similarity)) {
122 ret.push_back(paths[neighbor_id]);
123 }
124 }
125 return ret;
126}
127
128std::vector<SimilarPair> Similarity_Table::get_all_similar_pairs() {
129 std::vector<SimilarPair> similar_pairs;
130 FunctionLoader loader;
131 for (const auto& [ids, similarity] : similarity_table) {
132 const Path& path1 = paths[ids.first];
133 const Path& path2 = paths[ids.second];
134
135 if (!is_similar(path1, path2))
136 continue;
137
138 auto function = loader.load(path1);
139
140 similar_pairs.push_back({
141 similarity,
142 function.scope_location().size(),
143 ids.first,
144 ids.second,
145 });
146 }
147 return similar_pairs;
148}
149
151 if (paths[a.id1] != paths[b.id1])
152 return paths[a.id1] < paths[b.id1];
153
154 if (paths[a.id2] != paths[b.id2])
155 return paths[a.id2] < paths[b.id2];
156
157 return false;
158}
159
160void Similarity_Table::sort_pairs_by_similarity(std::vector<SimilarPair>& similar_pairs) const {
161 std::sort(
162 similar_pairs.begin(),
163 similar_pairs.end(),
164 [this](const SimilarPair& a, const SimilarPair& b) {
165 if (a.similarity != b.similarity)
166 return a.similarity > b.similarity;
167
168 return compare_paths(a, b);
169 });
170}
171
172void Similarity_Table::sort_pairs_by_line_number(std::vector<SimilarPair>& similar_pairs) const {
173 std::sort(
174 similar_pairs.begin(),
175 similar_pairs.end(),
176 [this](const SimilarPair& a, const SimilarPair& b) {
177 if (a.number_lines != b.number_lines)
178 return a.number_lines > b.number_lines;
179
180 return compare_paths(a, b);
181 });
182}
183
184int Similarity_Table::get_number_lines_in_pair(const Path& path1, const Path& path2) {
185 FunctionLoader loader;
186 auto f1 = loader.load(path1);
187 auto f2 = loader.load(path2);
188 return f1.scope_location().size() + f2.scope_location().size();
189}
190
191std::vector<Cluster> Similarity_Table::get_clusters() {
192 int n = paths.size();
193
194 std::vector<bool> visited(n, false);
195 std::vector<Cluster> clusters;
196
197 for (int i = 0; i < n; i++) {
198 if (visited[i]) continue;
199
200 std::vector<int> stack;
201 std::vector<int> component;
202
203 stack.push_back(i);
204 visited[i] = true;
205
206 while (!stack.empty()) {
207 int current = stack.back();
208 stack.pop_back();
209
210 component.push_back(current);
211
212 for (auto [neighbor, similarity] : similarity_graph[current]) {
213 if (!visited[neighbor] && is_above_threshold(similarity)) {
214 visited[neighbor] = true;
215 stack.push_back(neighbor);
216 }
217 }
218 }
219
220 if (component.size() > 1) {
221 clusters.push_back({component});
222 }
223 }
224
225 return clusters;
226}
227
228std::vector<ClusterInfo> Similarity_Table::get_clusters_info(bool sorted) {
229 auto raw_clusters = get_clusters();
230 std::vector<ClusterInfo> clusters_info;
231
232 for (const auto& cluster : raw_clusters) {
233 ClusterInfo info;
234
235 for (int id : cluster.members) {
236 info.paths.push_back(paths[id]);
237 }
238
239 for (size_t i = 0; i < info.paths.size(); i++) {
240 for (size_t j = i + 1; j < info.paths.size(); j++) {
241 int lines = get_number_lines_in_pair(info.paths[i], info.paths[j]);
242 if (lines > 0) {
243 info.total_pairs++;
244 info.total_lines += lines;
245 }
246 }
247 }
248
249 if (info.total_pairs > 0) {
250 clusters_info.push_back(info);
251 }
252 }
253
254 if (sorted) {
255 std::sort(clusters_info.begin(), clusters_info.end(),
256 [](const ClusterInfo& a, const ClusterInfo& b) {
257 return a.score() > b.score();
258 });
259 }
260
261 return clusters_info;
262}
Function load(const Path &path)
Loads a function from a path.
fs::path build_similarity_path() const
Builds similarity file path.
const FunctionLocation & scope_location() const
Gets the function location inside the source file.
Definition: function.cpp:17
Represents a logical path to a function in the codebase.
Definition: path.hpp:26
static PreprocessRunParams read_current_run_params()
read preprocessing parameters runs
static bool is_cache_compatible(const std::string &cache_version)
void update_similarity(double new_similarity_threshold)
Updates similarity threshold.
std::vector< SimilarPair > get_all_similar_pairs()
Gets all similar path pairs with scores, sorted.
bool compare_paths(const SimilarPair &a, const SimilarPair &b) const
std::vector< ClusterInfo > get_clusters_info(bool sorted)
Returns detailed information about all clusters found in the similarity table.
int get_number_lines_in_pair(const Path &path1, const Path &path2)
const Path & get_path(PathId id) const
void sort_pairs_by_line_number(std::vector< SimilarPair > &similar_pairs) const
Sorts path pairs by line count.
Similarity_Table()
Constructs with default similarity threshold.
void sort_pairs_by_similarity(std::vector< SimilarPair > &similar_pairs) const
Sorts path pairs by similarity.
double get_similarity(const Path &path1, const Path &path2)
Gets similarity between two paths.
std::vector< Cluster > get_clusters()
Generate clusters of similar functions using DFS on the similarity graph.
void normalize_pair(PathId &id1, PathId &id2) const
const std::vector< Path > & get_path_list() const
Gets list of all known paths.
Cache_Compatibility load()
bool is_similar(const Path &path1, const Path &path2)
Checks if two paths are similar.
std::vector< Path > get_similar_path_to_the_reference(const Path &reference)
Gets paths similar to reference path.
double get_threshold_similarity() const
Gets the similarity threshold.
Provides functionality to load Function objects from external sources.
Provides access to function storage locations.
void ensure_file_is_open(const Stream &file, const fs::path &file_name)
Ensures that a file stream is successfully opened.
Definition: utils.cpp:6
Similarity relationships storage and analysis.
std::vector< Path > paths
constexpr uint32_t size() const noexcept
Definition: function.hpp:28
Defines utility functions used across all files.