/*===================================================================*/
//  
//     place_partition.c
//
//        Aaron P. Hurst, 2003-2007
//              ahurst@eecs.berkeley.edu
//
/*===================================================================*/

#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <assert.h>
//#include <sys/stat.h>
//#include <unistd.h>

#include "place_base.h"
#include "place_gordian.h"

#if !defined(NO_HMETIS)
#include "libhmetis.h"

ABC_NAMESPACE_IMPL_START

#endif

// --------------------------------------------------------------------
// Global variables
//
// --------------------------------------------------------------------

Partition *g_place_rootPartition = NULL;
ConcreteNet **allNetsR2 = NULL, 
  **allNetsL2 = NULL, 
  **allNetsB2 = NULL, 
  **allNetsT2 = NULL;


// --------------------------------------------------------------------
// Function prototypes and local data structures
//
// --------------------------------------------------------------------

typedef struct FM_cell {
    int loc;
    int gain;
    ConcreteCell *cell;
    struct FM_cell *next, *prev;
    bool locked;
} FM_cell;

void FM_updateGains(ConcreteNet *net, int partition, int inc, 
                    FM_cell target [], FM_cell *bin [], 
                    int count_1 [], int count_2 []);


// --------------------------------------------------------------------
// initPartitioning()
//
/// \brief Initializes data structures necessary for partitioning.
//
/// Creates a valid g_place_rootPartition.
///
// --------------------------------------------------------------------
void initPartitioning() {
  int i;
  float area;

  // create root partition
  g_place_numPartitions = 1;
  if (g_place_rootPartition) free(g_place_rootPartition);
  g_place_rootPartition = malloc(sizeof(Partition));
  g_place_rootPartition->m_level = 0;
  g_place_rootPartition->m_area = 0;
  g_place_rootPartition->m_bounds = g_place_coreBounds;
  g_place_rootPartition->m_vertical = false;
  g_place_rootPartition->m_done = false;
  g_place_rootPartition->m_leaf = true;
      
  // add all of the cells to this partition
  g_place_rootPartition->m_members = malloc(sizeof(ConcreteCell*)*g_place_numCells);
  g_place_rootPartition->m_numMembers = 0;
  for (i=0; i<g_place_numCells; i++) 
    if (g_place_concreteCells[i]) {
      if (!g_place_concreteCells[i]->m_fixed) {
        area = getCellArea(g_place_concreteCells[i]);
        g_place_rootPartition->m_members[g_place_rootPartition->m_numMembers++] =
          g_place_concreteCells[i];
        g_place_rootPartition->m_area += area;
      }
    }
}


// --------------------------------------------------------------------
// presortNets()
//
/// \brief Sorts nets by corner positions.
//
/// Allocates allNetsX2 structures.
///
// --------------------------------------------------------------------
void presortNets() {
  allNetsL2 = (ConcreteNet**)realloc(allNetsL2, sizeof(ConcreteNet*)*g_place_numNets);
  allNetsR2 = (ConcreteNet**)realloc(allNetsR2, sizeof(ConcreteNet*)*g_place_numNets);
  allNetsB2 = (ConcreteNet**)realloc(allNetsB2, sizeof(ConcreteNet*)*g_place_numNets);
  allNetsT2 = (ConcreteNet**)realloc(allNetsT2, sizeof(ConcreteNet*)*g_place_numNets);
  memcpy(allNetsL2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets);
  memcpy(allNetsR2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets);
  memcpy(allNetsB2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets);
  memcpy(allNetsT2, g_place_concreteNets, sizeof(ConcreteNet*)*g_place_numNets);
  qsort(allNetsL2, g_place_numNets, sizeof(ConcreteNet*), netSortByL);
  qsort(allNetsR2, g_place_numNets, sizeof(ConcreteNet*), netSortByR);
  qsort(allNetsB2, g_place_numNets, sizeof(ConcreteNet*), netSortByB);
  qsort(allNetsT2, g_place_numNets, sizeof(ConcreteNet*), netSortByT);
}

// --------------------------------------------------------------------
// refinePartitions()
//
/// \brief Splits large leaf partitions.
//
// --------------------------------------------------------------------
bool refinePartitions() {

  return refinePartition(g_place_rootPartition);
}


// --------------------------------------------------------------------
// reallocPartitions()
//
/// \brief Reallocates the partitions based on placement information.
//
// --------------------------------------------------------------------
void reallocPartitions() {

  reallocPartition(g_place_rootPartition);
}


