namespace CCT {


//***************************************************************************
// ANodeFilter support
//***************************************************************************

bool
HasANodeTy(const ANode& node, long type)
{
  return (type == ANode::TyANY || node.type() == ANode::IntToANodeType(type));
}


const ANodeFilter ANodeTyFilter[ANode::TyNUMBER] = {
  ANodeFilter(HasANodeTy, ANode::ANodeTyToName(ANode::TyRoot).c_str(),
	      ANode::TyRoot),
  ANodeFilter(HasANodeTy, ANode::ANodeTyToName(ANode::TyProcFrm).c_str(),
	      ANode::TyProcFrm),
  ANodeFilter(HasANodeTy, ANode::ANodeTyToName(ANode::TyProc).c_str(),
	      ANode::TyProc),
  ANodeFilter(HasANodeTy, ANode::ANodeTyToName(ANode::TyLoop).c_str(),
	      ANode::TyLoop),
  ANodeFilter(HasANodeTy, ANode::ANodeTyToName(ANode::TyStmt).c_str(),
	      ANode::TyStmt),
  ANodeFilter(HasANodeTy, ANode::ANodeTyToName(ANode::TyCall).c_str(),
	      ANode::TyCall),
  ANodeFilter(HasANodeTy, ANode::ANodeTyToName(ANode::TyANY).c_str(),
	      ANode::TyANY)
};

  
//***************************************************************************
// ANodeChildIterator
//***************************************************************************


//***************************************************************************
// ANodeIterator
//***************************************************************************


//***************************************************************************
// ANodeSortedIterator
//***************************************************************************

static int
cmp(uint64_t x, uint64_t y)
{
  // compare without the possible overflow caused by (x - y)
  if (x == y) {
    return 0;
  }
  else if (x < y) {
    return -1;
  }
  else {
    return 1;
  }
}


static int
cmpByDynInfoSpecial(const ADynNode* x_dyn, const ADynNode* y_dyn)
{
  // INVARIANT: x and y are non-NULL

  // 1. lm-id for physical or logical frames
  int cmp_lmId = x_dyn->lmId() - y_dyn->lmId();
  if (cmp_lmId != 0) {
    return cmp_lmId;
  }

  // 2. physical ip
  int cmp_ip_real = cmp(x_dyn->lmIP_real(), y_dyn->lmIP_real());
  if (cmp_ip_real != 0) {
    return cmp_ip_real;
  }

  // 3. logical ip (if available)
  int cmp_ip = cmp(x_dyn->lmIP(), y_dyn->lmIP());
  return cmp_ip;
}


ANodeSortedIterator::
ANodeSortedIterator(const ANode* node,
		    ANodeSortedIterator::cmp_fptr_t compare_fn,
		    const ANodeFilter* filterFunc,
		    bool leavesOnly)
{
  ANodeIterator it(node, filterFunc, leavesOnly);
  for (ANode* cur = NULL; (cur = it.current()); it++) {
    scopes.Add((unsigned long) cur);
  }
  ptrSetIt = new WordSetSortedIterator(&scopes, compare_fn);
}


void
ANodeSortedIterator::dumpAndReset(ostream& os)
{
  os << "ANodeSortedIterator: " << endl;
  while (current()) {
    os << current()->toStringMe() << endl;
    (*this)++;
  } 
  reset();
}


int
ANodeSortedIterator::cmpByName(const void* a, const void* b)
{
  ANode* x = (*(ANode**)a);
  ANode* y = (*(ANode**)b);
  return x->name().compare(y->name()); // strcmp(x, y)
}


int
ANodeSortedIterator::cmpByLine(const void* a, const void* b)
{
  ANode* x = (*(ANode**)a);
  ANode* y = (*(ANode**)b);
  return ANodeLineComp(x, y);
}


int
ANodeSortedIterator::cmpByStructureInfo(const void* a, const void* b)
{
  ANode* x = (*(ANode**)a);
  ANode* y = (*(ANode**)b);

  if (x && y) {
    // 0. test for equality
    if (x == y) {
      return 0;
    }

    // INVARIANT: x != y, so never return 0
    
    // 1. distinguish by structure ids
    uint x_id = x->structureId();
    uint y_id = y->structureId();
    int cmp_sid = cmp(x_id, y_id);
    if (cmp_sid != 0) {
      return cmp_sid;
    }

    // 2. distinguish by types
    int cmp_ty = (int)x->type() - (int)y->type();
    if (cmp_ty != 0) {
      return cmp_ty;
    }

    // 3. distinguish by dynamic info (unnormalized CCTs)
    //    (for determinism, ensure x and y are both ADynNodes)
    ADynNode* x_dyn = dynamic_cast<ADynNode*>(x);
    ADynNode* y_dyn = dynamic_cast<ADynNode*>(y);
    if (x_dyn && y_dyn) {
      int cmp_dyn = cmpByDynInfoSpecial(x_dyn, y_dyn);
      if (cmp_dyn != 0) {
	return cmp_dyn;
      }
    }
    
    
    // 5. distinguish by id
    int cmp_id = (int)x->id() - (int)y->id();
    if (cmp_id != 0) {
      return cmp_id;
    }

    // 4. distinguish by tree context
    ANode* x_parent = x->parent();
    ANode* y_parent = y->parent();
    if (x_parent != y_parent) {
      int cmp_ctxt = cmpByStructureInfo(&x_parent, &y_parent);
      if (cmp_ctxt != 0) {
	return cmp_ctxt;
      }
    }
    // *. Could compare childCount() and other aspects of children.
    DIAG_Die("Prof::CCT::ANodeSortedIterator::cmpByStructureInfo: cannot compare:"
		<< "\n\tx: " << x->toStringMe(Prof::CCT::Tree::OFlg_Debug)
		<< "\n\ty: " << y->toStringMe(Prof::CCT::Tree::OFlg_Debug));
    return 0;
  }
  else if (x) {
    return 1; // x > y=NULL (only used for recursive case)
  }
  else if (y) {
    return -1; // x=NULL < y (only used for recursive case)
  }
  else {
    DIAG_Die(DIAG_UnexpectedInput);
  }
}


int
ANodeSortedIterator::cmpByDynInfo(const void* a, const void* b)
{
  ANode* x = (*(ANode**)a);
  ANode* y = (*(ANode**)b);

  // 0. test for equality
  if (x == y) {
    return 0;
  }

  // INVARIANT: x != y, so never return 0

  ADynNode* x_dyn = dynamic_cast<ADynNode*>(x);
  ADynNode* y_dyn = dynamic_cast<ADynNode*>(y);

  // 1. distinguish by dynamic info
  if (x_dyn && y_dyn) {
    return cmpByDynInfoSpecial(x_dyn, y_dyn);
  }

  // 2. distinguish by structure ids
  uint x_id = x->structureId();
  uint y_id = y->structureId();
  if (x_id != Prof::Struct::ANode::Id_NULL
      && y_id != Prof::Struct::ANode::Id_NULL) {
    int cmp_sid = cmp(x_id, y_id);
    if (cmp_sid != 0) {
      return cmp_sid;
    }
  }

  // 3. distinguish by type
  int cmp_ty = (int)x->type() - (int)y->type();
  if (cmp_ty != 0) {
    return cmp_ty;
  }


#if 1
  // 4. distinguish by id
  int cmp_id = (int)x->id() - (int)y->id();
  if (cmp_id != 0) {
    return cmp_id;
  }
#endif

  // *. Could compare childCount() and other aspects of children.
  DIAG_Die("Prof::CCT::ANodeSortedIterator::cmpByDynInfo: cannot compare:"
	   << "\n\tx: " << x->toStringMe(Prof::CCT::Tree::OFlg_Debug)
	   << "\n\ty: " << y->toStringMe(Prof::CCT::Tree::OFlg_Debug));
}


//***************************************************************************
// ANodeSortedChildIterator
//***************************************************************************

ANodeSortedChildIterator::
ANodeSortedChildIterator(const ANode* node,
			 ANodeSortedIterator::cmp_fptr_t compare_fn,
			 const ANodeFilter* f)
{
  ANodeChildIterator it(node, f);
  for (ANode* cur = NULL; (cur = it.current()); it++) {
    scopes.Add((unsigned long) cur);
  }
  ptrSetIt = new WordSetSortedIterator(&scopes, compare_fn);
}


void
ANodeSortedChildIterator::dumpAndReset(ostream& os)
{
  os << "ANodeSortedChildIterator: " << endl;
  while (current()) {
    os << current()->toStringMe() << endl;
    (*this)++;
  }
  reset();
}

//***************************************************************************

} // namespace CCT
namespace Struct {


//***************************************************************************
// ANodeFilter support
//***************************************************************************

bool
HasANodeTy(const ANode& node, long type)
{
  return (type == ANode::TyANY || node.type() == ANode::IntToANodeTy(type));
}


const ANodeFilter ANodeTyFilter[ANode::TyNUMBER] = {
  ANodeFilter(HasANodeTy, 
	      ANode::ANodeTyToName(ANode::TyRoot).c_str(), ANode::TyRoot),
  ANodeFilter(HasANodeTy, 
	      ANode::ANodeTyToName(ANode::TyGroup).c_str(), ANode::TyGroup),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyLM).c_str(), ANode::TyLM),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyFile).c_str(), ANode::TyFile),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyProc).c_str(), ANode::TyProc),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyAlien).c_str(), ANode::TyAlien),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyLoop).c_str(), ANode::TyLoop),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyStmt).c_str(), ANode::TyStmt),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyRef).c_str(), ANode::TyRef),
  ANodeFilter(HasANodeTy,
	      ANode::ANodeTyToName(ANode::TyANY).c_str(), ANode::TyANY)
};

  
//***************************************************************************
// ANodeChildIterator
//***************************************************************************


