// ***************************************************************************
// Initialize internal data structures.
// ***************************************************************************
void DescriptorDetails::init(CollHeap* heap)
{
  // Map all the IDs in the MV descriptor for easy access.
  // This call does a tree walk to initialize the idHash_ map of IDs to elements.
  MapIDsVisitorPtr visitor1 = new(heap) MapIDsVisitor(idHash_, ADD_MEMCHECK_ARGS(heap));
  descriptor_->treeWalk(visitor1);
  deletePtr(visitor1);
  
  RefFinderVisitorPtr visitor2 = new(heap) RefFinderVisitor(this, ADD_MEMCHECK_ARGS(heap));
  descriptor_->treeWalk(visitor2);
  deletePtr(visitor2);

  // Map all the JoinPreds to their participating columns.
  // This must be done before initializing outputs and other preds.
  initJoinPreds();

  // Init the list of JBBDetails
  const NAPtrList<QRJBBPtr>& jbbs = descriptor_->getJbbList();
  for (CollIndex i = 0; i < jbbs.entries(); i++)
  {
    QRJBBPtr jbb = jbbs[i];
    JBBDetailsPtr jbbDetails = 
      new(heap) JBBDetails(jbb, this, isAnMV_, ADD_MEMCHECK_ARGS(heap));
    jbbDetails->init(heap);
    addJBBDetails(jbbDetails);
  }
}
//*****************************************************************************
// Calculate the needed size of the permutationMatrix, allocate it, 
// and initialize it.
// The permutationMatrix has a column for each SelfJoin segment, and a row
// for each permutation.
//*****************************************************************************
void SelfJoinHandler::preparePermutationMatrix()
{
  totalPermutations_ = 1;
  CollIndex segments = segmentArray_.entries();
  UInt32 matrixWidth = 0;
  // The permVector holds for each SelfJoin segment its number of permutations.
  UInt32* permVector = new (heap_) UInt32[segments];
  ShiftMatrixFactoryPtr factory = ShiftMatrixFactory::getInstance(heap_);

  // Go over each segment in the array.
  for (CollIndex i=0; i<segments; i++)
  {
    SelfJoinSegmentPtr segment = segmentArray_[i];
    if (segment->isSelfJoinSegment())
    {
      totalPermutations_ *= segment->getPermutations();
      segment->setMatrixIndex(matrixWidth);
      permVector[matrixWidth] = segment->getPermutations();
      matrixWidth++;
      segment->setShiftMatrix(factory->getMatrixForSize(segment->getSize()));
    }
  }
  
  // Allocate the permutationMatrix.
  permMatrix_ = new(heap_) 
    Array2D(matrixWidth, totalPermutations_, 9999, ADD_MEMCHECK_ARGS(heap_));

  // And initialize it.
  initPermutationMatrix(permVector);
}
// ***************************************************************************
// ***************************************************************************
void JBBDetails::initBaseTables(CollHeap* heap)
{
  // Create BaseTableDetails objects for the JBB hub tables.
  const ElementPtrList& hubTables = jbbDesc_->getHub()->getJbbcList()->getList();
  for (CollIndex i=0; i<hubTables.entries(); i++)
  {
    const QRElementPtr jbbc = hubTables[i];
    if (jbbc->getElementType() != ET_Table)
      continue; // Skip reference to other JBBs.

    const QRTablePtr table = jbbc->downCastToQRTable();
    if (table->hasLOJParent())
      hasLOJs_ = TRUE;

    // Rest of the stuff only needed for MVs.
    if (!isAnMV_)
      continue;

    BaseTableDetailsPtr tableDetails = new(heap) 
      BaseTableDetails(table, TRUE, ADD_MEMCHECK_ARGS(heap));
    baseTablesByID_.insert(&tableDetails->getID(), tableDetails);
  }

  // Now do the same for the extra-hub tables.
  const QRTableListPtr extraHubTables = jbbDesc_->getExtraHub()->getTableList();
  if (extraHubTables != NULL)
  {
    for (CollIndex j=0; j<extraHubTables->entries(); j++)
    {
      const QRTablePtr table = (*extraHubTables)[j];
      if (table->hasLOJParent())
        hasLOJs_ = TRUE;

      // Rest of the stuff only needed for MVs.
      if (!isAnMV_)
        continue;

      BaseTableDetailsPtr tableDetails = new(heap) 
	BaseTableDetails(table, FALSE, ADD_MEMCHECK_ARGS(heap));
      baseTablesByID_.insert(&tableDetails->getID(), tableDetails);
    }
  }
}
//*****************************************************************************
// Create and initialize a ShiftMatrix of size size.
//*****************************************************************************
ShiftMatrix::ShiftMatrix(Int32  size,
	                 ADD_MEMCHECK_ARGS_DECL(CollHeap* heap))
  : NAIntrusiveSharedPtrObject(ADD_MEMCHECK_ARGS_PASS(heap)),
    theMatrix_(NULL),
    elements_(size),  
    combinations_(factorial(elements_)),  // The number of permutations is size!
    heap_(heap)
{
  // Allocate the array of permutations
  theMatrix_ = new(heap) 
    Array2D(combinations_, elements_, 9999, ADD_MEMCHECK_ARGS(heap));

  // Initialize the matrix.
  init();
}
//*****************************************************************************
// Get a ShiftMatrix for a specific size.
//*****************************************************************************
const ShiftMatrixPtr ShiftMatrixFactory::getMatrixForSize(Int32 size)
{
  // Did we already build a ShiftMatrix for this size?
  if (matrixSizeArray_.used(size))
  {
    // Yes - return it.
    return matrixSizeArray_[size];
  }
  else
  {
    // No - build one now.
    ShiftMatrixPtr newMatrix = new (heap_) ShiftMatrix(size, ADD_MEMCHECK_ARGS(heap_));
    // And keep the pointer in the array.
    matrixSizeArray_.insertAt(size, newMatrix);
    return newMatrix;
  }
}
MVCandidatesForJBBSubsetPtr QRGroupLattice::search(QRJBBPtr	           queryJbb, 
						   MVCandidatesForJBBPtr   mvCandidates, 
						   QRJoinSubGraphMapPtr    map,
                                                   ElementPtrList*         minimizedGroupingList)
{
  QRTRACER("QRGroupLattice::search()");
  DescriptorDetailsPtr queryDetails = mvCandidates->getAllCandidates()->getQueryDetails();
  LatticeKeyList keyList(heap_);

  if (minimizedGroupingList)
  {
    if (!elementListToKeyList(*minimizedGroupingList, keyList, map, queryDetails, FALSE))
      return NULL;
  }
  else
  {
    // Get the grouping columns of the passed query JBB, and create a key list
    // from them. Return now if there are no grouping items -- a query without a
    // Group By can't match an MV that has one.
    QRGroupByPtr groupBy = queryJbb->getGroupBy();
    if (!groupBy)
      return NULL;

    // An aggregate query with no GroupBy has an empty key list.
    if (!getGroupingLatticeKeys(keyList, map, queryDetails, queryJbb, TRUE, FALSE))
      return NULL;
  }

  // Create the search node, disallowing the addition of any new keys to the hash
  // table used by the lattice; if the key isn't already used, the node can have
  // no supersets.
  QRLatticeIndexSearchNode searchNode(keyList, lattice_, TRUE, heap_);
  if (!searchNode.isValid())   // because a key wasn't added
    return NULL;

  NAString keysText;
  searchNode.dumpNode(keysText, *lattice_->getKeyArr());
  QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, 
    "Searching LatticeIndex for: %s.", keysText.data());

  // Find the supersets of the query's group-by list in the MV group lattice
  // index. For each lattice index node returned, create an MVCandidate
  // corresponding to each MV represented by that node, and add it to the
  // candidate list passed by the caller.
  NAPtrList<QRLatticeIndexNodePtr> nodes;
  lattice_->findSupersets(searchNode, nodes);

  if (nodes.entries() == 0)
  {
    QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, "No match found.");
    return NULL;
  }

  MVCandidatesForJBBSubsetPtr jbbSubset = new(heap_) 
    MVCandidatesForJBBSubset(mvCandidates, ADD_MEMCHECK_ARGS(heap_));
  jbbSubset->setSubGraphMap(map);
  jbbSubset->setGroupBy(TRUE);

  if (minimizedGroupingList != NULL)
    jbbSubset->setMinimizedGroupingList(minimizedGroupingList);

  for (CollIndex i=0; i<nodes.entries(); i++)
    {
      QRLatticeIndexNodePtr thisNode = nodes[i];
      const SubGraphMapList& matchingMVs = thisNode->getMVs();
      NABoolean isPreferred = (*thisNode == searchNode); // preferred if exact match

      GroupingList* extraGroupingColumns = new(heap_) GroupingList(heap_);
      if (!isPreferred)
      {
	// For preferred match modes, there are no extraGroupingColumns.
	LatticeKeySubArray* diff = thisNode->computeDiff(searchNode, heap_);
	for(CollIndex i = 0; diff->nextUsed(i); i++)
	{
          LatticeIndexablePtr key = diff->element(i);
          QRElementPtr elem = keyToElement(key, map);
	  extraGroupingColumns->insert(elem);
	}
	delete diff;
      }

      for (CollIndex j=0; j<matchingMVs.entries(); j++)
        {
	  MVDetailsPtr mv = matchingMVs[j]->getMVDetails();
          QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, 
            "Found MV: %s!", mv->getMVName().data() );
	  jbbSubset->insert(mv, queryJbb, isPreferred, extraGroupingColumns, NULL, heap_);
        }
    }

  return jbbSubset;

}  // QRGroupLattice::search()
//*****************************************************************************
// Add a segment to the segment array.
//*****************************************************************************
void SelfJoinHandler::addSegment(SelfJoinSegment::SegmentType type)
{
  SelfJoinSegmentPtr newSegment = new (heap_) 
    SelfJoinSegment(type, segmentStart_, segmentEnd_, ADD_MEMCHECK_ARGS(heap_));
  segmentArray_.insertAt(segmentArray_.entries(), newSegment);
}