#!/usr/bin/perl -w
############################################################
#
# $Id: graph-get-clusters,v 1.31 2010/08/22 13:42:47 jvanheld Exp $
#
# Time-stamp: <2003-07-04 12:48:55 jvanheld>
#
############################################################

## use strict;

=pod

=head1 NAME

graph-get-clusters

=head1 DESCRIPTION

Compares a graph with a classification/clustering file.

The comparison is performed at the level of arcs: for each arc of the
graph, the programs tests if the soure and target nodes are found in a
common cluster (intra-cluster arcs) or in separate clusters
(inter-cluster arcs).

In the cluster file, each node can be associated to one or several
clusters. For example, the "cluster" file could contain the result of
a classification such as ghe Gene Ontology (GO), where each gene can
be associated to multiple GO classes.

=head1 AUTHORS

=over

=item Jacques van Helden <jvanheld@bigre.ulb.ac.be>

=item Sylvain Brohee <sylvain@bigre.ulb.ac.be>

=head1 CATEGORY

graph analysis

=head1 USAGE

graph-get-clusters [-i graph_file] -clusters cluster_file [-o outputfile] [-induced] [-return table|clusters|graph] [-distinct] [-v #] [...]

=head1 INPUT FORMAT

=head2 Graph format

See I<convert-graph> for a description of the supported input graph
formats. Default format for the input graph: tab.

=head2 Cluster format

Without the option -induced (default) : a two-column file with column
corresponding respectively to the node name and to the cluster name.

With the option -induced : a one column file specifying to nodes from
which the output graph will be induced.

=head1 OUTPUT FORMATS

There are three possible output formats: the list of intra-cluster
edges (option -return clusters), a contingency table (option -return
table) or an annotated graph (option -return graph).

=head2 contingency table

A tab-delimited file where each row represents a node of the graph,
and each column a cluster (class). The cells indicate the number of
arcs connecting each node to each cluster (class) (or the sum of the weights on the 
edges).

=head2 graph

The list of intra-cluster edges in the requested format.

=head2 annotated graph

Each row corresponds to one arc, identified by its source and target
nodes, and with additional columns for the annotations, indicating the
status of each arc (intra- or inter-cluster), plus some statistics
about the clusters associated to the source and target
nodes. Extra-columns are documented in the header of the file.

See I<convert-graph> for a description of the supported output graph
formats. Note that it is not recommended to use the tab-delimted
format for the output graph, because orphan nodes (nodes without any
arc) would not be displayed (the tab format is restricted to a
description of the arcs). 

=cut


BEGIN {
    if ($0 =~ /([^(\/)]+)$/) {
	push (@INC, "$`lib/");
    }
}
require "RSA.lib";
require RSAT::Graph2;


################################################################
## Main package
package main;
{

  ################################################################
  ## Palette for the cluster-specific node colors
  my @cluster_colors_dot = qw(blue 
			      green
			      orange
			      cyan
			      darkviolet
			      red
			      gray
			      magenta
			      black
			      darkgreen
			      darkblue
			      violet
			      darkgray);
  my @cluster_colors_gml = ('#FF0000', ## Red
			    '#00FF00', ## Green
			    '#FF3300', ## Scarlet
			    '#0000FF', ## Dark blue
			    '#800000', ## Light red
			    '#008000', ## Light green
			    '#000080', ## Light blue
			    '#FF6600', ## Vermillon
			    '#FFCC00', ## Tangerine
			    '#00FF00', ## Green
			    '#FF8040', ## Orange
			    '#804000', ## Brown
			    '#808000', ## Forest Green
			    '#408080', ## Grass green
			    '#7E587E', ## Plum 4
			    '#C9BE62', ## Khaki3
			    '#827839', ## Khaki4
			    '#0066FF', ## Peacock
			    '#00FF66'  ## Emerald
			   );


  ################################################################
  ## Initialise parameters
  local $start_time = &RSAT::util::StartScript();

  ################################################################
  ## Initialize the input graph
  $graph = new RSAT::Graph2();
  $graph->set_attribute("label", "input graph");

  ## Columns of the tab-delimited graph file
  local $source_col = 1;
  local $target_col = 2;
  local $weight_col = 0;
  local $color_col = 0;



  ################################################################
  ## Initialize the output graph (cluster graph)
  my $cluster_graph = new RSAT::Graph2();
  $cluster_graph->set_attribute("label", "cluster_graph");
  my @cluster_graph_array = ();
  my $cluster_graph_array_cpt = 0;
    
  ## Input formats
  local $input_format = "tab";
  %supported_input_format =(
			    tab=>1,
			    gml=>1
			   );
  $supported_input_formats = join (",", keys %supported_input_format);
  local $source_col = 1;
  local $target_col = 2;
  local $weight_col = 0;
  ## Output formats
  %supported_output_format =(
			     dot=>1,
			     gml=>1,
			     tab=>1

			    );
  $supported_output_formats = join (",", keys %supported_output_format);
  local $output_format = "gml";


  %main::infile = ();
  %main::outfile = ();
  $main::verbose = 0;
  $main::out = STDOUT;
  $distinct = 0;
  $induced = 0;
  my %duplicated_nodes;
  my @arcs_clusters;
  local $return_type = "clusters";
  %supported_return_format = ("table"=>1,
			      "graph"=>1,
			      "clusters"=>1);
  $supported_return_formats = join (",", keys %supported_return_format);

  ## Count the sum of the intra cluster edges or count their number
  local $weights = 0;
    
  ################################################################
  ## Read argument values
  &ReadArguments();

  ################################################################
  ## Check argument values
  @cluster_colors = ();
  if ($output_format eq "dot") {
    @cluster_colors = @cluster_colors_dot;
  } else {
    @cluster_colors = @cluster_colors_gml;
  }
  if ($induced && $return_type ne 'clusters') {
    &RSAT::error::FatalError("\t","Option -induced cannot be used with -return ",$return_type);
  }
  if ($distinct && $return_type ne 'clusters') {
    &RSAT::error::FatalError("\t","Option -distinct cannot be used with -return ",$return_type);
  }
  if (!$main::infile{clusters}) {
    &RSAT::error::FatalError("\t","You must submit a cluster file with option -clusters");
  }
  ################################################################
  ## Open output stream
  $main::out = &OpenOutputFile($main::outfile{output});

  ################################################################
  ## Read input graph
  $graph->graph_from_text($input_format,$main::infile{graph}, $source_col, $target_col, $weight_col,0, 0,$color_col, 1);
  $graph->load_classes($main::infile{clusters}, $induced);

  ################################################################
  ## Extract the intra-cluster subgraphs
  if ($return_type eq 'clusters') {

    ## Read and add the class specification to each node
    &RSAT::message::TimeWarn("Load classes") if ($main::verbose >= 2);

    ## Identification of cluster nodes
    &RSAT::message::TimeWarn("Identifying cluster nodes in the graph") if ($main::verbose >= 2);
    my @cluster_list = $graph->get_attribute("cluster_list");
    $cluster_nb = scalar @cluster_list;
    my @arcs = $graph->get_attribute("arcs");
    my %nodes_name_id = $graph->get_attribute("nodes_name_id");
    my %remaining_nodes_name_id = $graph->get_attribute("nodes_name_id");
    my %nodes_id_name = $graph->get_attribute("nodes_id_name");
    my %nodes_color = $graph->get_attribute("nodes_color");
    my @nodes_clusters = $graph->get_attribute("nodes_clusters");
    my %edge_cluster = ();
    my %cluster_color = ();
    my $arc_nb = scalar(@arcs);

    for (my $i = 0; $i < scalar(@arcs); $i++) {
      if (($main::verbose >= 3) && ($i%1000==0)) {
	&RSAT::message::psWarn("Treated", $i."/".$arc_nb, "arcs");
      }
      my $source_node = $arcs[$i][0];
      my $target_node = $arcs[$i][1];
      my $source_node_id = $nodes_name_id{$source_node};
      my $target_node_id = $nodes_name_id{$target_node};
      my @source_clusters = $graph->get_node_id_clusters($source_node_id, @nodes_clusters);
      my @target_clusters = $graph->get_node_id_clusters($target_node_id, @nodes_clusters);
      delete $remaining_nodes_name_id{$source_node};
      delete $remaining_nodes_name_id{$target_node};
      #         print "@nodes_clusters";

      foreach my $scluster (@source_clusters) {

	foreach my $tcluster (@target_clusters) {
	  if ($scluster eq $tcluster) {
	    if ($weight_col <= 0) {
	      $label =  join("_", $source_node, $target_node, $scluster);
	    } else {
	      $label = $arcs[$i][2];
	    }
	    my $scolor = $nodes_color{$source_node_id};
	    my $tcolor = $nodes_color{$target_node_id};
	    $arc_color = $arcs[$i][3];
	    if (!$induced && !$color_col) {
	      $arc_color = $cluster_color{$scluster};
	      if (!defined($arc_color)) {
		$arc_color = $cluster_colors[scalar(keys(%cluster_color)) % scalar(@cluster_colors)];
		$cluster_color{$scluster} = $arc_color;
	      }
	    }
	    if ($distinct) {
	      my $arc_key = $source_node."_arc_".$target_node;
	      my $edge_other_cluster = $edge_cluster{$arc_key};
	      if (defined($edge_other_cluster) && $edge_other_cluster ne $scluster) {
		if (!defined($duplicated_nodes{$source_node})) {
		  $duplicated_nodes{$source_node} = 2;
		} 
		$source_node = $source_node."_".$duplicated_nodes{$source_node};
		$duplicated_nodes{$source_node}++;
		if (!defined($duplicated_nodes{$target_node})) {
		  $duplicated_nodes{$target_node} = 2;
		}
		$target_node = $target_node."_".$duplicated_nodes{$target_node};
		$duplicated_nodes{$target_node}++;
	      } else {
		$edge_cluster{$arc_key} = $scluster;
	      }
	    }
	    $cluster_graph_array[$cluster_graph_array_cpt][0] = $source_node;
	    $cluster_graph_array[$cluster_graph_array_cpt][1] = $target_node;
	    $cluster_graph_array[$cluster_graph_array_cpt][2] = $label;
	    $cluster_graph_array[$cluster_graph_array_cpt][3] = $scolor;
	    $cluster_graph_array[$cluster_graph_array_cpt][4] = $tcolor;
	    $cluster_graph_array[$cluster_graph_array_cpt][5] = $arc_color;
	    push @{$arcs_clusters[$cluster_graph_array_cpt]}, $scluster;
	    $cluster_graph_array_cpt++;
	  }
	}
      }
    }
    $cluster_graph->load_from_array(@cluster_graph_array);
    ## If there are selected orphan nodes, add it to the induced graph
    if ($induced) {
      foreach my $node (keys %remaining_nodes_name_id) {
	my @clusters = $graph->get_nodes_clusters($node);
	if ((scalar @clusters) > 0) {
	  $cluster_graph->create_node($node);
	}
      }
    }
    $cluster_graph->set_array_attribute("arcs_attribute", @arcs_clusters);
    &Verbose() if ($main::verbose);
    ## Print the output graph
    print $out $cluster_graph->to_text($output_format);
  } else {
    ## Count the number of intra-cluster edge by looking for each arc
    ## if the target node is located in the same cluster than 
    ## the source node
    ## The results are stored in a two dimensional array. Rows represent
    ## the nodes and edges represent the clusters.
    ## Indices of the two-dimensional array;
    my %nodes_name_id = $graph->get_attribute("nodes_name_id");
    my %nodes_id_name = $graph->get_attribute("nodes_id_name");
    my %nodes_color = $graph->get_attribute("nodes_color");
    my @cluster_list = $graph->get_attribute("cluster_list");
    my %clusters_name_id = ();
    my %clusters_edge_nb = ();
    my @arcs_attribute = ();
    my %cluster_color = ();
    my $intra_cpt = 0;
    my $extra_cpt = 0;
    for (my $i = 0; $i < scalar(@cluster_list); $i++) {
      $cluster_name_id{$cluster_list[$i]} = $i;
    }
    my @arcs = $graph->get_attribute("arcs");
    my @result = ();
    my $arc_nb = scalar(@arcs);
    &RSAT::message::TimeWarn("Comparing graph with clusters") if ($main::verbose >= 2);
    for (my $i = 0; $i < @arcs; $i++) {
      if (($main::verbose >= 2) && ($i%1000==0)) {
	&RSAT::message::psWarn("Treated", $i."/".$arc_nb, "arcs");
      }
      my $source_node = $arcs[$i][0];
      my $target_node = $arcs[$i][1];
      my @source_clusters = $graph->get_nodes_clusters($source_node);
      my @target_clusters = $graph->get_nodes_clusters($target_node);
      my $add_value = 1;
      if ($weights) {
	$add_value = $arcs[$i][2];
	$add_value /= 2;
      }
      my $source_nb_clusters = scalar(@source_clusters);
      my $target_nb_clusters = scalar(@target_clusters);
      my $common_clusters = 0;
      my %sclusters = ();
      my %tclusters = ();
      my %stclusters = ();
      my $intra_cluster = "inter_cluster";
      my $arc_color = "#C0C0C0"; 
      $arc_color = $arcs[$i][3] if ($color_col);
      foreach my $scluster (@source_clusters) {
	$sclusters{$scluster}++;
	foreach my $tcluster (@target_clusters) {
	  $tclusters{$tcluster}++;
	  if ($scluster eq $tcluster) {
	    my $source_node_index = $nodes_name_id{$source_node};
	    my $target_node_index = $nodes_name_id{$target_node};
	    my $cluster_index = $cluster_name_id{$tcluster};
	    $result[$source_node_index][$cluster_index] += $add_value;
	    $result[$target_node_index][$cluster_index] += $add_value;
	    $clusters_edge_nb{$tcluster} += 1;
	    $common_clusters++;
	    $stclusters{$tcluster}++;
	    $intra_cluster = "intra_cluster";
	    if (!$color_col) {
	      $arc_color = $cluster_color{$scluster};
	      if (!defined($arc_color)) {
		$arc_color = $cluster_colors[scalar(keys(%cluster_color)) % scalar(@cluster_colors)];
		$cluster_color{$scluster} = $arc_color;
	      }
	    }
	  }
	}
      }

      # FILL THE ANNOTATED NETWORK
      my $index;
      if ($intra_cluster eq 'intra_cluster') {
	$index = (scalar(@arcs)-1) - $intra_cpt;
	$intra_cpt++;
      } else {
	$index = $extra_cpt;
	$extra_cpt++;
      }
      if (!defined($index)) {
	print $source_node."\t".$target_node."\n";
      }
      $cluster_graph_array[$index][0] = $source_node;
      $cluster_graph_array[$index][1] = $target_node;
      $cluster_graph_array[$index][2] = $arcs[$i][2];
      $cluster_graph_array[$index][3] = $node_colors{$source_node};
      $cluster_graph_array[$index][4] = $node_colors{$target_node};
      $cluster_graph_array[$index][5] = $arc_color;
      my %s_not_tclusters = &set_difference(\%sclusters,\%stclusters);
      my %not_s_tclusters = &set_difference(\%tclusters,\%stclusters);
      $arcs_attribute[$index][0] = $intra_cluster;
      $arcs_attribute[$index][1] = $source_nb_clusters; 
      $arcs_attribute[$index][2] = $target_nb_clusters; 
      $arcs_attribute[$index][3] = $common_clusters;
      $arcs_attribute[$index][4] = ($source_nb_clusters-$common_clusters);
      $arcs_attribute[$index][5] = ($target_nb_clusters-$common_clusters);
      $arcs_attribute[$index][6] = join(";",keys(%sclusters));
      $arcs_attribute[$index][7] = join(";",keys(%tclusters));
      $arcs_attribute[$index][8] = join(";",keys(%stclusters));
      $arcs_attribute[$index][9] = join(";",keys(%s_not_tclusters));
      $arcs_attribute[$index][10] = join(";",keys(%not_s_tclusters));
    }
      
    my @arcs_attribute_header = ("type","#s","#t","#st","#s!t","#!st","cl_s","cl_t","cl_st","cl_s!t","cl_!st");
    $cluster_graph->load_from_array(@cluster_graph_array);
    $cluster_graph->set_array_attribute("arcs_attribute",@arcs_attribute);
    $cluster_graph->set_array_attribute("arcs_attribute_header", @arcs_attribute_header);

    if ($return_type eq "table") {
      ## Header symbol
      print $out join ("\t", "#node", @cluster_list, "sum"), "\n";

#      ## Print cluster names
#      foreach my $cluster (@cluster_list) {
#	print $out "\t$cluster";
#      }
#      print $out "\tsum";
      ## Print node name and the number of intra-edge cluster
      for (my $i = 0; $i < scalar(@result); $i++) {
	print $out "$nodes_id_name{$i}";
	my $sum = 0;
	for (my $j = 0; $j < scalar(@cluster_list); $j++) {
	  my $cluster = $cluster_list[$j];
	  my $clusterIndex = $cluster_name_id{$cluster};
	  my $val = 0;
	  if (defined($result[$i][$j])) {
	    $val = $result[$i][$j];
	  }
	  $sum += $val;
	  print $out "\t$val";
	}
	print $out "\t$sum";
	print $out "\n";
      }
      print $out "; #Arcs";
      foreach my $cluster (@cluster_list) {
	my $val = ($clusters_edge_nb{$cluster});
	if (!defined($val)) {
	  $val = 0;
	}
	print $out "\t$val";
      }
      print $out "\n";
    } else {
      if ($main::verbose >= 1 && $output_format eq "tab") {
	print $out join("\t",  ";","1","source","source node"), "\n";
	print $out join("\t",  ";","2","target","target node"), "\n";
	print $out join("\t",  ";","3","label","arc label"), "\n";
	print $out join("\t",  ";","4","color","arc color"), "\n";
	print $out join("\t",  ";","5","type","arc type"), "\n";
	print $out join("\t",  ";","6","#s","number of clusters where source node is present"), "\n";
	print $out join("\t",  ";","7","#t","number of clusters where target node is present"), "\n";
	print $out join("\t",  ";","8","#st","number of clusters where both source node and target node are present"), "\n";
	print $out join("\t",  ";","9","#s!t","number of clusters where only source node is present"), "\n";
	print $out join("\t",  ";","10","#!st","number of clusters where only target node is present"), "\n";
	print $out join("\t",  ";","11","cl_s","clusters where source node is present"), "\n";
	print $out join("\t",  ";","12","cl_t","clusters where target node is present"), "\n";
	print $out join("\t",  ";","13","cl_st","clusters where both source node and target node are present"), "\n";
	print $out join("\t",  ";","14","cl_s!t","clusters where only source node is present"), "\n";
	print $out join("\t",  ";","15","cl_!st","clusters where only target node is present"), "\n";
      }
      print $out $cluster_graph->to_text($output_format);
    }
  }

  ################################################################
  ## Close output stream
  my $exec_time = &RSAT::util::ReportExecutionTime($start_time);
  print $main::out $exec_time if ($main::verbose >= 1);
  close $main::out if ($main::outfile{output});
  exit(0);
}

################################################################
################### SUBROUTINE DEFINITION ######################
################################################################


################################################################
## Display full help message 
sub PrintHelp {
    system "pod2text -c $0";
    exit()
}

################################################################
## Display short help message
sub PrintOptions {
    &PrintHelp();
}

################################################################
## Read arguments 
sub ReadArguments {
    my $arg;
    my @arguments = @ARGV; ## create a copy to shift, because we need ARGV to report command line in &Verbose()
    while (scalar(@arguments) >= 1) {
      $arg = shift (@arguments);
	## Verbosity
=pod

=head1 OPTIONS

=over 4

=item B<-v #>

Level of verbosity (detail in the warning messages during execution)

=cut
	if ($arg eq "-v") {
	    if (&IsNatural($arguments[0])) {
		$main::verbose = shift(@arguments);
	    } else {
		$main::verbose = 1;
	    }

	    ## Help message
=pod

=item B<-h>

Display full help message

=cut
	} elsif ($arg eq "-h") {
	    &PrintHelp();

	    ## List of options
=pod

=item B<-help>

Same as -h

=cut
	} elsif ($arg eq "-help") {
	    &PrintOptions();

	    ## Graph file
=pod

=item B<-i graphfile>

If no graph file is specified, the standard input is used.  This
allows to use the command within a pipe.

=cut
	} elsif ($arg eq "-i") {
	  $main::infile{graph} = shift(@arguments);

	    ## Source column
=pod

=item B<-scol>

Source column. Column containing the source nodes in the tab-delimited
graph file.  

=cut
	} elsif ($arg eq "-scol") {
	    $source_col = shift (@arguments);
	    unless (&IsNatural($source_col) && ($source_col > 0)) {
		&RSAT::error::FatalError(join("\t", $source_col, "Invalid value for the source column. Must be a strictly positive natural number"));
	    }

	    ## Target column
=pod

=item B<-tcol>

Target column. Column containing the target nodes in the tab-delimited
graph file.

=cut
	} elsif ($arg eq "-tcol") {
	    $target_col = shift (@arguments);
	    unless (&IsNatural($target_col) && ($target_col > 0)) {
		&RSAT::error::FatalError(join("\t", $target_col, "Invalid value for the target column. Must be a strictly positive natural number"));
	    }

	    ## Weight column
=pod

=item B<-wcol>

Weight column. Column containing the weight of the nodes in the tab-delimited
graph file. 

By specifying the weight of the node, the label of the arcs will not be 
changed from arc_label to arc_label_cluster_name. As a consequence, if you don't use the -distinct option, arcs belonging to more than one
cluster will be duplicated but will present the same label. Some visualization program like Cytoscape
cannot deal with such arcs. You should maybe use yEd for a more exact visualization of the extracted clusters.

=cut
	} elsif ($arg eq "-wcol") {
	    $weight_col = shift (@arguments);
	    unless (&IsNatural($weight_col) && ($weight_col > 0)) {
		&RSAT::error::FatalError(join("\t", $weight_col, "Invalid value for the weight column. Must be a strictly positive natural number"));
	    }

=pod

=item B<-eccol>

Edge color column. Column containing the color of the edges (RGB). (no default). If this option is not specified, the program will use different colors for each intra-cluster edge belonging a different cluster. Inter-cluster edges (-return graph option) are light grey.

=cut
	} elsif ($arg eq "-eccol") {
	    $color_col = shift (@arguments);
	    unless (&IsNatural($color_col) && ($color_col > 0)) {
		&RSAT::error::FatalError(join("\t", $color_col, "Invalid value for the color column. Must be a strictly positive natural number"));
	    }

=pod

=item B<-clusters cluster_file>

Cluster file. The cluster file specifies a list of node clusters.

=cut
	} elsif ($arg eq "-clusters") {
	    $main::infile{clusters} = shift(@arguments);
=pod

=item B<-in_format input_format>

Input format. Supported: tab, gml

=cut
	} elsif ($arg eq "-in_format") {
	    $input_format = shift(@arguments);
	    &RSAT::error::FatalError("$input_format\tInvalid input format. Supported: $supported_input_formats")
		unless ($supported_input_format{$input_format});

=pod

=item B<-distinct>

As some nodes may belong to more than one group, using this option will duplicate the nodes belonging to more
than one group.

=cut
	
	} elsif ($arg eq "-distinct") {
	    $distinct = 1;
=pod

=item B<-induced>

Using this option, only the first column of the cluster file will be taken into account. The output graph will
thus consist in the graph induced by all nodes of the first column.

=cut
	
	} elsif ($arg eq "-induced") {
	    $induced = 1;	    
	    
=pod	    
	    
=item B<-return>

Return format. cluster, table or graph

=cut
	} elsif ($arg eq "-return") {
	    $return_type = shift (@arguments);
	    &RSAT::error::FatalError("$return_type\tInvalid return option. Supported: $supported_return_formats")
		unless ($supported_return_format{$return_type});

	    
## Output file

=pod

=item	B<-o outputfile>

If no output file is specified, the standard output is used.  This
allows to use the command within a pipe.

=cut
	} elsif ($arg eq "-o") {
	    $main::outfile{output} = shift(@arguments);


	    ### Output format  
=cut

=item B<-out_format output_format>

See convert-graph for a list of supported output formats.

=cut 
	} elsif ($arg eq "-out_format") {
	    $output_format = shift(@arguments);
	    &RSAT::error::FatalError("$output_format\tInvalid output format. Supported: $supported_output_formats")
		unless ($supported_output_format{$output_format});


	} else {
	    &FatalError(join("\t", "Invalid option", $arg));

	}
    }


=pod

=back

=cut

}

################################################################
#### Computes the difference between two sets (hash)
sub set_difference {
  my $setA_ref = $_[0];
  my $setB_ref = $_[1];
  my %setA = %{$setA_ref};
  my %setB = %{$setB_ref};
  my %setC = ();
  foreach my $key (keys %setA) {
    if (!exists($setB{$key})) {
      $setC{$key}++;
    }
  }
  return %setC;
}
################################################################
#### verbose message
sub Verbose {
    my $this_out = $out;
    if ($output_format eq "gml") {
      $this_out = STDOUT;
    }
    print $this_out "; graph-get-clusters ";
    &PrintArguments($this_out);
    if (defined(%main::infile)) {
	print $this_out "; Input files\n";
	while (my ($key,$value) = each %main::infile) {
	    print $this_out ";\t$key\t$value\n";
	}
    }
    if (defined(%main::outfile)) {
	print $this_out "; Output files\n";
	while (my ($key,$value) = each %main::outfile) {
	    print $this_out ";\t$key\t$value\n";
	}
    }

    ## Report graph size
    my ($nodes, $arcs) = $graph->get_size();
    print $this_out "; Graph size\n";
    print $this_out ";\tnodes\t",$nodes,"\n";
    print $this_out ";\tarcs\t",$arcs,"\n";
    print $this_out "; Nb of clusters\t$cluster_nb\n";
    if ($main::verbose >= 1) {
      my $done_time = &AlphaDate();
      print $this_out "; Job started $start_time";
      print $this_out "; Job done    $done_time\n";
    }
    if ($this_out ne STDOUT) {
      if ($main::verbose >= 1) {
        my $done_time = &AlphaDate();
        print STDOUT "; Job started\t$start_time";
        print STDOUT "; Job done\t$done_time\n";
      }
    }
}



__END__

=pod

=head1 SEE ALSO

=over

=item I<graph-neighbours>

=item I<convert-graph>

=item I<compare-graphs>

=item I<graph-node-degree>

=item I<graph-neighbours>

=item I<compare-classes>

=item I<convert-classes>

=back

=cut
