//////////////////////////////////////////////////
package network;

// Standard packages.
import java.util.*;
import java.lang.*;
import java.io.*;
import java.awt.*;
import java.net.*;

// Packages specific to network design tool.
import lp.*;
import gui.*;
import general.*;
import physical.Place;


/* 
 * Network observes switches so as to update the bound upon
 * deletion or movement of switches.
 */
public class Network implements Observer {
  private final String swtName = "Switch";
  private final String cstName = "Constraint";
  private int swtCount = 0;
  private int cstCount = 0;
  // A collection of Switches and their interconnection by Links.
  public String description = null;
  public Set switches;
  public Vector switchVec;
  public Switch[] switchArray;

  public Set constraints; 

  public String lowerBoundText;
  public double[] traffic; // the traffic between switches
  public Set links;
  public Set allLinks; // holds the complete graph
  public Set linksInitial;   // the initial links, to be updated from linksB
  public Set linksSecond;
  public GeneralProperties gpData;

  public Frame frame;
  private String name;
  
  private Rectangle bounds;	    // Keeps extents of this network.
  private Rectangle prevBounds;       // compare with bounds
  // see if redraw is necessary
  public double lowerBound; // lower bound of the link costs
  public double cost = 0; // cost of this network.

  // flags which are modified by adding or deleting switches
  private boolean switchVecUpdated = false;
  private boolean switchArrayUpdated = false;
  private boolean constraintsUpdated = false;

  // dimension in one direction or two directions
  // it should take dSelectLinks.ONE_DIRECTION 
  // or dSelectLinks.TWO_DIRECTION
  private String linkDirection = null;;
  public String getLinkDirection() {
    return linkDirection;
  }
  public void setLinkDirection(String d) {
    linkDirection = d;
  }

  // links to be dimensioned
  private Hashtable linksToDimension = null;
  public Hashtable getLinksToDimension() {
    return linksToDimension;
  }
  public void setLinksToDimension(Hashtable h) {
    linksToDimension = h;
  }


  //-----------------------------------------------
  public Network(String Name)
       throws ClassNotFoundException { 
	 name = Name;
	 switches = new Set(Class.forName("network.Switch")); 
	 links = new Set(Class.forName("network.Link"));
	 allLinks = new Set(Class.forName("network.Link"));
	 linksInitial = new Set(Class.forName("network.Link"));
	 linksSecond = new Set(Class.forName("network.Link"));
	 constraints = new Set(Class.forName("network.SetPairConstraint"));
	 bounds = new Rectangle(0, 0, 0, 0);
  }

  /**
   * create a network with n switches randomly scattered inside
   * the rectangle (0, 0, 400, 400).
   * alpha and omega are uniformly distributed among [0,1).
   */
  public Network(int n) throws ClassNotFoundException {
    this("random network");
    Rectangle AREA = new Rectangle(0, 0, 400, 400);
    double LOCAL_PERCENTAGE= 0.80;
    double LOCAL_RADIUS = 100.0;
    double TRAFFIC_MAX = 1.0;
    Switch s;

    // generate switches
    double x, y, alpha, omega;
    Random random = new Random();
    for (int i = 0; i < n; i++) {
      x = random.nextDouble()*AREA.width + AREA.x;
      y = random.nextDouble()*AREA.height + AREA.y;
      alpha = random.nextDouble()*TRAFFIC_MAX;
      omega = random.nextDouble()*TRAFFIC_MAX;
      s = new Switch("S_" + switches.size(), x, y, alpha, omega);
      addSwitch(s);
    }
    //  addLocalTrafficConstraint(LOCAL_PERCENTAGE, LOCAL_RADIUS);
  }

  /**
   * add local traffic constraints.
   * percentage of the traffic is outside of the radius.
   */
  public void 
  addLocalTrafficConstraint(double percentage, double radius, 
			    int minNeighbor, int maxNeighbor ) {
    try {
      Set set1, set2;
      SetPairConstraint spc;
      updateSwitchArray();
      for (int i = 0; i < switchArray.length; i++) {
	set1 = new Set(Class.forName("network.Switch"));
	set2 = new Set(Class.forName("network.Switch"));
	set1.put(switchArray[i].getName(), switchArray[i]);
      
	// constraint limiting the remote traffic
	for (int j = 0; j < switchArray.length; j++) {
	  if (Switch.distance(switchArray[i], switchArray[j]) > radius) 
	    set2.put(switchArray[j].getName(), switchArray[j]);
	}
	if (set2.size() < switchArray.length - (maxNeighbor+1) ||
	    set2.size() > switchArray.length - (minNeighbor+1))
	  set2 = RemoteTrafficConstraint.remoteSwitches(switchArray[i], switches, radius,
							minNeighbor, maxNeighbor);
	spc = new SetPairConstraint("C_" + constraints.size(),
				    set1, set2,
				    percentage*switchArray[i].getAlpha(),
				    percentage*switchArray[i].getOmega());
	constraints.put(spc.getName(), spc);
      }
    }
    catch (ClassNotFoundException e) {
      System.out.println("Network.addLocalTrafficConstraint: " + e.getMessage());
    }
  }

  /**
   * add pairwise traffic constraints.
   */
  public void 
  addPairwiseTrafficConstraint(double relaxFactor) {
    Set set1, set2;
    SetPairConstraint spc;
    updateSwitchArray();
    double sigmaAlpha = 0, sigmaOmega = 0;
    double fij, fji, gij, gji, alpha, omega;
    for (int i = 0; i < switchArray.length; i++) {
      sigmaAlpha += switchArray[i].getAlpha();
      sigmaOmega += switchArray[i].getOmega();
    }
    for (int i = 0; i < switchArray.length-1; i++) {
      set1 = new Set(switchArray[0].getClass());
      set1.put(switchArray[i].getName(), switchArray[i]);
      for (int j = i+1; j < switchArray.length; j++) {
	set2 = new Set(switchArray[j].getClass());
	set2.put(switchArray[j].getName(), switchArray[j]);

	fij = switchArray[j].getOmega()/(sigmaOmega - switchArray[i].getOmega());
	fji = switchArray[i].getOmega()/(sigmaOmega - switchArray[j].getOmega());
	gij = switchArray[i].getAlpha()/(sigmaAlpha - switchArray[j].getAlpha());
	gji = switchArray[j].getAlpha()/(sigmaAlpha - switchArray[i].getAlpha());

	alpha = relaxFactor*Math.min(fij*switchArray[i].getAlpha(), 
				     gij*switchArray[j].getOmega());
	omega = relaxFactor*Math.min(fji*switchArray[j].getAlpha(),
				     gji*switchArray[i].getOmega());

	spc = new SetPairConstraint("C_" + constraints.size(),
				    set1, set2,
				    alpha, omega);
	constraints.put(spc.getName(), spc);
      }
    }
  }

  //----------------------------------------------------
  // create a network from the input file
  //
  public Network(InputStream f) throws IOException, GenericException, 
    ClassNotFoundException
  {
    this("");

    Switch s;
    Link l;
    SetPairConstraint spc;

    StreamTokenizer stk = new StreamTokenizer(f);
    stk.slashStarComments(true);
    stk.slashSlashComments(true);
    stk.wordChars('_', '_');
  
    int tokenType;
  
    // get the name of the network
    while( stk.nextToken() != StreamTokenizer.TT_WORD );
    name = stk.sval;

    while ( (tokenType = stk.nextToken()) != StreamTokenizer.TT_EOF ) {
      if (tokenType == '<' ) {
	stk.pushBack();
	s = new Switch(stk);
	addSwitch(s);
	//System.out.println(s);
      }
      else if (tokenType == '[' ) {
	stk.pushBack();
	l = readLink(stk);
	addLink(l);
	//System.out.println(l);
      }
      else if (tokenType == '{' ) {
	stk.pushBack();
	spc = readSetPairConstraint(stk);
	addConstraint(spc);
	//System.out.println(spc);
      }
    }
  }


