/*
  delay.c
  Contains the functions that do the actual work with the data set
  Namely, the loading and timing analysis functions
*/

#include <stdio.h>
#include "delay.h"
#include <malloc.h>

double G_Resistance_Per_Length;    // ohm/um
double G_Capacitance_Per_Length;   // F/um
double G_Sink_Node_Capacitance;    // F
double G_Buffer_Output_Resistance; // ohm

int G_Sinkcount, G_Nodecount;

RC_Node_t * G_Root_Ptr = NULL;
RC_Node_t * G_Tree_Ptr = NULL;

/* The loading function
   Name is subject to change

   Arguments:
   - Input : file pointer to load the data from
*/

void RC_Load(FILE * Input_FP)
{
  /* The following uses faith-based computing */
  /* It technically violates the project spec, trusting that everything (down
     to the line breaks and location of the single comment) will be EXACTLY
     as in sample-inputs/r1_top.txt
  */
  
  /* First, read in the overall parameters */

  fscanf(Input_FP,
	 "Resistance per unit length (ohm/um): %lf\n"
	 "Capacitance per unit length (F/um): %lf\n"
	 "Sink node capacitance (F): %lf\n"
	 "Buffer output resistance (ohm): %lf\n"
	 "Sinkcount: %d\n"
	 "Nodecount: %d\n"
	 "\n"
	 "%%thisnode  parnode  lcnode  rcnode  length(um)  xloc(um)  yloc(um)\n"
	 "\n",
	 &G_Resistance_Per_Length,
	 &G_Capacitance_Per_Length,
	 &G_Sink_Node_Capacitance,
	 &G_Buffer_Output_Resistance,
	 &G_Sinkcount,
	 &G_Nodecount);

  /* At this point, we should be right at the beginning of the node list */
  
  G_Tree_Ptr = malloc(G_Nodecount * sizeof(RC_Node_t));
  if (G_Tree_Ptr == NULL)
  {
    return;
  }
  RC_Node_t * Arr_Traverse = G_Tree_Ptr; //holds spot of first element

  /* Garbage variables to throw away certain results of scanf */

  /* Variables to temporarily store the entry info prior to processing */
  int Par_Node, LC_Node, RC_Node; /* Node numbers of neighbors (as opposed to
				     array indices */
  double Length;

  /* Loop until we run out of entries (at the end of the file) */
  //-read length into struct field and store parnode, lcnode, rcnode
  //in temp variables

  while (fscanf(Input_FP, "%*d %d %d %d %lf %*f %*f\n",
                           &Par_Node,
                           &LC_Node,
                           &RC_Node,
                           &Length) != EOF)
  {

  
    Arr_Traverse->Length = Length;

    /* If LC_Node or RC_Node is -1, the neighbor it refers to is not set;
       hence, set the pointer to NULL. */

    if (LC_Node == -1) 
    {
      Arr_Traverse->Left_Ptr = NULL;
    }
    else
    {
      Arr_Traverse->Left_Ptr = &G_Tree_Ptr[LC_Node - 1];
    }

    if (RC_Node == -1)
    {
      Arr_Traverse->Right_Ptr = NULL;
    }
    else
    {
      Arr_Traverse->Right_Ptr = &G_Tree_Ptr[RC_Node - 1];
    }



    /* If Par_Node is -1, there is no parent; it must be the root node */
    if (Par_Node > 0)
    {
      Arr_Traverse->Parent_Ptr = &G_Tree_Ptr[Par_Node - 1];
    }
    else 
    {
      G_Root_Ptr = Arr_Traverse;
      /* Also, throw out the length argument; it is not needed at ALL */
      Arr_Traverse->Length = 0;
    }

    Arr_Traverse++;
  } 
}

