// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC 2008-2018, All Rights Reserved
//
// +-----------------------------------------------------------------+
// |                   C O R I O L I S                               |
// |      K i t e  -  D e t a i l e d   R o u t e r                  |
// |                                                                 |
// |  Author      :                    Jean-Paul CHAPUT              |
// |  E-mail      :       Jean-Paul.Chaput@asim.lip6.fr              |
// | =============================================================== |
// |  C++ Module  :       "./NegociateWindow.cpp"                    |
// +-----------------------------------------------------------------+


#include <vector>
#include <algorithm>
#include <fstream>
#include <iomanip>
#include "hurricane/Breakpoint.h"
#include "hurricane/DebugSession.h"
#include "hurricane/UpdateSession.h"
#include "hurricane/Warning.h"
#include "hurricane/Bug.h"
#include "hurricane/RoutingPad.h"
#include "hurricane/Net.h"
#include "hurricane/Cell.h"
#include "crlcore/Utilities.h"
#include "crlcore/AllianceFramework.h"
#include "crlcore/Measures.h"
#include "crlcore/Histogram.h"
#include "anabatic/AutoContact.h"
#include "katana/DataNegociate.h"
#include "katana/TrackElement.h"
#include "katana/TrackMarker.h"
#include "katana/TrackCost.h"
#include "katana/Track.h"
#include "katana/TrackSegment.h"
#include "katana/RoutingPlane.h"
#include "katana/RoutingEventQueue.h"
#include "katana/RoutingEventHistory.h"
#include "katana/RoutingEventLoop.h"
#include "katana/NegociateWindow.h"
#include "katana/KatanaEngine.h"


namespace {

  using namespace std;
  using namespace Hurricane;
  using namespace CRL;
  using namespace Katana;