  /**
   * calculate link dimensions
   */
  public void calculateLinkDimension() {
    updateSwitchVec();
    updateSwitchArray();
    updateConstraints();
    clearAllRefPaths(links);
    getAllPairsShortestPathes(links);
    this.cost = dimensioningLinks();
  }

  /**
   * calculate overall lower bounds, which is based on the complete graph.
   */
  public void calculateOverAllLowerBound() {
    try {
      // clear the link set "allLinks"
      removeAllLinks(allLinks);
      allLinks = completeGraph();
      calcLowerBound(allLinks);
    }
    catch (ClassNotFoundException e) {
    }
  }

  /** LowerBound is calculated on the complete graph.
   *  so, first get the complete graph, then calculate
   *  lower bound.
   */
  public String getLowerBoundText() {
    String s = null;
    try {
      // clear the link set "allLinks"
      removeAllLinks(allLinks);
      allLinks = completeGraph();
      return getLowerBoundText(allLinks);
    }
    catch (ClassNotFoundException e) {
    }
    return s;
  }

  /**
   * calculate lower bounds on link costs.
   */
  public void calculateLowerBound(Set curLinks) {
    updateSwitchVec();
    updateSwitchArray();
    updateConstraints();
    clearAllRefPaths(curLinks);
    getAllPairsShortestPathes(curLinks);
  
    double[] coef;
    int src, dst;
    int n = switches.size();
    coef = new double[ n*n + 1 ];
    solve problem = new solve();
    lprec lpRec = new lprec(0, n*n);
    setConstraint(problem, lpRec, n);

    int i, j, k1, k2;
    for (i = 0; i < n; i++) {
      for (j = 0; j < n; j++) {
	coef[ i*n + j + 1 ] = dist[i][j];
      }
    }
    problem.set_obj_fn(lpRec, coef);
    // lpProblem.print_lp(lpRec);
    if (problem.solve(lpRec) != constant.OPTIMAL)
      System.err.println("no optimal result in lpsolve");
    // lpProblem.print_solution(lpRec);
    this.lowerBound = lpRec.getBestSolution(0);
    this.traffic = new double[lpRec.getColumns()];
    int nRows = lpRec.getRows();
    for (i = 0; i < traffic.length; i++) 
      traffic[i] = lpRec.getBestSolution(i+ nRows + 1);
    System.out.println("Lower Bound = " + this.lowerBound);
  }      

  //-------------------------------------------
  public double getCost() {
    return cost;
  }

  //--------------------------------------------
  public double getLowerBound() {
    return lowerBound;
  }

  //-------------------------------------------------------
  // read a set of switches from their names: {n1, n2, ...}
  //
  public Set readSwitchSet(StreamTokenizer stk)
       throws IOException, ClassNotFoundException {
	 int tokenType;
	 Switch s;
	 Set set = new Set(Class.forName("network.Switch"));
	 while (stk.nextToken() != '{');
	 while ( (tokenType = stk.nextToken()) != '}' ) {
	   if (tokenType == StreamTokenizer.TT_WORD
	       && ((s = (Switch)switches.get(stk.sval)) != null))
	     set.put(s.getName(), s);
	 }
	 return set;
  }

  //----------------------------------------------------
  // read a set of switches from a string: "{s1, s2, ...}"
  //
  public Set readSwitchSet(String s) {
    Set set = null;

    try {
      set = new Set(Class.forName("network.Switch"));
      String token;
      Switch swt;
      StringTokenizer stk = new StringTokenizer(s, "\t\n\r ,", false);
      while ( !(stk.nextToken()).equals("{"));
      while ( stk.hasMoreTokens() && !(token = stk.nextToken()).equals("}") ) {
	if ( (swt = (Switch)switches.get(token)) != null) {
	  set.put(swt.getName(), swt);
	}
      }
    }
    catch (NoSuchElementException e) {
    }
    catch (ClassNotFoundException e) {
    }
    return set;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String des) {
    description = des;
  }

  //-----------------------------------------------
  // read constraint of the network from input file
  // called only by the constructor
  // format :
  // {name {set1} {set2}(optional) out in}
  public SetPairConstraint readSetPairConstraint(StreamTokenizer stk)
       throws IOException, ClassNotFoundException, GenericException {
	 int tokenType;
	 Set swtSet1, swtSet2 = null;
	 String name;
	 double out, in;

	 // read the starting '{'
	 while (stk.nextToken() != '{');

	 // read the constraint name;
	 while (stk.nextToken() != StreamTokenizer.TT_WORD);
	 name = stk.sval;

	 // read the first set in the constraint
	 swtSet1 = readSwitchSet(stk);
  
	 while ( (tokenType = stk.nextToken()) != StreamTokenizer.TT_NUMBER 
		 && tokenType != '{' );
  
	 // read the second set in the constraint, if there is a second set
	 if (tokenType == '{') {
	   stk.pushBack();
	   swtSet2 = readSwitchSet(stk);
	 }
	 else // we have over read the next value, it should be "out", push it back
	   stk.pushBack();
  
	 // get the value of out flow
	 while ( stk.nextToken() != StreamTokenizer.TT_NUMBER );
	 out = stk.nval;

	 // get the value of in flow
	 while ( stk.nextToken() != StreamTokenizer.TT_NUMBER );
	 in = stk.nval;

	 // read the closing "}"
	 while (stk.nextToken() != '}');
  
	 if (swtSet1.size() == 0)
	   throw (new GenericException("set size zero in SetPairConstraint"));

	 SetPairConstraint spc;
	 if(swtSet2 == null) {
	   spc = new SetPairConstraint(name, swtSet1, in, out);
	   spc.update(switches);
	 }
	 else
	   spc = new SetPairConstraint(name, swtSet1, swtSet2, in, out);

	 return spc;
  }

  /** add constraint without checking if the name is already used. */
  public void addConstraint(SetPairConstraint spc) {
    constraints.put(spc.getName(), spc);
  }

  /** create an empty constraint, and add it to the set. */
  public void addConstraint() {
    String newCstName;
    do {
      ++ cstCount;
      newCstName = cstName + cstCount;
    }
    while (constraints.containsKey(newCstName));
    SetPairConstraint spc = new SetPairConstraint(newCstName, switches, switches, 1, 1);
    addConstraint(spc);
  }

