//========================================================================
// Copyright(C): RFAI France
//
// CLR Version : 4.0.30319.18052
// NameSpace : Matching
// FileName : Embedding.cs
//
// Created by : JC (jiangchar@gamil.com) at 1/1/2014 10:04:19 PM
//
//========================================================================
#region Using directives

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Graphs;
using System.IO;
using System.Text.RegularExpressions;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using Accord.MachineLearning;

#endregion

namespace Matching
{
    /// <summary>
    /// Cette classe fait l'embedding d'un graphe, l'ordre de fonction d'embedding est comme:
    /// (class) Embedding--GetNodeDegree--GetRessemblance--ExtraitGraph--Graph2Vector
    /// </summary>
    public class FMGEEmbedding : Embedding
    {
        /***************************************************************/
        /*************************Attributs*****************************/
        /***************************************************************/

        #region Attributs

        //-------------------tableaux de bins---------------------------
        protected Dictionary<string, List<double>> DicAnaFMG_TableNumericEdge;
        protected Dictionary<string, List<string>> DicAnaFMG_TableSybmolicEdge;
        protected Dictionary<string, List<double>> DicAnaFMG_TableNumericNode;
        protected Dictionary<string, List<string>> DicAnaFMG_TableSybmolicNode;
        //---------------------------------------------------------------

        public bool isLoaded = false;

        #endregion

        /***************************************************************/
        /*************************Accesseurs****************************/
        /***************************************************************/


        /***************************************************************/
        /************************Constructeurs**************************/
        /***************************************************************/

        #region Constructeurs

        /// <summary>
        /// constructeur
        /// </summary>
        public FMGEEmbedding()
        {
            DicAnaFMG_TableNumericEdge = new Dictionary<string, List<double>>(StringComparer.OrdinalIgnoreCase);
            DicAnaFMG_TableSybmolicEdge = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
            DicAnaFMG_TableNumericNode = new Dictionary<string, List<double>>(StringComparer.OrdinalIgnoreCase);
            DicAnaFMG_TableSybmolicNode = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
        }

        #endregion

        /***************************************************************/
        /*********************Méthodes publics**************************/
        /***************************************************************/

        #region Méthodes publics