  void  NegociateOverlapCost ( const TrackElement* segment, TrackCost& cost )
  {
    cdebug_log(159,1) << "NegociateOverlapCost() " << segment << endl;
    Interval intersect = segment->getCanonicalInterval();

    if (not intersect.intersect(cost.getInterval())) {
      cdebug_tabw(159,-1);
      return;
    }

    if (segment->getNet() != cost.getNet()) {
      cdebug_log(159,0) << segment->getNet() << " vs. " << cost.getNet() << endl;
      if (   segment->isBlockage()
         or (segment->isFixed()
            and not (segment->isVertical() and Session::getKatanaEngine()->isChannelStyle()))) {
        cdebug_log(159,0) << "Infinite cost from: " << segment << endl;
        cost.setInfinite   ();
        cost.setOverlap    ();
        cost.setHardOverlap();
        cost.setBlockage   ();
        cdebug_tabw(159,-1);
        return;
      }
    }

    if (cost.getInterval().getVMax() > intersect.getVMax()) cost.setLeftOverlap();
    if (cost.getInterval().getVMin() < intersect.getVMin()) cost.setRightOverlap();

    if (not intersect.contains(cost.getInterval()))
      intersect.intersection( cost.getInterval() );
    else {
      DbU::Unit beginOverlap = cost.getInterval().getVMin() - intersect.getVMin();
      DbU::Unit   endOverlap = intersect.getVMax() - cost.getInterval().getVMax();

      cost.setLonguestOverlap( std::min( beginOverlap, endOverlap ) );
      cost.setGlobalEnclosed();
    }

    DataNegociate* data = segment->getDataNegociate();
    if (not data) {
      cdebug_tabw(159,-1);
      return;
    }

    TrackElement*  refSegment = cost.getRefElement();
    DataNegociate* refData    = refSegment->getDataNegociate();
    AutoSegment*   refBase    = refSegment->base();
    AutoSegment*      base    =    segment->base();

    if (base and refBase and refData) {
      if (   (   base->getRpDistance() == 0)
         and (refBase->getRpDistance() >  0)
         and (refData->getState()      >  DataNegociate::RipupPerpandiculars)
         and (   data->getState()      == DataNegociate::RipupPerpandiculars)
         and (   data->getRipupCount() >  4)) {
        cost.setAtRipupLimit();
      }

      if (   refSegment->isNonPref()
         and segment->isUnbreakable()
         and (data->getState()      >= DataNegociate::Minimize)
         and (data->getStateCount() >= 2) ) {
        cost.setAtRipupLimit();
      }
    }

    cost.mergeRipupCount( data->getRipupCount() );
    if (segment->isLocal()) {
      cost.mergeDataState( data->getState() );
      if (data->getState() >=  DataNegociate::LocalVsGlobal) {
        cdebug_log(159,0) << "MaximumSlack/LocalVsGlobal for " << segment << endl;
        cost.setAtRipupLimit();
      }
    }

    if (data->getState() == DataNegociate::MaximumSlack) {
      cost.setAtRipupLimit();
    }

    if (segment->isGlobal()) {
      cost.setOverlapGlobal();
      if (   (cost.getFlags() & TrackCost::LocalAndTopDepth)
         and (data->getState() >= DataNegociate::MoveUp) ) {
        cost.setInfinite   ();
        cost.setOverlap    ();
        cost.setHardOverlap();
        cdebug_tabw(159,-1);
        return;
      }
    }

    cost.setOverlap();
    if ( segment->isLocal()
       or (cost.isForGlobal() and (Session::getRoutingGauge()->getLayerDepth(segment->getLayer()) < 3)) ) {
      cdebug_log(9000,0) << "Deter|     incTerminals() " << boolalpha << cost.isForGlobal() << " " << (data->getTerminals()*100) << endl;
      cost.incTerminals( data->getTerminals()*100 );
    } else {
      cdebug_log(9000,0) << "Deter|     isForGlobal() " << boolalpha << cost.isForGlobal() << endl;
    }

    cdebug_log(159,0) << "| Increment Delta: " << DbU::getValueString(intersect.getSize()) << endl;
    cost.incDelta( intersect.getSize() );

    // if (segment->base()->getId() == 70433) {
    //   cdebug_log(159,0) << "| G:" << cost.isForGlobal()
    //                     << " L:" << segment->isLocal()
    //                     << " rpD:" << segment->base()->getRpDistance()
    //                     << " state:" << data->getState()
    //                     << " (Dogleg:" << DataNegociate::Dogleg
    //                     << ") ripup:" << data->getRipupCount()
    //                     << endl;
    // }

    if (   cost.isForGlobal()
       and segment->isLocal()
       and (segment->base()->getRpDistance() <  2)
       and (data->getState()      >= DataNegociate::Dogleg)
     //and (data->getRipupCount() >  Session::getConfiguration()->getRipupLimit(Configuration::LocalRipupLimit) - 2)
       ) {
      cost.setInfinite();
      cdebug_log(159,0) << "Infinite cost from (RP access)" << segment << endl;
    }
      
    cdebug_tabw(159,-1);
  }


  void  loadRoutingPads ( NegociateWindow* nw )
  {
    AllianceFramework* af   = AllianceFramework::get ();
    RoutingGauge*      rg   = nw->getKatanaEngine()->getConfiguration()->getRoutingGauge();
  //bool               isVH = rg->isVH();

    for( Net* net : nw->getCell()->getNets() ) {
      if (net->getType() == Net::Type::POWER ) continue;
      if (net->getType() == Net::Type::GROUND) continue;
    //if (net->getType() == Net::Type::CLOCK ) continue;
      if (af->isBLOCKAGE(net->getName())) continue;

      for( RoutingPad* rp : net->getRoutingPads() ) {
        size_t depth   = rg->getLayerDepth(rp->getLayer());
        size_t rlDepth = depth - rg->getFirstRoutingLayer();
        if (rlDepth == 0) {
          TrackMarker::create( rp, depth+1 );
        //if (isVH) TrackMarker::create( rp, depth+2 );
        }
        if (rlDepth == 1) {
          if (depth+1 < rg->getDepth())
            TrackMarker::create( rp, depth+1 );
        }
      }
    }
  }


  class CompareByPriority {
    public:
      inline bool operator() ( const TrackSegment* lhs, const TrackSegment* rhs )
      { return lhs->getPriority() > rhs->getPriority(); }
  };
 