  //---------------------------------------------------
  // called only by constructor
  // Link format:
  // [name left_switch_name right_switch_name length]
  //
  private Link readLink(StreamTokenizer stk) throws IOException{
    int tokenType;
    String leftSwitch, rightSwitch;
    double length = 0;
    double capL2R = 0, capR2L = 0;
    String name;
    Link l;

    // get the starting '['
    while (stk.nextToken() != '[');

    // get the name
    while (stk.nextToken() != StreamTokenizer.TT_WORD);
    name = stk.sval;

    // get the name of left switch  
    while (stk.nextToken() != StreamTokenizer.TT_WORD);
    leftSwitch = stk.sval;
  
    // get the name of right switch
    while (stk.nextToken() != StreamTokenizer.TT_WORD);
    rightSwitch = stk.sval;
  
    while ( (tokenType = stk.nextToken()) != StreamTokenizer.TT_NUMBER &&
	    tokenType != ']');
    if (tokenType == StreamTokenizer.TT_NUMBER) {
      // get the number, it would be the length of the link
      length = stk.nval;
    
      // possibly capL2R and capR2L
      while ( (tokenType = stk.nextToken()) != StreamTokenizer.TT_NUMBER &&
	      tokenType != ']');
      if (tokenType == StreamTokenizer.TT_NUMBER) {
	// get the number, it would be the capL2R
	capL2R = stk.nval;
	// get capR2L
	while ( (tokenType = stk.nextToken()) != StreamTokenizer.TT_NUMBER &&
		tokenType != ']');     
	if (tokenType == StreamTokenizer.TT_NUMBER) {
	  capR2L = stk.nval;
	  while ( stk.nextToken() != ']' );
	} // end of capR2L
      } // end of capL2R
    } // end of length

    // and length should not be less than the Euclid distance
  
    Switch s1 = (Switch)switches.get(leftSwitch);
    Switch s2 = (Switch)switches.get(rightSwitch);
    l = new Link(name, s1, s2);
    l.setLength(length);
    l.setL2R(capL2R);
    l.setR2L(capR2L);
    return l;
  }

  //------------------------------------------------
  public String getName(){
    return name;
  }

  //-------------------------------------------------    
  /*
    public Switch getSwitch(int n) {
    return sarray[n];
    }
    */

  //-------------------------------------------------
  public int numSwitches() {
    return switches.size();
  }

  //--------------------------------------------    
  public int numLinks() {
    return links.size();
  }

  //--------------------------------------------
  // set the bounds from coordinates of switches
  public void setBounds() {
    if (switches.size() == 0) {
      bounds = new Rectangle(0, 0, 0, 0);
      return;
    }
    int sx, sy;
    Enumeration e = switches.elements();
    Switch s = (Switch)e.nextElement();
    sx = (int)s.getX();
    sy = (int)s.getY();
    bounds = new Rectangle(sx, sy, 0, 0);
    while (e.hasMoreElements()) {
      s = (Switch)e.nextElement();
      sx = (int)s.getX();
      sy = (int)s.getY();
      bounds.add(sx, sy);
    }
  }

  //--------------------------------------------
  // adjust the bounds by adding a switch
  public void setBounds(Switch s) {
    int sx, sy;
    sx = (int)s.getX();
    sy = (int)s.getY();
    if (switches.size() == 1)
      bounds = new Rectangle(sx, sy, 0, 0);
    else
      bounds.add(sx, sy);
  }

  public synchronized void addSwitch(Switch s) throws GenericException {
    switches.put(s.getName(), s); 
    switchVecUpdated = false;
    switchArrayUpdated = false;
    constraintsUpdated = false;
    setBounds(s);

    // add the network.Network as an observer.
    // any change of the switch will notify the network, 
    // so as to adjust the bounds, and etc
    s.addObserver(this);
    //updateLinks();
  }

  /** 
   * create a new switch and add it to the switch set. 
   * the name of the new switch is in the format of swtName + swtCount.
   */
  public void addSwitch() {
    String newSwtName;
    do {
      ++swtCount;
      newSwtName = swtName + swtCount;
    }
    while (switches.containsKey(newSwtName));

    Switch swt = new Switch(newSwtName);
    addSwitch(swt);
  }

  /**
   * change the name of the switch. return true if the new name is not used
   * by another switch, false otherwise.
   */
  public synchronized boolean changeName(Switch s, String name) {
    if (switches.containsKey(name))
      return false;

    switches.remove(s.getName());
    s.setName(name);
    switches.put(s.getName(), s);
    return true;
  }

  /**
   * change the name of the Constraint. return true if the new name is 
   * not used by another switch, false otherwise.
   */
  public synchronized boolean changeName(SetPairConstraint s, String name) {
    if (constraints.containsKey(name))
      return false;

    constraints.remove(s.getName());
    s.setName(name);
    constraints.put(s.getName(), s);
    return true;
  }


  //--------------------------------------------    
  public synchronized void removeSwitch(Switch s) {
    if (!switches.containsKey(s.getName()))
      return;

    // remove links which are connected to the switch
    removeRelatedLinks(s);

    // the Network no longer cares about this switch
    s.deleteObserver(this);

    switches.remove(s.getName());
    switchVecUpdated = false;
    switchArrayUpdated = false;
    constraintsUpdated = false;
    setBounds();
  }

  /**
   * remove links which are connected to the specified switch
   */
  public synchronized void removeRelatedLinks(Switch s) {
    Link link;
    for (Enumeration e = links.elements(); e.hasMoreElements();) {
      link = (Link)e.nextElement();
      if (link.getL().equals(s) || link.getR().equals(s)) {
	links.remove(link.getName());
      }
    }
  }

  //--------------------------------------------    
  public synchronized void removeConstraint(Object key) {
    constraints.remove(key);
  }

  /**
   * clear constraints. i.e. remove all constraints
   */
  public synchronized void clearConstraints() {
    try {
      constraints = new Set(Class.forName("network.SetPairConstraint"));
    }
    catch (ClassNotFoundException e) {
      System.out.println("Network.clearConstraints:" + e.getMessage());
    }
  }


  //--------------------------------------------
  public synchronized void addLink(Link l) throws GenericException {
    try {
      links.put(l.getName(), l); 
    }
    catch (Exception e) {
      throw(new GenericException(this, "In addLink(): " + e.getMessage()));
    } 
  }

  /**
   * remove a link from a link set
   * before the link is destroyed,
   * remove it from the observables.
   */
  public synchronized void removeLink(Set s, Link l) {
    if(s.remove(l.getName()) != null) {
      l.getL().deleteObserver(l);
      l.getR().deleteObserver(l);
    }
  }

  /**
   * remove all links from the set of links
   */
  public synchronized void removeAllLinks(Set s) {
    Link link;
    if (s == null) return;
    for (Enumeration e = s.elements(); e.hasMoreElements();) {
      link = (Link)e.nextElement();
      link.getL().deleteObserver(link);
      link.getR().deleteObserver(link);
      s.remove(link.getName());
    }
  }

  /**
   * return a set of links which form the complete graph
   */
  public synchronized Set completeGraph() throws ClassNotFoundException{
    Set l = new Set(Class.forName("network.Link"));
    Link link;
    updateSwitchArray();
    int n = switches.size();
    if (n < 2) return l;
    for (int i = 0; i < n; i++) {
      for (int j = i+1; j < n; j++) {
	link = new Link("C_" + switchArray[i].getName()
+ switchArray[j].getName(),
switchArray[i], switchArray[j]);
	l.put(link.getName(), link);
      }
    }
    return l;
  }