//***************************************************************************
// ANodeIterator
//***************************************************************************


//***************************************************************************
// ANodeSortedIterator
//***************************************************************************

ANodeSortedIterator::
ANodeSortedIterator(const ANode* node,
		    ANodeSortedIterator::cmp_fptr_t compare_fn,
		    const ANodeFilter* filterFunc,
		    bool leavesOnly)
{
  ANodeIterator it(node, filterFunc, leavesOnly);
  for (ANode* cur = NULL; (cur = it.current()); it++) {
    scopes.Add((unsigned long) cur);
  }
  ptrSetIt = new WordSetSortedIterator(&scopes, compare_fn);
}


void
ANodeSortedIterator::dumpAndReset(ostream& os)
{
  os << "ANodeSortedIterator: " << endl;
  while (current()) {
    os << current()->toString() << endl;
    (*this)++;
  } 
  reset();
}


int
ANodeSortedIterator::cmpByName(const void* a, const void* b)
{
  ANode* x = (*(ANode**)a);
  ANode* y = (*(ANode**)b);
  return x->name().compare(y->name()); // strcmp(x, y)
}


int
ANodeSortedIterator::cmpByLine(const void* a, const void* b)
{
  // WARNING: this assumes it will only see ACodeNodes!
  ACodeNode* x = (*(ACodeNode**)a);
  ACodeNode* y = (*(ACodeNode**)b);
  return ACodeNode::compare(x, y);
}