// --------------------------------------------------------------------
// refinePartition()
//
/// \brief Splits any large leaves within a partition.
//
// --------------------------------------------------------------------
bool refinePartition(Partition *p) {
  bool degenerate = false;
  int nonzeroCount = 0;
  int i;

  assert(p);

  // is this partition completed?
  if (p->m_done) return true;

  // is this partition a non-leaf node?
  if (!p->m_leaf) {
    p->m_done = refinePartition(p->m_sub1);
    p->m_done &= refinePartition(p->m_sub2);
    return p->m_done;
  }
  
  // leaf...
  // create two new subpartitions
  g_place_numPartitions++;
  p->m_sub1 = malloc(sizeof(Partition));
  p->m_sub1->m_level = p->m_level+1;
  p->m_sub1->m_leaf = true;
  p->m_sub1->m_done = false;
  p->m_sub1->m_area = 0;
  p->m_sub1->m_vertical = !p->m_vertical;
  p->m_sub1->m_numMembers = 0;
  p->m_sub1->m_members = NULL;
  p->m_sub2 = malloc(sizeof(Partition));
  p->m_sub2->m_level = p->m_level+1;
  p->m_sub2->m_leaf = true;
  p->m_sub2->m_done = false;
  p->m_sub2->m_area = 0;
  p->m_sub2->m_vertical = !p->m_vertical;
  p->m_sub2->m_numMembers = 0;
  p->m_sub2->m_members = NULL;
  p->m_leaf = false;

  // --- INITIAL PARTITION

  if (PARTITION_AREA_ONLY)
    partitionEqualArea(p);
  else 
    partitionScanlineMincut(p);

  resizePartition(p);

  // --- PARTITION IMPROVEMENT

  if (p->m_level < REPARTITION_LEVEL_DEPTH) {
    if (REPARTITION_FM)
      repartitionFM(p);
    else if (REPARTITION_HMETIS)
      repartitionHMetis(p);
  }
    
  resizePartition(p);
  
  // fix imbalances due to zero-area cells
  for(i=0; i<p->m_sub1->m_numMembers; i++)
    if (p->m_sub1->m_members[i]) 
      if (getCellArea(p->m_sub1->m_members[i]) > 0) {
        nonzeroCount++;
      }
  
  // is this leaf now done?
  if (nonzeroCount <= LARGEST_FINAL_SIZE)
      p->m_sub1->m_done = true;
  if (nonzeroCount == 0)
      degenerate = true;

  nonzeroCount = 0;
  for(i=0; i<p->m_sub2->m_numMembers; i++)
    if (p->m_sub2->m_members[i])
      if (getCellArea(p->m_sub2->m_members[i]) > 0) {
        nonzeroCount++;
      }

  // is this leaf now done?
  if (nonzeroCount <= LARGEST_FINAL_SIZE)
      p->m_sub2->m_done = true;
  if (nonzeroCount == 0)
      degenerate = true;

  // have we found a degenerate partitioning?
  if (degenerate) {
    printf("QPART-35 : WARNING: degenerate partition generated\n");
    partitionEqualArea(p);
    resizePartition(p);
    p->m_sub1->m_done = true;
    p->m_sub2->m_done = true;
  }
  
  // is this parent now finished?
  if (p->m_sub1->m_done && p->m_sub2->m_done) p->m_done = true;
  
  return p->m_done;
}