  /**
   * return a set of links which form the best star network
   */
  public Set bestStarNetwork() throws ClassNotFoundException {
    String tn1, tn2; // tn stands for "traffic name"
    String rdata;
    Set l = new Set(Class.forName("network.Link"));
    updateSwitchArray();
    int n = switches.size();
    if (n < 2) return l;
    dist = new double[n][n];
    double x, y;
    int i, j, c, node;
    StringBuffer sbuf1, sbuf2;
    double capacity;

    // calculate out(u) and in(u) by solving a maximum flow problem
    // as in JAF's thesis page 58
    double[] out, in;
    out = new double[n];
    in = new double[n];
    if (constraints.size() != 0) { // calculate in[] and out[]
      for (node = 0; node < n; node++) {
	sbuf1 = new StringBuffer();    
	sbuf2 = new StringBuffer();
	sbuf1.append("max: ");
	sbuf2.append("max: ");

	// keep track of all connections specified in the objective function
	// those connections not contained in the objective function
	// are discarded from the constrants to speed up the computation
	Hashtable ht1 = new Hashtable();
	Hashtable ht2 = new Hashtable();
    
	for (j = 0; j < n; j++) {
	  tn1 = getTrafficName(switchArray[node], switchArray[j]);
	  ht1.put(tn1, tn1);
	  tn2 = getTrafficName(switchArray[j], switchArray[node]);
	  ht2.put(tn2, tn2);
	  sbuf1.append(" + " + tn1);
	  sbuf2.append(" + " + tn2);
	}
	sbuf1.append(";\n");
	sbuf2.append(";\n");
      
	// specify the constraints
	sbuf1.append(getConstraintsText(ht1));
	sbuf2.append(getConstraintsText(ht2));

	rdata = CGI.linearProgramming(sbuf1.toString());
	out[node] = getObjectiveValue(rdata);

	rdata = CGI.linearProgramming(sbuf2.toString());
	in[node] = getObjectiveValue(rdata);
      }
    }
    else { // no constraints, so out[] and in[] are the same as alpha[] and omega[]
      double totalAlpha = 0;
      double totalOmega = 0;
      for (i = 0; i < n; i++) {
	totalAlpha += switchArray[i].getAlpha();
	totalOmega += switchArray[i].getOmega();
      }
      for (i = 0; i < n; i++) {
	out[i] = totalOmega - switchArray[i].getOmega() < switchArray[i].getAlpha() ?
	  totalOmega - switchArray[i].getOmega() : switchArray[i].getAlpha();
	in[i]  = totalAlpha - switchArray[i].getAlpha() < switchArray[i].getOmega() ?
	  totalAlpha - switchArray[i].getAlpha() : switchArray[i].getOmega();
      }
    }

    // get distances between switches
    for (i = 0; i < n; i++) {
      dist[i][i] = 0;
      for (j = i+1; j < n; j++) {
	dist[i][j] = Switch.distance( switchArray[i], switchArray[j]);
	dist[j][i] = dist[i][j];
      }
    }

    // find the best center
    double bestCost = Double.MAX_VALUE;
    double thisCost;
    int center = 0;
    for (c = 0; c < n; c++) {
      thisCost = 0;
      for (j = 0 ; j < n; j++) {
	thisCost += dist[c][j]*(switchArray[j].getAlpha()
+ switchArray[j].getOmega());
      }
      if (bestCost > thisCost) {
	bestCost = thisCost;
	center = c;
      }
    }
    cost = bestCost;

    // add new links to Set l
    Link link;
    for (i = 0; i < n; i++) {
      if (i != center) {
	link = new Link("S_"+switchArray[center].getName()
			+"_"+switchArray[i].getName(),  
			switchArray[i], switchArray[center]);
	link.setL2R(out[i]);
	link.setR2L(in[i]);
	l.put(link.getName(), link);
      }
    }
    return l;
  }

  //---------------------------------------------------------
  /* Following are the i/o methods */
  /*
    public void readSwitches(DataInputStream d) throws GenericException {
    Switch s = new Switch("Switch", new Place("Place", 0, 0, 0, 0));
    boolean EOF = false;
    try {
    while (s.readValues(d)) {
    addSwitch(s);
    s = new Switch("Switch", new Place("Place", 0, 0, 0, 0));
    } 
    } catch (Exception e) {
    throw(new GenericException("In readSwitches(): " + e.getMessage()));
    }    
    }
    */
  /*
    //----------------------------------------------------
    public void outStreamSwitches(DataOutputStream d) {
    try {
    for (Enumeration e = switches.elements() ; e.hasMoreElements() ;) {
    ((Switch) e.nextElement()).outStream(d);
    }
    } catch (Exception e) {
    throw(new GenericException(this, "In outStreamSwitches(): " +
    e.getMessage())); 
    }    
    }
    */

  /*
    //---------------------------------------------------------
    public Switch bestCenterFromAsciiFile(String FileName) {
    Switch s1 = new Switch("Switch", new Place("Place", 0, 0, 0, 0));
    try {
    DataInputStream i = new DataInputStream(new FileInputStream(FileName)) ;
    readSwitches(i);  
    findDistances(); 
    printDistances();
    s1 = bestStarCenter();
    for (Enumeration e = switches.elements() ; e.hasMoreElements() ;) {
    Integer j = new Integer(numL);
    Link l = new Link((Switch)e.nextElement(),s1,j.toString());
    addLink(l);
    }

    }
    catch(Exception e) {
    throw(new GenericException(this, "In bestCenterFromAsciiFile(): " +
    e.getMessage())); 
    }
    return s1;
    }
    */

  //-------------------------------------------------
  public void print() {
    System.out.println(this);
  }

  //--------------------------------------------------------
  public String toString() {
    StringBuffer b = new StringBuffer("// network name\n");
    b.append(name + "\n");

    b.append("// Description:\n");
    b.append(description + "\n");

    b.append("// Switches:\n");
    for (Enumeration e = switches.elements() ; e.hasMoreElements() ;) {
      b.append((Switch)e.nextElement());
      b.append("\n");
    }
  
    b.append("// Links:\n");
    for (Enumeration e = links.elements() ; e.hasMoreElements() ;) {
      b.append((Link)e.nextElement());
      b.append("\n");
    }

    b.append("// Constraints:\n");
    for (Enumeration e = constraints.elements(); e.hasMoreElements(); ) {
      b.append((SetPairConstraint)e.nextElement());
      b.append("\n");
    }
    return b.toString();
  }

  //-----------------------------------------------------
  public Rectangle getBounds() {
    // REQUIRES: private variable bounds is set properly.
    // RETURNS: Rectangle for covering the switches of this network.
    //          which is 10% larger than the bounds.
    // PROMISES: nothing.
    int x, y, width, height;
    x = (int)Math.floor(bounds.x - bounds.width*0.05);
    y = (int)Math.floor(bounds.y - bounds.height*0.05);
    width = (int)Math.ceil(bounds.width*1.10);
    height = (int)Math.ceil(bounds.height*1.10);
    return new Rectangle(x, y, width, height);
  }

  // SetPairConstraints, PlaceSet should be replaced by SwitchSet

  // when new links are put in, it should be cleared
  private boolean adjacentConstructed;


  // class link should have variables capL2R and capR2L, and length
  // and 2 Vectors of switch pairs whose shortest path use this link
  // Vector of links
  // public Vector linkVec; 

  // these variables are used internally by the Dijkstra's algorithm
  // to find out the shortest path.
  Vector[] adjs; 
  int[][] predecessor;
  double[][] dist; 
  Link[][] edge; // matrix of links

  /**
   * fills "adjs" and "edge" based on set of links: curLink,
   * to be used by Dijkstra's algorithm.
   */
  private void constructAdjacentAndEdge(Set curLinks) {
    int n = switches.size();
    int i, l, r;
    Link tmpLink;
    updateSwitchVec();
    adjs = new Vector[n];
    edge = new Link[n][n];
    for (i = 0; i < n; i++)
      adjs[i] = new Vector();
    for (Enumeration e = curLinks.elements(); e.hasMoreElements(); ) {
      tmpLink = (Link)e.nextElement();
      l = switchVec.lastIndexOf( tmpLink.getL() );
      r = switchVec.lastIndexOf( tmpLink.getR() );

      // every link corresponds to two edges
      edge[l][r] = tmpLink;
      edge[r][l] = tmpLink;
      adjs[l].addElement(new Integer(r));
      adjs[r].addElement(new Integer(l));
    }
  }

