/*
 *$Log$
 */
package lb;
import java.util.*;

import general.*;
import network.*;

class Node {
  double theta;
  double phi;
  double x;
  double y;
  double z;

  public Node(double theta, double phi) {
    this.theta = theta;
    this.phi = phi;
    this.x = Math.sin(theta)*Math.cos(phi);
    this.y = Math.sin(theta)*Math.sin(phi);
    this.z = Math.cos(theta);
  }

  public Node(double x, double y, double z) {
    double r = Math.sqrt(x*x+y*y+z*z);
    this.x = x/r;
    this.y = y/r;
    this.z = z/r;
    this.theta = Math.acos(this.z);
    this.phi = Math.atan2(this.y, this.x);
  }

  public String toString() {
    return("[" + x + "," + y + "," + z + "]");
  }

  /** 
    distance between two nodes on the sphere, with
    coordinates phi and theta 
    */
  public static double distance(Node a, Node b) {

    //    double cosd = Math.sin(a.theta)*Math.sin(b.theta)*Math.cos(a.phi-b.phi)+
    //      Math.cos(a.theta)*Math.cos(b.theta);
    double cosd = a.x*b.x + a.y*b.y + a.z*b.z;
    return Math.acos(cosd);
  }  
}

/** calculate the average of lowerbound with flat traffic constraint. 
 usage: Asptotic NumberOfSwitches NumberOfIterations
 */
public class Asptotic {
  int numSwitch;
  Node[] nodes;
  double[][] dist;
  String LPHeader;

  public Asptotic(int numSwitch) {
    this.numSwitch = numSwitch;
    nodes = new Node[numSwitch];
    Random rdm = new Random();
    dist = new double[numSwitch][numSwitch];

    // generate uniform distribution on the surface of a sphere
    for (int i = 0; i < numSwitch; i++) {
      /*
      double phi = rdm.nextDouble()*2.0*Math.PI;
      double thetax = rdm.nextDouble()*Math.PI/2.0;
      double thetay = rdm.nextDouble();
      double theta;
      if (thetay > Math.sin(thetax)) {
	theta = Math.PI/2.0 - thetax;
	if (theta < 0) theta += Math.PI;
      }
      else
	theta = thetax;
      nodes[i] = new Node(theta, phi);
      */
      double x, y, z, r2;
      do {
	x = rdm.nextDouble()*2 - 1.0;
	y = rdm.nextDouble()*2 - 1.0;
	z = rdm.nextDouble()*2 - 1.0;
	r2 = x*x + y*y + z*z;
      }
      while (r2 > 1);
      nodes[i] = new Node(x, y, z);
      //      System.out.println("Node " + i + " = " + nodes[i]);
      
      // calculate the distances
      for (int j = 0; j < i; j++) {
	dist[i][j] = Node.distance(nodes[i], nodes[j]);
	dist[j][i] = dist[i][j];
      }
      dist[i][i] = 0;
    }

    // generate LP Header
    // first, objective function
    StringBuffer sbuf = new StringBuffer("max: ");
    for (int i = 0; i < numSwitch; i++)
      for (int j = 0; j < i; j++)
	sbuf.append(" + " + dist[i][j] + trafficName(i, j) +
		    " + " + dist[j][i] + trafficName(j, i));
    sbuf.append(";\n");
    // second, alpha and omega constraints
    for (int i = 0; i < numSwitch; i++) {
      for (int j = 0; j < numSwitch; j++) {
	if (j != i) {
	  sbuf.append(" +" + trafficName(i, j));
	}
      }
      sbuf.append("<=1.0;\n");
    }
    LPHeader = sbuf.toString();
  }

  String trafficName(int i, int j) {
    return ("T" + i + "_" + j);
  }

  public static void main(String[] args) {
    int numSwitch = Integer.parseInt(args[0]);
    int numIterate = Integer.parseInt(args[1]);
    double sum = 0;
    double cost;
    double avg;
    System.out.println("number of switches is " + numSwitch);
    for (int i = 0; i < numIterate; i++) {
      Asptotic asp = new Asptotic(numSwitch);
      cost = asp.lowerbound();
      System.out.println("i=" + i + " lb=" + cost);
      sum += cost;
    }
    avg = sum/numIterate;
    System.out.println("average = " + avg);
  }

  double lowerbound() {
    String rdata = CGI.linearProgramming(LPHeader);
    return LP.parseObjectiveValue(rdata)/(numSwitch*Math.PI);
  }
}


