/* -*- mode: C++ -*- */
//////////////////////////////////////////////////////////////////////////////
// FILE   : graphtools.tol
// PURPOSE: Defines NameBlock GraphTools
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
NameBlock GraphTools =
//////////////////////////////////////////////////////////////////////////////
[[
//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.Append.Indexed = "Adds and index a new element to a "
"set and returns 0 if it´s all right. If an element with given name exists "
"already then returns its index, else returns 0";
Real Append.Indexed(Set set, Anything newElement, Text name)
//////////////////////////////////////////////////////////////////////////////
{
  Real idx = FindIndexByName(set, name );
  If(idx != 0, 
  {
    WriteLn("[GraphTools::Append.Indexed] Cannot add an existant element "+
            Name(set)+"["<<idx+"] with name "+ name,"E");
    0
  },
  {
    Set aux =  [[ Copy(newElement) ]];
    Anything PutName(name, aux[1]);
    Set Append(set, aux, True);
    idx
  })
};

//////////////////////////////////////////////////////////////////////////////
Class @Feature.Abstract
//////////////////////////////////////////////////////////////////////////////
{
  Static Text _.autodoc.description = "Class for handling with abstract "
  "features of arbitrary entities, numerical or not, that will be stored in "
  "an external matrix with a row for each entity.";
  //The name of the feature
  Text _.name;
  //The number of the feature in the full set of features
  Real _.position;

  ////////////////////////////////////////////////////////////////////////////
  Static Text _.autodoc.member.get = "Returns the stored value corresponding "
  "to this feature related to an individual entity.";
  Anything get(Matrix store, Real entity)
  ////////////////////////////////////////////////////////////////////////////
};

//////////////////////////////////////////////////////////////////////////////
Class @Feature.Numeric : @Feature.Abstract
//////////////////////////////////////////////////////////////////////////////
{
  Static Text _.autodoc.description = "Class for handling with numerical "
  "numerical features of arbitrary entities that will be stored in an "
  "external matrix with a row for each entity.";

  ////////////////////////////////////////////////////////////////////////////
  Static Text _.autodoc.member.New = "Creates a new instance of "
  "Class @Feature.Numeric";
  Static @Feature.Numeric New(Text name, Real position)
  ////////////////////////////////////////////////////////////////////////////
  {
    @Feature.Numeric aux =
    [[
      Text _.name = name;
      Real _.position  = position
    ]]
  };
  
  ////////////////////////////////////////////////////////////////////////////
  Anything get(Matrix store, Real entity)
  ////////////////////////////////////////////////////////////////////////////
  {
    MatDat(store, entity, _.position)
  }
};

//////////////////////////////////////////////////////////////////////////////
Class @Feature.NonNumeric : @Feature.Abstract
//////////////////////////////////////////////////////////////////////////////
{
  Static Text _.autodoc.description = "Class for handling with numerical "
  "numerical features of arbitrary entities that will be stored in an "
  "external matrix with a row for each entity. Here the stored value is the "
  "index of the feature stored in an arbitrary domain.";

  //The set of valid values for this feature indexed by name
  Set _.domain;

  ////////////////////////////////////////////////////////////////////////////
  Static Text _.autodoc.member.New = "Creates a new instance of "
  "Class @Feature.NonNumeric";
  Static @Feature.NonNumeric New(Text name, Real position, Set domain)
  ////////////////////////////////////////////////////////////////////////////
  {
    @Feature.NonNumeric aux =
    [[
      Text _.name = name;
      Real _.position  = position;
      Set _.domain = domain    
    ]];
    Real SetIndexByName(aux::_.domain);
    aux
  }; 

  ////////////////////////////////////////////////////////////////////////////
  Anything get(Matrix store, Real entity)
  ////////////////////////////////////////////////////////////////////////////
  {
    Real idx = MatDat(store, entity, _.position);
    _.domain[idx]
  }

};

//////////////////////////////////////////////////////////////////////////////
Class @FeatureSet
//////////////////////////////////////////////////////////////////////////////
{
  //Stores and index by name a set of instances od @Feature.Abstract
  Set _.feature = Copy(Empty);
  //In each row of this matrix are stored the values or indexes of features
  //related to an individual entity
  Matrix _.data = Rand(0,0,0,0);

  ////////////////////////////////////////////////////////////////////////////
  Static Text _.autodoc.member.New = "Creates a new instance of "
  "Class @FeatureSet";
  Static @FeatureSet New(Real void)
  ////////////////////////////////////////////////////////////////////////////
  {
    @FeatureSet aux
  };

  ////////////////////////////////////////////////////////////////////////////
  Static Text _.autodoc.member.add.feature.numeric = 
  "Adds a new numerical feature";
  Real add.feature.numeric(Text name)
  ////////////////////////////////////////////////////////////////////////////
  {
    GraphTools::Append.Indexed(
      _.feature,
      @Feature.Numeric::New(name, Card(_.feature)+1),
      name)
  };

  ////////////////////////////////////////////////////////////////////////////
  Static Text _.autodoc.member.add.feature.nonnumeric=
  "Adds a new non numerical feature";
  Real add.feature.nonnumeric(Text name, Set domain)
  ////////////////////////////////////////////////////////////////////////////
  {
    GraphTools::Append.Indexed(
      _.feature,
      @Feature.NonNumeric::New(name, Card(_.feature)+1, domain),
      name)
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.feature.index = "Returns the index of a feature or 0 "
  "if it not exists";
  Real feature.index(Text name)
  ////////////////////////////////////////////////////////////////////////////
  {
    FindIndexByName(_.feature, name )
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get = "Returns the value of a feature for an entity";
  Anything get(Real entity, 
               Anything feature) //Real position or Text name
  ////////////////////////////////////////////////////////////////////////////
  {
    @Feature.Abstract f = _.feature[feature];
    f::get(_.data, entity)
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.all = "Returns a set with the values of all "
  "features for an entity";
  Set get.all(Real entity) 
  ////////////////////////////////////////////////////////////////////////////
  { 
    EvalSet(_.feature, Anything get.all.cycle(@FeatureSet f) 
    { 
      f::get(_.data, entity)
    })
  }

};


//////////////////////////////////////////////////////////////////////////////
Class @Graph
//////////////////////////////////////////////////////////////////////////////
{
  Text _.autodoc.description = "Class for handling with generic indexed by "
  "name graphs in TOL. In abstract, a graph is a pair of sets \n"
  "  V : a finite set of vertices \n"
  "  E : a finite set of edges relating an input vertex with an output \n"
  "Each vertex has a set of features related with the system\n"
  "Each edge also has its own set of features.\n";

  //The identifier of the graph
  Text _.name;
  //Indicates if the graph is directed or symmetric
  Real _.directed;
  //Features of vertices. First feature must be the identifier of vertices
  @FeatureSet _.vertex = @FeatureSet::New(?);
  //Number of vertices
  Real _.vertex.num = 0;  
  //Features of edges. First two features must be input and output vertex
  @FeatureSet _.edge = @FeatureSet::New(?);
  //Number of edges
  Real _.edge.num = 0;  
  //Sparse matrix storing index of edge for each cell (out,in) 
  VMatrix _.edge.map = Rand(0,0,0,0);
  VMatrix _.edge.adjacency = Rand(0,0,0,0);
  Real _.edge.check.rep = ?;

  ////////////////////////////////////////////////////////////////////////////
  Static Text _.autodoc.member.New = "Creates a new instance of "
  "Class @Graph";
  Static @Graph New(Text name, Real directed)
  ////////////////////////////////////////////////////////////////////////////
  {
    @Graph aux =
    [[
      Text _.name = name;
      Real _.directed = directed
    ]]
  };

  ////////////////////////////////////////////////////////////////////////////
  Real initialize(Set vertex.names)
  ////////////////////////////////////////////////////////////////////////////
  {
    Real _.vertex.num := Card(vertex.names);
    Set For(1,_.vertex.num, Real(Real k)
    {
      Text PutName(vertex.names[k], vertex.names[k]);
      k
    });
    Real _.vertex::add.feature.nonnumeric("Identifier", vertex.names);
    Matrix _.vertex::_.data := DifEq(1/(1-B),Rand(_.vertex.num,1,1,1));
    Real _.edge::add.feature.numeric("Output");
    Real _.edge::add.feature.numeric("Input");
    True
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.edge.build = "";
  Real edge.build(Matrix data)
  ////////////////////////////////////////////////////////////////////////////
  {
    Real _.edge.num := Rows(data);
    Matrix out_ = SubCol(data,[[1]]);
    Matrix in_  = SubCol(data,[[2]]);
    Matrix out  = If(_.directed, out_, Min(in_, out_));
    Matrix in   = If(_.directed, in_,  Max(in_, out_));
    Matrix _.edge::_.data := out | in | SubCol(data,Range(3,Columns(data),1));
    Matrix idx = DifEq(1/(1-B),Rand(_.edge.num,1,1,1));
    VMatrix triplet = Triplet(out|in|idx,_.vertex.num,_.vertex.num);
    VMatrix _.edge.map := Convert(triplet,"Cholmod.R.Sparse");
    Matrix idx.rep = Rand(_.edge.num,1,1,1);
    VMatrix triplet.rep = Triplet(out|in|idx.rep,_.vertex.num,_.vertex.num);
    VMatrix _.edge.adjacency := Convert(triplet.rep,"Cholmod.R.Sparse");
    Real _.edge.check.rep := VMatMax(_.edge.adjacency)>1;
    If(_.edge.check.rep,
    {
      WriteLn("[GraphTools::@Graph "+_.name+"::edge.build()] "+
              "There are repeated edges","W")
    });
    True
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.vertex.names = "Returns the names of all "
  "vertices";
  Set get.vertex.names(Real void)
  ////////////////////////////////////////////////////////////////////////////
  {
    ((_.vertex::_.feature)[1])::_.domain
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.vertex.name = "Returns the name of one "
  "vertex";
  Text get.vertex.name(Real idx)
  ////////////////////////////////////////////////////////////////////////////
  {
    (((_.vertex::_.feature)[1])::_.domain)[idx]
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.vertex.index = "Returns the index of a vertex or 0 "
  "if it not exists";
  Real get.vertex.index(Text name)
  ////////////////////////////////////////////////////////////////////////////
  {
    FindIndexByName(((_.vertex::_.feature)[1])::_.domain, name )
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.vertex.features = "Returns all the features for "
  "an vertex";
  Set get.vertex.features(Real vertex.idx)
  ////////////////////////////////////////////////////////////////////////////
  {
    _.vertex::get.all(vertex.idx)
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.vertex.feature = "Returns specified feature for "
  "an vertex. Feature identifier can be the name (Text) or the position "
  "(Real)";
  Anything get.vertex.feature(
    Real vertex.idx,
    Anything feature.id) //Text name or Real num
  ////////////////////////////////////////////////////////////////////////////
  {
    _.edge::get(vertex.idx, feature.id)
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.edge.index = "Returns the index of an edge or 0 "
  "if it not exists";
  Real get.edge.index(Real out_, Real in_)
  ////////////////////////////////////////////////////////////////////////////
  {
    Real out  = If(_.directed, out_, Min(in_, out_));
    Real in   = If(_.directed, in_,  Max(in_, out_));
    Real edge.idx = VMatDat(_.edge.map, out, in);
    edge.idx / VMatDat(_.edge.adjacency, out, in) 
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.edge.exists = "Returns true if exists the edge from "
  "out_ to in_";
  Real edge.exists(Real out_, Real in_)
  ////////////////////////////////////////////////////////////////////////////
  {
    get.edge.index(out_, in_)!=0
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.edge.features = "Returns all the features for "
  "an edge";
  Set get.edge.features(Real edge.idx)
  ////////////////////////////////////////////////////////////////////////////
  {
    _.edge::get.all(edge.idx)
  };

  ////////////////////////////////////////////////////////////////////////////
  Text _.autodoc.member.get.edge.feature = "Returns specified feature for "
  "an edge. Feature identifier can be the name (Text) or the position (Real";
  Anything get.edge.feature(
    Real edge.idx,
    Anything feature.id) //Text name or Real num
  ////////////////////////////////////////////////////////////////////////////
  {
    _.edge::get(edge.idx, feature.id)
  }

/*
PENDIENTE! : Funciones de adyacencia o vecindad de vértices

http://www.absoluteastronomy.com/topics/Adjacency_matrix

////////////////////////////////////////////////////////////////////////////
Crea las matrices de adyancencia hasta un orden pedido por el usuario y las 
guarda en un set. Sólo crea las que no existan aún. Sólo devuelve la del
orden solicitado.
Matrix get.adyacency(Real order)
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
Devuelve los vértices adyacentes de salida de orden dado para el vértice
especificado.  V -> ... (n-1) ... -> U
Set get.adyacency.output(Real V, Real order)
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
Devuelve los vértices adyacentes U de entrada de orden dado para el vértice
especificado.  U -> ... (n-1) ... -> V
Set get.adyacency.input(Real V, Real order)
////////////////////////////////////////////////////////////////////////////

*/

}

]];