  /**
   * called by getAllPairsShortestPathes() and singleSourceShortestPathes(),
   * predecessor[][] should already be declared
   * initialize predecessor[][] and selected[]
   */
  private boolean initializeSingleSource(int s, boolean[] selected) {
    if (predecessor.length < s+1)
      return false;
    if (predecessor[s].length < switches.size())
      return false;
    for (int i = 0; i < switches.size(); i++) {
      predecessor[s][i] = -1;
      selected[i] = false;
    }
    return true;
  }

  /**
   * get all pairs shortest pathes based on the set of current links.
   */
  private void getAllPairsShortestPathes(Set curLinks) {
    int n = switches.size();
    updateSwitchVec();
    updateSwitchArray();
    predecessor = new int[n][n];
    dist = new double[n][n];
    int i, j, index = 0;
    for (i = 0; i < n; i++)
      for (j = 0; j < n; j++)
	dist[i][j] = Double.MAX_VALUE;

    double min;
    constructAdjacentAndEdge(curLinks);
    for (int src = 0; src < n; src++) {
      // set the dist[][] and predecessor[][] accordingly
      singleSourceShortestPathes(src);
    }

    // print out dist and predecessor. Just for debug
    /*
      System.out.println("dist = ");
      for (i = 0; i < n; i++) {
      for (j = 0; j < n; j++)
      System.out.print(dist[i][j] + " ");
      System.out.println();
      }
      System.out.println("predecessor = ");
      for (i = 0; i < n; i++) {
      for (j = 0; j < n; j++)
      System.out.print(predecessor[i][j] + " ");
      System.out.println();
      }
      */
  } // end of getAllPairsShortestPathes()

  /**
   * get single source shortest pathes.
   * edge[][], dist[][], predecessor[][] should be initialized.
   */
  public void singleSourceShortestPathes(int src) {
    int n = switches.size();
    boolean[] selected = new boolean[n];
    initializeSingleSource(src, selected);
    dist[src][src] = 0;
    double min;
    int i, j, index = 0;
    /*
      System.out.println("in single source, dist = ");
      for (i = 0; i < n; i++) {
      for (j = 0; j < n; j++)
      System.out.print(dist[i][j] + " ");
      System.out.println();
      }
      */
    for (int count = 0; count < n; count++) {
      // extract min dist from the src
      min = Double.MAX_VALUE;
      for (i = 0; i < n; i++) {
	if (selected[i] == false && dist[src][i] < min) {
	  min = dist[src][i];
	  index = i;
	}
      }
      selected[index] = true;
      for (Enumeration e = adjs[index].elements(); e.hasMoreElements();) {
	j = ((Integer)e.nextElement()).intValue();
	// do relax on all switches adjacent to the selected one
	if (edge[index][j] != null &&
	    dist[src][j] > dist[src][index] + edge[index][j].length) {
	  dist[src][j] = dist[src][index] + edge[index][j].length;
	  predecessor[src][j] = index;
	}
      }
    }
  }


  //--------------------------------------------------
  // set reference path on links on the path from switch i to switch j
  // take care of the directions. every reference is a switch pair.
  private synchronized void setRefPaths(int i, int j) {
    SwitchPair sp1, sp2; 
    if ( i == j ) return;
    // a path from sp.l to sp.r will pass the link from left to right
    sp1 = new SwitchPair( (Switch)switchVec.elementAt(i),
			  (Switch)switchVec.elementAt(j) );
    sp2 = new SwitchPair( (Switch)switchVec.elementAt(j), 
			  (Switch)switchVec.elementAt(i) );
    int src = i;
    int dst = j;
    int pred;
    while ( (pred = predecessor[src][dst]) != -1 ) {
      if (edge[pred][dst].getL().equals(switchVec.elementAt(pred)))
	edge[pred][dst].refPath.addElement(sp1);
      else
	edge[pred][dst].refPath.addElement(sp2);
      dst = pred;
    }
  } // end of setRefPaths()

  //-------------------------------------------------
  // clear all reference path on the set of links. called by dimensioningLinks()
  //
  private void clearAllRefPaths(Set l) {
    for( Enumeration e = l.elements(); e.hasMoreElements();) {
      ((Link)e.nextElement()).refPath.removeAllElements();
    }
  }

  /*
    solve lpProblem;
    lprec lpRec;
    */

  private void clearArrayOfDouble( double[] x ) {
    for (int i = 0; i < x.length; i++)
      x[i] = 0;
  }

  //------------------------------------------
  // set constraint for lp_solve
  //
  private void setConstraint(solve lpProblem, lprec lpRec, int n) {
    double coef1[], coef2[];
    int src, dst;
    coef1 = new double[ n*n + 1 ];
    coef2 = new double[ n*n + 1 ];

    int i, j, k1, k2;

    lpProblem.set_maxim(lpRec);
    clearArrayOfDouble(coef1);

    // should set_obj_fn() before add any constraint
    // the actual objective function will be set later
    lpProblem.set_obj_fn(lpRec, coef1);
    // lpProblem.print_lp(lpRec);

    // add constraints from alpha and omega
    for (src = 0; src < n; src++) {
      clearArrayOfDouble(coef1);
      clearArrayOfDouble(coef2);
      for (dst = 0; dst < n; dst++) {
	if (dst != src) {
	  k1 = src*n + dst + 1;
	  k2 = dst*n + src + 1;
	  coef1[k1] = 1;
	  coef2[k2] = 1;
	}
      }
      lpProblem.add_constraint( lpRec, coef1, constant.LE,
				((Switch)switchVec.elementAt(src)).getAlpha() );
      lpProblem.add_constraint( lpRec, coef2, constant.LE,
				((Switch)switchVec.elementAt(src)).getOmega() );
    }    

    // now add link constraints
    SetPairConstraint spc;
    double rhs1, rhs2;
    for (Enumeration e = constraints.elements();
	 e.hasMoreElements(); ) {
      spc = (SetPairConstraint)e.nextElement();
      rhs1 = spc.out;
      rhs2 = spc.in;
      clearArrayOfDouble(coef1);
      clearArrayOfDouble(coef2);

      Enumeration sA = spc.a.elements(); // switches on side A
      for (; sA.hasMoreElements();) {
	src = switchVec.indexOf((Switch)sA.nextElement());
	// System.out.print("src = " + src);
	Enumeration sB = spc.b.elements(); // switches on side B
	for (; sB.hasMoreElements();) {
	  dst = switchVec.indexOf((Switch)sB.nextElement());
	  // System.out.print(" dst = " + dst);
	  // coef1, rhs1, k1: flow from side A to side B
	  // ocef2, rhs2, k2: flow from side B to side A
	  k1 = src*switchVec.size() + dst + 1;
	  k2 = dst*switchVec.size() + src + 1;
	  coef1[k1] = 1;
	  coef2[k2] = 1;
	}
      }
      lpProblem.add_constraint(lpRec, coef1, constant.LE, rhs1);
      lpProblem.add_constraint(lpRec, coef2, constant.LE, rhs2);
    }
    /*
      System.out.println("lp problem constructed in setConstraint");
      lpProblem.print_lp(lpRec);
      */
  }      