  void  computeNetPriority ( Net* net )
  {
    DebugSession::open( net, 159, 160 );

    cdebug_log(159,1) << "::computeNetPriority() " << net << endl;

    vector<TrackSegment*> segments;
    for ( Segment* segment : net->getSegments() ) {
      TrackSegment* canonical = dynamic_cast<TrackSegment*>( Session::lookup( segment ) );
      if (canonical) segments.push_back( canonical );
    }

    for ( TrackSegment* segment : segments ) segment->computePriority();
    sort( segments.begin(), segments.end(), CompareByPriority() );

    for ( TrackSegment* segment : segments ) {
      segment->computeAlignedPriority();
    }

    cdebug_tabw(159,-1);

    DebugSession::close();
  }


} // Anonymous namespace.


namespace Katana {

  using std::ofstream;
  using std::cerr;
  using std::endl;
  using std::setw;
  using std::left;
  using std::right;
  using std::setprecision;
  using Hurricane::Breakpoint;
  using Hurricane::Warning;
  using Hurricane::Bug;
  using Hurricane::tab;
  using Hurricane::ForEachIterator;
  using Hurricane::DebugSession;
  using Hurricane::UpdateSession;
  using CRL::Histogram;
  using CRL::addMeasure;
  using Anabatic::AutoContact;
  using Anabatic::AutoSegment;
  using Anabatic::AutoSegmentLut;
  using Anabatic::perpandicularTo;


// -------------------------------------------------------------------
// Class  :  "NegociateWindow".


  NegociateWindow::NegociateWindow ( KatanaEngine* katana )
    : _flags       (Flags::NoFlags)
    , _interrupt   (false)
    , _katana        (katana)
    , _gcells      ()
    , _segments    ()
    , _eventQueue  ()
    , _eventHistory()
    , _eventLoop   (10,70)
  { }


  NegociateWindow* NegociateWindow::create ( KatanaEngine* katana )
  {
    NegociateWindow* negociateWindow = new NegociateWindow ( katana );
    return negociateWindow;
  }


  NegociateWindow::~NegociateWindow ()
  { }


  void  NegociateWindow::destroy ()
  { delete this; }


  Cell* NegociateWindow::getCell () const
  { return _katana->getCell(); }


  void  NegociateWindow::setGCells ( const vector<GCell*>& gcells )
  {
    _gcells = gcells;

    if (not Session::getKatanaEngine()->isChannelStyle()) loadRoutingPads( this );
    Session::revalidate();

    for ( auto element : Session::getKatanaEngine()->_getAutoSegmentLut() ) {
      TrackElement* segment = Session::lookup( element.second );
      if (segment) segment->getDataNegociate()->update();
    }

    _statistics.setGCellsCount( _gcells.size() );
  }


  void  NegociateWindow::addRoutingEvent ( TrackElement* segment, uint32_t level )
  {
    DataNegociate* data = segment->getDataNegociate();
    if (not data or not data->hasRoutingEvent())
      _eventQueue.add( segment, level );
    else
      cerr << Bug( "NegociateWidow::addRoutingEvent(): Try to adds twice the same TrackElement event."
                   "\n       %p:%s."
                 , (void*)segment->base()->base()
                 , getString(segment).c_str()
                 ) << endl;
  }


