#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "al_utility.hpp" #include "finite_function.hpp" using namespace std; const CellType CUR_BASE = 4; const int ARGS_COUNT = 2; const string CLASSES_FILENAME = "classes.txt"; FiniteFunction identical_x; FiniteFunction identical_y; template pair, FiniteFunction> get_identicals() { if ( BASE == 3 ) return make_pair( FiniteFunction(string("000 111 222")), FiniteFunction(string("012 012 012")) ); else if ( BASE == 4 ) return make_pair( FiniteFunction(string("0000 1111 2222 3333")), FiniteFunction(string("0123 0123 0123 0123")) ); } struct FunctionTask { bool is_finished; vector> current; }; template class FixedIniter { public: // rules это отображение обязательных значений аргументов, к // значениями функции, которые не должны при них изменяться explicit FixedIniter(map, 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::get_index(arg1, arg2)) = value; _used_indexes.insert(FiniteFunction::get_index(arg1, arg2)); } } int operator() (int first, int second) const { return _cur_values[FiniteFunction::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(BASE) - 1); if ( is_overflow ) _cur_values.at(i) = 0; else return true; } throw "Wrong logic"; } private: set _used_indexes; map, int> _rules; vector _cur_values; }; template void write_function_class(ofstream &f_out, Iterable begin, Iterable end) { for (;begin != end; ++begin) f_out << *begin << " "; f_out << endl; } template void append_classes(const ClassesContainer& classes) { std::ofstream f_out(CLASSES_FILENAME.c_str(), ios_base::app); for (const auto& func_class: classes) { write_function_class(f_out, func_class.begin(), func_class.end()); } f_out.close(); } /* template void get_permutations(vector> &permutations) { permutations.clear(); array 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 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) { auto h_res = h(x,y,z); is_equaled_x = is_equaled_x && (h_res == x); is_equaled_y = is_equaled_y && (h_res == y); is_equaled_z = is_equaled_z && (h_res == z); } if ( !is_equaled_x && !is_equaled_y && !is_equaled_z ) return false; } return true; } template bool is_one_arg_func_fourth(const FourthArgsFiniteFunction &h) { bool is_equaled_x = true; bool is_equaled_y = true; bool is_equaled_z = true; bool is_equaled_w = 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) { for (CellType w = 0; w < CUR_BASE; ++w) { auto h_res = h(x,y,z,w); is_equaled_x = is_equaled_x && (h_res == x); is_equaled_y = is_equaled_y && (h_res == y); is_equaled_z = is_equaled_z && (h_res == z); is_equaled_w = is_equaled_w && (h_res == w); } } if ( !is_equaled_x && !is_equaled_y && !is_equaled_z && !is_equaled_w) return false; } return true; } template 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 bool is_projection_fourth(const FourthArgsFiniteFunction &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,x,y) == h(x,x,y,x) && h(x,x,y,x) == h(x,y,x,x) && h(x,y,x,x) == h(y,x,x,x) && h(y,x,x,x) == x); } return is_projection; } template 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; auto h_res = h(x,y,z); is_equaled_x = is_equaled_x && (h_res == x); is_equaled_y = is_equaled_y && (h_res == y); is_equaled_z = is_equaled_z && (h_res == z); if ( !is_equaled_x && !is_equaled_y && !is_equaled_z ) return false; } return is_equaled_x || is_equaled_y || is_equaled_z; } template bool is_semiprojection_fourth(const FourthArgsFiniteFunction &h) { bool is_equaled_x = true; bool is_equaled_y = true; bool is_equaled_z = true; bool is_equaled_w = 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) { for (CellType w = 0; w < CUR_BASE; ++w) { set s; s.insert(x); s.insert(y); s.insert(z); s.insert(w); if ( s.size() == 4 ) // если все различны, то не рассматриваем continue; auto h_res = h(x,y,z,w); is_equaled_x = is_equaled_x && (h_res == x); is_equaled_y = is_equaled_y && (h_res == y); is_equaled_z = is_equaled_z && (h_res == z); is_equaled_w = is_equaled_w && (h_res == w); if ( !is_equaled_x && !is_equaled_y && !is_equaled_z && !is_equaled_w ) return false; } } return is_equaled_x || is_equaled_y || is_equaled_z || is_equaled_w; } bool is_passed_rosenberg(const FiniteFunction &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; } if ( CUR_BASE == 4 ) { auto g_1 = [f, h_1](const CellType x, const CellType y, CellType z, CellType u) -> CellType { return f( h_1(x,y,z), u ); }; if ( !is_one_arg_func_fourth(g_1) ) { if ( is_projection_fourth(g_1) || is_semiprojection_fourth(g_1) ) return false; } auto g_2 = [f, h_2](const CellType x, const CellType y, CellType z, CellType u) -> CellType { return f( h_2(x,y,z), u ); }; if ( !is_one_arg_func_fourth(g_2) ) { if ( is_projection_fourth(g_2) || is_semiprojection_fourth(g_2) ) return false; } auto g_3 = [f, h_3](const CellType x, const CellType y, CellType z, CellType u) -> CellType { return f( h_3(x,y,z), u ); }; if ( !is_one_arg_func_fourth(g_3) ) { if ( is_projection_fourth(g_3) || is_semiprojection_fourth(g_3) ) return false; } auto g_4 = [f, h_4](const CellType x, const CellType y, CellType z, CellType u) -> CellType { return f( h_4(x,y,z), u ); }; if ( !is_one_arg_func_fourth(g_4) ) { if ( is_projection_fourth(g_4) || is_semiprojection_fourth(g_4) ) return false; } auto g_both = [f](const CellType x, const CellType y, CellType z, CellType u) -> CellType { return f( f(x,y), f(z,u) ); }; if ( !is_one_arg_func_fourth(g_both) ) { if ( is_projection_fourth(g_both) || is_semiprojection_fourth(g_both) ) return false; } } return true; } template vector> get_funcs() { size_t count = 0; map, int> rules; for (int i = 0; i < BASE; ++i) rules[make_pair(i,i)] = i; FixedIniter initer(rules); //vector> permutations; //get_permutations(permutations); vector> funcs; set< FiniteFunction > permutated_funcs; do { ++count; auto cur_func = FiniteFunction(initer); funcs.push_back(cur_func); } while (initer.set_next()); cout << "Total " << count << " functions" << endl; funcs.erase( remove_if( funcs.begin(), funcs.end(), [](const FiniteFunction & 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 set> generate_function_class(FiniteFunction base_function) { auto FiniteFunctionHasher = [](const FiniteFunction &f) -> uint32_t { return f.get_hash(); }; unordered_set< FiniteFunction, 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, 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 f_left_1, f_left_2; //tie(f_left_1, f_left_2) = f_main.apply_to_first(f_applied); FiniteFunction 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>(func_class.begin(), func_class.end()); } // Возвращает новый функциональный класс, размером не сильно больше, чем max_size // Вторым результатом возвращает true, если вычисления закончились успешно // и false, если прервались по достижению max_size template pair>, bool> extend_function_class( const vector>& base_class, size_t max_size ) { if ( base_class.size() >= max_size ) { return make_pair( vector>(base_class), false ); } auto FiniteFunctionHasher = [](const FiniteFunction &f) -> uint32_t { return f.get_hash(); }; unordered_set< FiniteFunction, decltype(FiniteFunctionHasher) > func_class(1024, FiniteFunctionHasher); for (auto&& base_function: base_class) func_class.insert(base_function); bool is_finished = false; size_t last_size = 0; // размер в прошлой итерации while (true) { unordered_set< FiniteFunction, decltype(FiniteFunctionHasher) > new_funcs(1024, FiniteFunctionHasher); for (const auto& f_main: func_class) { new_funcs.insert(f_main.reversed()); for (const auto& f_applied: func_class) { FiniteFunction f_left; f_left = f_main.apply_to_first_partial(f_applied); new_funcs.insert(f_left); } } new_funcs.erase(identical_x); new_funcs.erase(identical_y); if ( new_funcs.size() == last_size ) { is_finished = true; break; } func_class.insert(new_funcs.begin(), new_funcs.end()); last_size = new_funcs.size(); // слишком много насчитали -- выходим if ( func_class.size() > max_size ) { break; } } vector> res(func_class.begin(), func_class.end()); res.shrink_to_fit(); return make_pair( res, is_finished ); } template bool is_bad_class( const Iterable& func_class, const set>& bad_funcs ) { for (auto&& func: func_class) if ( bad_funcs.find(func) != bad_funcs.end() ) return true; return false; } size_t total_possible_functions; atomic completed_tasks; atomic tasks_to_extend; // количество тасков, которые не обработаны list< vector> > shared_function_classes; mutex shared_functions_mutex; deque task_list; mutex task_mutex; vector processed_task_list; mutex processed_task_mutex; set> bad_functions; set> good_functions; atomic current_max_coeff; void do_work() { std::chrono::milliseconds SLEEP_TIME(10); while ( true ) { FunctionTask task; task_mutex.lock(); if ( task_list.begin() != task_list.end() ) { task = task_list.front(); task_list.pop_front(); task_mutex.unlock(); } else { task_mutex.unlock(); if ( completed_tasks < total_possible_functions ) { // Не все таски, подождём, пока добавят ещё std::this_thread::sleep_for(SLEEP_TIME); continue; } else { cout << "thread " << this_thread::get_id() << ": " << " finished" << endl; break; } } tie(task.current, task.is_finished) = extend_function_class( task.current, get_math_coeff(current_max_coeff) ); processed_task_mutex.lock(); processed_task_list.push_back(task); processed_task_mutex.unlock(); --tasks_to_extend; } } void process_task_lists() { cout << "processing starts " << endl; std::chrono::milliseconds SLEEP_TIME(10); vector local_processed_tasks; while ( completed_tasks < total_possible_functions ) { if ( tasks_to_extend ) { // подождём, пока не закончатся таски в очереди std::this_thread::sleep_for(SLEEP_TIME); continue; } task_mutex.lock(); if ( task_list.size() != 0 ) cout << "IMPOSSIBLE task_list.size!!" << endl; task_list.clear(); task_list.shrink_to_fit(); task_mutex.unlock(); // опустошим выполненные таски processed_task_mutex.lock(); for (auto && task: processed_task_list) local_processed_tasks.push_back(task); processed_task_list.clear(); processed_task_list.shrink_to_fit(); processed_task_mutex.unlock(); cout << "sorting finished of "<< local_processed_tasks.size() << endl; size_t total_funcs = 0; for (auto&& task: local_processed_tasks) total_funcs += task.current.size(); cout << "estimated size: " << sizeof(FunctionTask) * total_funcs / 1024 / 1024 << " MB" << endl; // Обеспечим увеличение размеров, чтобы не было проблем со включением одного // в другое sort( local_processed_tasks.begin(), local_processed_tasks.end(), [](const FunctionTask& a, const FunctionTask& b) { return a.current.size() < b.current.size(); } ); // сохраним, чем должен был равняться максимальный размер auto last_math_coeff = get_math_coeff(current_max_coeff); // а для всех новых увеличим его ++current_max_coeff; for ( auto&& task: local_processed_tasks ) { // если в каких-то больше, чем положено, то не трогаем их if ( task.current.size() > last_math_coeff) break; if ( !task.is_finished ) { if ( is_bad_class(task.current, bad_functions) ) { //cout << "bad class" << endl; ++completed_tasks; if ( print_progress(completed_tasks, total_possible_functions) ) { append_classes(shared_function_classes); shared_function_classes.clear(); } } else { task_mutex.lock(); ++tasks_to_extend; task_list.push_back(task); task_mutex.unlock(); } } else { //cout << "task finished, appending" << endl; ++completed_tasks; if ( print_progress(completed_tasks, total_possible_functions) ) { append_classes(shared_function_classes); shared_function_classes.clear(); } sort(task.current.begin(), task.current.end()); auto func_class = task.current; bool is_need_append = true; vector functions_to_remove; is_need_append = !is_bad_class(task.current, bad_functions); for (auto&& f: task.current) if ( good_functions.find(f) == good_functions.end() ) { is_need_append = false; break; } // Делаем плохими ВСЕ функции без учёта того, порждают ли они // минимальный класс. Очень опасно! Должны гарантировать, что // классы меньше быть не могут, потому что мы их всех уже // перебрали // иначе ДОЛЖНО быть в if ( is_need_append ) for (auto&& func: func_class) bad_functions.insert(func); if ( is_need_append ) { shared_function_classes.push_back(func_class); } for (auto&& to_remove: functions_to_remove) { shared_function_classes.erase(to_remove); cout << "Removing class from shared_function_classes" << endl; } } } local_processed_tasks.erase( remove_if( local_processed_tasks.begin(), local_processed_tasks.end(), [last_math_coeff](const FunctionTask& task) { return task.current.size() <= last_math_coeff; }), local_processed_tasks.end() ); local_processed_tasks.shrink_to_fit(); // Поспим, чтобы не работать слишком часто std::this_thread::sleep_for(SLEEP_TIME); } } int main() { tie(identical_x, identical_y) = get_identicals(); cout << "sizeof FiniteFunction<" << (int)CUR_BASE << "> = " << sizeof(FiniteFunction) << endl; cout << "sizeof FunctionTask = "<< sizeof(FunctionTask) << endl; auto FiniteFunctionHasher = [](const FiniteFunction &f) -> uint32_t { return f.get_hash(); }; auto THREADS_COUNT = max(static_cast(thread::hardware_concurrency()), 2); cout << "Using " << THREADS_COUNT << " threads" << endl; auto funcs = get_funcs(); cout << "Removing permutations " << funcs.size() << " functions" << endl; list< set> > function_classes; unordered_set< FiniteFunction, decltype(FiniteFunctionHasher) > allowed_functions(funcs.begin(), funcs.end(), 128, FiniteFunctionHasher); allowed_functions.erase(identical_x); allowed_functions.erase(identical_y); completed_tasks = 0; current_max_coeff = 1; for (auto&& func: allowed_functions) { good_functions.insert(func); FunctionTask task; task.current = {func}; task.is_finished = false; task_list.push_back(task); } cout << "Total funcs in list " << task_list.size() << " functions" << endl; total_possible_functions = task_list.size(); tasks_to_extend = task_list.size(); vector< thread > thread_pool; thread task_processer(process_task_lists); // удалим старый контент std::ofstream f_out("classes.txt"); f_out.close(); for (int i = 0; i < THREADS_COUNT - 1; ++i) thread_pool.push_back(thread(do_work)); for (auto&& t: thread_pool) t.join(); task_processer.join(); cout << "Shared " << shared_function_classes.size() << " functions!" << endl; // перегоняем список с классами в массив с классами vector< vector> > vector_classes( shared_function_classes.begin(), shared_function_classes.end() ); append_classes(vector_classes); return 0; }