  /**
   * should get all pairs shortest paths first.
   */
  private double dimensioningLinks() {
    int n = switches.size();
    int i, j, k1, k2;
    Switch src, dst;
    Link curLink;
    SetPairConstraint spc;
    double cost = 0;
    double capacity;
    String constraintsInLPFormat = getConstraintsText();
    String rdata;
    String tn1, tn2; // tn stands for "traffic name"
    StringBuffer sbuf1 = null;
    StringBuffer sbuf2 = null;
    // mark the paths used by traffic from switch i to switch j
    for (i = 0; i < n; i++) {
      for (j = i+1; j < n; j++) {
	setRefPaths(i,j);
      }
    }

    for (Enumeration e = links.elements(); e.hasMoreElements(); ) {
      curLink = (Link)e.nextElement();
      sbuf1 = new StringBuffer();    
      sbuf2 = new StringBuffer();
      sbuf1.append("max: ");
      sbuf2.append("max: ");

      // keep track of all connections specified in the objective function
      // those connections not contained in the objective function
      // are discarded from the constrants to speed up the computation
      Hashtable ht1 = new Hashtable();
      Hashtable ht2 = new Hashtable();
    
      for (j = 0; j < curLink.refPath.size(); j++) {
	src = (Switch)(((SwitchPair)curLink.refPath.elementAt(j)).l);
	dst = (Switch)(((SwitchPair)curLink.refPath.elementAt(j)).r);
	tn1 = getTrafficName(src, dst);
	ht1.put(tn1, tn1);
	tn2 = getTrafficName(dst, src);
	ht2.put(tn2, tn2);
	sbuf1.append(" + " + tn1);
	sbuf2.append(" + " + tn2);
      }
      sbuf1.append(";\n");
      sbuf2.append(";\n");

      // specify the constraints
      sbuf1.append(getConstraintsText(ht1));
      sbuf2.append(getConstraintsText(ht2));
      rdata = CGI.linearProgramming(sbuf1.toString());
      System.out.print(curLink.getName() + " L2R:" );
      System.out.println(capacity = getObjectiveValue(rdata));
      curLink.setL2R(capacity);
      cost += capacity*curLink.getLength();
      rdata = CGI.linearProgramming(sbuf2.toString());
      System.out.print(curLink.getName() + " R2L:" );
      System.out.println(capacity = getObjectiveValue(rdata));
      curLink.setR2L(capacity);
      cost += capacity*curLink.getLength();
    }
    return cost; // just a double value for now
  } // end of dimensioningLinks()

  /** return formatted traffic on selected links */
  public Vector linkDimensioningText(Hashtable selectedLinks) {
    int n = switches.size();
    int i, j, k1, k2;
    Switch src, dst;
    Link curLink;
    SetPairConstraint spc;
    double cost = 0;
    double capacity;
    float weight;
    Vector v = new Vector();
    StringBuffer sbufL2R = new StringBuffer();
    StringBuffer sbufR2L = new StringBuffer();
    
    for (Enumeration e = selectedLinks.elements(); e.hasMoreElements(); ) {
      curLink = (Link)e.nextElement();
      sbufL2R.setLength(0);
      sbufR2L.setLength(0);
      sbufL2R.append("max: ");
      sbufR2L.append("max: ");
      for (j = 0; j < curLink.refPath.size(); j++) {
	src = (Switch)(((SwitchPair)curLink.refPath.elementAt(j)).l);
	dst = (Switch)(((SwitchPair)curLink.refPath.elementAt(j)).r);
	weight = ((Float)curLink.refParam.elementAt(j)).floatValue();
	sbufL2R.append(" + " + weight + " " + getTrafficName(src, dst));
	sbufR2L.append(" + " + weight + " " + getTrafficName(dst, src));
      }
      sbufL2R.append(";\n");
      sbufR2L.append(";\n");
      v.addElement(sbufL2R.toString());
      if ( linkDirection != null &&
	   linkDirection.equals(dSelectLinks.TWO_DIRECTION) )
	v.addElement(sbufR2L.toString());
    }
    return v;
  } // end of linkDimensioningText(Vector selectedLinks)    


  /**
   * update links. when switches are removed, some links should be removed also.
   */
  public synchronized void updateLinks() {
    Link l;
    for (Enumeration e = links.elements(); e.hasMoreElements();) {
      l = (Link)e.nextElement();
      if (!switches.containsKey(l.getL().getName()) ||
	  !switches.containsKey(l.getR().getName()) )
	removeLink(links, l);
    }
  }

  // get switch vector from switch set "switches".
  public synchronized Vector getSwitchVec() {
    Vector s = new Vector(switches.size());
    Enumeration e = switches.elements();
    while (e.hasMoreElements()) {
      s.addElement(e.nextElement());
    }
    return s;
  }

  public synchronized void updateSwitchVec() {
    if (switchVecUpdated == false) {
      switchVec = getSwitchVec();
      switchVecUpdated = true;
    }
  }

  // get switch array from switch set "switches".
  public synchronized Switch[] getSwitchArray() {
    Switch[] s = new Switch[switches.size()];
    Enumeration e = switches.elements();
    int i = 0;
    while (e.hasMoreElements()) {
      s[i] = (Switch)e.nextElement();
      i++;
    }
    return s;
  }

  public synchronized void updateSwitchArray() {
    if (switchArrayUpdated == false) {
      switchArray = getSwitchArray();
      switchArrayUpdated = true;
    }
  }

  public synchronized int getSwitchCount() {
    return switches.size();
  }

  public synchronized void updateConstraints() {
    if (constraintsUpdated == true) return;

    Enumeration e = constraints.elements();
    SetPairConstraint c;
    for (;e.hasMoreElements();) {
      c = (SetPairConstraint)e.nextElement();
      c.update(switches);
      if (c.getA().size() == 0 ||	c.getB().size() == 0)
	removeConstraint(c.getName());
    }
  }


  // Delaunay Triangulation on switches
  public void DelaunayTriangulation() {
    int i;
    switchVec = getSwitchVec();

    String rdata = null;

    // generate a string containing the number of switches,
    // and the location of the switches.
    StringBuffer sbuf = new StringBuffer();
    sbuf.append( switchVec.size() ); // first, the number of switches
    sbuf.append( "\n" );
    for (i = 0; i < switchVec.size(); i++) {
      sbuf.append( ((Switch)switchVec.elementAt(i)).getX() );
      sbuf.append( " " );
      sbuf.append( ((Switch)switchVec.elementAt(i)).getY() );
      sbuf.append( " \n" );
    }
    sbuf.append( "\n" );
    String sdata = sbuf.toString();

    rdata = CGI.DelaunayTriangulation(sdata);
    //System.out.println(rdata);
    // parse the output from voronoi.cgi, add links if there is no such
    // a link connecting the corresponding switches.
    StringTokenizer stk = new StringTokenizer(rdata, "\t\n\r ,", false);
    while ( !stk.nextToken().equals("$$$$$$$$") ); // start of the numbers
    int a, b, c;
    boolean linkAB, linkBC, linkCA;
    Link l;
    while (stk.hasMoreTokens()) {
      linkAB = false;
      linkBC = false;
      linkCA = false;
      try {
	a = Integer.valueOf(stk.nextToken()).intValue();
	b = Integer.valueOf(stk.nextToken()).intValue();
	c = Integer.valueOf(stk.nextToken()).intValue();
	Enumeration enum = links.elements();
	while (enum.hasMoreElements()) {
	  l = (Link)enum.nextElement();
	  if ( linkAB == false && 
	       (( l.getL().equals(switchVec.elementAt(a)) &&
		  l.getR().equals(switchVec.elementAt(b)) ) ||
		( l.getL().equals(switchVec.elementAt(b)) &&
		  l.getR().equals(switchVec.elementAt(a)) ) ) )
	    linkAB = true; // linkAB is already there
	  if ( linkBC == false && 
	       (( l.getL().equals(switchVec.elementAt(b)) &&
		  l.getR().equals(switchVec.elementAt(c)) ) ||
		( l.getL().equals(switchVec.elementAt(c)) &&
		  l.getR().equals(switchVec.elementAt(b)) ) ) )
	    linkBC = true; // linkBC is already there
	  if ( linkCA == false && 
	       (( l.getL().equals(switchVec.elementAt(c)) &&
		  l.getR().equals(switchVec.elementAt(a)) ) ||
		( l.getL().equals(switchVec.elementAt(a)) &&
		  l.getR().equals(switchVec.elementAt(c)) ) ) )
	    linkCA = true; // linkCA is already there
	}
	if (linkAB == false)
	  this.addLink(new Link("D_" + a + "_" + b, (Switch)switchVec.elementAt(a),
				(Switch)switchVec.elementAt(b)));
	if (linkBC == false)
	  this.addLink(new Link("D_" + b + "_" + c, (Switch)switchVec.elementAt(b),
				(Switch)switchVec.elementAt(c)));	
	if (linkCA == false)
	  this.addLink(new Link("D_" + c + "_" + a, (Switch)switchVec.elementAt(c),
				(Switch)switchVec.elementAt(a)));	
      }
      catch (NumberFormatException ex) {
      } // this will not happen as long as the voronoi.cgi works correct
    } // end of while (stk.hasMoreTokens())
    cost = 0;
  } // end of Delaunay Triangulation