  TrackElement* NegociateWindow::createTrackSegment ( AutoSegment* autoSegment, Flags flags )
  {
    DebugSession::open( autoSegment->getNet(), 159, 160 );

    cdebug_log(159,1) << "NegociateWindow::createTrackSegment() - " << autoSegment << endl;
    RoutingPlane* plane     = Session::getKatanaEngine()->getRoutingPlaneByLayer(autoSegment->getLayer());
    Track*        refTrack  = plane->getTrackByPosition( autoSegment->getAxis() );
    Track*        insTrack  = NULL;
    size_t        trackSpan = 1;

  // Special case: fixed AutoSegments must not interfere with blockages.
  // Ugly: uses of getExtensionCap().
    if (autoSegment->isFixed()) {
      if (Session::isChannelStyle() and autoSegment->isReduced()) {
        cdebug_log(159,0) << "* Fixed segment is reduced, ignore " << autoSegment << endl;
        cdebug_tabw(159,-1);
        return NULL;
      }

      size_t        begin;
      size_t        end;
      Interval      fixedSpan;
      Interval      blockageSpan;

      if (Session::isChannelStyle() and autoSegment->isNonPref()) {
        cdebug_log(159,0) << "Fixed in non-preferred direction, do not attempt to set on track." << endl;
        cdebug_tabw(159,-1);
        DebugSession::close();
        return NULL;
      }
      if (not refTrack) {
        string message = "NULL refTrack for " + getString(autoSegment) + " brace for crashing!";
        Breakpoint::stop( 0, message );
      }
      
      if (refTrack->getAxis() != autoSegment->getAxis()) { 
        trackSpan = 2;
        refTrack  = plane->getTrackByPosition( autoSegment->getAxis(), Constant::Inferior );
        insTrack  = refTrack;
      }

      Track* track = refTrack;
      for ( size_t ispan=0 ; track and (ispan < trackSpan) ; ++ispan, track=track->getNextTrack() ) {
        autoSegment->getCanonical( fixedSpan );
        fixedSpan.inflate( Session::getExtensionCap(autoSegment->getLayer())-1 );

        track->getOverlapBounds( fixedSpan, begin, end );
        for ( ; (begin < end) ; begin++ ) {
          TrackElement* other = track->getSegment(begin);
          cdebug_log(159,0) << "| overlap: " << other << endl;

          if (not other->isBlockage()) continue;

          other->getCanonical( blockageSpan );
          blockageSpan.inflate( Session::getExtensionCap(autoSegment->getLayer()) );

          cdebug_log(159,0) << "  fixed:" << fixedSpan << " vs. blockage:" << blockageSpan << endl;
          if (not fixedSpan.intersect(blockageSpan)) continue;

        // Overlap between fixed & blockage.
          cdebug_log(159,0) << "* Blockage overlap: " << autoSegment << endl;
        //Session::destroyRequest( autoSegment );
          cerr << Warning( "Overlap between fixed %s and blockage at %s."
                         , getString(autoSegment).c_str()
                         , getString(blockageSpan).c_str() ) << endl;
          cdebug_tabw(159,-1);
          DebugSession::close();
          return NULL;
        }
      }
    }

    Interval span;
    autoSegment = autoSegment->getCanonical( span );

    bool           created;
    TrackElement*  trackSegment = TrackSegment::create( autoSegment, insTrack, created );

    if (not (flags & Flags::LoadingStage))
      cdebug_log(159,0) << "* lookup: " << autoSegment << endl;

    if (created) {
      cdebug_log(159,0) << "* " << trackSegment << endl;

      if (trackSegment->isNonPref()) {
        _segments.push_back( trackSegment );
        cdebug_log(159,0) << "Non-preferred direction, do not attempt to set on track." << endl;
        cdebug_tabw(159,-1);
        DebugSession::close();
        return trackSegment;
      }

      Interval uside = autoSegment->getAutoSource()->getGCell()->getSide( perpandicularTo(autoSegment->getDirection()) );
      Interval constraints;
      autoSegment->getConstraints( constraints );
      cdebug_log(159,0) << "* Constraints " << constraints << endl;

      uside.intersection( constraints );
      cdebug_log(159,0) << "* Constraints+U-side " << constraints << endl;
      cdebug_log(159,0) << "* Nearest " << refTrack << endl;

      if (not refTrack)
        throw Error( "NegociateWindow::createTrackSegment(): No track near axis of %s."
                   , getString(autoSegment).c_str() );

      if (not insTrack) {
        insTrack = refTrack;
        if (refTrack->getAxis() > uside.getVMax()) insTrack = refTrack->getPreviousTrack();
        if (refTrack->getAxis() < uside.getVMin()) insTrack = refTrack->getNextTrack();
      }
      if (not insTrack)
        throw Error( "NegociateWindow::createTrackSegment(): No track near axis of %s (after adjust)." 
                   , getString(autoSegment).c_str() );

      cdebug_log(159,0) << "* GCell U-side " << uside << endl;
      cdebug_log(159,0) << "* " << plane << endl;
      cdebug_log(159,0) << "* " << insTrack << endl;

      if (trackSpan == 1)
        trackSegment->setAxis( insTrack->getAxis(), AutoSegment::SegAxisSet );
      trackSegment->invalidate();

      if (trackSegment->isFixed() and not trackSegment->getTrack()) {
        Session::addInsertEvent( trackSegment, insTrack, refTrack->getAxis() );
      } else {
        _segments.push_back( trackSegment );
      }
    }

    if (not created and not (flags & Flags::LoadingStage)) {
      cdebug_log(159,0) << "TrackSegment already exists (and not in loading stage)." << endl;
    }

    cdebug_tabw(159,-1);
    DebugSession::close();

    return trackSegment;
  }


