#ifndef _graphparsing_1ec_n5_PAESER_H
#define _graphparsing_1ec_n5_PARSER_H

#include <cstdio>
#include <vector>
#include <stdarg.h>

#include "graphparsing_1ec_n5_depparser.h"
#include "common/token/word.h"
#include "common/token/pos.h"


namespace graphparsing_1ec_n5
{
using namespace std;
const int MAX_DP_FROM = 3;  // dp 最大转移来源数
const int MAX_DP_EDGE = 3;  // dp 最大边加入数

const int TYPE_INT = 0;
const int TYPE_N   = 1;
const int TYPE_L   = 2;
const int TYPE_R   = 3;
const int TYPE_LR  = 4;
const int TYPE_MAX = 4;

//vector< vector<tscore> > s; // 边的分数
static const tscore (*edge_score)[MAX_SENTENCE_SIZE][2];
class StateItem
{
public:
    //int type;
    //int left, right, cross;
    tscore score;
    StateItem *from[MAX_DP_FROM];
    int add_edge[MAX_DP_EDGE][2];

    void Reset()
    {
        score = 0;
        return;
    }
    void Update(bool set, ...)
    {
        va_list p;
        va_start(p,set);
        tscore sum = 0;
        StateItem *f[MAX_DP_FROM]={0};
        int e[MAX_DP_EDGE][2]={0};
        
        for( int i=0; ; i++ )
        {
            f[i] = va_arg( p, StateItem * );
            if( f[i]==NULL )
                break;
            sum += f[i]->score;
            //printf("item: %lld\n", f[i]->score);
        }
        for( int i=0, x,y; ; i++ )
        {
            x = va_arg( p, int );
            y = va_arg( p, int );
            if( x==0 )
                break;
            //printf("edge: %d %d\n", x,y);
            if( edge_score[y][x][0] > edge_score[x][y][0] )
                swap(x,y);
            if( edge_score[x][y][0]>0 )
            {
                e[i][0] = x;
                e[i][1] = y;
                sum += edge_score[x][y][0];
            }
        }

        if( set || sum>score )
        {
            score = sum;
            for( int i=0; i<MAX_DP_FROM; i++ )
                from[i] = f[i];
            for( int i=0; i<MAX_DP_EDGE; i++ )
                for( int k=0; k<2; k++ )
                    add_edge[i][k] = e[i][k];
        }
        return;
    }
};
static int num_point; // 点的个数
static vector< vector< vector< vector<StateItem> > > > dp; // dp 状态存储

static void Init(const tscore edge_score[][MAX_SENTENCE_SIZE][2]) // 建立dp空间，对边的权重预处理，取最大值。
{
    // 建立dp空间
    dp.resize( num_point+TYPE_MAX+1 );
    for( int t=0; t<=num_point+TYPE_MAX; t++ )
    {
        dp[t].resize( num_point+1 );
        for( int l=1; l<=num_point; l++ )
        {
            dp[t][l].resize( num_point+1 );
            for( int r=1; r<=num_point; r++ )
                dp[t][l][r].resize( num_point+1 );
        }
    }
    /*
    // 建立边权空间
    s.resize( num_point+1 );
    s[0].resize( 1 );
    for( int i=1; i<=num_point; i++ )
        s[i].resize( num_point+1 );
    for( int x=1; x<num_point; x++ )
        for( int y=x+1; y<=num_point; y++ )
            if( edge_score[x][y][0] > edge_score[y][x][0] )
                s[x][y] = s[y][x] = edge_score[x][y][0];
            else
                s[x][y] = s[y][x] = edge_score[y][x][0];
    */
    /*
    for( int x=1; x<num_point; x++ )
        for( int y=x+1; y<=num_point; y++ )
            printf("%d %d: %lld\n", x, y, s[x][y]);
     */
    return;
}
static void Clear_All() // 释放所有申请的空间
{
    for( int t=0; t<=TYPE_MAX; t++ )
    {
        for( int l=1; l<=num_point; l++ )
        {
            for( int r=1; r<=num_point; r++ )
                dp[t][l][r].clear();
            dp[t][l].clear();
        }
        dp[t].clear();
    }
    dp.clear();
    /*
    for( int i=0; i<=num_point; i++ )
        s[i].clear();
    s.clear();
    */
    return;
}

static void Dp_LRN( int i, int j, int x );
static tscore Dynamic_Programming() // 对 Int 类型进行动归，并返回整个图的最优值
{
    // 初始化
    for( int i=1; i<num_point; i++ )
    {
        // d_Int[i][i+1] = s[i][i+1];
        dp[TYPE_INT][i][i+1][0].Update( true, NULL, i,i+1, 0,0 );
        for( int x=1; x<=num_point; x++ )
            // d_L[i][i+1][x] = d_R[i][i+1][x] = d_N[i][i+1][x] = d_LR[i][i+1][x] = s[i][i+1];
            for( int t=TYPE_N; t<=TYPE_LR; t++ )
                dp[t][i][i+1][x].Update( true, NULL, i,i+1, 0,0 );
    }
    
    // Int
    for( int dist=2; dist<num_point; dist++ )
        for( int i=1, j; (j=i+dist) <= num_point; i++ )
        {
            // d_Int[i][j] = 0;
            dp[TYPE_INT][i][j][0].Reset();
            for( int k=i+1; k<j; k++ )
            {
                // d_Int[i][j] = max( d_Int[i][j],
                //      d_LR[i][k][j] + d_Int[k][j] + s[i][j] );
                dp[TYPE_INT][i][j][0].Update( false,
                    &dp[TYPE_LR][i][k][j], &dp[TYPE_INT][k][j][0], NULL, i,j, 0,0 );
                for( int l=k+1; l<j; l++ )
                {
                    // d_Int[i][j] = max( d_Int[i][j],
                    //      d_R[i][k][l] + d_Int[k][l] + d_L[l][j][k]
                    //      + s[i][l] + s[k][j] + s[i][j] );
                    dp[TYPE_INT][i][j][0].Update( false,
                        &dp[TYPE_R][i][k][l], &dp[TYPE_INT][k][l][0], &dp[TYPE_L][l][j][k], NULL,
                        i,l, k,j, i,j, 0,0 );
                    // d_Int[i][j] = max( d_Int[i][j],
                    //      d_LR[i][k][l] + d_Int[k][l] + d_Int[l][j]
                    //      + s[i][l] + s[i][j] );
                    dp[TYPE_INT][i][j][0].Update( false,
                        &dp[TYPE_LR][i][k][l], &dp[TYPE_INT][k][l][0], &dp[TYPE_INT][l][j][0], NULL,
                        i,l, i,j, 0,0 );
                }
                for( int l=i+1; l<k; l++ )
                {
                    // d_Int[i][j] = max( d_Int[i][j],
                    //      d_Int[i][l] + d_L[l][k][i] + d_N[k][j][l]
                    //      + s[i][k] + s[l][j] + s[i][j] );
                    dp[TYPE_INT][i][j][0].Update( false,
                        &dp[TYPE_INT][i][l][0], &dp[TYPE_L][l][k][i], &dp[TYPE_N][k][j][l], NULL,
                        i,k, l,j, i,j, 0,0 );
                    // d_Int[i][j] = max( d_Int[i][j],
                    //      d_R[i][l][k] + d_Int[l][k] + d_L[k][j][l]
                    //      + s[i][k] + s[l][j] + s[i][j] );
                    dp[TYPE_INT][i][j][0].Update( false,
                        &dp[TYPE_R][i][l][k], &dp[TYPE_INT][l][k][0], &dp[TYPE_L][k][j][l], NULL,
                        i,k, l,j, i,j, 0,0 );
                }
            }

            for( int x=1; x<i; x++ )
                Dp_LRN( i, j, x );
            for( int x=j+1; x<=num_point; x++ )
                Dp_LRN( i, j, x );

        }
    //    return d_Int[1][num_point];
    return dp[TYPE_INT][1][num_point][0].score;
}
static void Dp_LRN( int i, int j, int x) // 对 Int 之外的类型动归
{
    // N
    // d_N[i][j][x] = d_Int[i][j];
    dp[TYPE_N][i][j][x].Update( true, &dp[TYPE_INT][i][j][0], NULL, 0,0 );
    for( int k=i+1; k<j; k++ )
        // d_N[i][j][x] = max( d_N[i][j][x],
        //      d_N[i][k][x] + d_Int[k][j] + s[x][k] );
        dp[TYPE_N][i][j][x].Update( false,
            &dp[TYPE_N][i][k][x], &dp[TYPE_INT][k][j][0], NULL, x,k, 0,0 );

    // L
    // d_L[i][j][x] = d_Int[i][j];
    dp[TYPE_L][i][j][x].Update( true, &dp[TYPE_INT][i][j][0], NULL, 0,0 );
    for( int k=i+1; k<j; k++ )
    {
        // d_L[i][j][x] = max( d_L[i][j][x],
        //      d_L[i][k][x] + d_N[k][j][i] + s[x][k] + s[i][j] );
        dp[TYPE_L][i][j][x].Update( false,
            &dp[TYPE_L][i][k][x], &dp[TYPE_N][k][j][i], NULL, x,k, i,j, 0,0 );
        // d_L[i][j][x] = max( d_L[i][j][x],
        //      d_Int[i][k] +  d_L[k][j][i] + s[x][k] + s[i][j] );
        dp[TYPE_L][i][j][x].Update( false,
            &dp[TYPE_INT][i][k][0], &dp[TYPE_L][k][j][i], NULL, x,k, i,j, 0,0 );
    }

    // R
    // d_R[i][j][x] = d_Int[i][j];
    dp[TYPE_R][i][j][x].Update( true, &dp[TYPE_INT][i][j][0], NULL, 0,0 );
    for( int k=i+1; k<j; k++ )
    {
        // d_R[i][j][x] = max( d_R[i][j][x],
        //      d_N[i][k][j] + d_R[k][j][x] + s[x][k] + s[i][j] );
        dp[TYPE_R][i][j][x].Update( false,
            &dp[TYPE_N][i][k][j], &dp[TYPE_R][k][j][x], NULL, x,k, i,j, 0,0 );
        // d_R[i][j][x] = max( d_R[i][j][x],
        //      d_R[i][k][j] + d_Int[k][j]  + s[x][k] + s[i][j] );
        dp[TYPE_R][i][j][x].Update( false,
            &dp[TYPE_R][i][k][j], &dp[TYPE_INT][k][j][0], NULL, x,k, i,j, 0,0 );
    }

    // C
    for( int b=j, a=i+1; a<b; a++ )
    {
        // d_C[x][i][a][b] = 0;
        dp[TYPE_MAX+x][i][a][b].Update( true, NULL, 0,0 );
        for( int k=i+2; k<a; k++ )
            // d_C[x][i][a][b] = max( d_C[x][i][a][b],
            //      d_C[x][i][a][k] + d_Int[a][b] + s[k][b] );
            dp[TYPE_MAX+x][i][a][b].Update( false,
                &dp[TYPE_MAX+x][i][a][k], &dp[TYPE_INT][a][b][0], NULL, k,b, 0,0 );
    }
    for( int a=j, b=i+2; b<a; b++ )
    {
        // d_C[x][i][a][b] = 0;
        dp[TYPE_MAX+x][i][a][b].Update( true, NULL, 0,0 );
        for( int k=i+1; k<b; k++ )
        {
            // d_C[x][i][a][b] = max( d_C[x][i][a][b],
            //      d_C[x][i][k][b] + d_Int[b][a] + s[k][a] );
            dp[TYPE_MAX+x][i][a][b].Update( false,
                &dp[TYPE_MAX+x][i][k][b], &dp[TYPE_INT][b][a][0], NULL, k,a, 0,0 );
            // d_C[x][i][a][b] = max( d_C[x][i][a][b],
            //      d_Int[i][k] + d_Int[k][b] + d_Int[b][a]
            //      + s[x][k] + s[i][b] + s[k][a] );
            dp[TYPE_MAX+x][i][a][b].Update( false,
                &dp[TYPE_INT][i][k][0], &dp[TYPE_INT][k][b][0], &dp[TYPE_INT][b][a][0], NULL,
                x,k, i,b, k,a, 0,0 );
        }
    }

    // LR
    // d_LR[i][j][x] = max( d_L[i][j][x], d_R[i][j][x] );
    dp[TYPE_LR][i][j][x].Update( true, &dp[TYPE_L][i][j][x], NULL, 0,0 );
    dp[TYPE_LR][i][j][x].Update( false, &dp[TYPE_R][i][j][x], NULL, 0,0 );
    for( int k=i+1; k<j; k++ )
    {
        // d_LR[i][j][x] = max( d_LR[i][j][x],
        //      d_L[i][k][x] + d_R[k][j][x] + s[x][k] + s[i][j] );
        dp[TYPE_LR][i][j][x].Update( false,
            &dp[TYPE_L][i][k][x], &dp[TYPE_R][k][j][x], NULL, x,k, i,j, 0,0 );
        // d_LR[i][j][x] = max( d_LR[i][j][x],
        //      d_C[x][i][k][j] + s[k][x] + s[i][j] );
        dp[TYPE_LR][i][j][x].Update( false,
            &dp[TYPE_MAX+x][i][k][j], NULL, k,x, i,j, 0,0 );
    }

    /*
    // LR
    // d_LR[i][j][x] = max( d_L[i][j][x], d_R[i][j][x] );
    dp[TYPE_LR][i][j][x].Update( true, &dp[TYPE_L][i][j][x], NULL, 0,0 );
    dp[TYPE_LR][i][j][x].Update( false, &dp[TYPE_R][i][j][x], NULL, 0,0 );
    for( int k=i+1; k<j; k++ )
        // d_LR[i][j][x] = max( d_LR[i][j][x],
        //      d_L[i][k][x] + d_R[k][j][x] + s[x][k] + s[i][j] );
        dp[TYPE_LR][i][j][x].Update( false,
            &dp[TYPE_L][i][k][x], &dp[TYPE_R][k][j][x], NULL, x,k, i,j, 0,0 );
    */
    return;
}

static void Dfs_Arc( StateItem *p, vector< Arc > &r ) // Dfs 遍历所有边
{
    for( int i=0; i<MAX_DP_FROM; i++ )
        if( p->from[i]!=NULL )
            Dfs_Arc( p->from[i], r );
    for( int i=0; i<MAX_DP_EDGE; i++ )
        if( p->add_edge[i][0] )
            r.push_back( BiGram<int>( p->add_edge[i][0], p->add_edge[i][1] ) );
    return;
}

static void parse(int num_point_p, const tscore edge_score_p[][MAX_SENTENCE_SIZE][2], vector< Arc > &result) // 主函数
{
    num_point = num_point_p;
    edge_score = edge_score_p;
    Init( edge_score );
    Dynamic_Programming();
    result.clear();
    Dfs_Arc( &dp[TYPE_INT][1][num_point][0], result );
    Clear_All();
    return;
}

}
#endif