  public Rectangle getPrevBounds() {
    return prevBounds;
  }

  public void setPrevBounds(Rectangle r) {
    prevBounds = r;
  }

  /**
   * make current network the best star network
   */
  public void setBestStar() {
    try {
      removeAllLinks(links);
      links = bestStarNetwork();
    }
    catch (ClassNotFoundException e) {
    }
  }

  /**
   * set the links to be the complete graph of the switches
   */
  public void setCompleteGraph() {
    try {
      removeAllLinks(links);
      links = completeGraph();
    }
    catch (ClassNotFoundException e) {
    }
  }

  /**
   * set the links to be the MST of the switches
   */
  public void setMST() {
    try {
      links = MST_Kruskal();
      cost = 0;
    }
    catch (ClassNotFoundException e) {
    }
  }

  /**
   * calculate minimum spanning tree using Kruskal's algorithm.
   * calculation based on a complete graph of the swithes,
   * while keep existing links intact.
   * return a set of links.
   */
  public synchronized Set MST_Kruskal() throws ClassNotFoundException{
    Set l = new Set(Class.forName("network.Link"));
    int n = switches.size();
    if (n <= 1) return l;

    int i, j, count = 0;

    // create the disjoint sets from switchVec
    updateSwitchVec();
    DisjointSetElement dse[] = new DisjointSetElement[n];
    for (i = 0; i < n; i++)
      dse[i] = new DisjointSetElement(switchVec.elementAt(i));

    // create the complete graph
    Link linkArray[] = new Link[n*(n-1)/2];
    for (i = 0; i < n; i++) {
      for (j = i + 1; j < n; j++) {
	linkArray[count] = new Link((Switch)switchVec.elementAt(i),
				    (Switch)switchVec.elementAt(j));
	count++;
      }
    }

    // sort the links by nondecreasing order of their lengths
    general.Utility.QuickSort(linkArray, 0, linkArray.length - 1);

    int u, v;
    Link link;
    // for every existing link (u,v) in links, if u and v are not in the same set,
    // then unite the two sets u and v belongs to.
    for (Enumeration e = links.elements(); e.hasMoreElements();) {
      link = (Link)e.nextElement();
      l.put(link.getName(), link);
      u = switchVec.indexOf( link.getL() );
      v = switchVec.indexOf( link.getR() );
      if ( !dse[u].findSet().equals( dse[v].findSet() ) ) {
	dse[u].union(dse[v]);
      }
    }

    // for every link (u,v) in the complete graph,
    // in order by nondecreasing weight,
    // if u and v are not in the same set, then put (u,v) into our
    // selection, and unite the two sets u and v belongs to.
    for (i = 0; i < linkArray.length; i++) {
      u = switchVec.indexOf( linkArray[i].getL() );
      v = switchVec.indexOf( linkArray[i].getR() );
      if ( !dse[u].findSet().equals( dse[v].findSet() ) ) {
	l.put(linkArray[i].getName(), linkArray[i]);
	dse[u].union(dse[v]);
      }
    }

    return l;
  }
      
  public void update(Observable o, Object arg) {
    if (o instanceof network.Switch) {
      if (arg.equals(Switch.CHANGED)) {
	setBounds();
      }
      else if (arg.equals(Switch.DESTROYED)) {
	setBounds();
      }
    }
  }

  private void cloneLinks(Set src, Set dst) {
    Link l1, l2;
    removeAllLinks(dst);
    for (Enumeration e = src.elements(); e.hasMoreElements();) {
      l1 = (Link)e.nextElement();
      l2 = new Link(l1.getName(), l1.getL(), l1.getR(), l1.getLength());
      dst.put(l2.getName(), l2);
    }
  }

  public void setAsInitial() {
    cloneLinks(links, linksInitial);
  }

  public void setAsSecond() {
    cloneLinks(links, linksSecond);
  }

  public void adjustInitial(GeneralProperties gpData) {
    removeAllLinks(links);
    cloneLinks(linksInitial, links);
    getAllPairsShortestPathes(links);
    Link link;
    int src, dst, i;

    Link[] linkArray = new Link[linksSecond.size()];
    i = 0;
    for (Enumeration e = linksSecond.elements(); e.hasMoreElements();) {
      linkArray[i] = (Link)e.nextElement();
      i++;
    }
    // sort the links in non-decreasing order
    general.Utility.QuickSort(linkArray, 0, linkArray.length - 1);
    double ratio = ((Double)gpData.get("Ratio")).doubleValue();
    if ( ((String)gpData.get("Order")).equals(dNetworkAdjust.INCREASING_ORDER)) {
      for (i = 0; i < linkArray.length; i++) {
	link = linkArray[i];
	src = switchVec.indexOf(link.getL());
	dst = switchVec.indexOf(link.getR());
	if (dist[src][dst] > ratio*link.getLength()) {
	  addLink(link);
	  getAllPairsShortestPathes(links);
	}
      }
    }
    else {
      for (i = linkArray.length-1; i >= 0; i--) {
	link = linkArray[i];
	src = switchVec.indexOf(link.getL());
	dst = switchVec.indexOf(link.getR());
	if (dist[src][dst] > ratio*link.getLength()) {
	  addLink(link);
	  getAllPairsShortestPathes(links);
	}
      }
    }
  }

  /**
   * get the unique string for the variable represent the traffic
   * from switch src to switch dst.
   * due to restrictions on the length of variable names(24 characters) by
   * the linear programming program, this function is replaced.
   */
  /*
    private String getTrafficName(Switch src, Switch dst) {
    return "from_" + src.getName() + "_to_" + dst.getName();
    }
    */

  /**
   * get the unique string for the variable represent the traffic
   * from switch src to switch dst.
   */
  private String getTrafficName(Switch src, Switch dst) {
    updateSwitchVec();
    int srcNum = switchVec.indexOf(src);
    int dstNum = switchVec.indexOf(dst);
    return "From_" + srcNum + "_to_" + dstNum;
  }