int
ANodeSortedIterator::cmpById(const void* a, const void* b)
{
  ANode* x = (*(ANode**)a);
  ANode* y = (*(ANode**)b);
  return (x->id() - y->id());
}


int ANodeSortedIterator::cmpByMetric_mId = -1;

int 
ANodeSortedIterator::cmpByMetric_fn(const void* a, const void *b)
{
  ANode* x = *(ANode**) a;
  ANode* y = *(ANode**) b;

  double vx = x->hasMetric(cmpByMetric_mId) ? x->metric(cmpByMetric_mId) : 0.0;
  double vy = y->hasMetric(cmpByMetric_mId) ? y->metric(cmpByMetric_mId) : 0.0;
  double difference = vy - vx;
  
  if (difference < 0) return -1;	
  else if (difference > 0) return 1;	
  return 0;
}


//***************************************************************************
// ANodeSortedChildIterator
//***************************************************************************

ANodeSortedChildIterator::
ANodeSortedChildIterator(const ANode* node,
			 ANodeSortedIterator::cmp_fptr_t compare_fn,
			 const ANodeFilter* f)
{
  ANodeChildIterator it(node, f);
  for (ANode* cur = NULL; (cur = it.current()); it++) {
    scopes.Add((unsigned long) cur);
  }
  ptrSetIt = new WordSetSortedIterator(&scopes, compare_fn);
}


void
ANodeSortedChildIterator::dumpAndReset(ostream& os)
{
  os << "ANodeSortedChildIterator: " << endl;
  while (current()) {
    os << current()->toString() << endl;
    (*this)++;
  }
  reset();
}

//***************************************************************************

} // namespace Struct