[Avida-SVN] r2033 - development/source/analyze
kaben at myxo.css.msu.edu
kaben at myxo.css.msu.edu
Sat Sep 1 19:06:22 PDT 2007
Author: kaben
Date: 2007-09-01 22:06:22 -0400 (Sat, 01 Sep 2007)
New Revision: 2033
Added:
development/source/analyze/cAnalyzeGenotypeTreeStats.cc
development/source/analyze/cAnalyzeGenotypeTreeStats.h
Modified:
development/source/analyze/cAnalyze.cc
Log:
Added coded for the analyze command PRINT_TREE_STATS.
It's used like so:
SET_BATCH 0
PURGE_BATCH
LOAD detail-fake.pop
LOAD historic-fake.pop
PRINT_TREE_STATS
producing a file "data/tree_stats.dat" reading, for example
# Legend:
# 1: Average cumulative stemminess
0.311842
Caveats: until I add error-checks, the detail and historic files must describe
a phylogenetic tree (otherwise Avida will crash). I will work on this tomorrow.
Modified: development/source/analyze/cAnalyze.cc
===================================================================
--- development/source/analyze/cAnalyze.cc 2007-09-01 00:21:15 UTC (rev 2032)
+++ development/source/analyze/cAnalyze.cc 2007-09-02 02:06:22 UTC (rev 2033)
@@ -40,6 +40,7 @@
#include "cAnalyzeFlowCommandDef.h"
#include "cAnalyzeFunction.h"
#include "cAnalyzeGenotype.h"
+#include "cAnalyzeGenotypeTreeStats.h"
#include "tAnalyzeJob.h"
#include "cAvidaContext.h"
#include "cDataFile.h"
@@ -3220,97 +3221,113 @@
// Load in the variables...
cString filename("tree_stats.dat");
if (cur_string.GetSize() != 0) filename = cur_string.PopWord();
+
+ ofstream& fp = m_world->GetDataFileOFStream(filename);
+
+ fp << "# Legend:" << endl;
+ fp << "# 1: Average cumulative stemminess" << endl;
+ fp << endl;
-
- cAnalyzeGenotype * genotype = NULL;
- tListIterator<cAnalyzeGenotype> batch_it(batch[cur_batch].List());
- const int num_gens = batch[cur_batch].List().GetSize();
-
- // Put all of the genotypes in an array for easy reference and collect
- // other information on them as we process them.
- tArray<cAnalyzeGenotype *> gen_array(num_gens);
- tHashTable<int, int> id_hash; // Store array pos for each id.
- tArray<int> id_array(num_gens), pid_array(num_gens);
- tArray<int> depth_array(num_gens), birth_array(num_gens);
- int array_pos = 0;
- while ((genotype = batch_it.Next()) != NULL) {
- // Put the genotype in an array.
- gen_array[array_pos] = genotype;
- id_hash.Add(genotype->GetID(), array_pos);
- id_array[array_pos] = genotype->GetID();
- pid_array[array_pos] = genotype->GetParentID();
- depth_array[array_pos] = genotype->GetDepth();
- birth_array[array_pos] = genotype->GetUpdateBorn();
- array_pos++;
- }
-
- // Now collect information about the offspring of each individual.
- tArray<int> ppos_array(num_gens), offspring_count(num_gens);
- offspring_count.SetAll(0);
- for (int pos = 0; pos < num_gens; pos++) {
- int parent_id = gen_array[pos]->GetParentID();
- if (parent_id == -1) { // Organism has no parent (i.e., ancestor)
- ppos_array[pos] = -1;
- continue;
- }
- int parent_pos = -1;
- id_hash.Find(parent_id, parent_pos);
- ppos_array[pos] = parent_pos;
- offspring_count[parent_pos]++;
- }
-
- // For each genotype, figure out how far back you need to go to get to a
- // branch point.
- tArray<int> branch_dist_array(num_gens);
- tArray<int> branch_pos_array(num_gens);
- branch_dist_array.SetAll(-1);
- branch_pos_array.SetAll(-1);
- bool found = true;
- int loop_count = 0;
- while (found == true) {
- found = false;
- for (int pos = 0; pos < num_gens; pos++) {
- if (branch_dist_array[pos] > -1) continue; // continue if its set.
- found = true;
- int parent_pos = ppos_array[pos];
- if (parent_pos == -1) branch_dist_array[pos] = 0; // Org is root.
- else if (offspring_count[parent_pos] > 1) { // Parent is branch.
- branch_dist_array[pos] = 1;
- branch_pos_array[pos] = parent_pos;
- }
- else if (branch_dist_array[parent_pos] > -1) { // Parent calculated.
- branch_dist_array[pos] = branch_dist_array[parent_pos]+1;
- branch_pos_array[pos] = branch_pos_array[parent_pos];
- }
- // Otherwise, we are not yet ready to calculate this entry.
- }
- loop_count++;
- }
-
-
- // Cumulative Stemminess
- for (int pos = 0; pos < num_gens; pos++) {
- // We're only interested in internal n-furcating nodes.
- if (pid_array[pos] == -1) continue; // Don't want root.
- if (offspring_count[pos] <= 1) continue; // No leaves or nonfurcating nodes
-
- // @CAO Find distance to all children.
- // @CAO Find distance to parent branch.
- // @CAO DO math.
- }
-
-
- cout << "LOOP COUNT:" << loop_count << endl;
- for (int i = 0; i < num_gens; i++) {
- int branch_pos = branch_pos_array[i];
- int branch_id = (branch_pos == -1) ? -1 : id_array[branch_pos];
- cout << i << " "
- << id_array[i] << " "
- << offspring_count[i] << " "
- << branch_dist_array[i] << " "
- << branch_id << " "
- << endl;
- }
+ cAnalyzeGenotypeTreeStats agts;
+ agts.AnalyzeBatchTree(batch[cur_batch].List());
+
+ fp << agts.AverageStemminess();
+ fp << endl;
+
+ /*
+ Below is the original implementation by Ofria.
+ -- kgn
+ */
+
+ //cAnalyzeGenotype * genotype = NULL;
+ //tListIterator<cAnalyzeGenotype> batch_it(batch[cur_batch].List());
+ //const int num_gens = batch[cur_batch].List().GetSize();
+ //
+ //// Put all of the genotypes in an array for easy reference and collect
+ //// other information on them as we process them.
+ //tArray<cAnalyzeGenotype *> gen_array(num_gens);
+ //tHashTable<int, int> id_hash; // Store array pos for each id.
+ //tArray<int> id_array(num_gens), pid_array(num_gens);
+ //tArray<int> depth_array(num_gens), birth_array(num_gens);
+ //int array_pos = 0;
+ //while ((genotype = batch_it.Next()) != NULL) {
+ // // Put the genotype in an array.
+ // gen_array[array_pos] = genotype;
+ // id_hash.Add(genotype->GetID(), array_pos);
+ // id_array[array_pos] = genotype->GetID();
+ // pid_array[array_pos] = genotype->GetParentID();
+ // depth_array[array_pos] = genotype->GetDepth();
+ // birth_array[array_pos] = genotype->GetUpdateBorn();
+ // array_pos++;
+ //}
+ //
+ //// Now collect information about the offspring of each individual.
+ //tArray<int> ppos_array(num_gens), offspring_count(num_gens);
+ //offspring_count.SetAll(0);
+ //for (int pos = 0; pos < num_gens; pos++) {
+ // int parent_id = gen_array[pos]->GetParentID();
+ // if (parent_id == -1) { // Organism has no parent (i.e., ancestor)
+ // ppos_array[pos] = -1;
+ // continue;
+ // }
+ // int parent_pos = -1;
+ // id_hash.Find(parent_id, parent_pos);
+ // ppos_array[pos] = parent_pos;
+ // offspring_count[parent_pos]++;
+ //}
+ //
+ //// For each genotype, figure out how far back you need to go to get to a
+ //// branch point.
+ //tArray<int> branch_dist_array(num_gens);
+ //tArray<int> branch_pos_array(num_gens);
+ //branch_dist_array.SetAll(-1);
+ //branch_pos_array.SetAll(-1);
+ //bool found = true;
+ //int loop_count = 0;
+ //while (found == true) {
+ // found = false;
+ // for (int pos = 0; pos < num_gens; pos++) {
+ // if (branch_dist_array[pos] > -1) continue; // continue if its set.
+ // found = true;
+ // int parent_pos = ppos_array[pos];
+ // if (parent_pos == -1) branch_dist_array[pos] = 0; // Org is root.
+ // else if (offspring_count[parent_pos] > 1) { // Parent is branch.
+ // branch_dist_array[pos] = 1;
+ // branch_pos_array[pos] = parent_pos;
+ // }
+ // else if (branch_dist_array[parent_pos] > -1) { // Parent calculated.
+ // branch_dist_array[pos] = branch_dist_array[parent_pos]+1;
+ // branch_pos_array[pos] = branch_pos_array[parent_pos];
+ // }
+ // // Otherwise, we are not yet ready to calculate this entry.
+ // }
+ // loop_count++;
+ //}
+ //
+ //
+ //// Cumulative Stemminess
+ //for (int pos = 0; pos < num_gens; pos++) {
+ // // We're only interested in internal n-furcating nodes.
+ // if (pid_array[pos] == -1) continue; // Don't want root.
+ // if (offspring_count[pos] <= 1) continue; // No leaves or nonfurcating nodes
+ //
+ // // @CAO Find distance to all children.
+ // // @CAO Find distance to parent branch.
+ // // @CAO DO math.
+ //}
+ //
+ //
+ //cout << "LOOP COUNT:" << loop_count << endl;
+ //for (int i = 0; i < num_gens; i++) {
+ // int branch_pos = branch_pos_array[i];
+ // int branch_id = (branch_pos == -1) ? -1 : id_array[branch_pos];
+ // cout << i << " "
+ // << id_array[i] << " "
+ // << offspring_count[i] << " "
+ // << branch_dist_array[i] << " "
+ // << branch_id << " "
+ // << endl;
+ //}
}
Added: development/source/analyze/cAnalyzeGenotypeTreeStats.cc
===================================================================
--- development/source/analyze/cAnalyzeGenotypeTreeStats.cc (rev 0)
+++ development/source/analyze/cAnalyzeGenotypeTreeStats.cc 2007-09-02 02:06:22 UTC (rev 2033)
@@ -0,0 +1,288 @@
+
+#include "cAnalyzeGenotypeTreeStats.h"
+
+#include "cAnalyzeGenotype.h"
+#include "tHashTable.h"
+
+void cAnalyzeGenotypeTreeStats::AnalyzeBatchTree(tList<cAnalyzeGenotype> &genotype_list){
+ cAnalyzeGenotype * genotype = NULL;
+ tListIterator<cAnalyzeGenotype> batch_it(genotype_list);
+ const int num_gens = genotype_list.GetSize();
+
+ int array_pos = 0;
+
+ /*
+ Put all of the genotypes in an array for easy reference and collect other information on them as we process them. {{{4
+ */
+ tArray<cAnalyzeGenotype *> gen_array(num_gens);
+ tHashTable<int, int> id_hash; // Store array pos for each id.
+ tArray<int> id_array(num_gens), pid_array(num_gens);
+ tArray<int> depth_array(num_gens), birth_array(num_gens);
+
+ array_pos = 0;
+ batch_it.Reset();
+ while ((genotype = batch_it.Next()) != NULL) {
+ id_hash.Add(genotype->GetID(), array_pos);
+ array_pos++;
+ }
+
+ m_agl.Resize(num_gens);
+ array_pos = 0;
+ batch_it.Reset();
+ while ((genotype = batch_it.Next()) != NULL) {
+ // Put the genotype in an array.
+ m_agl[array_pos].genotype = genotype;
+ m_agl[array_pos].id = genotype->GetID();
+ m_agl[array_pos].pid = genotype->GetParentID();
+ m_agl[array_pos].depth = genotype->GetDepth();
+ m_agl[array_pos].birth = genotype->GetUpdateBorn();
+ array_pos++;
+ }
+
+ //// Now collect information about the offspring of each individual. {{{4
+ tArray<int> ppos_array(num_gens), offspring_count(num_gens);
+ offspring_count.SetAll(0);
+
+ // For each genotype, figure out how far back you need to go to get to a branch point. {{{4
+ tArray<int> anc_branch_dist_array(num_gens);
+ tArray<int> anc_branch_pos_array(num_gens);
+ anc_branch_dist_array.SetAll(-1);
+ anc_branch_pos_array.SetAll(-1);
+ bool found = true;
+ int loop_count = 0;
+
+ /*
+ Link each offspring to its parent. {{{4
+ */
+ for (int pos = 0; pos < num_gens; pos++) {
+ //cAnalyzeGenotype * genotype = gen_array[pos];
+ cAnalyzeGenotype * genotype = m_agl[pos].genotype;
+ int parent_id = genotype->GetParentID();
+ if (-1 != parent_id){
+ id_hash.Find(parent_id, m_agl[pos].ppos);
+ int parent_position = m_agl[pos].ppos;
+ m_agl[parent_position].offspring_positions.Push(pos);
+ /* XXX I think I'll be able to remove this. */
+ cAnalyzeGenotype * parent_genotype = m_agl[parent_position].genotype;
+ genotype->LinkParent(parent_genotype);
+ }
+ }
+
+ /*
+ Count offspring of each parent. {{{4
+ */
+ for (int pos = 0; pos < num_gens; pos++) {
+ m_agl[pos].offspring_count = m_agl[pos].offspring_positions.GetSize();
+ }
+
+
+ /*
+ For each genotype, figure out how far back you need to go to get to a branch point. {{{4
+ */
+ found = true;
+ loop_count = 0;
+ while (found == true) {
+ found = false;
+ for (int pos = 0; pos < num_gens; pos++) {
+ if (m_agl[pos].anc_branch_dist > -1) continue; // continue if its set.
+ found = true;
+ int parent_pos = m_agl[pos].ppos;
+ if (parent_pos == -1) {
+ m_agl[pos].anc_branch_dist = 0; // Org is root.
+ } else if (m_agl[parent_pos].offspring_count > 1) { // Parent is branch.
+ m_agl[pos].anc_branch_dist = 1;
+ m_agl[pos].anc_branch_id = m_agl[parent_pos].id;
+ m_agl[pos].anc_branch_pos = parent_pos;
+ } else if (m_agl[parent_pos].anc_branch_dist > -1) { // Parent calculated.
+ m_agl[pos].anc_branch_dist = m_agl[parent_pos].anc_branch_dist + 1;
+ m_agl[pos].anc_branch_id = m_agl[parent_pos].anc_branch_id;
+ m_agl[pos].anc_branch_pos = m_agl[parent_pos].anc_branch_pos;
+ }
+ // Otherwise, we are not yet ready to calculate this entry.
+ }
+ loop_count++;
+ }
+
+ // compute number of subtree nodes.
+ int branch_tree_size = 0;
+ for (int pos = 0; pos < num_gens; pos++) {
+ if(m_agl[pos].offspring_count != 1){
+ branch_tree_size++;
+ }
+ }
+
+ m_agl2.Resize(branch_tree_size); // Store agl data for each id.
+ tHashTable<int, int> id_hash_2;
+ int array_pos_2 = 0;
+ if (true) for (int pos = 0; pos < num_gens; pos++) {
+ int offs_count = m_agl[pos].offspring_count;
+ if (offs_count != 1){
+ m_agl2[array_pos_2].id = m_agl[pos].id;
+ m_agl2[array_pos_2].pid = m_agl[pos].pid;
+ m_agl2[array_pos_2].depth = m_agl[pos].depth;
+ m_agl2[array_pos_2].birth = m_agl[pos].birth;
+ m_agl2[array_pos_2].anc_branch_dist = m_agl[pos].anc_branch_dist;
+ m_agl2[array_pos_2].anc_branch_id = m_agl[pos].anc_branch_id;
+ /*
+ missing still are ppos (skip this), offspring_count (redundant),
+ anc_branch_pos, off_branch_dist_acc (to be calculated),
+ offspring_positions
+ */
+ id_hash_2.Add(m_agl2[array_pos_2].id, array_pos_2);
+ array_pos_2++;
+ }
+ }
+
+ // find branch ancestor positions. {{{4
+ for (int pos = 0; pos < branch_tree_size; pos++){
+ int anc_branch_id = m_agl2[pos].anc_branch_id;
+ id_hash_2.Find(anc_branch_id, m_agl2[pos].anc_branch_pos);
+ int anc_branch_pos = m_agl2[pos].anc_branch_pos;
+ if(0 <= anc_branch_pos){
+ m_agl2[anc_branch_pos].offspring_positions.Push(pos);
+ }
+ }
+
+ /*
+ For DFS of branch tree, locate root. {{{4
+ */
+ cAGLData *root = 0;
+ for (int pos = 0; pos < branch_tree_size; pos++){
+ m_agl2[pos].traversal_visited = false;
+ /*
+ root : anc_branch_dist: 0 anc_branch_id: -1 anc_branch_pos: -1
+
+ Only one of these conditions should be needed. I'm mixing-in a
+ sanity check here. I ought to move it...
+ */
+ if( (m_agl2[pos].anc_branch_dist == 0)
+ ||(m_agl2[pos].anc_branch_id == -1)
+ ||(m_agl2[pos].anc_branch_pos == -1)
+ ){
+ root = &(m_agl2[pos]);
+ /* Sanity check. */
+ if(!( (m_agl2[pos].anc_branch_dist == 0)
+ &&(m_agl2[pos].anc_branch_id == -1)
+ &&(m_agl2[pos].anc_branch_pos == -1)
+ )
+ ){
+ // FIXME : ERROR("while looking for root of subtree, found inconsistencies -");
+ return;
+ }
+ }
+ }
+
+ /*
+ DFS of branch tree, to accumulate branch distances. {{{4
+ */
+ tList<cAGLData> dfs_stack;
+ if(0 != root){
+ /* DFS. */
+ dfs_stack.Push(root);
+ cAGLData *node = 0;
+ while (0 < dfs_stack.GetSize()){
+ node = dfs_stack.Pop();
+ if (0 != node){
+ if (! node->traversal_visited){
+ dfs_stack.Push(node);
+ node->off_branch_dist_acc = 0;
+ for (int i = 0; i < node->offspring_positions.GetSize(); i++){
+ int pos = node->offspring_positions[i];
+ if (! m_agl2[pos].traversal_visited){
+ dfs_stack.Push(&(m_agl2[pos]));
+ }
+ }
+ node->traversal_visited = true;
+ } else {
+ /*
+ Child nodes, if any, have been visited and have added their
+ off_branch_dist_acc to this node.
+ */
+ if(0 <= node->anc_branch_pos){
+ /*
+ Only accumulate to parent if there is a parent (i.e., this
+ is not the root.)
+ */
+ m_agl2[node->anc_branch_pos].off_branch_dist_acc += node->anc_branch_dist;
+ m_agl2[node->anc_branch_pos].off_branch_dist_acc += node->off_branch_dist_acc;
+ }
+ }
+ }
+ }
+ } else {
+ // FIXME : ERROR("couldn't find root of subtree -");
+ return;
+ }
+
+ /*
+ Compute cumulative stemminesses. {{{4
+ */
+ for (int pos = 0; pos < branch_tree_size; pos++){
+ if (0 == m_agl2[pos].anc_branch_dist){
+ /* Correct stemminess for root... */
+ /*
+ Let me rephrase that. If anc_branch_dist is zero and all is well,
+ we're dealing with the root.
+ */
+ m_agl2[pos].cumulative_stemminess = 0.0;
+ } else {
+ m_agl2[pos].cumulative_stemminess =
+ (double)(m_agl2[pos].anc_branch_dist)
+ /
+ (
+ (double)(m_agl2[pos].off_branch_dist_acc) + (double)(m_agl2[pos].anc_branch_dist)
+ )
+ ;
+ }
+ }
+
+ /*
+ Compute average cumulative stemminess. {{{4
+ */
+ m_stemminess_sum = 0.0;
+ m_average_stemminess = 0.0;
+ m_inner_nodes = 0;
+ if (1 < branch_tree_size) {
+ for (int pos = 0; pos < branch_tree_size; pos++){
+ bool not_leaf = true;
+ if (m_should_exclude_leaves) {
+ not_leaf = (0 < m_agl2[pos].off_branch_dist_acc);
+ }
+ bool not_root = (0 < m_agl2[pos].anc_branch_id);
+ if (not_leaf && not_root){
+ m_stemminess_sum += m_agl2[pos].cumulative_stemminess;
+ m_inner_nodes++;
+ }
+ }
+ }
+ if(0 < m_inner_nodes){
+ m_average_stemminess = m_stemminess_sum / (m_inner_nodes);
+ }
+
+ /*
+ Print branch tree. {{{4
+ Use to get expected data for making following test.
+ */
+
+ if (true){
+ for (int pos = 0; pos < branch_tree_size; pos++){
+ cout << "id: " << m_agl2[pos].id;
+ cout << " offspring_count: " << m_agl2[pos].offspring_positions.GetSize();
+ cout << " anc_branch_id: " << m_agl2[pos].anc_branch_id;
+ cout << " anc_branch_dist: " << m_agl2[pos].anc_branch_dist;
+ cout << " off_branch_dist_acc: " << m_agl2[pos].off_branch_dist_acc;
+ cout << " cumulative_stemminess: " << m_agl2[pos].cumulative_stemminess;
+ cout << endl;
+ if (0 < m_agl2[pos].offspring_positions.GetSize()) {
+ cout << " offspring ids:";
+ for (int i = 0; i < m_agl2[pos].offspring_positions.GetSize(); i++){
+ cout << " " << m_agl2[pos].offspring_positions[i];
+ }
+ cout << endl;
+ }
+ }
+ cout << "m_stemminess_sum: " << m_stemminess_sum << endl;
+ cout << "m_average_stemminess: " << m_average_stemminess << endl;
+ cout << "m_inner_nodes: " << m_inner_nodes << endl;
+ }
+}
Added: development/source/analyze/cAnalyzeGenotypeTreeStats.h
===================================================================
--- development/source/analyze/cAnalyzeGenotypeTreeStats.h (rev 0)
+++ development/source/analyze/cAnalyzeGenotypeTreeStats.h 2007-09-02 02:06:22 UTC (rev 2033)
@@ -0,0 +1,92 @@
+#ifndef cAnalyzeGenotypeTreeStats_h
+#define cAnalyzeGenotypeTreeStats_h
+
+#ifndef tArray_h
+#include "tArray.h"
+#endif
+#ifndef tList_h
+#include "tList.h"
+#endif
+
+
+class cAnalyzeGenotype;
+
+
+/*
+Convenience class cAGLData serves to collect various data I found I needed
+while building the class cAnalyzeGenotypeTreeStats.
+*/
+class cAGLData {
+public:
+ cAnalyzeGenotype *genotype;
+ int id;
+ int pid;
+ int depth;
+ int birth;
+ int ppos;
+ int offspring_count;
+ int anc_branch_dist;
+ int anc_branch_id;
+ int anc_branch_pos;
+ int off_branch_dist_acc;
+ double cumulative_stemminess;
+ bool traversal_visited;
+ tArray<int> offspring_positions;
+
+ cAGLData()
+ : genotype(0)
+ , id(-1)
+ , pid(-1)
+ , depth(-1)
+ , birth(-1)
+ , ppos(-1)
+ , offspring_count(-1)
+ , anc_branch_dist(-1)
+ , anc_branch_id(-1)
+ , anc_branch_pos(-1)
+ , off_branch_dist_acc(-1)
+ , cumulative_stemminess(-1.)
+ , traversal_visited(false)
+ , offspring_positions(0)
+ {}
+};
+
+/*
+class cAnalyzeGenotypeTreeStats
+
+This class will eventually collect various kinds of statistics about various
+kinds of trees (e.g., phylogenetic).
+
+For now it only figures out "average cumulative stemminess".
+*/
+class cAnalyzeGenotypeTreeStats {
+public:
+ tArray<cAGLData> m_agl;
+ tArray<cAGLData> m_agl2;
+
+ double m_stemminess_sum;
+ double m_average_stemminess;
+ int m_inner_nodes;
+ bool m_should_exclude_leaves;
+
+public:
+ cAnalyzeGenotypeTreeStats()
+ : m_agl(0)
+ , m_agl2(0)
+ , m_stemminess_sum(0.0)
+ , m_average_stemminess(0.0)
+ , m_inner_nodes(0)
+ , m_should_exclude_leaves(true)
+ {}
+
+ tArray<cAGLData> &AGL(){ return m_agl; }
+ tArray<cAGLData> &AGL2(){ return m_agl2; }
+ double StemminessSum(){ return m_stemminess_sum; }
+ double AverageStemminess(){ return m_average_stemminess; }
+ int InnerNodes(){ return m_inner_nodes; }
+ bool ExcludesLeaves(){ return m_should_exclude_leaves; }
+
+ void AnalyzeBatchTree(tList<cAnalyzeGenotype> &genotype_list);
+};
+
+#endif
More information about the Avida-cvs
mailing list