#ifndef _graphparsing_1ec_n4_PAESER_H
#define _graphparsing_1ec_n4_PARSER_H

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

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


namespace graphparsing_1ec_n4
{
    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 )
                	continue;
                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( TYPE_MAX+1 );
        for( int t=0; t<=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 );
        }

        // 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 dpparse(int num_point_p, const tscore edge_score_p[][MAX_SENTENCE_SIZE][2], vector< Arc > &result) // 主函数
    {
    	/*for (int i = 1; i <= num_point_p; i++)
			for (int j = 1; j <= num_point_p; j++)
				std::cout << i << ' ' << j << ' ' << edge_score_p[i][j][0] << std::endl;*/

        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