  /**
   * calculate lower bounds on link costs.
   */
  public void calcLowerBound(Set curLinks) {
    updateSwitchVec();
    updateSwitchArray();
    updateConstraints();
    clearAllRefPaths(curLinks);
    getAllPairsShortestPathes(curLinks);
    int n = switches.size();
    StringBuffer sbuf = new StringBuffer();

    // specify the objective function
    sbuf.append("max: ");
    int i, j, k1, k2;
    for (i = 0; i < n; i++) {
      for (j = 0; j < n; j++) {
	if (dist[i][j] > 0) {
	  sbuf.append(" +" + dist[i][j] + " " + 
		      getTrafficName(switchArray[i], switchArray[j]) );
	}
      }
    }
    sbuf.append(";\n");

    // specify the constraints
    sbuf.append(getConstraintsText());
    String sdata = sbuf.toString();
    CalcNetworkLowerBound calcThread = new CalcNetworkLowerBound(sdata);
    calcThread.run();

    /*
      lowerBoundText = CGI.linearProgramming(sdata);
      lowerBound = getObjectiveValue(lowerBoundText);
      // clear the sbuf, to be reused to organize the output
      sbuf.setLength(0);
      String line;
      StringTokenizer stk = new StringTokenizer(lowerBoundText, "\n");
      while (stk.hasMoreTokens()) {
      line = stk.nextToken();
      if (line.startsWith("from_") && !line.endsWith(" 0"))
      sbuf.append(line + "\n");
      }
      lowerBoundText = sbuf.toString();
      */
  }

  /**
   * get string to calculate lower bounds on link costs.
   * Jan. 24th, '97. Hongzhou Ma
   */
  public String getLowerBoundText(Set curLinks) {
    ShortestRoute shtrt = new ShortestRoute(switches, curLinks);
    shtrt.apply();
    StringBuffer sbuf = new StringBuffer();
    double distance;
    Object[] swtArray = switches.getArray();
    Switch swtA, swtB;
    // specify the objective function
    sbuf.append("max: ");
    int i, j, k1, k2;
    for (i = 0; i < swtArray.length; i++) {
      for (j = 0; j < swtArray.length; j++) {
	swtA = (Switch)swtArray[i];
	swtB = (Switch)swtArray[j];
	distance = shtrt.getDistance(swtA, swtB);
	if (distance > 0) {
	  sbuf.append(" +" + distance + " " + 
		      getTrafficName(swtA, swtB) );
	}
      }
    }
    sbuf.append(";\n");

    // specify the constraints
    sbuf.append(getConstraintsText());
    return sbuf.toString();
  }

  public double getObjectiveValue(String s) {
    StringTokenizer stk = new StringTokenizer(s, "\n");
    String line;
    while (stk.hasMoreTokens()) {
      if ((line = stk.nextToken()).startsWith("Value of objective function:")) {
	line.trim();
	return Double.valueOf(line.substring(line.lastIndexOf(' '))).
	  doubleValue();
      }
    }
    return -1; // if reaches here, it's an error
  }

  /**
   * put constraints into lp format
   */
  public String getConstraintsText() {
    StringBuffer sbuf = new StringBuffer();
    StringBuffer bufL2R = null;
    StringBuffer bufR2L = null;
    int i, j;
    Switch src, dst;
    int n = switches.size();
    updateSwitchArray();
    updateConstraints();

    // add constraints from alpha and omega
    for (i = 0; i < n; i++) {
      bufL2R = new StringBuffer();
      bufR2L = new StringBuffer();
      for (j = 0; j < n; j++) {
	if (j != i) {
	  bufL2R.append(" + " + getTrafficName(switchArray[i], switchArray[j]));
	  bufR2L.append(" + " + getTrafficName(switchArray[j], switchArray[i]));
	}
      }
      sbuf.append(bufL2R.toString() + " <=" + switchArray[i].getAlpha() + ";\n");
      sbuf.append(bufR2L.toString() + " <=" + switchArray[i].getOmega() + ";\n");
    }

    // now add set pair constraints
    SetPairConstraint spc;
    for (Enumeration e = constraints.elements();
	 e.hasMoreElements(); ) {
      spc = (SetPairConstraint)e.nextElement();
      bufL2R = new StringBuffer();
      bufR2L = new StringBuffer();
      for (Enumeration sA = spc.a.elements(); sA.hasMoreElements();) {
	src = (Switch)sA.nextElement();
	for (Enumeration sB = spc.b.elements(); sB.hasMoreElements();) {
	  dst = (Switch)sB.nextElement();
	  bufL2R.append(" + " + getTrafficName(src, dst));
	  bufR2L.append(" + " + getTrafficName(dst, src));
	}
      }
      sbuf.append(bufL2R.toString() + " <=" + spc.out + ";\n");
      sbuf.append(bufR2L.toString() + " <=" + spc.in  + ";\n");
    }
    return sbuf.toString();
  }      

  /**
   * put constraints into lp format.
   * get rid of all traffics not specified in the objective Hashtable ht.
   * ht: the Hashtable contains all traffic names specified in the objective function
   * not used for a while. Feb. 2nd.
   */
  public String getConstraintsText(Hashtable ht) {
    StringBuffer sbuf = new StringBuffer();
    StringBuffer bufL2R = null;
    StringBuffer bufR2L = null;
    String tn1, tn2; // tn stands for "traffic name"
    int count1, count2;
    int i, j;
    Switch src, dst;
    int n = switches.size();
    updateSwitchArray();
    updateConstraints();
    // add constraints from alpha and omega
    for (i = 0; i < n; i++) {
      count1 = 0;
      count2 = 0;
      bufL2R = new StringBuffer();
      bufR2L = new StringBuffer();
      for (j = 0; j < n; j++) {
	if (j != i) {
	  tn1 = getTrafficName(switchArray[i], switchArray[j]);
	  tn2 = getTrafficName(switchArray[j], switchArray[i]);
	  if (ht.containsKey(tn1)) {
	    bufL2R.append(" + " + tn1);
	    ++count1;
	  }
	  if (ht.containsKey(tn2)) {
	    bufR2L.append(" + " + tn2);
	    ++count2;
	  }
	}
      }
      if (count1 > 0)
	sbuf.append(bufL2R.toString() + " <=" + switchArray[i].getAlpha() + ";\n");
      if (count2 > 0)
	sbuf.append(bufR2L.toString() + " <=" + switchArray[i].getOmega() + ";\n");
    }

    // now add set pair constraints
    SetPairConstraint spc;
    for (Enumeration e = constraints.elements();
	 e.hasMoreElements(); ) {
      spc = (SetPairConstraint)e.nextElement();
      count1 = 0;
      count2 = 0;
      bufL2R = new StringBuffer();
      bufR2L = new StringBuffer();
      for (Enumeration sA = spc.a.elements(); sA.hasMoreElements();) {
	src = (Switch)sA.nextElement();
	for (Enumeration sB = spc.b.elements(); sB.hasMoreElements();) {
	  dst = (Switch)sB.nextElement();
	  tn1 = getTrafficName(src, dst);
	  tn2 = getTrafficName(dst, src);
	  if (ht.containsKey(tn1)) {
	    ++count1;
	    bufL2R.append(" + " + tn1);
	  }
	  if (ht.containsKey(tn2)) {
	    ++count2;
	    bufR2L.append(" + " + tn2);
	  }
	}
      }
      // if there is no terms in the constraint, we do not need to put in
      // the constraint
      if (count1 > 0) 
	sbuf.append(bufL2R.toString() + " <=" + spc.out + ";\n");
      if (count2 > 0)
	sbuf.append(bufR2L.toString() + " <=" + spc.in  + ";\n");
    }
    return sbuf.toString();
  }   

} // end of class Network
//////////////////////////////////////////////////