  double  NegociateWindow::computeWirelength ()
  {
    bool isSymbolic = getKatanaEngine()->getConfiguration()->getRoutingGauge()->isSymbolic();
    set<TrackElement*> accounteds;
    double totalWL = 0.0;

    for ( size_t igcell=0 ; igcell<_gcells.size() ; ++igcell ) {
      double        gcellWL = 0.0;
      Segment*      segment;
      TrackElement* trackSegment;

      const vector<AutoContact*>& contacts = _gcells[igcell]->getContacts();
      for ( size_t i=0 ; i<contacts.size() ; i++ ) {
        for( Hook* hook : contacts[i]->getBodyHook()->getSlaveHooks() ) {
          Hook* sourceHook = dynamic_cast<Segment::SourceHook*>(hook);
          if (not sourceHook) continue;
          
          segment       = dynamic_cast<Segment*>(sourceHook->getComponent());
          trackSegment  = Session::lookup( segment );
          if (trackSegment) {
            if (accounteds.find(trackSegment) != accounteds.end()) continue;

            accounteds.insert( trackSegment );
            if (isSymbolic)
              gcellWL += DbU::getLambda( trackSegment->getLength() );
            else
              gcellWL += DbU::toPhysical( trackSegment->getLength(), DbU::UnitPower::Nano );
          }
        }
      }

    // Partial sum to limit rounding errors.
      totalWL += gcellWL;
    }

    if (not isSymbolic) totalWL /= 1000.0;
    return totalWL;
  }


  void  NegociateWindow::_computePriorities ()
  {
    for ( Net* net : getCell()->getNets() ) {
      if (NetRoutingExtension::isAnalog(net)) computeNetPriority( net );
    }
  }


  void  NegociateWindow::_createRouting  ( Anabatic::GCell* gcell )
  {
    cdebug_log(159,1) << "NegociateWindow::_createRouting() - " << gcell << endl;

    Segment*     segment;
    AutoSegment* autoSegment;

    cdebug_log(159,0) << "AutoSegments from AutoContacts" << endl;
    const vector<AutoContact*>& contacts = gcell->getContacts();
    for ( size_t i=0 ; i<contacts.size() ; i++ ) {
      for( Component* component : contacts[i]->getSlaveComponents() ) {
        segment      = dynamic_cast<Segment*>(component);
        autoSegment  = Session::base()->lookup( segment );
        cdebug_log(159,0) << autoSegment << endl;
        if (autoSegment and autoSegment->isCanonical()) {
          createTrackSegment( autoSegment, Flags::LoadingStage );
        }
      }
    }

    cdebug_log(159,0) << "_segments.size():" << _segments.size() << endl;
    cdebug_tabw(159,-1);
  }


  void  NegociateWindow::_pack ( size_t& count, bool last )
  {
    uint64_t limit     = _katana->getEventsLimit();
    _katana->setStage( StagePack );

    RoutingEventQueue  packQueue;
  //for ( size_t i = (count > 600) ? count-600 : 0
  //    ; (i<_eventHistory.size()-(last ? 0 : 100)) and not isInterrupted() ; i++ ) {
    for ( size_t i=0 ; i<_eventHistory.size() ; ++i ) {
      RoutingEvent* event = _eventHistory.getNth(i);

      if ( event and not event->isCloned() ) {
        cerr << "Cloned:" << event->isCloned()
             << " UTurn:" << event->getSegment()->isUTurn() << " " << event->getSegment() << endl;
      }
          
      if ( event and not event->isCloned() and event->getSegment()->isUTurn() ) {
        event->reschedule( packQueue, 0 );
      }
    }
    packQueue.commit();

    while ( not packQueue.empty() and not isInterrupted() ) {
      RoutingEvent* event = packQueue.pop();

      if (tty::enabled()) {
        cmess2 << "        <pack.event:" << tty::bold << setw(8) << setfill('0')
               << RoutingEvent::getProcesseds() << tty::reset
               << " remains:" << right << setw(8) << setfill('0')
               << packQueue.size() << ">"
               << setfill(' ') << tty::reset << tty::cr;
        cmess2.flush();
      } else {
        cmess2 << "        <pack.event:" << setw(8) << setfill('0')
               << RoutingEvent::getProcesseds() << setfill(' ') << " "
               << event->getEventLevel() << ":" << event->getPriority() << "> "
               << event->getSegment()
               << endl;
        cmess2.flush();
      }

      event->process( packQueue, _eventHistory, _eventLoop );

      if (RoutingEvent::getProcesseds() >= limit) setInterrupt( true );
    }
  // Count will be wrong!
  }