void RC_Timing()
{
  /* See ALGORITHMS.txt */

  /* First, compute the node-by-node resistance/capacitance values */
  RC_Compute_RC(G_Root_Ptr, G_Buffer_Output_Resistance);

  /* Next, compute the delays themselves */
  RC_Compute_Delay(G_Root_Ptr, 0);

  /* Find the minimum/maximum leaf node values */

  /* Buffers to hold lists of nodes that have max/min values */
  int Minimum_List[G_Sinkcount];
  int Maximum_List[G_Sinkcount];
  // Prefill them buffers
  Minimum_List[0] = 0;
  Maximum_List[0] = 0;
  int Number_Minimum = 1;
  int Number_Maximum = 1;

  double Minimum_Delay = G_Tree_Ptr[0].Delay;
  double Maximum_Delay = G_Tree_Ptr[0].Delay;
  
  int j;
  for (j=1; j < G_Nodecount; j++)
    {
      // Is this a leaf node?
      if (NULL == G_Tree_Ptr[j].Left_Ptr && NULL == G_Tree_Ptr[j].Right_Ptr)
	{
	  if (G_Tree_Ptr[j].Delay < Minimum_Delay)
	    {
	      Number_Minimum = 1;
	      Minimum_Delay = G_Tree_Ptr[j].Delay;
	      Minimum_List[0] = j;
	    }
	  else if (Minimum_Delay == G_Tree_Ptr[j].Delay)
	    {
	      Minimum_List[Number_Minimum] = j;
	      Number_Minimum++;
	    }

	  if (G_Tree_Ptr[j].Delay > Maximum_Delay)
	    {
	      Number_Maximum = 1;
	      Maximum_Delay = G_Tree_Ptr[j].Delay;
	      Maximum_List[0] = j;
	    }
	  else if (Maximum_Delay == G_Tree_Ptr[j].Delay)
	    {
	      Maximum_List[Number_Maximum] = j;
	      Number_Maximum++;
	    }
	}
    } // End of min/max tally

  // Lastly, spit output
  printf("Max_dly:\n");
  printf("max_dly_nodes = ");
  for (j=0; j < Number_Maximum; j++)
    {
      printf("%d, ", Maximum_List[j] + 1);
    }
  printf("\n");
  printf("max_dly = %e\n", Maximum_Delay);
  putchar('\n');
  printf("Min_dly:\n");
  printf("min_dly_nodes = ");
  for (j=0; j < Number_Minimum; j++)
    {
      printf("%d, ", Minimum_List[j] + 1);
    }
  printf("\n");
  printf("min_dly = %e\n", Minimum_Delay);


}

double RC_Compute_RC(RC_Node_t * Root_Ptr,
		     double Parent_Resistance)
{
  /* Compute the resistance using the wire length */
  double This_Resistance = Parent_Resistance + 
    G_Resistance_Per_Length * Root_Ptr->Length;

  // Fill the field
  Root_Ptr->Resistance = This_Resistance;

  /* Now calculate the node capacitance */
  /* The following has been optimized somewhat using algebra
     Primary idea: Do not multiply/add in the constants until the end 
  */
  double This_Capacitance = 0;
  if ((Root_Ptr->Left_Ptr != NULL) && 
      (Root_Ptr->Right_Ptr != NULL))
    {
      This_Capacitance = Root_Ptr->Left_Ptr->Length
	+ Root_Ptr->Right_Ptr->Length;
    }
  else if (Root_Ptr->Right_Ptr != NULL)
    {
      This_Capacitance = Root_Ptr->Right_Ptr->Length;
    }
  else if (Root_Ptr->Left_Ptr != NULL)
    {
      This_Capacitance = Root_Ptr->Left_Ptr->Length;
    }

  /* The capacitance due to the connection to parent (or to source) is added in
     UNLESS this is the root node */

  if (NULL != Root_Ptr->Parent_Ptr)
    {
      This_Capacitance += Root_Ptr->Length;
    }

  /* Finally, multiply in the constants */
  This_Capacitance *= G_Capacitance_Per_Length / (double)2;

  /* If this is a leaf node, add the sink capacitance */
  if ((Root_Ptr->Left_Ptr == NULL) && 
      (Root_Ptr->Right_Ptr == NULL))
    {
      This_Capacitance += G_Sink_Node_Capacitance;
    }

  Root_Ptr->Capacitance = This_Capacitance;

  double Tree_Capacitance = This_Capacitance;
  /* If we have any children, compute them and use the results to compute
     Tree_Capacitance for this node */
  if (Root_Ptr->Left_Ptr)
    {
      Tree_Capacitance += RC_Compute_RC(Root_Ptr->Left_Ptr, This_Resistance);
    }
  if (Root_Ptr->Right_Ptr)
    {
      Tree_Capacitance += RC_Compute_RC(Root_Ptr->Right_Ptr, This_Resistance);
    }

  Root_Ptr->Tree_Capacitance = Tree_Capacitance;

  return Tree_Capacitance;
}

void RC_Compute_Delay(RC_Node_t * Root_Ptr,
		      double Parent_Contribution)
{
  double This_Parent_Contribution = 0;

  if (Root_Ptr->Parent_Ptr)
    {
      This_Parent_Contribution =
	Parent_Contribution + 
	(Root_Ptr->Parent_Ptr->Tree_Capacitance - Root_Ptr->Tree_Capacitance)
	* Root_Ptr->Parent_Ptr->Resistance * Root_Ptr->Parent_Ptr->Resistance;
    }

  /* Is this a leaf node? */
  if (Root_Ptr->Left_Ptr || Root_Ptr->Right_Ptr)
    {
      /* If not, recurse into children */
      if (Root_Ptr->Left_Ptr)
	{
	  RC_Compute_Delay(Root_Ptr->Left_Ptr, This_Parent_Contribution);
	}
      if (Root_Ptr->Right_Ptr)
	{
	  RC_Compute_Delay(Root_Ptr->Right_Ptr, This_Parent_Contribution);
	}
    }
  else
    {
      /* If so, calculate the delay */
      Root_Ptr->Delay = (This_Parent_Contribution +
			 Root_Ptr->Capacitance 
			 * Root_Ptr->Resistance * Root_Ptr->Resistance)
	/ (Root_Ptr->Resistance);
    }
}