// --------------------------------------------------------------------
// repartitionHMetis()
//
/// \brief Repartitions the two subpartitions using the hMetis min-cut library.
///
/// The number of cut nets between the two partitions will be minimized.
//
// --------------------------------------------------------------------
void repartitionHMetis(Partition *parent) {
#if defined(NO_HMETIS)
  printf("QPAR_02 : \t\tERROR: hMetis not available.  Ignoring.\n");
#else

  int n,c,t, i;
  float area;
  int *edgeConnections = NULL;
  int *partitionAssignment = (int *)calloc(g_place_numCells, sizeof(int));
  int *vertexWeights = (int *)calloc(g_place_numCells, sizeof(int));
  int *edgeDegree = (int *)malloc(sizeof(int)*(g_place_numNets+1));
  int numConnections = 0;
  int numEdges = 0;
  float initial_cut;
  int targets = 0;
  ConcreteCell *cell = NULL;
  int options[9];
  int afterCuts = 0;

  assert(parent);
  assert(parent->m_sub1);
  assert(parent->m_sub2);

  printf("QPAR-02 : \t\trepartitioning with hMetis\n");

  // count edges
  edgeDegree[0] = 0;
  for(n=0; n<g_place_numNets; n++) if (g_place_concreteNets[n])
    if (g_place_concreteNets[n]->m_numTerms > 1) {
      numConnections += g_place_concreteNets[n]->m_numTerms;
      edgeDegree[++numEdges] = numConnections;
    }
  
  if (parent->m_vertical) {
    // vertical
    initial_cut = parent->m_sub2->m_bounds.x;
    
    // initialize all cells
    for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
      if (g_place_concreteCells[c]->m_x < initial_cut)
        partitionAssignment[c] = 0;
      else
        partitionAssignment[c] = 1;
    }
  
    // initialize cells in partition 1
    for(t=0; t<parent->m_sub1->m_numMembers; t++) if (parent->m_sub1->m_members[t]) {
      cell = parent->m_sub1->m_members[t];
      vertexWeights[cell->m_id] = getCellArea(cell);
      // pay attention to cells that are close to the cut
      if (abs(cell->m_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) {
        targets++;
        partitionAssignment[cell->m_id] = -1;
      }
    }
    
    // initialize cells in partition 2
    for(t=0; t<parent->m_sub2->m_numMembers; t++) if (parent->m_sub2->m_members[t]) {
      cell = parent->m_sub2->m_members[t];
      vertexWeights[cell->m_id] = getCellArea(cell);
      // pay attention to cells that are close to the cut
      if (abs(cell->m_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) {
        targets++;
        partitionAssignment[cell->m_id] = -1;
      }        
    }
    
  } else {
    // horizontal
    initial_cut = parent->m_sub2->m_bounds.y;
    
    // initialize all cells
    for(c=0; c<g_place_numCells; c++) if (g_place_concreteCells[c]) {
      if (g_place_concreteCells[c]->m_y < initial_cut)
        partitionAssignment[c] = 0;
      else
        partitionAssignment[c] = 1;
    }
    
    // initialize cells in partition 1
    for(t=0; t<parent->m_sub1->m_numMembers; t++) if (parent->m_sub1->m_members[t]) {
      cell = parent->m_sub1->m_members[t];
      vertexWeights[cell->m_id] = getCellArea(cell);
      // pay attention to cells that are close to the cut
      if (abs(cell->m_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) {
        targets++;
        partitionAssignment[cell->m_id] = -1;
      }
    }
    
    // initialize cells in partition 2
    for(t=0; t<parent->m_sub2->m_numMembers; t++) if (parent->m_sub2->m_members[t]) {
      cell = parent->m_sub2->m_members[t];
      vertexWeights[cell->m_id] = getCellArea(cell);
      // pay attention to cells that are close to the cut
      if (abs(cell->m_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) {
        targets++;
        partitionAssignment[cell->m_id] = -1;
      }        
    }
  }

  options[0] = 1;  // any non-default values?
  options[1] = 3; // num bisections
  options[2] = 1;  // grouping scheme
  options[3] = 1;  // refinement scheme
  options[4] = 1;  // cycle refinement scheme
  options[5] = 0;  // reconstruction scheme
  options[6] = 0;  // fixed assignments?
  options[7] = 12261980; // random seed
  options[8] = 0;  // debugging level

  edgeConnections = (int *)malloc(sizeof(int)*numConnections);

  i = 0;
  for(n=0; n<g_place_numNets; n++) if (g_place_concreteNets[n]) {
    if (g_place_concreteNets[n]->m_numTerms > 1)
      for(t=0; t<g_place_concreteNets[n]->m_numTerms; t++)
        edgeConnections[i++] = g_place_concreteNets[n]->m_terms[t]->m_id;
  }

  HMETIS_PartRecursive(g_place_numCells, numEdges, vertexWeights,
               edgeDegree, edgeConnections, NULL,
               2, (int)(100*MAX_PARTITION_NONSYMMETRY),
               options, partitionAssignment, &afterCuts);
    
  /*
  printf("HMET-20 : \t\t\tbalance before %d / %d ... ", parent->m_sub1->m_numMembers,
         parent->m_sub2->m_numMembers);
  */

  // reassign members to subpartitions
  parent->m_sub1->m_numMembers = 0;
  parent->m_sub1->m_area = 0;
  parent->m_sub2->m_numMembers = 0;
  parent->m_sub2->m_area = 0;
  parent->m_sub1->m_members = (ConcreteCell**)realloc(parent->m_sub1->m_members, 
       sizeof(ConcreteCell*)*parent->m_numMembers); 
  parent->m_sub2->m_members = (ConcreteCell**)realloc(parent->m_sub2->m_members, 
       sizeof(ConcreteCell*)*parent->m_numMembers); 
 
  for(t=0; t<parent->m_numMembers; t++) if (parent->m_members[t]) {
    cell = parent->m_members[t];
    area = getCellArea(cell);
    if (partitionAssignment[cell->m_id] == 0) {
      parent->m_sub1->m_members[parent->m_sub1->m_numMembers++] = cell;
      parent->m_sub1->m_area += area;
    }
    else {
      parent->m_sub2->m_members[parent->m_sub2->m_numMembers++] = cell;
      parent->m_sub2->m_area += area;
    }
  }
  /*
  printf("after %d / %d\n", parent->m_sub1->m_numMembers,
         parent->m_sub2->m_numMembers);
  */

  // cout << "HMET-21 : \t\t\tloc: " << initial_cut <<  " targetting: " << targets*100/parent->m_members.length() << "%" << endl;
  // cout << "HMET-22 : \t\t\tstarting cuts= " << beforeCuts << " final cuts= " << afterCuts << endl;

  free(edgeConnections);
  free(vertexWeights);
  free(edgeDegree);
  free(partitionAssignment);
#endif
}


// --------------------------------------------------------------------
// repartitionFM()
//
/// \brief Fiduccia-Matheyses mincut partitioning algorithm.
//
/// UNIMPLEMENTED (well, un-C-ified)
//
// --------------------------------------------------------------------
void repartitionFM(Partition *parent) {
#if 0
    assert(!parent->leaf && parent->m_sub1->leaf && parent->m_sub2->leaf);

    // count of each net's number of cells in each bipartition
    int count_1[m_design->nets.length()];
    memset(count_1, 0, sizeof(int)*m_design->nets.length());
    int count_2[m_design->nets.length()];
    memset(count_2, 0, sizeof(int)*m_design->nets.length());

    FM_cell target[m_design->cells.length()];
    memset(target, 0, sizeof(FM_cell)*m_design->cells.length());
    FM_cell *bin[FM_MAX_BIN+1];
    FM_cell *locked = 0;
    memset(bin, 0, sizeof(FM_cell *)*(FM_MAX_BIN+1));

    int max_gain = 0;
    int before_cuts = 0, current_cuts = 0;
    double initial_cut;
    int targets = 0;
    long cell_id;
    double halfArea = parent->m_area / 2.0;
    double areaFlexibility = parent->m_area * MAX_PARTITION_NONSYMMETRY;
    ConcreteNet *net;

    // INITIALIZATION
    //   select cells to partition

    if (parent->vertical) {
    // vertical

    initial_cut = parent->m_sub2->m_bounds.x;

    // initialize all cells
    for(h::list<ConcreteCell *>::iterator it = rootPartition->m_members.begin(); !it; it++) {
        cell_id = (*it)->getID();
        if ((*it)->temp_x < initial_cut)
        target[cell_id].loc = -1;
        else
        target[cell_id].loc = -2;
        target[cell_id].cell = *it;
        target[cell_id].gain = 0;
    }

    // initialize cells in partition 1
    for(h::list<ConcreteCell *>::iterator it = parent->m_sub1->m_members.begin(); !it; it++) {
        cell_id = (*it)->getID();
        // pay attention to cells that are close to the cut
        if (abs((*it)->temp_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) {
        targets++;
        target[cell_id].loc = 1;
        }
    }

    // initialize cells in partition 2
    for(h::list<ConcreteCell *>::iterator it = parent->m_sub2->m_members.begin(); !it; it++) {
        cell_id = (*it)->getID();
        // pay attention to cells that are close to the cut
        if (abs((*it)->temp_x-initial_cut) < parent->m_bounds.w*REPARTITION_TARGET_FRACTION) {
        targets++;
        target[cell_id].loc = 2;
        }        
    }

       // count the number of cells on each side of the partition for every net 
    for(h::hash_map<ConcreteNet *>::iterator n_it = m_design->nets.begin(); !n_it; n_it++) {
        for(ConcretePinList::iterator p_it = (net = *n_it)->getPins().begin(); !p_it; p_it++)
        if (abs(target[(*p_it)->getCell()->getID()].loc) == 1)
            count_1[net->getID()]++;
        else if (abs(target[(*p_it)->getCell()->getID()].loc) == 2)
            count_2[net->getID()]++;
        else if ((*p_it)->getCell()->temp_x < initial_cut) 
            count_1[net->getID()]++;
        else
            count_2[net->getID()]++;
        if (count_1[net->getID()] > 0 && count_2[net->getID()] > 0) before_cuts++;
    }
    
    } else {
    // horizontal

    initial_cut = parent->m_sub2->m_bounds.y;

    // initialize all cells
    for(h::list<ConcreteCell *>::iterator it = rootPartition->m_members.begin(); !it; it++) {
        cell_id = (*it)->getID();
        if ((*it)->temp_y < initial_cut)
        target[cell_id].loc = -1;
        else
        target[cell_id].loc = -2;
        target[cell_id].cell = *it;
        target[cell_id].gain = 0;
    }

    // initialize cells in partition 1
    for(h::list<ConcreteCell *>::iterator it = parent->m_sub1->m_members.begin(); !it; it++) {
        cell_id = (*it)->getID();
        // pay attention to cells that are close to the cut
        if (abs((*it)->temp_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) {
        targets++;
        target[cell_id].loc = 1;
        }
    }

    // initialize cells in partition 2
    for(h::list<ConcreteCell *>::iterator it = parent->m_sub2->m_members.begin(); !it; it++) {
        cell_id = (*it)->getID();
        // pay attention to cells that are close to the cut
        if (abs((*it)->temp_y-initial_cut) < parent->m_bounds.h*REPARTITION_TARGET_FRACTION) {
        targets++;
        target[cell_id].loc = 2;
        }        
    }

       // count the number of cells on each side of the partition for every net 
    for(h::hash_map<ConcreteNet *>::iterator n_it = m_design->nets.begin(); !n_it; n_it++) {
        for(ConcretePinList::iterator p_it = (net = *n_it)->getPins().begin(); !p_it; p_it++)
        if (abs(target[(*p_it)->getCell()->getID()].loc) == 1)
            count_1[net->getID()]++;
        else if (abs(target[(*p_it)->getCell()->getID()].loc) == 2)
            count_2[net->getID()]++;
        else if ((*p_it)->getCell()->temp_y < initial_cut) 
            count_1[net->getID()]++;
        else
            count_2[net->getID()]++;
        if (count_1[net->getID()] > 0 && count_2[net->getID()] > 0) before_cuts++;
    }
    }

    // INITIAL GAIN CALCULATION
    for(long id=0; id < m_design->cells.length(); id++)
    if (target[id].loc > 0) {
        assert(target[id].cell != 0);
        assert(target[id].gain == 0);

        // examine counts for the net on each pin
        for(ConcretePinMap::iterator p_it = target[id].cell->getPins().begin(); !p_it; p_it++)
        if ((*p_it)->isAttached()) {
            int n_id = (*p_it)->getNet()->getID();
            if (target[id].loc == 1 && count_1[n_id] == 1) target[id].gain++;
            if (target[id].loc == 1 && count_2[n_id] == 0) target[id].gain--;
            if (target[id].loc == 2 && count_1[n_id] == 0) target[id].gain--;
            if (target[id].loc == 2 && count_2[n_id] == 1) target[id].gain++;
        }

        assert(target[id].cell->getPins().length() >= abs(target[id].gain));

        // add it to a bin
        int bin_num = min(max(0, target[id].gain),FM_MAX_BIN);
        max_gain = max(max_gain, bin_num);

        assert(bin_num >= 0 && bin_num <= FM_MAX_BIN);
        target[id].next = bin[bin_num];
        target[id].prev = 0;
        if (bin[bin_num] != 0)
        bin[bin_num]->prev = &target[id];
        bin[bin_num] = &target[id];
    }

    // OUTER F-M LOOP
    current_cuts = before_cuts;
    int num_moves = 1;
    int pass = 0;
    while(num_moves > 0 && pass < FM_MAX_PASSES) {
    pass++;
    num_moves = 0;    

    // check_list(bin, locked, targets); // DEBUG

    // move all locked cells back
    int moved_back = 0;
    while(locked != 0) {
        FM_cell *current = locked;
        current->locked = false;

        int bin_num = min(max(0, current->gain),FM_MAX_BIN);       
        max_gain = max(max_gain, bin_num);

        locked = current->next;
        if (locked != 0)
        locked->prev = 0;

        if (bin[bin_num] != 0)
        bin[bin_num]->prev = current;
        current->next = bin[bin_num];
        bin[bin_num] = current;

        moved_back++;
    }
    // cout << "\tmoved back: " << moved_back << endl;
    // check_list(bin, locked, targets); // DEBUG    
    
    max_gain = FM_MAX_BIN;
    while(bin[max_gain] == 0 && max_gain > 0) max_gain--;

    // INNER F-M LOOP (single pass)
    while(1) {

        int bin_num = FM_MAX_BIN;
        FM_cell *current = bin[bin_num];

        // look for next cell to move
        while (bin_num > 0 && (current == 0 || 
        (current->loc==1 && current->cell->getArea()+parent->m_sub2->m_area > halfArea+areaFlexibility) ||
        (current->loc==2 && current->cell->getArea()+parent->m_sub1->m_area > halfArea+areaFlexibility))) {

        if (current == 0) current = bin[--bin_num]; else current = current->next;        
        }
        if (bin_num == 0)
        break;

        num_moves++;
        current->locked = true;
        // cout << "moving cell " << current->cell->getID() << " gain=" << current->gain << " pins= " << current->cell->getPins().length() << " from " << current->loc;

        // change partition marking and areas
        if (current->loc == 1) {
        current->loc = 2;
        parent->m_sub1->m_area -= current->cell->getArea();
        parent->m_sub2->m_area += current->cell->getArea();

        // update partition counts on all nets attached to this cell
        for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); 
            !p_it; p_it++) {
            
            if (!(*p_it)->isAttached()) // ignore unattached pins
            continue;
            net = (*p_it)->getNet();
            
            count_1[net->getID()]--;
            count_2[net->getID()]++;

            // cout << "\tnet " << net->getID() << " was " << count_1[net->getID()]+1 << "/" << count_2[net->getID()]-1 << " now " << count_1[net->getID()] << "/" << count_2[net->getID()] << endl;

            // if net becomes critical, update gains on attached cells and resort bins
            if (count_1[net->getID()] == 0) { current_cuts--; FM_updateGains(net, 2, -1, target, bin, count_1, count_2); }
            if (count_2[net->getID()] == 1) { current_cuts++; FM_updateGains(net, 1, -1, target, bin, count_1, count_2); }
            
            // check_list(bin, locked, targets); // DEBUG
        }

        } else {
        current->loc = 1;
        parent->m_sub2->m_area -= current->cell->getArea();
        parent->m_sub1->m_area += current->cell->getArea();

        // update gains on all nets attached to this cell
        for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); 
            !p_it; p_it++) {
            
            if (!(*p_it)->isAttached()) // ignore unattached pins
            continue;
            net = (*p_it)->getNet();
            count_2[net->getID()]--;
            count_1[net->getID()]++;

            // cout << "\tnet " << net->getID() << " was " << count_1[net->getID()]-1 << "/" << count_2[net->getID()]+1 << " now " << count_1[net->getID()] << "/" << count_2[net->getID()] << endl;

            if (count_2[net->getID()] == 0) { current_cuts--; FM_updateGains(net, 2, -1, target, bin, count_1, count_2); }
            if (count_1[net->getID()] == 1) { current_cuts++; FM_updateGains(net, 1, -1, target, bin, count_1, count_2); }
        
            // check_list(bin, locked, targets); // DEBUG
        }
        }

        //cout << " cuts=" << current_cuts << endl;

        // move current to locked

/*
        cout << "b=" << bin[bin_num] << " ";
        cout << current->prev << "-> ";
        if (current->prev == 0)
        cout << "X";
        else cout << current->prev->next;
        cout  << "=" << current << "=";
        if (current->next == 0)
        cout << "X";
        else
        cout << current->next->prev;
        cout << " ->" << current->next << endl;
*/

        if (bin[bin_num] == current)
        bin[bin_num] = current->next;
        if (current->prev != 0)
        current->prev->next = current->next;
        if (current->next != 0)
        current->next->prev = current->prev;

/*
        cout << "b=" << bin[bin_num] << " ";
        cout << current->prev << "-> ";
        if (current->prev == 0)
        cout << "X";
        else cout << current->prev->next;
        cout  << "=" << current << "=";
        if (current->next == 0)
        cout << "X";
        else
        cout << current->next->prev;
        cout << " ->" << current->next << endl;
*/

        current->prev = 0;
        current->next = locked;
        if (locked != 0)
        locked->prev = current;
        locked = current;
        
        // check_list(bin, locked, targets); // DEBUG    

        // update max_gain
        max_gain = FM_MAX_BIN;
        while(bin[max_gain] == 0 && max_gain > 0) max_gain--;
    }

    // cout << "\tcurrent cuts= " << current_cuts << " moves= " << num_moves << endl;
    }

    // reassign members to subpartitions
    cout << "FIDM-20 : \tbalance before " << parent->m_sub1->m_members.length() << "/"
       << parent->m_sub2->m_members.length() << " ";
    parent->m_sub1->m_members.clear();
    parent->m_sub1->m_area = 0;
    parent->m_sub2->m_members.clear();
    parent->m_sub2->m_area = 0;
    for(h::list<ConcreteCell *>::iterator it = parent->m_members.begin(); !it; it++) {
    if (target[(*it)->getID()].loc == 1 || target[(*it)->getID()].loc == -1) {
        parent->m_sub1->m_members.push_back(*it);
        parent->m_sub1->m_area += (*it)->getArea();
    }
    else {
        parent->m_sub2->m_members.push_back(*it);
        parent->m_sub2->m_area += (*it)->getArea();
    }
    }
    cout << " after " << parent->m_sub1->m_members.length() << "/"
       << parent->m_sub2->m_members.length() << endl;


    cout << "FIDM-21 : \tloc: " << initial_cut <<  " targetting: " << targets*100/parent->m_members.length() << "%" << endl;
    cout << "FIDM-22 : \tstarting cuts= " << before_cuts << " final cuts= " << current_cuts << endl;
#endif
}

// ----- FM_updateGains()
//   moves a cell between bins
#if 0
void FM_updateGains(ConcreteNet *net, int partition, int inc, 
                    FM_cell target [], FM_cell *bin [], 
                    int count_1 [], int count_2 []) {

    for(ConcretePinList::iterator it = net->getPins().begin(); !it; it++) {
    FM_cell *current = &(target[(*it)->getCell()->getID()]);
    assert(current->cell != 0);
    
    int old_gain = current->gain;
    current->gain = 0;

    // examine counts for the net on each pin
    for(ConcretePinMap::iterator p_it = current->cell->getPins().begin(); !p_it; p_it++)
        if ((*p_it)->isAttached()) {
        int n_id = (*p_it)->getNet()->getID();
        if (current->loc == 1 && count_1[n_id] == 1) current->gain++;
        if (current->loc == 1 && count_2[n_id] == 0) current->gain--;
        if (current->loc == 2 && count_1[n_id] == 0) current->gain--;
        if (current->loc == 2 && count_2[n_id] == 1) current->gain++;
        }

    if (!current->locked) {
        // remove cell from existing bin
        int bin_num =  min(max(0, old_gain),FM_MAX_BIN);
        if (bin[bin_num] == current)
        bin[bin_num] = current->next;
        if (current->prev != 0)
        current->prev->next = current->next;
        if (current->next != 0)
        current->next->prev = current->prev;
        // add cell to correct bin
        bin_num =  min(max(0, current->gain),FM_MAX_BIN);
        current->prev = 0;
        current->next = bin[bin_num];
        if (bin[bin_num] != 0)
        bin[bin_num]->prev = current;
        bin[bin_num] = current;
    }
    }
    
}
#endif


// --------------------------------------------------------------------
// partitionEqualArea()
//
/// \brief Splits a partition into two halves of equal area.
//
// --------------------------------------------------------------------
void partitionEqualArea(Partition *parent) {
  float halfArea, area;
  int i=0;
  
  // which way to sort?
  if (parent->m_vertical)
    // sort by X position
    qsort(parent->m_members, parent->m_numMembers, sizeof(ConcreteCell*), cellSortByX);
  else
    // sort by Y position
    qsort(parent->m_members, parent->m_numMembers, sizeof(ConcreteCell*), cellSortByY);

  // split the list
  halfArea = parent->m_area*0.5;
  parent->m_sub1->m_area = 0.0;
  parent->m_sub1->m_numMembers = 0;
  parent->m_sub1->m_members = (ConcreteCell**)realloc(parent->m_sub1->m_members, 
                                  sizeof(ConcreteCell*)*parent->m_numMembers);
  parent->m_sub2->m_area = 0.0;
  parent->m_sub2->m_numMembers = 0;
  parent->m_sub2->m_members = (ConcreteCell**)realloc(parent->m_sub2->m_members, 
                                  sizeof(ConcreteCell*)*parent->m_numMembers);

  for(; parent->m_sub1->m_area < halfArea; i++) 
    if (parent->m_members[i]) {
      area = getCellArea(parent->m_members[i]);
      parent->m_sub1->m_members[parent->m_sub1->m_numMembers++] = parent->m_members[i];
      parent->m_sub1->m_area += area;
  }
  for(; i<parent->m_numMembers; i++) 
    if (parent->m_members[i]) {
      area = getCellArea(parent->m_members[i]);
      parent->m_sub2->m_members[parent->m_sub2->m_numMembers++] = parent->m_members[i];
      parent->m_sub2->m_area += area;
    }
  
}


// --------------------------------------------------------------------
// partitionScanlineMincut()
//
/// \brief Scans the cells within a partition from left to right and chooses the min-cut.
//
// --------------------------------------------------------------------
void partitionScanlineMincut(Partition *parent) {
#if 0
  int current_cuts = 0;
  int minimum_cuts = INT_MAX;
  ConcreteCell *minimum_location = NULL;
  double currentArea = 0, halfArea = parent->m_area * 0.5;
  double areaFlexibility = parent->m_area * MAX_PARTITION_NONSYMMETRY;
  double newLine, oldLine = -DBL_MAX;

  for(ConcreteNetList::iterator n_it = m_design->nets.begin(); !n_it; n_it++)
    (*n_it)->m_mark = 0;
  for(h::list<ConcreteCell *>::iterator i = parent->m_members.begin();
      !i.isDone(); i++) {
    assert(*i);
    for(ConcretePinMap::iterator j = (*i)->getPins().begin();
    !j.isDone(); j++) {
      assert(*j);
      if((*j)->isAttached()) {
    (*j)->getNet()->m_mark = 1;
      }
    }
  }

  if (parent->vertical) {
    parent->m_members.sort(sortByX);
    int all1 = 0, all2 = 0;
    h::list<ConcreteCell *>::iterator local = parent->m_members.begin();
    for(; !local.isDone(); local++) {
      currentArea += (*local)->getArea();
      if (currentArea < halfArea-areaFlexibility)
    continue;
      if (currentArea > halfArea+areaFlexibility)
    break;
      newLine = (*local)->temp_x;
      while(all1 < g_place_numNets && allNetsL2[all1]->getBoundingBox().left() <= newLine) {
    if(allNetsL2[all1]->m_mark) {
      current_cuts++;
    }
    all1++;
      }
      while(all2 < g_place_numNets && allNetsR2[all2]->getBoundingBox().right() <= newLine) {
    if(allNetsR2[all2]->m_mark) {
      current_cuts--;
    }
    all2++;
      }
      if (current_cuts < minimum_cuts) {
    minimum_cuts = current_cuts;
    minimum_location = *local;
      }
      oldLine = newLine;
    }
  }
  else {
    parent->m_members.sort(sortByY);
    int all1 = 0, all2 = 0;
    h::list<ConcreteCell *>::iterator local = parent->m_members.begin();
    for(; !local.isDone(); local++) {
      currentArea += (*local)->getArea();
      if (currentArea < halfArea-areaFlexibility)
    continue;
      if (currentArea > halfArea+areaFlexibility)
    break;
      newLine = (*local)->temp_y;
      while(all1 < g_place_numNets && allNetsB2[all1]->getBoundingBox().top() <= newLine) {
    if(allNetsB2[all1]->m_mark) {
      current_cuts++;
    }
    all1++;
      }
      while(all2 < g_place_numNets && allNetsT2[all2]->getBoundingBox().bottom() <= newLine) {
    if(allNetsT2[all2]->m_mark) {
      current_cuts--;
    }
    all2++;
      }
      if (current_cuts < minimum_cuts) {
    minimum_cuts = current_cuts;
    minimum_location = *local;
      }
      oldLine = newLine;
    }
  }
  if (minimum_location == NULL) {
    return partitionEqualArea(parent);
  }
  h::list<ConcreteCell *>::iterator it = parent->m_members.begin();
  parent->m_sub1->m_members.clear();
  parent->m_sub1->m_area = 0;
  for(; *it != minimum_location; it++) {
    parent->m_sub1->m_members.push_front(*it);
    parent->m_sub1->m_area += (*it)->getArea();
  }
  parent->m_sub2->m_members.clear();
  parent->m_sub2->m_area = 0;
  for(; !it; it++) {
    parent->m_sub2->m_members.push_front(*it);
    parent->m_sub2->m_area += (*it)->getArea();
  }
#endif
}


// --------------------------------------------------------------------
// reallocPartition()
//
/// \brief Reallocates a partition and all of its children.
//
// --------------------------------------------------------------------
void reallocPartition(Partition *p) {

  if (p->m_leaf) {
    return;
  }

  // --- INITIAL PARTITION

  if (PARTITION_AREA_ONLY)
    partitionEqualArea(p);
  else 
    partitionScanlineMincut(p);

  resizePartition(p);

  // --- PARTITION IMPROVEMENT
  if (p->m_level < REPARTITION_LEVEL_DEPTH) {
    if (REPARTITION_HMETIS)
      repartitionHMetis(p);
    
    resizePartition(p);
  }

  reallocPartition(p->m_sub1);
  reallocPartition(p->m_sub2);
}


// --------------------------------------------------------------------
// resizePartition()
//
/// \brief Recomputes the bounding boxes of the child partitions based on their relative areas.
//
// --------------------------------------------------------------------
void resizePartition(Partition *p) {
  // compute the new bounding box
  p->m_sub1->m_bounds.x = p->m_bounds.x;
  p->m_sub1->m_bounds.y = p->m_bounds.y;
  if (p->m_vertical) {
    p->m_sub1->m_bounds.w = p->m_bounds.w*(p->m_sub1->m_area/p->m_area);
    p->m_sub1->m_bounds.h = p->m_bounds.h;
    p->m_sub2->m_bounds.x = p->m_bounds.x + p->m_sub1->m_bounds.w;
    p->m_sub2->m_bounds.w = p->m_bounds.w*(p->m_sub2->m_area/p->m_area);
    p->m_sub2->m_bounds.y = p->m_bounds.y;
    p->m_sub2->m_bounds.h = p->m_bounds.h;
  } else {
    p->m_sub1->m_bounds.h = p->m_bounds.h*(p->m_sub1->m_area/p->m_area);
    p->m_sub1->m_bounds.w = p->m_bounds.w;
    p->m_sub2->m_bounds.y = p->m_bounds.y + p->m_sub1->m_bounds.h;
    p->m_sub2->m_bounds.h = p->m_bounds.h*(p->m_sub2->m_area/p->m_area);
    p->m_sub2->m_bounds.x = p->m_bounds.x;
    p->m_sub2->m_bounds.w = p->m_bounds.w;
  }
}


// --------------------------------------------------------------------
// incrementalSubpartition()
//
/// \brief Adds new cells to an existing partition.  Partition sizes/locations are unchanged.
///
/// The function recurses, adding new cells to appropriate subpartitions.
//
// --------------------------------------------------------------------
void incrementalSubpartition(Partition *p, ConcreteCell *newCells [], const int numNewCells) {
  int c;
  ConcreteCell **newCells1 = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*numNewCells), 
    **newCells2 = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*numNewCells);
  int numNewCells1 = 0, numNewCells2 = 0;
  float cut_loc;

  assert(p);

  // add new cells to partition list
  p->m_members = (ConcreteCell**)realloc(p->m_members, 
       sizeof(ConcreteCell*)*(p->m_numMembers+numNewCells));
  memcpy(&(p->m_members[p->m_numMembers]), newCells, sizeof(ConcreteCell*)*numNewCells);
  p->m_numMembers += numNewCells;

  // if is a leaf partition, finished
  if (p->m_leaf) return;

  // split new cells into sub-partitions based on location
  if (p->m_vertical) {
    cut_loc = p->m_sub2->m_bounds.x;
    for(c=0; c<numNewCells; c++)
      if (newCells[c]->m_x < cut_loc)
        newCells1[numNewCells1++] = newCells[c];
      else
        newCells2[numNewCells2++] = newCells[c];
  } else {
    cut_loc = p->m_sub2->m_bounds.y;
    for(c=0; c<numNewCells; c++)
      if (newCells[c]->m_y < cut_loc)
        newCells1[numNewCells1++] = newCells[c];
      else
        newCells2[numNewCells2++] = newCells[c];    
  }

  if (numNewCells1 > 0) incrementalSubpartition(p->m_sub1, newCells1, numNewCells1);
  if (numNewCells2 > 0) incrementalSubpartition(p->m_sub2, newCells2, numNewCells2);

  free(newCells1);
  free(newCells2);
}