  size_t  NegociateWindow::_negociate ()
  {
    cdebug_log(9000,0) << "Deter| NegociateWindow::_negociate()" << endl;
    cdebug_log(159,1) << "NegociateWindow::_negociate() - " << _segments.size() << endl;

    cmess1 << "     o  Negociation Stage." << endl;

    unsigned long limit     = _katana->getEventsLimit();
    bool          profiling = _katana->profileEventCosts();
    ofstream      ofprofile;

    if (profiling) ofprofile.open( "katana.profile.txt" );

    _eventHistory.clear();
    _eventQueue.load( _segments );
    cmess2 << "        <queue:" <<  right << setw(8) << setfill('0') << _eventQueue.size() << ">" << endl;
    if (cdebug.enabled(9000)) _eventQueue.dump();

    size_t count = 0;
    _katana->setStage( StageNegociate );
    while ( not _eventQueue.empty() and not isInterrupted() ) {
      RoutingEvent* event = _eventQueue.pop();

      if (ofprofile.is_open()) {
        size_t depth = _katana->getConfiguration()->getLayerDepth( event->getSegment()->getLayer() );
        if (depth < 6) {
          ofprofile << setw(10) << right << count << " ";
          for ( size_t i=0 ; i<6 ; ++i ) {
            if (i == depth)
              ofprofile << setw(10) << right << setprecision(2) << event->getPriority() << " ";
            else
              ofprofile << setw(10) << right << setprecision(2) << 0.0 << " ";
          }

          ofprofile << setw( 2) << right << event->getEventLevel() << endl;
        }
      }

      if (tty::enabled()) {
        cmess2 << "        <event:" << tty::bold << right << setw(8) << setfill('0')
               << RoutingEvent::getProcesseds() << tty::reset
               << " remains:" << right << setw(8) << setfill('0')
               << _eventQueue.size()
               << setfill(' ') << tty::reset << ">" << tty::cr;
        cmess2.flush ();
      } else {
        cmess2 << "        <event:" << right << setw(8) << setfill('0')
               << RoutingEvent::getProcesseds() << setfill(' ') << " "
               << event->getEventLevel() << ":" << event->getPriority()
               << ":" << DbU::getValueString(event->getSegment()->getLength()) << "> "
               << event->getSegment()
               << endl;
        cmess2.flush();
      }

    //if (RoutingEvent::getProcesseds() == 14473)
    //  Breakpoint::stop( 0, "Before processing RoutingEvent 14473." );

      event->process( _eventQueue, _eventHistory, _eventLoop );
      count++;

      // if (event->getSegment()->getNet()->getId() == 239546) {
      //   UpdateSession::close();
      //   ostringstream message;
      //   message << "After processing an event from Net id:239546\n" << event;
      //   Breakpoint::stop( 0, message.str() );
      //   UpdateSession::open();
      // }

    //if (count and not (count % 500)) {
    //  _pack( count, false );
    //} 

      // if (RoutingEvent::getProcesseds() == 65092) {
      //   UpdateSession::close();
      //   Breakpoint::stop( 0, "Overlap has happened" );
      //   UpdateSession::open();
      // }
      if (RoutingEvent::getProcesseds() >= limit) setInterrupt( true );
    }
  //_pack( count, true );
      _negociateRepair();

      if (_katana->getConfiguration()->runRealignStage()) {
        cmess1 << "     o  Realign Stage." << endl;
        
        cdebug_log(159,0) << "Loadind realign queue." << endl;
        _katana->setStage( StageRealign );
        for ( size_t i=0 ; (i<_eventHistory.size()) and not isInterrupted() ; i++ ) {
          RoutingEvent* event = _eventHistory.getNth(i);
          if (not event->isCloned() and event->getSegment()->canRealign())
            event->reschedule( _eventQueue, 0 );
        }
        _eventQueue.commit();
        cmess2 << "        <realign.queue:" <<  right << setw(8) << setfill('0')
               << _eventQueue.size() << ">" << setfill(' ') << endl;
        count = 0;
        while ( not _eventQueue.empty() and not isInterrupted() ) {
          RoutingEvent* event = _eventQueue.pop();
          if (tty::enabled()) {
            cmess2 << "        <realign.event:" << tty::bold << setw(8) << setfill('0')
                   << RoutingEvent::getProcesseds() << tty::reset
                   << " remains:" << right << setw(8) << setfill('0')
                   << _eventQueue.size() << ">"
                   << setfill(' ') << tty::reset << tty::cr;
            cmess2.flush();
          } else {
            cmess2 << "        <realign.event:" << setw(8) << setfill('0')
                   << RoutingEvent::getProcesseds() << setfill(' ') << " "
                   << event->getEventLevel() << ":" << event->getPriority() << "> "
                   << event->getSegment()
                   << endl;
            cmess2.flush();
          }
          event->process( _eventQueue, _eventHistory, _eventLoop );
          count++;
          if (RoutingEvent::getProcesseds() >= limit) setInterrupt( true );
        }

        _negociateRepair();
      }

      if (count and cmess2.enabled() and tty::enabled()) cmess1 << endl;
  //}

    size_t eventsCount = _eventHistory.size();

    _eventHistory.clear();
    _eventQueue.clear();

    if (RoutingEvent::getAllocateds() > 0) {
      cerr << Bug( "%d events remains after clear.", RoutingEvent::getAllocateds() ) << endl;
    }

    if (ofprofile.is_open()) ofprofile.close();
    _statistics.setEventsCount( eventsCount );
    cdebug_tabw(159,-1);

    return eventsCount;
  }


