private static void add_node() {
      int local_port = 0;
      while(taken_ports.Contains(local_port = rand.Next(0, max_range) + base_port));
      AHAddress address = new AHAddress(new RNGCryptoServiceProvider());
      Node node = new StructuredNode(address, brunet_namespace);
      ArrayList arr_tas = new ArrayList();
      for(int j = 0; j < max_range / 10; j++) {
        int remote_port = 0;
        do {
          remote_port = rand.Next(0, max_range) + base_port;
        } while(remote_port == local_port);
        PortTAAuthorizer port_auth = new PortTAAuthorizer(remote_port);
        arr_tas.Add(port_auth);
      }
      arr_tas.Add(new ConstantAuthorizer(TAAuthorizer.Decision.Allow));
      TAAuthorizer ta_auth = new SeriesTAAuthorizer(arr_tas);
      node.AddEdgeListener(new TcpEdgeListener(local_port, null));//, ta_auth));
//      node.AddEdgeListener(new TunnelEdgeListener(node));
      node.RemoteTAs = RemoteTA;
      (new Thread(node.Connect)).Start();
      taken_ports[local_port] = node;
      nodes.Add((Address) address, node);
      new TableServer(node);
      dhts.Add(node, new Dht(node, DEGREE));
      network_size++;
    }
    public static void Main(string []args) {
      if (args.Length < 1) {
        Console.WriteLine("please specify the number of p2p nodes."); 
        Environment.Exit(0);
      }
      else if (args.Length < 2) {
        Console.WriteLine("please specify the number of missing edges."); 
        Environment.Exit(0);
      }

      int base_port = 54111;
      int network_size = Int32.Parse(args[0]);
      int missing_edges_count = Int32.Parse(args[1]);
      string brunet_namespace = "testing";
      SortedList nodes = new SortedList();
      Console.WriteLine("Initializing...");

      ArrayList RemoteTA = new ArrayList();
      for(int i = 0; i < network_size; i++) {
        RemoteTA.Add(TransportAddressFactory.CreateInstance("brunet.udp://127.0.0.1:" + (base_port + i)));
      }

      Random rand = new Random();
      for(int i = 0; i < network_size; i++) {
        Console.WriteLine("Starting node: {0}", i);
        AHAddress address = new AHAddress(new RNGCryptoServiceProvider());
        Node node = new StructuredNode(address, brunet_namespace);
        ArrayList arr_tas = new ArrayList();
        for(int j = 0; j < missing_edges_count; j++) {
          int remote_port = 0;
          do {
            remote_port = rand.Next(0, network_size - 1) + base_port;
          } while(remote_port == base_port + i);
          PortTAAuthorizer port_auth = new PortTAAuthorizer(remote_port);
          arr_tas.Add(port_auth);
        }
        arr_tas.Add(new ConstantAuthorizer(TAAuthorizer.Decision.Allow));
        TAAuthorizer ta_auth = new SeriesTAAuthorizer(arr_tas);
        node.AddEdgeListener(new UdpEdgeListener(base_port + i, null, ta_auth));
        node.AddEdgeListener(new Tunnel.TunnelEdgeListener(node));
        node.RemoteTAs = RemoteTA;
        Thread t = new Thread(new ThreadStart(node.Connect));
        t.Start();
        nodes.Add((Address) address, node);
        Console.WriteLine("Sleeping for 2 seconds");
        System.Threading.Thread.Sleep(2000);        
      }

      //wait for 60 more seconds
      int count = 0;
      while(true) {
        Console.WriteLine("Going to sleep for 5 seconds.");
        System.Threading.Thread.Sleep(5000);

        Console.WriteLine("Checking ring...");
        Address start_addr = (Address) nodes.GetKeyList()[0];
        Address curr_addr = start_addr;

        for (int i = 0; i < network_size; i++) {
          Node node = (Node) nodes[curr_addr];
          ConnectionTable con_table = node.ConnectionTable;
          Connection con = con_table.GetLeftStructuredNeighborOf((AHAddress) curr_addr);
          Console.WriteLine("Hop {2}\t Address {0}\n\t Connection to left {1}\n", curr_addr, con, i);
          Address next_addr = con.Address;

          if (next_addr == null) {
            Console.WriteLine("Found disconnection.");
            break;
          }

          Connection lc = ((Node)nodes[next_addr]).ConnectionTable.GetRightStructuredNeighborOf((AHAddress) next_addr);
          if( (lc == null) || !curr_addr.Equals(lc.Address)) {
            Address left_addr = lc.Address;
            Console.WriteLine(curr_addr + " != " + left_addr);
            Console.WriteLine("Right had edge, but left has no record of it!\n{0} != {1}", con, lc);
            break;
          }
          else if(next_addr.Equals(start_addr) && i != network_size -1) {
            Console.WriteLine("Completed circle too early.  Only {0} nodes in the ring.",
                              (i + 1));
            break;
          }
          curr_addr = next_addr;
        }
        count++;
        if(start_addr.Equals(curr_addr)) {
          Console.WriteLine("Ring properly formed!");
          Console.WriteLine("This only took .... {0} seconds", (count * 5));
          break;
        }
      }

      count = 0;
      while(true) {
        Console.WriteLine("Going to sleep for 5 seconds.");
        System.Threading.Thread.Sleep(5000);

        Console.WriteLine("Checking ring...");
        Address start_addr = (Address) nodes.GetKeyList()[0];
        Address curr_addr = start_addr;

        for (int i = 0; i < network_size; i++) {
          Node node = (Node) nodes[curr_addr];
          ConnectionTable con_table = node.ConnectionTable;
          Connection con = con_table.GetRightStructuredNeighborOf((AHAddress) curr_addr);
          Console.WriteLine("Hop {2}\t Address {0}\n\t Connection to right {1}\n", curr_addr, con, i);
          Address next_addr = con.Address;

          if (next_addr == null) {
            Console.WriteLine("Found disconnection.");
          }
          Connection left_con = ((Node)nodes[next_addr]).ConnectionTable.GetLeftStructuredNeighborOf((AHAddress) next_addr);
          if(left_con == null) {
            Console.WriteLine("Found disconnection.");
          }
          else if(!curr_addr.Equals(left_con.Address)) {
            Address left_addr = left_con.Address;
            Console.WriteLine(curr_addr + " != " + left_addr);
            Console.WriteLine("Left had edge, but right has no record of it! {0}", left_con);
            break;
          }
          else if(next_addr.Equals(start_addr) && i != network_size -1) {
            Console.WriteLine("Completed circle too early.  Only " + count + " nodes in the ring.");
            break;
          }
          curr_addr = next_addr;
        }
        count++;
        if(start_addr.Equals(curr_addr)) {
          Console.WriteLine("Ring properly formed!");
          Console.WriteLine("This only took .... {0} seconds", (count * 5));
          break;
        }
      }

      foreach(DictionaryEntry de in nodes) {
        Node node = (Node)de.Value;
        node.Disconnect();
      }
    }
    public static void Main(string []args) {
      if (args.Length < 1) {
	Console.WriteLine("please specify the number edge protocol."); 
        Environment.Exit(0);
      }
      if (args.Length < 2) {
        Console.WriteLine("please specify the number of p2p nodes."); 
        Environment.Exit(0);
      }
      if (args.Length < 3) {
        Console.WriteLine("please specify the number of missing edges."); 
        Environment.Exit(0);
      }
      string proto = "function";
      try {
	proto = args[0].Trim();
      } catch(Exception) {}

      bool tunnel = false;
      int base_port = 54000;
      int network_size = Int32.Parse(args[1]);
      int missing_count = Int32.Parse(args[2]);
      try {
	tunnel = args[3].Trim().Equals("tunnel");
      } catch (Exception) {}

      Console.WriteLine("use tunnel edges: {0}", tunnel);

      Random rand = new Random();

      ArrayList missing_edges = new ArrayList();
      for (int i = 0; i < missing_count; i++) {
	int idx = -1;
	int left, right;
	do {
	  idx = rand.Next(0, network_size);
	  left = (idx + 1)%network_size;
	  if (idx == 0) {
	    right = network_size - 1;
	  } else {
	    right = idx - 1;
	  }
	} while (missing_edges.Contains(idx));// ||
	//missing_edges.Contains(left) ||
	//missing_edges.Contains(right));
	
	Console.WriteLine("Will drop a left edge on idx {0}: ", idx);
	missing_edges.Add(idx);
      }
      
      //
      // Sort missing edges.
      //
      missing_edges.Sort();
      SortedList dist = new SortedList();
      //
      // Compute the average distance between missing edges. 
      //
      if (missing_count > 1) {
	for (int i = 0; i < missing_count; i++) {
	  int idx = (int) missing_edges[i];
	  int idx_next;
	  int d;
	  if (i == missing_count - 1) {
	    idx_next = (int) missing_edges[0];
	    d = (network_size - 1) - idx + idx_next;
	  } else {
	    idx_next = (int) missing_edges[i+1];
	    d = idx_next - idx - 1;
	  }
	  if (!dist.Contains(d)) {
	    dist[d] = 0;
	  } else {
	    int c = (int) dist[d];
	    dist[d] = c + 1;
	  }
	}
      }
      double sum = 0.0;
      int num = 0;
      Console.WriteLine("distribution of missing edges separation");
      foreach(DictionaryEntry de in dist) {
	int k = (int) de.Key;
	int c = (int) de.Value;
	Console.WriteLine("{0} {1}", k, c);
	sum = sum + k*c;
	num = num + c;
      }

      Console.WriteLine("average separation: {0}", (double) sum/num);
      string brunet_namespace = "testing";
      Console.WriteLine("Initializing...");

      ArrayList RemoteTA = new ArrayList();
      for(int i = 0; i < network_size; i++) {
	if (proto.Equals("udp")) {
	  RemoteTA.Add(TransportAddressFactory.CreateInstance("brunet.udp://localhost:" + (base_port + i)));
	} else if (proto.Equals("function")) { 
	  RemoteTA.Add(TransportAddressFactory.CreateInstance("brunet.function://localhost:" + (base_port + i)));
	}
      }

      for(int i = 0; i < network_size; i++) {
        AHAddress address = new AHAddress(new RNGCryptoServiceProvider());
        Node node = new StructuredNode(address, brunet_namespace);
        _sorted_node_list.Add((Address) address, node);
	_node_list.Add(node);
	RouteTestHandler test_handler = new RouteTestHandler();
	node.GetTypeSource(new PType(routing_test)).Subscribe(test_handler, address.ToMemBlock());
	RpcManager rpc_man = RpcManager.GetInstance(node);
	rpc_man.AddHandler("rpc_routing_test", new  RpcRoutingTestHandler(node));
      }

      for (int i = 0; i < network_size; i++) {
	Node node = (Node) _sorted_node_list.GetByIndex(i);
	Console.WriteLine("Configuring node: {0} ", node.Address);
	TAAuthorizer ta_auth = null;
	if (missing_edges.Contains(i)) {
	  int remote_port;
	  if (i == network_size - 1) {
	    remote_port = base_port;
	  } else {
	    remote_port = base_port + i + 1;
	  }

	  PortTAAuthorizer port_auth = new PortTAAuthorizer(remote_port);
	  Console.WriteLine("Adding a port TA authorizer at: {0} for remote port: {1}", base_port + i, remote_port);
	  ArrayList arr_tas = new ArrayList();
	  arr_tas.Add(port_auth);
	  arr_tas.Add(new ConstantAuthorizer(TAAuthorizer.Decision.Allow));
	  ta_auth = new SeriesTAAuthorizer(arr_tas);
	}
	
	if (proto.Equals("udp")) { 
	  node.AddEdgeListener(new UdpEdgeListener(base_port + i, null, ta_auth));
	} else if(proto.Equals("function")) {
	  node.AddEdgeListener(new FunctionEdgeListener(base_port + i, -1.00, ta_auth));
	}
	
	if (tunnel) {
	  Console.WriteLine("Adding a tunnel edge listener");
	  node.AddEdgeListener(new Tunnel.TunnelEdgeListener(node));
	}
	_node_to_port[node] = base_port + i;
        node.RemoteTAs = RemoteTA;	
      }

      //start nodes one by one.
      for (int i  = 0; i < network_size; i++) {
	Node node = (Node) _node_list[i];
	Console.WriteLine("Starting node: {0}, {1}", i, node.Address);
        node.Connect();
	Console.WriteLine("Going to sleep for 2 seconds.");
        System.Threading.Thread.Sleep(2000);
      }

      //wait for 300000 more seconds
      Console.WriteLine("Going to sleep for 300000 seconds.");
      System.Threading.Thread.Sleep(300000);
      bool complete = CheckStatus();

      int count = 0;
      //
      // Send a large number of packets as exact packets to random destinations
      // and make sure exact routing is perfect.
      //
      
      for (int i = 0; i < network_size; i++) {
	for (int j = 0; j < network_size; j++) {
	  
	  int src_idx = i;
	  int dest_idx = j;
	  Node src_node = (Node) _sorted_node_list.GetByIndex(src_idx);
	  Node dest_node = (Node) _sorted_node_list.GetByIndex(dest_idx);
	  //Console.WriteLine("{0} -> {1}", src_idx, dest_idx);
	  Address dest_address = (Address) dest_node.Address;
	  ISender s = new AHExactSender(src_node, dest_address);
	  MemBlock p = dest_address.ToMemBlock();
	  s.Send(new CopyList(new PType(routing_test), p));
	  _sent++;
	  //System.Threading.Thread.Sleep(10);
	  s.Send(new CopyList(new PType(routing_test), p));
	  _sent++;
	  //System.Threading.Thread.Sleep(10);
	}
      }
      //wait for 10 more seconds
      Console.WriteLine("Going to sleep for 10 seconds.");
      System.Threading.Thread.Sleep(10000);      
      Console.WriteLine("Final statistics");
      lock(_class_lock) {
	Console.WriteLine("Sent: {0}, Received: {1}, Wrongly routed: {2}", 
			  _sent, _received, _wrongly_routed);
      }

      int missing_rpcs = 0;
      int correct_rpcs = 0;
      int incorrect_rpcs = 0;
      Hashtable queue_to_address = new Hashtable();
      for (int i = 0; i < network_size; i++) {
	for (int j = 0; j < network_size; j++) {
	  
	  int src_idx = i;
	  int dest_idx = j;
	  Node src_node = (Node) _sorted_node_list.GetByIndex(src_idx);
	  Node dest_node = (Node) _sorted_node_list.GetByIndex(dest_idx);
	  //Console.WriteLine("{0} -> {1}", src_idx, dest_idx);
	  Address dest_address = (Address) dest_node.Address;
	  ISender s = new AHExactSender(src_node, dest_address);
	  RpcManager rpc_man = RpcManager.GetInstance(src_node);
	  Channel q = new Channel();
	  lock (_class_lock) {
	    queue_to_address[q] = dest_address;
	  }
	  q.CloseAfterEnqueue();
	  q.CloseEvent += delegate(object o, EventArgs cargs) {
	    lock(_class_lock) {
	      Channel qu = (Channel) o;
	      if (qu.Count == 0) {
		missing_rpcs++;
	      }
	      queue_to_address.Remove(qu);
	    }
	  };
	  q.EnqueueEvent += delegate(object o, EventArgs cargs) {
	    lock(_class_lock) {
	      Channel qu = (Channel) o;
	      RpcResult rpc_reply = (RpcResult) qu.Peek();
	      byte []result = (byte[]) rpc_reply.Result;
	      Address target = new AHAddress(result);
	      if (target.Equals(queue_to_address[qu])) {
		correct_rpcs++;
	      } else {
		incorrect_rpcs++;
	      }
	    }
	  };
	  rpc_man.Invoke(s, q, "rpc_routing_test.GetIdentification", new object[]{});
	}
      }
      
      //wait for 10 more seconds
      while (true) {
	int c = -1;
	lock(_class_lock) {
	  c = incorrect_rpcs + missing_rpcs + correct_rpcs;
	}
	if (c < network_size*network_size) {
	  Console.WriteLine("Going to sleep for 10 seconds.");
	  System.Threading.Thread.Sleep(10000);
	} else {
	  break;
	}
      }
      
      Console.WriteLine("Final statistics");
      Console.WriteLine("correct rpcs: {0}, incorrect rpcs: {1}, missing rpcs: {2}", 
			correct_rpcs, incorrect_rpcs, missing_rpcs);
      
      System.Environment.Exit(1);
    }