#pragma once
#include <vector>
#include <random>
#include <map>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <iostream>
#include <fstream>
#include <semaphore.h>
#include <stdio.h>
#include <string>
#include <assert.h>
#include <math.h>

#define MAXLEN (256)
#define MAXBODYLEN (8)
#define INF (1e-20)

typedef double real_t;

static std::default_random_engine _gen;
static std::normal_distribution<real_t> _norm(0, 1);
inline real_t randn()
{
    return _norm(_gen);
}

inline real_t sigmoid(real_t x) { return 1.0 / (1.0 + exp(-x)); }

struct ArgStruct
{
    void *ptr;
    int id;
    
    ArgStruct(void *_ptr, int _id);
};

struct Triplet
{
    int h, r, t;

    friend bool operator < (Triplet u, Triplet v);
    friend bool operator == (Triplet u, Triplet v);
};


struct Variable
{
    real_t value, grad;

    Variable();
};

struct Parameter
{
    Variable var;
    real_t m, v, vm, t;
    real_t record;
    
    Parameter(bool randinit=true);

    void clear();
    void set_init_value(real_t x);
    void update(real_t lr=0.01, real_t weight_decay=0, bool maximize=false);
};

struct Rule
{
    std::vector<int> r_body;
    int head;
    Parameter wt;
    real_t contribution;

    Rule();

    void clear();
    friend bool operator == (const Rule& a, const Rule& b);
    friend bool operator < (const Rule &a, const Rule &b);
};

class Graph
{
public:
    Graph(int _n_vertex, int _n_edge_type);
    ~Graph();
    
    int n_vertex;
    int n_edge_type;
    int offset;
    // linklist[v_from][e] = neibor of v_from via e.
    std::vector<int> **linklist;

    inline int rev(int edge_id)
    {
        return edge_id % 2 ? edge_id - 1 : edge_id + 1;
    }

    void add_edge(int v_from, int v_to, int edge_type);
};

struct RankListEntry
{
    int id;
    double val;
    
    friend bool operator < (RankListEntry u, RankListEntry v);
};

struct Result
{
    double h1, h3, h10, mr, mrr;

    Result();
    Result(double mr_, double mrr_, double h1_, double h3_, double h10_);
};