  void  NegociateWindow::_negociateRepair ()
  {
    cdebug_log(159,1) << "NegociateWindow::_negociateRepair() - " << _segments.size() << endl;

    uint64_t limit = _katana->getEventsLimit();
    uint64_t count = 0;
    if (count and cmess2.enabled() and tty::enabled()) cmess1 << endl;

    cmess1 << "     o  Repair Stage." << endl;
    cdebug_log(159,0) << "Loadind Repair queue." << endl;

    _katana->setStage( StageRepair );
    for ( size_t i=0 ; (i<_eventHistory.size()) and not isInterrupted() ; i++ ) {
      RoutingEvent* event = _eventHistory.getNth(i);
      if (not event->isCloned() and (event->getState() >= DataNegociate::Unimplemented)) {
        event->setState( DataNegociate::Repair );
        event->reschedule( _eventQueue, 0 );
      }
    }
    _eventQueue.commit();
    cmess2 << "        <repair.queue:" <<  right << setw(8) << setfill('0')
           << _eventQueue.size() << ">" << setfill(' ') << endl;

    while ( not _eventQueue.empty() and not isInterrupted() ) {
      RoutingEvent* event = _eventQueue.pop();

      if (tty::enabled()) {
        cmess2 << "        <repair.event:" << tty::bold << setw(8) << setfill('0')
               << RoutingEvent::getProcesseds() << tty::reset
               << " remains:" << right << setw(8) << setfill('0')
               << _eventQueue.size() << ">"
               << setfill(' ') << tty::reset << tty::cr;
        cmess2.flush();
      } else {
        cmess2 << "        <repair.event:" << setw(8) << setfill('0')
               << RoutingEvent::getProcesseds() << setfill(' ') << " "
               << event->getEventLevel() << ":" << event->getPriority() << "> "
               << event->getSegment()
               << endl;
        cmess2.flush();
      }

      event->process( _eventQueue, _eventHistory, _eventLoop );

      count++;
      if (RoutingEvent::getProcesseds() >= limit) setInterrupt( true );
    }

    cdebug_tabw(159,-1);
  }


