First working version
This commit is contained in:
566
main.cpp
566
main.cpp
@@ -1,9 +1,575 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <cctype>
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef int8_t CellType;
|
||||
const CellType CUR_BASE = 3;
|
||||
const int ARGS_COUNT = 2;
|
||||
|
||||
// Пока положим, что только два аргумента, чтобы упростить реализацию
|
||||
template <CellType BASE>
|
||||
class FiniteFunction {
|
||||
public:
|
||||
FiniteFunction() {}
|
||||
|
||||
// Устанавливает результат функции, использую другую функцию как
|
||||
// инициализатор
|
||||
template <class Callable>
|
||||
FiniteFunction(Callable initer) : _num(0) {
|
||||
for (CellType first = 0; first < BASE; ++first)
|
||||
for (CellType second = 0; second < BASE; ++second) {
|
||||
auto inited_result = initer(first, second);
|
||||
set_result(first, second, inited_result);
|
||||
}
|
||||
update_num();
|
||||
}
|
||||
|
||||
explicit FiniteFunction(string text_repr) {
|
||||
size_t cur_ind = 0;
|
||||
for (auto ch: text_repr) {
|
||||
if ( !isdigit(ch) )
|
||||
continue;
|
||||
_results.at(cur_ind) = stoi(string(1, ch));
|
||||
++cur_ind;
|
||||
}
|
||||
update_num();
|
||||
}
|
||||
~FiniteFunction() {}
|
||||
|
||||
// Устанавливает результат функции по двум аргументам
|
||||
void set_result(CellType first, CellType second, CellType result) {
|
||||
_results[get_index(first, second)] = result;
|
||||
}
|
||||
|
||||
int operator() (CellType first, CellType second) const {
|
||||
return _results[get_index(first, second)];
|
||||
}
|
||||
|
||||
bool operator < (const FiniteFunction &f) const {
|
||||
return _num < f._num;
|
||||
}
|
||||
|
||||
bool operator == (const FiniteFunction &f) const {
|
||||
return _num == f._num;
|
||||
}
|
||||
|
||||
FiniteFunction reversed() const {
|
||||
// f(x,y) -> f(y,x)
|
||||
FiniteFunction res;
|
||||
for (CellType first = 0; first < BASE; ++first)
|
||||
for (CellType second = 0; second < BASE; ++second) {
|
||||
res.set_result(first, second, (*this)(second, first));
|
||||
}
|
||||
res.update_num();
|
||||
return res;
|
||||
}
|
||||
|
||||
FiniteFunction equaled() const {
|
||||
// f(x,y) -> f(x,x)
|
||||
FiniteFunction res;
|
||||
for (CellType first = 0; first < BASE; ++first)
|
||||
for (CellType second = 0; second < BASE; ++second) {
|
||||
res.set_result(first, second, (*this)(first, first));
|
||||
}
|
||||
res.update_num();
|
||||
return res;
|
||||
}
|
||||
|
||||
FiniteFunction apply_to_first_partial(const FiniteFunction &g) const {
|
||||
// -> this(g(x,y), x)
|
||||
FiniteFunction res;
|
||||
for (CellType first = 0; first < BASE; ++first)
|
||||
for (CellType second = 0; second < BASE; ++second) {
|
||||
res.set_result(first, second, (*this)(g(first, second), first));
|
||||
}
|
||||
res.update_num();
|
||||
return res;
|
||||
}
|
||||
|
||||
tuple<FiniteFunction, FiniteFunction> apply_to_first(const FiniteFunction &g) const {
|
||||
// -> this(g(x,y), x), this(g(x,y), y)
|
||||
FiniteFunction res_1, res_2;
|
||||
for (CellType first = 0; first < BASE; ++first)
|
||||
for (CellType second = 0; second < BASE; ++second) {
|
||||
res_1.set_result(first, second, (*this)(g(first, second), first));
|
||||
res_2.set_result(first, second, (*this)(g(first, second), second));
|
||||
}
|
||||
res_1.update_num();
|
||||
res_2.update_num();
|
||||
return make_tuple(res_1, res_2);
|
||||
}
|
||||
|
||||
FiniteFunction apply_two(const FiniteFunction &g, const FiniteFunction &h) const {
|
||||
FiniteFunction res;
|
||||
for (CellType first = 0; first < BASE; ++first)
|
||||
for (CellType second = 0; second < BASE; ++second) {
|
||||
res.set_result(
|
||||
first,
|
||||
second,
|
||||
(*this)(g(first, second), h(first, second))
|
||||
);
|
||||
}
|
||||
res.update_num();
|
||||
return res;
|
||||
}
|
||||
|
||||
FiniteFunction apply_permutation(array<int, BASE> perm) const {
|
||||
FiniteFunction res;
|
||||
for (CellType first = 0; first < BASE; ++first)
|
||||
for (CellType second = 0; second < BASE; ++second) {
|
||||
res.set_result(
|
||||
first,
|
||||
second,
|
||||
find(
|
||||
perm.begin(),
|
||||
perm.end(),
|
||||
(*this)(perm[first], perm[second])
|
||||
) - perm.begin()
|
||||
);
|
||||
}
|
||||
res.update_num();
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t get_hash() const {
|
||||
return _num;
|
||||
}
|
||||
|
||||
static size_t get_index(CellType first, CellType second) {
|
||||
return first * BASE + second;
|
||||
}
|
||||
template <CellType _BASE>
|
||||
friend ostream& operator << (ostream& os, const FiniteFunction<_BASE> &f);
|
||||
private:
|
||||
void update_num() {
|
||||
_num = 0;
|
||||
for (auto&& val: _results) {
|
||||
_num *= BASE;
|
||||
_num += val;
|
||||
}
|
||||
}
|
||||
uint32_t _num;
|
||||
array<CellType, BASE*BASE> _results;
|
||||
};
|
||||
|
||||
|
||||
template <CellType BASE>
|
||||
ostream& operator << (ostream& os, const FiniteFunction<BASE> &f){
|
||||
for (int first = 0; first < BASE; ++first) {
|
||||
for (int second = 0; second < BASE; ++second) {
|
||||
os << f(first, second);
|
||||
}
|
||||
if ( first != BASE-1 )
|
||||
os << " ";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
template <size_t BASE>
|
||||
class FixedIniter {
|
||||
public:
|
||||
// rules это отображение обязательных значений аргументов, к
|
||||
// значениями функции, которые не должны при них изменяться
|
||||
explicit FixedIniter(map<pair<size_t, size_t>, int> rules) : _rules(rules), _cur_values(BASE*BASE, 0) {
|
||||
for (auto it = rules.begin(); it != rules.end(); ++it) {
|
||||
size_t arg1 = it->first.first;
|
||||
size_t arg2 = it->first.second;
|
||||
int value = it->second;
|
||||
_cur_values.at(FiniteFunction<BASE>::get_index(arg1, arg2)) = value;
|
||||
|
||||
_used_indexes.insert(FiniteFunction<BASE>::get_index(arg1, arg2));
|
||||
}
|
||||
}
|
||||
|
||||
int operator() (int first, int second) const {
|
||||
return _cur_values[FiniteFunction<BASE>::get_index(first, second)];
|
||||
}
|
||||
|
||||
// Возвращает true, пока может построить следующую функцию
|
||||
// если не может это сделать, то возвращает false
|
||||
bool set_next() {
|
||||
bool is_overflow = true;
|
||||
|
||||
for (size_t i = 0; is_overflow; ++i) {
|
||||
if ( i >= _cur_values.size() )
|
||||
return false;
|
||||
if ( _used_indexes.count(i) > 0 )
|
||||
continue;
|
||||
_cur_values.at(i) += 1;
|
||||
bool is_overflow = (_cur_values.at(i) > static_cast<int>(BASE) - 1);
|
||||
if ( is_overflow )
|
||||
_cur_values.at(i) = 0;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
throw "Wrong logic";
|
||||
}
|
||||
private:
|
||||
set<size_t> _used_indexes;
|
||||
map<pair<size_t, size_t>, int> _rules;
|
||||
vector<int> _cur_values;
|
||||
};
|
||||
|
||||
template <class Iterable>
|
||||
void write_function_class(ofstream &f_out, Iterable begin, Iterable end) {
|
||||
for (;begin != end; ++begin)
|
||||
f_out << *begin << " ";
|
||||
f_out << endl;
|
||||
}
|
||||
/*
|
||||
template <int BASE>
|
||||
void get_permutations(vector<array<int, BASE>> &permutations) {
|
||||
permutations.clear();
|
||||
array<int, BASE> cur_perm;
|
||||
for (int i = 0; i < BASE; ++i)
|
||||
cur_perm[i] = i;
|
||||
do {
|
||||
permutations.push_back(cur_perm);
|
||||
} while ( next_permutation(cur_perm.begin(), cur_perm.end()) );
|
||||
}
|
||||
*/
|
||||
|
||||
template<class TripleArgsFiniteFunction>
|
||||
bool is_one_arg_func(const TripleArgsFiniteFunction &h) {
|
||||
bool is_equaled_x = true;
|
||||
bool is_equaled_y = true;
|
||||
bool is_equaled_z = true;
|
||||
for (CellType x = 0; x < CUR_BASE; ++x)
|
||||
for (CellType y = 0; y < CUR_BASE; ++y) {
|
||||
for (CellType z = 0; z < CUR_BASE; ++z) {
|
||||
is_equaled_x = is_equaled_x && (h(x,y,z) == x);
|
||||
is_equaled_y = is_equaled_y && (h(x,y,z) == y);
|
||||
is_equaled_z = is_equaled_z && (h(x,y,z) == z);
|
||||
}
|
||||
if ( !is_equaled_x && !is_equaled_y && !is_equaled_z )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class TripleArgsFiniteFunction>
|
||||
bool is_projection(const TripleArgsFiniteFunction &h) {
|
||||
bool is_projection = true;
|
||||
for (CellType x = 0; x < CUR_BASE; ++x)
|
||||
for (CellType y = 0; y < CUR_BASE; ++y) {
|
||||
is_projection = (is_projection
|
||||
&& h(x,x,y) == h(x,y,x)
|
||||
&& h(x,y,x) == h(y,x,x)
|
||||
&& h(y,x,x) == x);
|
||||
}
|
||||
return is_projection;
|
||||
}
|
||||
|
||||
template<class TripleArgsFiniteFunction>
|
||||
bool is_semiprojection(const TripleArgsFiniteFunction &h) {
|
||||
bool is_equaled_x = true;
|
||||
bool is_equaled_y = true;
|
||||
bool is_equaled_z = true;
|
||||
for (CellType x = 0; x < CUR_BASE; ++x)
|
||||
for (CellType y = 0; y < CUR_BASE; ++y)
|
||||
for (CellType z = 0; z < CUR_BASE; ++z) {
|
||||
// если все различны, то не рассматриваем
|
||||
if ( x != y && x != z && y != z )
|
||||
continue;
|
||||
is_equaled_x = is_equaled_x && (h(x,y,z) == x);
|
||||
is_equaled_y = is_equaled_y && (h(x,y,z) == y);
|
||||
is_equaled_z = is_equaled_z && (h(x,y,z) == z);
|
||||
}
|
||||
return is_equaled_x || is_equaled_y || is_equaled_z;
|
||||
}
|
||||
|
||||
bool is_passed_rosenberg(const FiniteFunction<CUR_BASE> &f) {
|
||||
auto h_1 = [f](const CellType x, const CellType y, CellType z) -> CellType {
|
||||
return f( f(x,y), f(x,z) );
|
||||
};
|
||||
if ( !is_one_arg_func(h_1) ) {
|
||||
if ( is_projection(h_1) || is_semiprojection(h_1) )
|
||||
return false;
|
||||
}
|
||||
|
||||
auto h_2 = [f](const CellType x, const CellType y, CellType z) -> CellType {
|
||||
return f( f(x, y), f(z, x) );
|
||||
};
|
||||
if ( !is_one_arg_func(h_2) ) {
|
||||
if ( is_projection(h_2) || is_semiprojection(h_2) )
|
||||
return false;
|
||||
}
|
||||
|
||||
auto h_3 = [f](const CellType x, const CellType y, CellType z) -> CellType {
|
||||
return f( f(x,y), f(y,z) );
|
||||
};
|
||||
if ( !is_one_arg_func(h_3) ) {
|
||||
if ( is_projection(h_3) || is_semiprojection(h_3) )
|
||||
return false;
|
||||
}
|
||||
|
||||
auto h_4 = [f](const CellType x, const CellType y, CellType z) -> CellType {
|
||||
return f( f(x,y), f(z,y) );
|
||||
};
|
||||
if ( !is_one_arg_func(h_4) ) {
|
||||
if ( is_projection(h_4) || is_semiprojection(h_4) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <CellType BASE>
|
||||
vector<FiniteFunction<BASE>> get_funcs() {
|
||||
size_t count = 0;
|
||||
|
||||
map<pair<size_t, size_t>, int> rules;
|
||||
for (int i = 0; i < BASE; ++i)
|
||||
rules[make_pair<size_t, size_t>(i,i)] = i;
|
||||
FixedIniter<BASE> initer(rules);
|
||||
|
||||
//vector<array<int, BASE>> permutations;
|
||||
//get_permutations<BASE>(permutations);
|
||||
|
||||
vector<FiniteFunction<BASE>> funcs;
|
||||
set< FiniteFunction<BASE> > permutated_funcs;
|
||||
do {
|
||||
++count;
|
||||
auto cur_func = FiniteFunction<BASE>(initer);
|
||||
//if ( permutated_funcs.find(cur_func) != permutated_funcs.end() )
|
||||
// continue;
|
||||
// если закомментить, то 17 классов, вместо 20
|
||||
//for (auto&& permutation: permutations)
|
||||
// permutated_funcs.insert(cur_func.apply_permutation(permutation));
|
||||
funcs.push_back(cur_func);
|
||||
} while (initer.set_next());
|
||||
cout << "Total " << count << " functions" << endl;
|
||||
|
||||
funcs.erase(
|
||||
remove_if(
|
||||
funcs.begin(),
|
||||
funcs.end(),
|
||||
[](const FiniteFunction<BASE> & f) {
|
||||
return !is_passed_rosenberg(f);
|
||||
}
|
||||
),
|
||||
funcs.end()
|
||||
);
|
||||
|
||||
cout << "After Rosenberg " << funcs.size() << " functions" << endl;
|
||||
//if ( permutated_funcs.size() != count )
|
||||
// throw "Permutation's logic error!";
|
||||
return funcs;
|
||||
}
|
||||
|
||||
|
||||
template <CellType BASE>
|
||||
set<FiniteFunction<BASE>> generate_function_class(FiniteFunction<BASE> base_function) {
|
||||
auto FiniteFunctionHasher = [](const FiniteFunction<BASE> &f) -> uint32_t {
|
||||
return f.get_hash();
|
||||
};
|
||||
unordered_set<
|
||||
FiniteFunction<CUR_BASE>,
|
||||
decltype(FiniteFunctionHasher)
|
||||
> func_class(1024, FiniteFunctionHasher);
|
||||
func_class.insert(base_function);
|
||||
//cout << "start with " << base_function << " ";
|
||||
|
||||
size_t last_size = 0; // размер в прошлой итерации
|
||||
while (true) {
|
||||
unordered_set<
|
||||
FiniteFunction<CUR_BASE>,
|
||||
decltype(FiniteFunctionHasher)
|
||||
> new_funcs(1024, FiniteFunctionHasher);
|
||||
|
||||
for (const auto& f_main: func_class) {
|
||||
new_funcs.insert(f_main.reversed());
|
||||
//new_funcs.insert(f_main.equaled());
|
||||
for (const auto& f_applied: func_class) {
|
||||
//FiniteFunction<BASE> f_left_1, f_left_2;
|
||||
//tie(f_left_1, f_left_2) = f_main.apply_to_first(f_applied);
|
||||
FiniteFunction<BASE> f_left;
|
||||
f_left = f_main.apply_to_first_partial(f_applied);
|
||||
new_funcs.insert(f_left);
|
||||
}
|
||||
}
|
||||
if ( new_funcs.size() == last_size ) {
|
||||
break;
|
||||
}
|
||||
|
||||
func_class.insert(new_funcs.begin(), new_funcs.end());
|
||||
last_size = new_funcs.size();
|
||||
}
|
||||
return set<FiniteFunction<BASE>>(func_class.begin(), func_class.end());
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto FiniteFunctionHasher = [](const FiniteFunction<CUR_BASE> &f) -> uint32_t {
|
||||
return f.get_hash();
|
||||
};
|
||||
|
||||
auto funcs = get_funcs<CUR_BASE>();
|
||||
cout << "Removing permutations " << funcs.size() << " functions" << endl;
|
||||
/*vector<array<int, 3>> permutations;
|
||||
get_permutations<3>(permutations);
|
||||
string function_to_str("000 112 212");
|
||||
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[1]) << endl;
|
||||
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[2]) << endl;
|
||||
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[3]) << endl;
|
||||
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[4]) << endl;
|
||||
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[5]) << endl;
|
||||
*/
|
||||
//cout << "022_210_002 -> " << FiniteFunction<3>(string("022 210 002")) << endl;
|
||||
|
||||
//set<FiniteFunction<3>> allowed_functions(funcs.begin(), funcs.end());
|
||||
list< set<FiniteFunction<CUR_BASE>> > function_classes;
|
||||
unordered_set<
|
||||
FiniteFunction<CUR_BASE>,
|
||||
decltype(FiniteFunctionHasher)
|
||||
> allowed_functions(funcs.begin(), funcs.end(), 128, FiniteFunctionHasher);
|
||||
|
||||
FiniteFunction<CUR_BASE> identical_x(string("000 111 222"));
|
||||
FiniteFunction<CUR_BASE> identical_y(string("012 012 012"));
|
||||
|
||||
allowed_functions.erase(identical_x);
|
||||
allowed_functions.erase(identical_y);
|
||||
|
||||
cout << "Total " << allowed_functions.size() << " functions" << endl;
|
||||
std::ofstream f_all_out("all_classes.txt");
|
||||
while ( allowed_functions.size() > 0 ) {
|
||||
//if (allowed_functions.size() % 10 == 0)
|
||||
//cout << 100 - allowed_functions.size() * 100. / funcs.size() << "%" << endl;
|
||||
auto base_function = *allowed_functions.begin();
|
||||
|
||||
allowed_functions.erase(base_function);
|
||||
set< FiniteFunction<CUR_BASE> > func_class = generate_function_class(base_function);
|
||||
func_class.erase(identical_x);
|
||||
func_class.erase(identical_y);
|
||||
//f_all_out << base_function << " -> ";
|
||||
//write_function_class(f_all_out, func_class.begin(), func_class.end());
|
||||
|
||||
bool is_need_append = true;
|
||||
vector<decltype(function_classes)::iterator> functions_to_remove;
|
||||
for (auto it = function_classes.begin(); it != function_classes.end(); ++it) {
|
||||
if ( func_class.size() < it->size() ) {
|
||||
if (includes(
|
||||
it->begin(),
|
||||
it->end(),
|
||||
func_class.begin(),
|
||||
func_class.end()
|
||||
)
|
||||
) {
|
||||
// новый класс функций часть уже существующего
|
||||
functions_to_remove.push_back(it);
|
||||
}
|
||||
} else {
|
||||
if (includes(
|
||||
func_class.begin(),
|
||||
func_class.end(),
|
||||
it->begin(),
|
||||
it->end()
|
||||
)
|
||||
) {
|
||||
// новый класс функций надмножество существующего
|
||||
is_need_append = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( is_need_append )
|
||||
function_classes.push_back(func_class);
|
||||
for (auto&& to_remove: functions_to_remove)
|
||||
function_classes.erase(to_remove);
|
||||
}
|
||||
cout << function_classes.size() << " functions!" << endl;
|
||||
// перегоняем список с классами в массив с классами
|
||||
vector< set<FiniteFunction<CUR_BASE>> > vector_classes(
|
||||
function_classes.begin(),
|
||||
function_classes.end()
|
||||
);
|
||||
/*// фильтруем перестановки
|
||||
vector<array<int, CUR_BASE>> permutations;
|
||||
get_permutations<CUR_BASE>(permutations);
|
||||
map<size_t, vector<vector<set<FiniteFunction<CUR_BASE>>>::iterator> > classes_per_size;
|
||||
for (auto it = vector_classes.begin(); it != vector_classes.end(); ++it) {
|
||||
classes_per_size[it->size()].push_back(it);
|
||||
}
|
||||
|
||||
set<vector<set<FiniteFunction<CUR_BASE>>>::iterator> classes_to_remove;
|
||||
|
||||
|
||||
for (auto&& class_fixed_size: classes_per_size) {
|
||||
typedef pair<FiniteFunction<CUR_BASE>,vector<set<FiniteFunction<CUR_BASE>>>::iterator> sorting_arr_type;
|
||||
cout << class_fixed_size.first << " -> " <<
|
||||
class_fixed_size.second.size() << endl;
|
||||
|
||||
vector< sorting_arr_type > all_class_elements;
|
||||
for (auto &&cur_class_it: class_fixed_size.second) {
|
||||
for (auto&& cur_fn: (*cur_class_it)) {
|
||||
if ( (cur_fn == identical_x) || (cur_fn == identical_y) )
|
||||
continue;
|
||||
for (auto&& permutation: permutations)
|
||||
all_class_elements.push_back(make_pair(
|
||||
cur_fn.apply_permutation(permutation),
|
||||
cur_class_it
|
||||
));
|
||||
}
|
||||
}
|
||||
cout << all_class_elements.size() << endl;
|
||||
// сортируем массив
|
||||
sort(all_class_elements.begin(),all_class_elements.end());
|
||||
for (auto&& el: all_class_elements)
|
||||
cout << el.first << "|";
|
||||
// выбираем, кого удалить
|
||||
for (size_t i = 0; i < all_class_elements.size(); ++i) {
|
||||
decltype(classes_to_remove) cur_vals_to_remove;
|
||||
size_t j;
|
||||
for (
|
||||
j = i;
|
||||
(j < all_class_elements.size()) &&
|
||||
(all_class_elements.at(i).first == all_class_elements.at(j).first);
|
||||
++j
|
||||
) {
|
||||
cout << all_class_elements.at(i).first << "<->" <<
|
||||
all_class_elements.at(j).first << " " <<
|
||||
(all_class_elements.at(i).second == all_class_elements.at(j).second )
|
||||
<<endl;
|
||||
cur_vals_to_remove.insert(all_class_elements.at(j).second);
|
||||
}
|
||||
|
||||
for (
|
||||
auto it = next(cur_vals_to_remove.begin());
|
||||
it != cur_vals_to_remove.end();
|
||||
++it
|
||||
) {
|
||||
classes_to_remove.insert(*it);
|
||||
}
|
||||
|
||||
cout << "size " << cur_vals_to_remove.size() << endl;
|
||||
i = j;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
cout << "classes_to_remove.size() = " <<classes_to_remove.size() << endl;
|
||||
// удаляем
|
||||
{
|
||||
decltype(vector_classes) new_vector_classes;
|
||||
for (auto it = vector_classes.begin(); it != vector_classes.end(); ++it)
|
||||
if ( classes_to_remove.find(it) == classes_to_remove.end() )
|
||||
new_vector_classes.push_back(*it);
|
||||
swap(new_vector_classes, vector_classes);
|
||||
}*/
|
||||
std::ofstream f_out("classes.txt");
|
||||
for (const auto& func_class: vector_classes) {
|
||||
write_function_class(f_out, func_class.begin(), func_class.end());
|
||||
}
|
||||
f_out.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user