//
// Created by 423A35C7 on 2023-12-02.
//

#ifndef GRAPH_H
#define GRAPH_H

#include <algorithm>
#include <iostream>
#include <memory>
#include <random>
#include <cstring>
#include <list>
#include <map>

std::default_random_engine engine(time(nullptr));

class MergeFindSet {
    using int_ptr = std::unique_ptr<int>;

public:
    const int length;

    explicit MergeFindSet(const int n)
        : length(n), elements(new int[n]) {
        memset(this->elements.get(), -1, n * sizeof(int));
    }

    int find(const int index) {
        return elements.get()[index] == -1
                   ? index
                   : elements.get()[index] = find(elements.get()[index]);
    }

    void merge(const int a, const int b) {
        elements.get()[find(b)] = find(a);
    }

    friend std::ostream& operator<<(std::ostream&out, const MergeFindSet&merge_find_set);

private:
    int_ptr elements;
};

std::ostream& operator<<(std::ostream&out, const MergeFindSet&merge_find_set) {
    for (int i = 0; i < merge_find_set.length; ++i) {
        out << merge_find_set.elements.get()[i] << "\t";
    }
    return out;
}


/**
 * \brief 打印高维类型
 * \tparam T 最内层的类型
 * \param high_dimension 高维指针，应为 int**, bool*** 等类似的类型
 * \param length 长度（每一维的长度都应一样）
 * \param dimensino_num 维度
 */
template<typename T>
void print(void* high_dimension, const int length, const int dimensino_num = 1) {
    void** temp = static_cast<void **>(high_dimension);
    if (dimensino_num <= 1) {
        for (int i = 0; i < length; ++i) {
            std::cout << static_cast<T *>(high_dimension)[i] << "\t";
        }
        std::cout << std::endl;
        return;
    }
    for (int i = 0; i < length; ++i) {
        print<T>(temp[i], length, dimensino_num - 1);
    }
    std::cout << std::endl;
}

class Graph {
public:
    ~Graph() {
        for (int i = 0; i < this->node_num; ++i) {
            delete this->adjacency[i];
        }
        delete this->adjacency;
        for (int i = 0; i < this->node_num; ++i) {
            delete this->incidence[i];
        }
        delete this->incidence;
    }

    explicit Graph(const int n)
        : merge_find_set_(n) {
        this->node_num = n;
        std::uniform_int_distribution<int> get_random(1, 100);
        this->adjacency = new int *[n];
        this->incidence = new bool *[n];
        for (int i = 0; i < n; ++i) {
            this->adjacency[i] = new int[n];
            this->incidence[i] = new bool[n];
            memset(this->incidence[i], 0, n * sizeof(bool));
            this->adjacency[i][i] = INT_MAX;
            for (int j = 0; j < i; ++j) {
                this->adjacency[i][j] = this->adjacency[j][i] = get_random(engine);
            }
        }
    }

    void print_adjacency() const {
        print<int>(this->adjacency, this->node_num, 2);
    }

    void print_incidence() const {
        print<bool>(this->incidence, this->node_num, 2);
    }

    void print_merge_find_set() {
        std::cout << this->merge_find_set_ << std::endl;
    }

    bool same_league(const int start, const int target) {
        return this->merge_find_set_.find(start) == this->merge_find_set_.find(target);
    }

    /**
     * \brief 把目标城市所在联盟合并到起始城市所在联盟中
     * \param start 起始城市
     * \param target 目标城市
     */
    void merge(const int start, const int target) {
        this->merge_find_set_.merge(start, target);
    }

    /**
     * \brief 开始记录关系矩阵是否被改变
     */
    void start_record_incidence() {
        this->incidence_had_changed = false;
    }

    /**
     * \brief 停止记录并返回关系矩阵是否被改变
     * \return 关系矩阵是否被改变
     */
    bool stop_record_incidence() {
        const bool temp = this->incidence_had_changed;
        this->incidence_had_changed = false;
        return temp;
    }

    friend void one_turn(Graph*);

protected:
    /**
     * \brief 邻接矩阵，表示任意两个城市之间的距离
     */
    int** adjacency; // 邻接矩阵
    /**
     * \brief 城市数量
     */
    int node_num;
    /**
     * \brief 关系矩阵，任意两个城市之间如果有直接的公路则为true（是对称的，反自反的关系）（不一定满足传递性）
     */
    bool** incidence; // 关系矩阵
    bool incidence_had_changed;
    MergeFindSet merge_find_set_;
};


class ComplexGraph : public Graph {
public:
    ~ComplexGraph() {
        // this->~Graph(); // 好像析构函数会自动调用，不需要手动调用
        delete this->alpha;
    }

    explicit ComplexGraph(const int n)
        : Graph(n) {
        this->alpha = new int[n];
        std::uniform_int_distribution<int> get_random(1, n / 2);
        for (int i = 0; i < n; ++i) {
            this->alpha[i] = get_random(engine);
        }
    }

    void print_alpha() {
        // print<int>(this->alpha, this->node_num, 1);
        std::map<int, std::list<int>> index_to_league;
        for (int i = 0; i < this->node_num; ++i) {
            int ancestor = this->merge_find_set_.find(i);
            if (!index_to_league.contains(ancestor)) {
                index_to_league.insert({ancestor, {}});
            }
            index_to_league[ancestor].push_back(i);
        }
        for (const auto&[ancestor, child]: index_to_league) {
            std::cout << "城市联盟  { ";
            for (auto value: child) {
                std::cout << value << " ";
            }
            std::cout << " }  的规模系数为 ";
            std::cout << this->get_alpha(ancestor) << std::endl;
        }
    }

    void set_beta(const int beta) {
        this->beta = beta;
    }

    /**
     * \brief 获取城市所在联盟的规模系数
     * \param index 城市序号
     * \return 城市所在联盟的规模系数
     */
    [[nodiscard]] int get_alpha(const int index) {
        return this->alpha[this->merge_find_set_.find(index)];
    }

    /**
     * \brief 城市所在联盟的规模系数增加1
     * \param index 城市序号
     */
    void increase_alpha(const int index) {
        this->alpha[this->merge_find_set_.find(index)]++;
    }

    friend void one_turn(ComplexGraph*);

private:
    /**
     * \brief 每个城市的规模系数
     */
    int* alpha;
    int beta = 0; // 限定系数
};

#endif //GRAPH_H