        /// <summary>
        /// Transformer un objet de graphe à un objet de vectorFMGE.
        /// Au début, il construit une modèle de vecteur à partir de tableaux dans la phase d'appentissage,
        /// Après grâce à une filtre, on supprime les attributs inutile(parte1 ou partie2 ou partie3),
        /// Pour chaque attribut dans le graphe, s'il y a une place dans le vecteur modèle, on l'ajoute.
        /// Erreur: quand il existe un attribut numerique mais il ne peut pas convertir en un chiffre. throw une exception;
        /// Erreur: s'il existe des attributs qui ne sont pas dans les tableaux de bins.
        /// Entrée:Graphe avec Resemblance et noeud degré
        /// </summary>
        /// <param name="GraphIn">un graphe à convertir à un vecteur</param>
        /// <param name="part1">s'il contient le graph level information</param>
        /// <param name="part2">s'il contient le structure level information</param>
        /// <param name="part3">s'il contient le elementary level information-/param>
        /// <returns>un nouveau vecteurFMGE supprimant les parties designés</returns>
        public override VectorFMGE Graph2Vector(Graph graphIn, bool part1, bool part2, bool part3)
        {
            //pre-treatment
            //Graph GraphWithDegree = GetNodeDegree(graphIn);
            //Graph GraphWithAss = GetRessemblance(GraphWithDegree);

            VectorFMGE VecGraFMG = new VectorFMGE();
            VecGraFMG.Name = graphIn.Id;

            VecGraFMG.FSMFV.Add("graph order", graphIn.ListNodes.Count);
            VecGraFMG.FSMFV.Add("graph size", graphIn.ListEdges.Count);

            //**************************************************************************************************************
            //*****************Construire une modèle de vecteur avec tous les attributs*************************************
            //*****************pour les attributs qu'on ne veut pas garder, on les supprime après***************************
            //**************************************************************************************************************
            foreach (KeyValuePair<string, List<double>> k in DicAnaFMG_TableNumericEdge)
            {
                //pour chaque attribut numerique, il existe forcement 3 intervals
                if (!VecGraFMG.FSMFV.ContainsKey(k.Key + "_1"))
                {
                    string str = k.Key;
                    //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                    //pour une valeur numerique, il y a " interval.
                    VecGraFMG.FSMFV.Add(str + "_1", 0);
                    VecGraFMG.FSMFV.Add(str + "_2", 0);
                    VecGraFMG.FSMFV.Add(str + "_3", 0);
                }
            }
            foreach (KeyValuePair<string, List<string>> k in DicAnaFMG_TableSybmolicEdge)
            {
                foreach (string s in k.Value)
                {
                    string str = k.Key;
                    //pour chaque attribut numerique, il existe forcement 3 intervals
                    if (!VecGraFMG.FSMFV.ContainsKey(str + "_" + s))
                    {
                        //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                        //pour une valeur numerique, il y a " interval.
                        VecGraFMG.FSMFV.Add(str + "_" + s, 0);
                    }
                }

            }
            foreach (KeyValuePair<string, List<double>> k in DicAnaFMG_TableNumericNode)
            {
                string str = k.Key;
                //pour chaque attribut numerique, il existe forcement 3 intervals
                if (!VecGraFMG.FSMFV.ContainsKey(str + "_1"))
                {
                    //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                    //pour une valeur numerique, il y a " interval.
                    VecGraFMG.FSMFV.Add(str + "_1", 0);
                    VecGraFMG.FSMFV.Add(str + "_2", 0);
                    VecGraFMG.FSMFV.Add(str + "_3", 0);
                }
            }
            foreach (KeyValuePair<string, List<string>> k in DicAnaFMG_TableSybmolicNode)
            {
                foreach (string s in k.Value)
                {
                    string str = k.Key;
                    //pour chaque attribut numerique, il existe forcement 3 intervals
                    if (!VecGraFMG.FSMFV.ContainsKey(str + "_" + s))
                    {
                        //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                        //pour une valeur numerique, il y a " interval.
                        VecGraFMG.FSMFV.Add(str + "_" + s, 0);
                    }
                }
            }

            //**************************************************************************************************************
            //*****************Construction de la modèle fini***************************************************************
            //**************************************************************************************************************

            //filtre pour supprimer les attributs qu'on ne veut pas garder
            VecGraFMG.FSMFV = FilterAttribute(VecGraFMG.FSMFV, part1, part2, part3);

            //**************************************************************************************************************
            //**************************************************pour les noeuds*********************************************
            //**************************************************************************************************************
            foreach (Node node in graphIn.ListNodes)
            {
                foreach (AttributeGXL attributeGXL in node.AttributesGXL)
                {
                    //si la valeur d'attribut est numerique
                    if (String.Compare(attributeGXL.Type, "int", true) == 0 ||
                        String.Compare(attributeGXL.Type, "float", true) == 0||
                        String.Compare(attributeGXL.Type, "integer", true) == 0)
                    {
                        double DouGraFMG_Val;
                        try
                        {
                            DouGraFMG_Val = Convert.ToDouble(attributeGXL.Value);
                        }
                        catch(Exception e)
                        {
                            Console.Write(e.Message);
                            throw new Exception("Type of value error. ", e);
                        }
                        //pour chaque attribut numerique, il existe forcement 3 intervals
                        //if (!VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_1"))
                        //{
                        //    //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                        //    //pour une valeur numerique, il y a " interval.
                        //    VecGraFMG.FSMFV.Add(attributeGXL.Name + "_1", 0);
                        //    VecGraFMG.FSMFV.Add(attributeGXL.Name + "_2", 0);
                        //    VecGraFMG.FSMFV.Add(attributeGXL.Name + "_3", 0);
                        //}

                        //si cette valeur n'est pas dans ce tableau, retourner false
                        if (!DicAnaFMG_TableNumericNode.ContainsKey(attributeGXL.Name))
                            throw new Exception("Bins files are not correct");
                        //decider la longeur de pas
                        double DouGraFMG_Pas = (DicAnaFMG_TableNumericNode[attributeGXL.Name][1] - DicAnaFMG_TableNumericNode[attributeGXL.Name][0])/3;
                        if (VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_1"))
                        {
                            //s'il est dans la premiere groupe
                            if (DouGraFMG_Val < DicAnaFMG_TableNumericNode[attributeGXL.Name][0] + DouGraFMG_Pas)
                                VecGraFMG.FSMFV[attributeGXL.Name + "_1"] += 1;
                            //s'il est dans la deuxieme groupe
                            else if (DouGraFMG_Val < (DicAnaFMG_TableNumericNode[attributeGXL.Name][0] + DouGraFMG_Pas * 2))
                                VecGraFMG.FSMFV[attributeGXL.Name + "_2"] += 1;
                            else
                                VecGraFMG.FSMFV[attributeGXL.Name + "_3"] += 1;
                        }
                    }
                    //si la valeur d'attribut est symbolique
                    else
                    {
                        //si cette valeur n'est pas dans ce tableau qui est le resultat de learn phase, retourner null
                        if (!DicAnaFMG_TableSybmolicNode.ContainsKey(attributeGXL.Name))
                            throw new Exception("Bins files are not correct");
                        //if (!VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_" + attributeGXL.Value))
                        //{
                        //    //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                        //    //pour un attribut d'une valeur symbolique, il y a 1 interval.
                        //    for (int i = 0; i < DicAnaFMG_TableSybmolicNode[attributeGXL.Name].Count; i++)
                        //    {
                        //        VecGraFMG.FSMFV.Add(attributeGXL.Name + "_" + DicAnaFMG_TableSybmolicNode[attributeGXL.Name][i], 0);
                        //    }
                        //}
                        if (VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_" + attributeGXL.Value)) 
                            VecGraFMG.FSMFV[attributeGXL.Name + "_" + attributeGXL.Value] += 1;
                    }
                }
            }
            //**************************************************************************************************************
            //**************************************************pour les arcs***********************************************
            //**************************************************************************************************************
            foreach (Edge edge in graphIn.ListEdges)
            {
                foreach (AttributeGXL attributeGXL in edge.AttributesGXL)
                {
                    //si la valeur d'attribut est numerique
                    if (String.Compare(attributeGXL.Type, "int", true) == 0 ||
                        String.Compare(attributeGXL.Type, "float", true) == 0 ||
                        String.Compare(attributeGXL.Type, "integer", true) == 0
                        )
                    {
                        double DouGraFMG_Val;
                        try
                        {
                            DouGraFMG_Val = Convert.ToDouble(attributeGXL.Value);
                        }
                        catch (Exception e)
                        {
                            Console.Write(e.Message);
                            throw new Exception("Type of value error. ", e);
                        }

                        //if (!VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_1"))
                        //{
                        //    //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                        //    //pour une valeur numerique, il y a " interval.
                        //    VecGraFMG.FSMFV.Add(attributeGXL.Name + "_1", 0);
                        //    VecGraFMG.FSMFV.Add(attributeGXL.Name + "_2", 0);
                        //    VecGraFMG.FSMFV.Add(attributeGXL.Name + "_3", 0);
                        //}
                        //si cette valeur n'est pas dans ce tableau, retourner null
                        if (!DicAnaFMG_TableNumericEdge.ContainsKey(attributeGXL.Name))
                            throw new Exception("Bins files are not correct");
                        if (VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_1"))
                        {
                            //decider la longeur de pas
                            double DouGraFMG_Pas = (DicAnaFMG_TableNumericEdge[attributeGXL.Name][1] - DicAnaFMG_TableNumericEdge[attributeGXL.Name][0]) / 3;
                            //s'il est dans la premiere groupe
                            if (DouGraFMG_Val < DicAnaFMG_TableNumericEdge[attributeGXL.Name][0] + DouGraFMG_Pas)
                                VecGraFMG.FSMFV[attributeGXL.Name + "_1"] += 1;
                            //s'il est dans la deuxieme groupe
                            else if (DouGraFMG_Val < (DicAnaFMG_TableNumericEdge[attributeGXL.Name][0] + DouGraFMG_Pas * 2))
                                VecGraFMG.FSMFV[attributeGXL.Name + "_2"] += 1;
                            else
                                VecGraFMG.FSMFV[attributeGXL.Name + "_3"] += 1;
                        }
                    }
                    else
                    {
                        //si cette valeur n'est pas dans ce tableau, retourner null
                        if (!DicAnaFMG_TableSybmolicEdge.ContainsKey(attributeGXL.Name))
                            throw new Exception("Bins files are not correct");
                        //if (!VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_" + attributeGXL.Value))
                        //{
                        //    //pour chaque attribut, on compte le nombre de fois qu'il apprait, au début, c'est zéro.
                        //    //pour un attribut d'une valeur symbolique, il y a 1 interval.
                        //    for (int i = 0; i < DicAnaFMG_TableSybmolicEdge[attributeGXL.Name].Count; i++)
                        //    {
                        //        VecGraFMG.FSMFV.Add(attributeGXL.Name + "_" + DicAnaFMG_TableSybmolicEdge[attributeGXL.Name][i], 0);
                        //    }
                        //}
                        if (VecGraFMG.FSMFV.ContainsKey(attributeGXL.Name + "_" + attributeGXL.Value))
                             VecGraFMG.FSMFV[attributeGXL.Name + "_" + attributeGXL.Value] += 1;
                    }
                }
            }
            return VecGraFMG;
        }
        //-------------------------------fin de Graph2Vector-------------------------------------------------------------------

        /// <summary>
        /// Cette méthode calcule les attributs supplimentaires et les ajoute dans un nouveau graphe.
        /// Ce processus n'est pas obligatoire.
        /// Erreur:quand il existe une type int ou float mais la valeur ne peut pas être converti à une valeur numerique
        /// </summary>
        /// <param name="GrapnIn">Graphe à calculer</param>
        /// <returns>Graphe avec ressemblance </returns>
        public Graph GetRessemblance(Graph GraphIn)
        {
            int numberAttribute = 0;
            Graph GraphOut = new Graph();
            GraphOut = GraphCopy(GraphIn);
            //**************************************************************************************************************
            //**************************************************pour les arcs***********************************************
            //**************************************************************************************************************
            //pour chaque arc, on prend en compte les attributs de noeud source et les attributs de noeud destination, s'il existe
            //des attributs similaire, on ajoute un attrubut <resemblance>
            foreach (Edge edge in GraphIn.ListEdges)
            {
                Dictionary<KeyValuePair<string, string>, string> DicStrStrStrGetFMG =
                    new Dictionary<KeyValuePair<string, string>, string>();
                numberAttribute = 0;
                foreach (AttributeGXL attributeGXL in edge.NodeSource.AttributesGXL)
                {
                    numberAttribute += 1;
                    DicStrStrStrGetFMG.Add(
                        new KeyValuePair<string, string>(attributeGXL.Name, attributeGXL.Type), attributeGXL.Value);
                }

                numberAttribute = 0;
                foreach (AttributeGXL attributeGXL in edge.NodeTarget.AttributesGXL)
                {
                    numberAttribute += 1;
                    //s'il contient un même attribut avec le noeud 1, calcule min(s1, s2)/max(s1, s2) et l'ajoute sur l'arc
                    KeyValuePair<string, string> KeyGetFMG =
                        new KeyValuePair<string, string>(attributeGXL.Name, attributeGXL.Type);
                    if (DicStrStrStrGetFMG.ContainsKey(KeyGetFMG))
                    {
                        AttributeGXL AttGetFMG = new AttributeGXL();

                        //-----------------------------------numerique valeur---------------------------------------------
                        if (String.Compare(attributeGXL.Type, "int", true) == 0 ||
                            String.Compare(attributeGXL.Type, "float", true) == 0 ||
                            String.Compare(attributeGXL.Type, "Integer", true) == 0 ||
                            String.Compare(attributeGXL.Type, "double", true) == 0)
                        {
                            double IntGetFMG_JudgeType;
                            try
                            {
                                IntGetFMG_JudgeType = Convert.ToDouble(attributeGXL.Value);
                            }
                            catch (Exception e)
                            {
                                Console.Write(e.Message);
                                throw new Exception("The type of value is wrong. \n", e);
                            }
                            // chercher la valeur dans le noeud 1
                            string StrGetFMG = DicStrStrStrGetFMG[KeyGetFMG];
                            AttGetFMG.Name = "Resemblance_" + attributeGXL.Name;
                            AttGetFMG.Type = "float";
                            double DouGetFMG;
                            //s'il n'est pas divisé par 0
                            if (Math.Max(Math.Abs(Convert.ToDouble(attributeGXL.Value)),
                                Math.Abs(Convert.ToDouble(StrGetFMG))) != 0.0)
                            {
                                DouGetFMG = Math.Min(Math.Abs(Convert.ToDouble(attributeGXL.Value)),
                                    Math.Abs(Convert.ToDouble(StrGetFMG))) /
                                    Math.Max(Math.Abs(Convert.ToDouble(attributeGXL.Value)),
                                    Math.Abs(Convert.ToDouble(StrGetFMG)));
                            }
                            //s'il est divisé par 0
                            else
                            {
                                DouGetFMG = 0.0;
                            }
                            AttGetFMG.Value = DouGetFMG.ToString();
                        }
                        //-----------------------------------symbolique valeur-------------------------------------------
                        else
                        {
                            AttGetFMG.Name = "Resemblance_" + attributeGXL.Name;
                            AttGetFMG.Type = "float";
                            if (attributeGXL.Value.Equals(DicStrStrStrGetFMG[KeyGetFMG]))
                                AttGetFMG.Value = "1";
                            else
                                AttGetFMG.Value = "0";
                        }
                        Edge EdgeInGraphOut = GraphOut.findEdge(edge.Id);
                        EdgeInGraphOut.AttributesGXL.Add(AttGetFMG);
                    }
                }
            }
            //**************************************************************************************************************
            //**************************************************pour les noeuds*********************************************
            //**************************************************************************************************************
            //pour chaque noeud, on prend en compte les attributs de tous les arcs sortie, s'il existe
            //des attributs similaire, on ajoute un attrubut <resemblance>, s'il y a un noeud connecté plusieurs arcs,
            //pour chaque paire, on calcule une resemblance et calcule ensuite le moyen de ces resemblances.
            foreach (Node node in GraphIn.ListNodes)
            {
                List<List<AttributeGXL>> LisLisAttGetFMG_Table = new List<List<AttributeGXL>>();
                //pour ajouter les attribut de resemblance, il faut au moins 2 arcs
                if (node.ListEdgesOut.Count > 1)
                {
                    //pour un graphe non orienté, tous les arcs sont dans la listOut.
                    for (int edge1 = 0; edge1 <= node.ListEdgesOut.Count - 2; edge1++)
                    {
                        for (int edge2 = edge1 + 1; edge2 <= node.ListEdgesOut.Count - 1; edge2++)
                        {
                            Dictionary<KeyValuePair<string, string>, string> DicStrStrStrGetFMG =
                                new Dictionary<KeyValuePair<string, string>, string>();
                            //stocker les attributs pour chaque pair d'arc
                            List<AttributeGXL> LisAttGetFMG_Cell = new List<AttributeGXL>();
                            //pour tous les attributs d'arc 1
                            foreach (AttributeGXL attributeGXL in node.ListEdgesOut[edge1].AttributesGXL)
                            {
                                if (attributeGXL.Name.IndexOf("Resemblance_") != 0)
                                    DicStrStrStrGetFMG.Add(
                                        new KeyValuePair<string, string>(attributeGXL.Name, attributeGXL.Type),
                                                                         attributeGXL.Value);
                            }
                            foreach (AttributeGXL attributeGXL in node.ListEdgesOut[edge2].AttributesGXL)
                            {
                                //s'il contient un même attribut avec le noeud 1, calcule min(s1, s2)/max(s1, s2) et l'ajoute sur l'arc
                                KeyValuePair<string, string> KeyGetFMG = new KeyValuePair<string, string>(attributeGXL.Name,
                                    attributeGXL.Type);
                                if (DicStrStrStrGetFMG.ContainsKey(KeyGetFMG))
                                {
                                    AttributeGXL attributeNode = new AttributeGXL();
                                    //-----------------------------------numerique valeur---------------------------------------------
                                    if (String.Compare(attributeGXL.Type, "int", true) == 0 ||
                                        String.Compare(attributeGXL.Type, "float", true) == 0 ||
                                        String.Compare(attributeGXL.Type, "Integer", true) == 0 ||
                                        String.Compare(attributeGXL.Type, "double", true) == 0
                                        )
                                    {
                                        double IntGetFMG_JudgeType;
                                        try
                                        {
                                            IntGetFMG_JudgeType = Convert.ToDouble(attributeGXL.Value);
                                        }
                                        catch (Exception e)
                                        {
                                            Console.Write(e.Message);
                                            throw new Exception("The type of value is wrong. \n", e);
                                        }
                                        // chercher la valeur dans le noeud 1
                                        string StrGetFMG = DicStrStrStrGetFMG[KeyGetFMG];
                                        attributeNode.Name = "Resemblance_" + attributeGXL.Name;
                                        attributeNode.Type = "float";
                                        double doubleValue;
                                        //s'il n'est pas divisé par 0
                                        if (Math.Max(Math.Abs(Convert.ToDouble(attributeGXL.Value)),
                                            Math.Abs(Convert.ToDouble(StrGetFMG))) != 0.0)
                                        {
                                            doubleValue = Math.Min(Math.Abs(Convert.ToDouble(attributeGXL.Value)),
                                                Math.Abs(Convert.ToDouble(StrGetFMG))) /
                                                Math.Max(Math.Abs(Convert.ToDouble(attributeGXL.Value)),
                                                Math.Abs(Convert.ToDouble(StrGetFMG)));
                                        }
                                        //s'il est divisé par 0
                                        else
                                        {
                                            doubleValue = 0.0;
                                        }
                                        attributeNode.Value = doubleValue.ToString();
                                    }
                                    //-----------------------------------symbolique valeur-------------------------------------------
                                    else
                                    {
                                        attributeNode.Name = "Resemblance_" + attributeGXL.Name;
                                        attributeNode.Type = "float";
                                        if (attributeGXL.Value.Equals(DicStrStrStrGetFMG[KeyGetFMG]))
                                            attributeNode.Value = "1";
                                        else
                                            attributeNode.Value = "0";
                                    }
                                    LisAttGetFMG_Cell.Add(attributeNode);
                                }
                            }
                            LisLisAttGetFMG_Table.Add(LisAttGetFMG_Cell);
                        }
                    }

                    //verifier si le nombre d'attribut de noeud source et de noeud destination des arcs sont different, on retourne false
                    //normalement, le nombre d'assemblance pour chaque paire de noeuds sont egale à attributeTable[0].Count
                    foreach (List<AttributeGXL> listAttributeGXL in LisLisAttGetFMG_Table)
                    {
                        if (LisLisAttGetFMG_Table[0].Count != listAttributeGXL.Count)
                            throw new FormatException(
                                "The number of attribute in different edge connected in a same node isn't the same");
                    }
                    int nbAttributeInCell = LisLisAttGetFMG_Table.Count;
                    //Attribut à ajouter
                    //apres parcourir tous les pairs, on calcule le moyen d'attribut
                    for (int int_elementInCell = 0; int_elementInCell <= LisLisAttGetFMG_Table[0].Count - 1; int_elementInCell++)
                    {
                        AttributeGXL AttGetFMG_newAtt = new AttributeGXL();
                        double DouGetFMG = 0.0;
                        //la valeur dans LisLisAttGetFMG_Table[int_nbCell][int_elementInCell].Value est forcement numerique
                        for (int int_nbCell = 0; int_nbCell <= LisLisAttGetFMG_Table.Count - 1; int_nbCell++)
                        {
                            try
                            {
                                DouGetFMG += Convert.ToDouble(LisLisAttGetFMG_Table[int_nbCell][int_elementInCell].Value);
                            }
                            catch
                            {
                                throw new FormatException("Value error");
                            }
                        }
                        AttGetFMG_newAtt.Value = (DouGetFMG / nbAttributeInCell).ToString();
                        AttGetFMG_newAtt.Name = LisLisAttGetFMG_Table[0][int_elementInCell].Name;
                        AttGetFMG_newAtt.Type = LisLisAttGetFMG_Table[0][int_elementInCell].Type;
                        Node NodGetFMG_Out = GraphOut.findNode(node.Id);
                        NodGetFMG_Out.AttributesGXL.Add(AttGetFMG_newAtt);
                    }
                }
            }
            return GraphOut;
        }
        //-------------------------------fin de GetRessemblance----------------------------------------------------------------

        /// <summary>
        /// Cette méthode calcule et ajoute le degré de noeud dans un nouveau graphe(copy).
        /// Ce processus est obligatoire.
        /// </summary>
        /// <param name="GraphIn">Graphe à calculer</param>
        /// <returns>Graphe avec noeud degré</returns>
        public Graph GetNodeDegree(Graph GraphIn)
        {
            Graph GraphOut = new Graph();
            GraphOut = GraphCopy(GraphIn);
            foreach (Node node in GraphOut.ListNodes)
            {
                AttributeGXL AttAddFMG = new AttributeGXL();
                AttAddFMG.Name = "degree";
                AttAddFMG.Type = "int";
                AttAddFMG.Value = node.ListEdgesOut.Count.ToString();
                node.AttributesGXL.Add(AttAddFMG);
            }
            return GraphOut;
        }
        //-------------------------------fin de GetNodeDegree------------------------------------------------------------------

        /// <summary>
        /// Cette méthode crée un nouveau graphe dans le mémoire
        /// </summary>
        /// <param name="GraphIn">le graphe à copier </param>
        /// <returns>nouveau graphe </returns>
        public Graph GraphCopy(Graph GraphIn)
        {
            Graph GraGraFMG_Out = new Graph();
            foreach (Node node in GraphIn.ListNodes)
            {
                Node NodGraFMG_Add = new Node(node.Id);
                foreach (AttributeGXL att in node.AttributesGXL)
                {
                    AttributeGXL newAttributeGXL = new AttributeGXL();
                    newAttributeGXL.Name = att.Name;
                    newAttributeGXL.Type = att.Type;
                    newAttributeGXL.Value = att.Value;
                    NodGraFMG_Add.AttributesGXL.Add(newAttributeGXL);
                }
                GraGraFMG_Out.addNode(NodGraFMG_Add);
            }

            foreach (Edge edge in GraphIn.ListEdges)
            {
                Edge EdgGraFMG_Add = new Edge(edge.Id);
                EdgGraFMG_Add.NodeSource = GraGraFMG_Out.findNode(edge.NodeSource.Id);
                EdgGraFMG_Add.NodeTarget = GraGraFMG_Out.findNode(edge.NodeTarget.Id);
                foreach (AttributeGXL att in edge.AttributesGXL)
                {
                    AttributeGXL newAttributeGXL = new AttributeGXL();
                    newAttributeGXL.Name = att.Name;
                    newAttributeGXL.Type = att.Type;
                    newAttributeGXL.Value = att.Value;
                    EdgGraFMG_Add.AttributesGXL.Add(newAttributeGXL);
                }
                GraGraFMG_Out.addEdge(EdgGraFMG_Add);
            }

            foreach (Node node in GraphIn.ListNodes)
            {
                foreach (Edge edge in node.ListEdgesIn)
                {
                    Node NodGraFMG_InGraphOut = GraGraFMG_Out.findNode(node.Id);
                    NodGraFMG_InGraphOut.ListEdgesIn.Add(GraGraFMG_Out.findEdge(edge.Id));
                }
                foreach (Edge edge in node.ListEdgesOut)
                {
                    Node NodGraFMG_InGraphOut = GraGraFMG_Out.findNode(node.Id);
                    NodGraFMG_InGraphOut.ListEdgesIn.Add(GraGraFMG_Out.findEdge(edge.Id));
                }
            }

            foreach (Node node in GraGraFMG_Out.ListNodes)
            {
                GenericLabel labelNode = new GenericLabel();
                Type typeNode = labelNode.GetType();
                object obj = Activator.CreateInstance(typeNode);
                node.Label = (Label)obj;
                node.Label.Id = node.Id;
                node.Label.fromAttributes(node.AttributesGXL);
            }

            foreach (Edge edge in GraGraFMG_Out.ListEdges)
            {
                GenericLabel labelEdge = new GenericLabel();
                Type typeEdge = labelEdge.GetType();
                object obj = Activator.CreateInstance(typeEdge);
                edge.Label = (Label)obj;
                edge.Label.Id = edge.Id;
                edge.Label.fromAttributes(edge.AttributesGXL);
            }
            return GraGraFMG_Out;
        }
        //-------------------------------fin de GraphCopy----------------------------------------------------------------------

        #region off-line learn phase

        /// <summary>
        /// Cette fonction va parcourir tous les graphes
        /// pour les valeurs numeriques dans listAllClique, chercher un interval et calcule l'interval que chaque clique se situe.
        /// Entrée: graphe avec ressemblance et noeud degré
        /// </summary>
        /// <param name="listClique">une liste de graphe qu'on veut analyser</param>
        public void LearnBins(List<Graph> listClique)
        {
            DicAnaFMG_TableNumericNode = new Dictionary<string, List<double>>(StringComparer.OrdinalIgnoreCase);
            DicAnaFMG_TableSybmolicNode = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
            DicAnaFMG_TableNumericEdge = new Dictionary<string, List<double>>(StringComparer.OrdinalIgnoreCase);
            DicAnaFMG_TableSybmolicEdge = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
            //AnalyseNodeAttribute doit être appellé après la méthode ExtraitClique

            foreach (Graph clique in listClique)
            {
                //Graph GraphWithDegree = GetNodeDegree(clique);
                //Graph GraphWithAss = GetRessemblance(GraphWithDegree);
                //**************************************************************************************************************
                //**************************************************pour les noeuds*********************************************
                //**************************************************************************************************************
                foreach (Node node in clique.ListNodes)
                {
                    foreach (AttributeGXL attributeGXL in node.AttributesGXL)
                    {
                        // verifier s'il est numerique ou symbolique
                        if (String.Compare(attributeGXL.Type, "int", true) == 0 ||
                            String.Compare(attributeGXL.Type, "float", true) == 0 ||
                            String.Compare(attributeGXL.Type, "Integer", true) == 0 ||
                            String.Compare(attributeGXL.Type, "double", true) == 0)
                        {
                            double IntGetFMG_Value;
                            try
                            {
                                IntGetFMG_Value = Convert.ToDouble(attributeGXL.Value);
                            }
                            catch (Exception e)
                            {
                                Console.Write(e.Message);
                                throw new Exception("Type of value error. ", e);
                            }
                            //si l'attribut est déja existé
                            if (DicAnaFMG_TableNumericNode.ContainsKey(attributeGXL.Name))
                            {
                                if (IntGetFMG_Value < DicAnaFMG_TableNumericNode[attributeGXL.Name][0])
                                {
                                    DicAnaFMG_TableNumericNode[attributeGXL.Name][0] = IntGetFMG_Value;
                                }
                                if (IntGetFMG_Value > DicAnaFMG_TableNumericNode[attributeGXL.Name][1])
                                    DicAnaFMG_TableNumericNode[attributeGXL.Name][1] = IntGetFMG_Value;
                            }
                            else
                            {
                                List<double> LisAnaFMG_New = new List<double>();
                                //stocker la valeur courant comme la valeur minimal et maximal
                                LisAnaFMG_New.Add(IntGetFMG_Value);
                                LisAnaFMG_New.Add(IntGetFMG_Value);
                                DicAnaFMG_TableNumericNode.Add(attributeGXL.Name, LisAnaFMG_New);
                            }
                        }
                        //s'il est symbolique
                        else
                        {
                            string IntGetFMG_Value = attributeGXL.Value;
                            //si l'attribut est déja existé
                            if (DicAnaFMG_TableSybmolicNode.ContainsKey(attributeGXL.Name))
                            {
                                //si la valeur n'existe pas
                                if (DicAnaFMG_TableSybmolicNode[attributeGXL.Name].IndexOf(attributeGXL.Value) == -1)
                                {
                                    DicAnaFMG_TableSybmolicNode[attributeGXL.Name].Add(attributeGXL.Value);
                                }
                            }
                            else
                            {
                                DicAnaFMG_TableSybmolicNode.Add(attributeGXL.Name, new List<string>() { attributeGXL.Value });
                            }
                        }
                    }
                }
                //**************************************************************************************************************
                //**************************************************pour les arcs***********************************************
                //**************************************************************************************************************
                foreach (Edge edge in clique.ListEdges)
                {
                    foreach (AttributeGXL attributeGXL in edge.AttributesGXL)
                    {
                        // verifier s'il est numerique ou symbolique
                        if (String.Compare(attributeGXL.Type, "int", true) == 0 ||
                            String.Compare(attributeGXL.Type, "float", true) == 0 ||
                            String.Compare(attributeGXL.Type, "Integer", true) == 0 ||
                            String.Compare(attributeGXL.Type, "double", true) == 0)
                        {
                            double IntGetFMG_Value;
                            try
                            {
                                IntGetFMG_Value = Convert.ToDouble(attributeGXL.Value);
                            }
                            catch (Exception e)
                            {
                                Console.Write(e.Message);
                                throw new Exception("Type of value error. ", e);
                            }

                            //si l'attribut est déja existé
                            if (DicAnaFMG_TableNumericEdge.ContainsKey(attributeGXL.Name))
                            {
                                if (IntGetFMG_Value < DicAnaFMG_TableNumericEdge[attributeGXL.Name][0])
                                    DicAnaFMG_TableNumericEdge[attributeGXL.Name][0] = IntGetFMG_Value;
                                if (IntGetFMG_Value > DicAnaFMG_TableNumericEdge[attributeGXL.Name][1])
                                    DicAnaFMG_TableNumericEdge[attributeGXL.Name][1] = IntGetFMG_Value;
                            }
                            else
                            {
                                List<double> LisAnaFMG_New = new List<double>();
                                //stocker la valeur courant comme la valeur minimal et maximal
                                LisAnaFMG_New.Add(IntGetFMG_Value);
                                LisAnaFMG_New.Add(IntGetFMG_Value);
                                DicAnaFMG_TableNumericEdge.Add(attributeGXL.Name, LisAnaFMG_New);
                            }
                        }
                        //s'il est symbolique
                        else
                        {
                            string IntGetFMG_Value = attributeGXL.Value;
                            //si l'attribut est déja existé
                            if (DicAnaFMG_TableSybmolicEdge.ContainsKey(attributeGXL.Name))
                            {
                                //si la valeur n'existe pas
                                if (DicAnaFMG_TableSybmolicEdge[attributeGXL.Name].IndexOf(attributeGXL.Value) == -1)
                                {
                                    DicAnaFMG_TableSybmolicEdge[attributeGXL.Name].Add(attributeGXL.Value);
                                }
                            }
                            else
                            {
                                DicAnaFMG_TableSybmolicEdge.Add(attributeGXL.Name, new List<string>() { attributeGXL.Value });
                            }
                        }
                    }
                }
            }
            isLoaded = true;
        }
        //-------------------------------fin de LoadBins-----------------------------------------------------------------------

        /// <summary>
        /// stocker les infos de bins dans les fichiers en forme binaire.
        /// </summary>
        public void SaveBins(String dic)
        {
            FileStream fs = new FileStream(dic + "\\NumericNodebBins.dat", FileMode.Create);
            BinaryFormatter formatter = new BinaryFormatter();
            try
            {
                formatter.Serialize(fs, DicAnaFMG_TableNumericNode);
                fs.Close();
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            fs = new FileStream(dic + "\\SybmolicNodebBins.dat", FileMode.Create);
            try
            {
                formatter.Serialize(fs, DicAnaFMG_TableSybmolicNode);
                fs.Close();
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            fs = new FileStream(dic + "\\NumericEdgeBins.dat", FileMode.Create);
            try
            {
                formatter.Serialize(fs, DicAnaFMG_TableNumericEdge);
                fs.Close();
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            fs = new FileStream(dic + "\\SybmolicEdgebBins.dat", FileMode.Create);
            try
            {
                formatter.Serialize(fs, DicAnaFMG_TableSybmolicEdge);
                fs.Close();
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                fs.Close();
            }
        }
        //-------------------------------fin de SaveBins-----------------------------------------------------------------------
        
        /// <summary>
        /// lire les fichiers binaires qui stockent les tableaux d'apprentissage.
        /// </summary>
        public void LoadBins(String dic)
        {
            DicAnaFMG_TableNumericEdge = null;
            DicAnaFMG_TableSybmolicEdge = null;
            DicAnaFMG_TableNumericNode = null;
            DicAnaFMG_TableSybmolicNode = null;
            FileStream fs;
            try
            {
                fs = new FileStream(dic + "\\NumericNodebBins.dat", FileMode.Open);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw new Exception("Cannot find NumericNodebBins.dat, verify this directionary", e);
            }
            try
            {
                BinaryFormatter formatter = new BinaryFormatter();

                // Deserialize the hashtable from the file and 
                // assign the reference to the local variable.
                DicAnaFMG_TableNumericNode = (Dictionary<String, List<double>>)formatter.Deserialize(fs);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }

            fs.Close();

            try
            {
                fs = new FileStream(dic + "\\SybmolicNodebBins.dat", FileMode.Open);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw new Exception("Cannot find SybmolicNodebBins.dat, verify this directionary", e);
            }
            try
            {
                BinaryFormatter formatter = new BinaryFormatter();

                // Deserialize the hashtable from the file and 
                // assign the reference to the local variable.
                DicAnaFMG_TableSybmolicNode = (Dictionary<String, List<String>>)formatter.Deserialize(fs);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }

            fs.Close();

            try
            {
                fs = new FileStream(dic + "\\NumericEdgeBins.dat", FileMode.Open);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw new Exception("Cannot find NumericEdgeBins.dat, verify this directionary", e);
            }
            try
            {
                BinaryFormatter formatter = new BinaryFormatter();

                // Deserialize the hashtable from the file and 
                // assign the reference to the local variable.
                DicAnaFMG_TableNumericEdge = (Dictionary<String, List<double>>)formatter.Deserialize(fs);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }

            fs.Close();

            try
            {
                fs = new FileStream(dic + "\\SybmolicEdgebBins.dat", FileMode.Open);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw new Exception("Cannot find SybmolicEdgebBins.dat, verify this directionary", e);
            }
            try
            {
                BinaryFormatter formatter = new BinaryFormatter();

                // Deserialize the hashtable from the file and 
                // assign the reference to the local variable.
                DicAnaFMG_TableSybmolicEdge = (Dictionary<String, List<String>>)formatter.Deserialize(fs);
            }
            catch (SerializationException e)
            {
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
                throw;
            }
            finally
            {
                fs.Close();
            }
            isLoaded = true;
        }
        //-------------------------------fin de LoadBins-----------------------------------------------------------------------

        /// <summary>
        /// Cette méthode va calculer le taux d'erreur et le taux de reconnaissance et verifier la qualité de notre méthode 
        /// de clustering et classification.
        /// </summary>
        /// <param name="resultValidation">L'ensemble de label de l'ensemble de validation</param>
        /// <param name="resultTest">L'ensemble de label de l'ensemble de test</param>
        /// <param name="validation">L'ensemble de vecteur de l'ensemble de validation</param>
        /// <param name="test">L'ensemble de vecteur de l'ensemble de test</param>
        /// <param name="k">Nombre de classe</param>
        /// <param name="resultValidation_records"></param>
        /// <param name="resultTest_records"></param>
        /// <returns></returns>
        public double TestFGME(List<String> resultValidation, List<String> resultTest,
            List<VectorFMGE> validation, List<VectorFMGE> test, int k, int resultValidation_records, int resultTest_records)
        {
            List<int> res = new List<int>();
            //dictionary<numero de groupe, nombre d'element>
            var Group_number_v = (from ec in resultValidation
                                group ec by ec into Gro
                                select new { key = Gro.Key, num = Gro.Count() }).ToDictionary(q => q.key, q => q.num);
            var Group_number_t = (from ec in resultTest
                                group ec by ec into Gro
                                select new { key = Gro.Key, num = Gro.Count() }).ToDictionary(q => q.key, q => q.num);
            //-----------------------convertir en tableau-----------------------------------------
            List<double[]> LisTesFGM_train = new List<double[]>();

            foreach (VectorFMGE v in validation)
            {
                LisTesFGM_train.Add(v.GetMatrix());
            }
            double[][] LisTesFGM_waitForValid = LisTesFGM_train.ToArray();

            List<double[]> LisTesFGM_test = new List<double[]>();

            foreach (VectorFMGE v in test)
            {
                LisTesFGM_test.Add(v.GetMatrix());
            }
            double[][] LisTesFGM_waitForTest = LisTesFGM_test.ToArray();

            // Create a new K-Means algorithm with 3 clusters 
            KMeans kmeans = new KMeans(k);

            int[] group;
            double minPara = 0.01;
            try
            {
                double error = 1.0;
                double distance = 0.0;
                double minValue = 1.0;
                // Compute the algorithm, retrieving an integer array 
                //  containing the labels for each of the observations 
                Random rnd = new Random();
                int i = 10000;
                //pour trouver la meilleur threshold et minimiser l'erreur, on calcule 10000 fois de la phase de validation.
                while (error > 0.2 && i > 0)
                {
                    i--;
                    double parameter;
                    if (distance == 0)
                    {
                        parameter = rnd.NextDouble();
                        kmeans = new KMeans(k);
                        group = kmeans.Compute(LisTesFGM_waitForValid, parameter, out distance);
                    }
                    else
                    {
                        parameter = rnd.NextDouble() * distance;
                        kmeans = new KMeans(k);
                        group = kmeans.Compute(LisTesFGM_waitForValid, parameter);
                    }
                    error = GetError(Group_number_v, group.ToList());
                    if (minValue > error)
                    {
                        minValue = error;
                        minPara = parameter;
                    }

                }
            }
            catch (Exception e)
            {
                Console.Write(e.Message);
                throw new Exception("clustering error, verify your graph base is comparable. \n", e);
            }
            //On calcule l'erreur d'ensemble de test avec la meilleur threshold
            group = kmeans.Compute(LisTesFGM_waitForValid, minPara);
            // In order to classify new, unobserved instances, you can 
            // use the kmeans.Clusters.Nearest method, as shown below: 
            foreach (double[] d in LisTesFGM_waitForTest)
            {
                res.Add(kmeans.Clusters.Nearest(d));
            }

            double errorTest = GetError(Group_number_t, res);

            return errorTest;
        }
        //-------------------------------fin de TestFGME-----------------------------------------------------------------------

        #endregion

        #endregion

        /***************************************************************/
        /*********************Méthodes privées**************************/
        /***************************************************************/

        #region Méthodes privées

        /// <summary>
        /// Calcule le taux d'erreur de reconnaissance
        /// </summary>
        /// <param name="verf">L'ensemble de label et le nom du graphe</param>
        /// <param name="res">L'ensemble de label à comparer</param>
        /// <returns></returns>
        private double GetError(Dictionary<String, int> verf, List<int> res)
        {
            double error = 0.0;
            int bas = 0;
            foreach (KeyValuePair<String, int> str in verf)
            {
                List<int> tmp = new List<int>();
                for (int j = 0; j < str.Value; j++)
                {
                    tmp.Add(res[bas + j]);

                }

                bas += str.Value;

                var result = from ex in tmp
                             group ex by ex into gro
                             orderby gro.Count() descending
                             select gro.First();

                var diff = from ex in tmp
                           where ex != result.First()
                           select ex;

                error += diff.Count();
            }
            return error / res.Count;
        }
        //-------------------------------fin de GetError-----------------------------------------------------------------------

        #endregion

        /***************************************************************/
        /*********************Méthodes protégée*************************/
        /***************************************************************/

        #region Méthodes protégée

        /// <summary>
        /// Cette méthode est pour choisir les parties de vecteurs
        /// </summary>
        /// <param name="DicVector">les noms d'attribut avec ses valeurs</param>
        /// <param name="part1">S'il contient le graphe level information</param>
        /// <param name="part2">S'il contient le structure level information</param>
        /// <param name="part3">S'il contient le elementary level information</param>
        /// <returns>les noms d'attribut avec ses valeurs filtrés</returns>
        protected Dictionary<String, double> FilterAttribute(Dictionary<String, double> DicVector,
    bool part1, bool part2, bool part3)
        {
            Dictionary<String, double> DicVectorOut = new Dictionary<String, double>(DicVector);
            if (part1 == false)
            {
                DicVectorOut.Remove("graph order");
                DicVectorOut.Remove("graph size");
            }
            if (part2 == false)
            {
                //rechercher tout les noms d'attributs commencé par Resemblance et les supprimer
                IEnumerable<String> IEnuFilEmb_results = from result in DicVector.Keys
                                                         where Regex.IsMatch(result, "Resemblance.*")
                                                         select result;

                foreach (String str in IEnuFilEmb_results)
                {
                    DicVectorOut.Remove(str);
                }
            }
            if (part3 == false)
            {
                //rechercher tout les noms d'attributs ne commencé pas par Resemblance ,graph order,graph size, node degree
                IEnumerable<String> IEnuFilEmb_results = from result in DicVector.Keys
                                                         where Regex.IsMatch(result, "Resemblance.*") == false &&
                                                         Regex.IsMatch(result, "graph order") == false &&
                                                         Regex.IsMatch(result, "graph size") == false &&
                                                         Regex.IsMatch(result, "node degree") == false
                                                         select result;

                foreach (String str in IEnuFilEmb_results)
                {
                    DicVectorOut.Remove(str);
                }
            }
            return DicVectorOut;
        }
        //-------------------------------fin de FilterAttribute-----------------------------------------------------------------------

        #endregion
    }
}