// --------------------------------------------------------------------
// incrementalPartition()
//
/// \brief Adds new cells to an existing partition.  Partition sizes/locations are unchanged.
///
/// The function recurses, adding new cells to appropriate subpartitions.
//
// --------------------------------------------------------------------
void incrementalPartition() {
  int c = 0, c2 = 0;
  int numNewCells = 0;
  ConcreteCell **allCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells),
    **newCells = (ConcreteCell **)malloc(sizeof(ConcreteCell*)*g_place_numCells);

  assert(g_place_rootPartition);

  // update cell list of root partition
  memcpy(allCells, g_place_concreteCells, sizeof(ConcreteCell*)*g_place_numCells);
  qsort(allCells, g_place_numCells, sizeof(ConcreteCell*), cellSortByID);
  qsort(g_place_rootPartition->m_members, g_place_rootPartition->m_numMembers,
        sizeof(ConcreteCell*), cellSortByID);

  // scan sorted lists and collect cells not in partitions
  while(!allCells[c++]);
  while(!g_place_rootPartition->m_members[c2++]);

  for(; c<g_place_numCells; c++, c2++) {
    while(c2 < g_place_rootPartition->m_numMembers &&
          allCells[c]->m_id > g_place_rootPartition->m_members[c2]->m_id) c2++;
    while(c < g_place_numCells && 
          (c2 >= g_place_rootPartition->m_numMembers ||
           allCells[c]->m_id < g_place_rootPartition->m_members[c2]->m_id)) {
      // a new cell!
      newCells[numNewCells++] = allCells[c];
      c++;
    }
  }
  
  printf("QPRT-50 : \tincremental partitioning with %d new cells\n", numNewCells);
  if (numNewCells>0) incrementalSubpartition(g_place_rootPartition, newCells, numNewCells);

  free(allCells);
  free(newCells);
}
ABC_NAMESPACE_IMPL_END