  void  NegociateWindow::run ( Flags flags )
  {
    cdebug_log(159,1) << "NegociateWindow::run()" << endl;

    cmess1 << "  o  Running Negociate Algorithm" << endl;

    TrackElement::setOverlapCostCB( NegociateOverlapCost );
    RoutingEvent::resetProcesseds();

    for ( size_t igcell=0 ; igcell<_gcells.size() ; ++igcell ) {
      _createRouting( _gcells[igcell] );
    }
    Session::revalidate();
    _computePriorities();
    
    if (not (flags & Flags::PreRoutedStage)) {
      _katana->preProcess();
      _katana->_computeCagedConstraints();
      Session::revalidate();
    }

    _katana->setMinimumWL( computeWirelength() );

#if defined(CHECK_DATABASE)
    uint32_t overlaps = 0;
    Session::getKatanaEngine()->_check( overlaps, "after _createRouting(GCell*)" );
#endif 

    if (flags & Flags::PairSymmetrics) {
      _katana->pairSymmetrics();
      Session::revalidate();
    }

    _flags |= flags;
    _negociate();
    printStatistics();

    if (flags & Flags::PreRoutedStage) {
      _katana->setFixedPreRouted();
    }
    Session::revalidate();

    for ( RoutingPlane* plane : _katana->getRoutingPlanes() ) {
      for ( Track* track : plane->getTracks() )
        track->repair();
    }
    Session::revalidate();
    Session::get()->isEmpty();

# if defined(CHECK_DATABASE)
    _katana->_check( overlaps, "after negociation" );
# endif

    cdebug_tabw(159,-1);
  }


  void  NegociateWindow::printStatistics () const
  {
    cmess1 << "  o  Computing statistics." << endl;
    cmess1 << Dots::asSizet("     - Processeds Events Total",RoutingEvent::getProcesseds()) << endl;
    cmess1 << Dots::asSizet("     - Unique Events Total"
                           ,(RoutingEvent::getProcesseds() - RoutingEvent::getCloneds())) << endl;
    cmess1 << Dots::asSizet("     - # of GCells",_statistics.getGCellsCount()) << endl;
    _katana->printCompletion();

    _katana->addMeasure<size_t>( "Events" , RoutingEvent::getProcesseds(), 12 );
    _katana->addMeasure<size_t>( "UEvents", RoutingEvent::getProcesseds()-RoutingEvent::getCloneds(), 12 );

    Histogram* densityHistogram = new Histogram ( 1.0, 0.1, 2 );
    _katana->addMeasure<Histogram>( "GCells Density Histogram", densityHistogram );

    densityHistogram->setFileExtension( ".density.histogram" );
    densityHistogram->setMainTitle    ( "GCell Densities" );
    densityHistogram->setTitle        ( "Avg. Density", 0 );
    densityHistogram->setTitle        ( "Peak Density", 1 );
    densityHistogram->setColor        ( "green", 0 );
    densityHistogram->setColor        ( "red"  , 1 );

    const vector<GCell*>& gcells = getKatanaEngine()->getGCells();

    getKatanaEngine()->setDensityMode( Anabatic::AnabaticEngine::MaxHVDensity );
    for ( size_t igcell=0 ; igcell<gcells.size() ; ++igcell ) {
      densityHistogram->addSample( gcells[igcell]->getDensity(), 0 );
    }

    getKatanaEngine()->setDensityMode( Anabatic::AnabaticEngine::MaxDensity );
    for ( size_t igcell=0 ; igcell<gcells.size() ; ++igcell ) {
      densityHistogram->addSample( gcells[igcell]->getDensity(), 1 );
    }

    densityHistogram->normalize( 0 );
    densityHistogram->normalize( 1 );
  }


  string  NegociateWindow::_getString () const
  {
    ostringstream  os;

    os << "<" << _getTypeName() << ">";
    return os.str();
  }


  Record* NegociateWindow::_getRecord () const
  {
    Record* record = new Record ( _getString() );
                                     
    record->add( getSlot( "_gcells", _gcells ) );
    return record;
  }


} // Katana namespace.