/* Copyright (C) 2014 InfiniDB, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/*****************************************************************************
 * $Id: index.cpp 2035 2013-01-21 14:12:19Z rdempsey $
 *
 ****************************************************************************/

#include <iostream>
using namespace std;

#include "primitiveprocessor.h"
#include "we_index.h"
#include "messagelog.h"
#include "messageobj.h"

/** @file
 * Brief description of the file contents
 *
 * More detailed description
 */

using namespace logging;

#ifdef VERBOSE
#define GET_BITTEST(test, string, x)                                                               \
  if (in->Shift + x >= in->SSlen)                                                                  \
  {                                                                                                \
    test = (string & masks[in->SSlen - in->Shift]);                                                \
    lastStage = true;                                                                              \
    cerr << "  bittest is 0x" << hex << (int)test << dec << " this is the last iteration" << endl; \
  }                                                                                                \
  else                                                                                             \
  {                                                                                                \
    test = (string >> (in->SSlen - in->Shift - x)) & masks[x];                                     \
    lastStage = false;                                                                             \
    cerr << "  bittest is 0x" << hex << (int)test << dec << endl;                                  \
  }
#else
#define GET_BITTEST(test, string, x)                           \
  if (in->Shift + x >= in->SSlen)                              \
  {                                                            \
    test = (string & masks[in->SSlen - in->Shift]);            \
    lastStage = true;                                          \
  }                                                            \
  else                                                         \
  {                                                            \
    test = (string >> (in->SSlen - in->Shift - x)) & masks[x]; \
    lastStage = false;                                         \
  }
#endif

#define IDXWALK_INIT()                                                                  \
  niceBlock = reinterpret_cast<uint8_t*>(block);                                        \
  blockOffset = (in->SubBlock * WriteEngine::SUBBLOCK_TOTAL_BYTES) + (in->SBEntry * 8); \
  treePtr = reinterpret_cast<WriteEngine::IdxBitTestEntry*>(&niceBlock[blockOffset]);

#define GET_GROUP_SIZE()                                                                                     \
  switch (treePtr->group)                                                                                    \
  {                                                                                                          \
    case 0: bitTestGroupSize = 1; break;                                                                     \
    case 1: bitTestGroupSize = 2; break;                                                                     \
    case 2: bitTestGroupSize = 4; break;                                                                     \
    case 3: bitTestGroupSize = 8; break;                                                                     \
    case 4: bitTestGroupSize = 16; break;                                                                    \
    case 5: bitTestGroupSize = 32; break;                                                                    \
    default: cerr << "PrimitiveProcessor::IndexWalk*(): bad group field " << treePtr->group << endl; return; \
  }

#ifdef VERBOSE
#define ADD_ELEMENT(index, shift, state)                                                        \
  element = new IndexWalkHeader();                                                              \
  memcpy(element, in, sizeof(IndexWalkHeader));                                                 \
  element->ism.Command = INDEX_WALK_RESULTS;                                                    \
  element->Shift += shift;                                                                      \
  element->LBID = treePtr[index].fbo;                                                           \
  element->SubBlock = treePtr[index].sbid;                                                      \
  element->SBEntry = treePtr[index].entry;                                                      \
  element->State = state;                                                                       \
  cerr << "  (no convert) creating a result from subblock entry " << (int)index                 \
       << " with Shift=" << (int)element->Shift << " LBID=" << element->LBID                    \
       << " Subblock=" << (int)element->SubBlock << " Subblock entry=" << (int)element->SBEntry \
       << " State=" << (int)state;                                                              \
  if (element->LBID == in->LBID && element->Shift < element->SSlen)                             \
  {                                                                                             \
    cerr << "  recursing..." << endl;                                                           \
    p_IdxWalk(element, out);                                                                    \
    delete element;                                                                             \
  }                                                                                             \
  else                                                                                          \
  {                                                                                             \
    cerr << "  adding this to the result set" << endl;                                          \
    out->push_back(element);                                                                    \
  }
#else
#define ADD_ELEMENT(index, shift, state)                            \
  element = new IndexWalkHeader();                                  \
  memcpy(element, in, sizeof(IndexWalkHeader));                     \
  element->ism.Command = INDEX_WALK_RESULTS;                        \
  element->Shift += shift;                                          \
  element->LBID = treePtr[index].fbo;                               \
  element->SubBlock = treePtr[index].sbid;                          \
  element->SBEntry = treePtr[index].entry;                          \
  element->State = state;                                           \
  if (element->LBID == in->LBID && element->Shift < element->SSlen) \
  {                                                                 \
    p_IdxWalk(element, out);                                        \
    delete element;                                                 \
  }                                                                 \
  else                                                              \
    out->push_back(element);
#endif

#ifdef VERBOSE
#define ADD_ELEMENT_WITH_CONVERT(index, shift, state)                                           \
  element = new IndexWalkHeader();                                                              \
  memcpy(element, in, sizeof(IndexWalkHeader));                                                 \
  element->ism.Command = INDEX_WALK_RESULTS;                                                    \
  element->Shift += shift;                                                                      \
  element->LBID = treePtr[index].fbo;                                                           \
  element->SubBlock = treePtr[index].sbid;                                                      \
  element->SBEntry = treePtr[index].entry;                                                      \
  element->State = state;                                                                       \
  cerr << "  (convert) creating a result from subblock entry " << (int)index                    \
       << " with Shift=" << (int)element->Shift << " LBID=" << element->LBID                    \
       << " Subblock=" << (int)element->SubBlock << " Subblock entry=" << (int)element->SBEntry \
       << " State=" << (int)state << endl;                                                      \
  if (convertToSingleOp != -1)                                                                  \
  {                                                                                             \
    cerr << "  converting it to a single COP filter" << endl;                                   \
    element->NVALS = 1;                                                                         \
    if (convertToSingleOp == 1)                                                                 \
    {                                                                                           \
      element->COP1 = element->COP2;                                                            \
      element->SearchString[0] = element->SearchString[1];                                      \
    }                                                                                           \
  }                                                                                             \
  if (element->LBID == in->LBID && element->Shift < element->SSlen)                             \
  {                                                                                             \
    cerr << "  recursing..." << endl;                                                           \
    p_IdxWalk(element, out);                                                                    \
    delete element;                                                                             \
  }                                                                                             \
  else                                                                                          \
  {                                                                                             \
    cerr << "  adding it to the result set" << endl;                                            \
    out->push_back(element);                                                                    \
  }
#else
#define ADD_ELEMENT_WITH_CONVERT(index, shift, state)               \
  element = new IndexWalkHeader();                                  \
  memcpy(element, in, sizeof(IndexWalkHeader));                     \
  element->ism.Command = INDEX_WALK_RESULTS;                        \
  element->Shift += shift;                                          \
  element->LBID = treePtr[index].fbo;                               \
  element->SubBlock = treePtr[index].sbid;                          \
  element->SBEntry = treePtr[index].entry;                          \
  element->State = state;                                           \
  if (convertToSingleOp != -1)                                      \
  {                                                                 \
    element->NVALS = 1;                                             \
    if (convertToSingleOp == 1)                                     \
    {                                                               \
      element->COP1 = element->COP2;                                \
      element->SearchString[0] = element->SearchString[1];          \
    }                                                               \
  }                                                                 \
  if (element->LBID == in->LBID && element->Shift < element->SSlen) \
  {                                                                 \
    p_IdxWalk(element, out);                                        \
    delete element;                                                 \
  }                                                                 \
  else                                                              \
    out->push_back(element);
#endif

namespace primitives
{
void PrimitiveProcessor::indexWalk_1(const IndexWalkHeader* in, vector<IndexWalkHeader*>* out) throw()
{
  uint16_t bitTest;
  uint8_t* niceBlock;
  int blockOffset, bitTestGroupSize, i;
  WriteEngine::IdxBitTestEntry* treePtr;
  IndexWalkHeader* element;
  int cmp;

  IDXWALK_INIT();

  if (in->SubBlock == 1 && in->Shift == 0)  // assume this is the first lookup step
  {
    // which happens in the direct pointer block
    bitTest = in->SearchString[0] >> (in->SSlen - 5);
#ifdef VERBOSE
    cerr << "  first iteration of search for 0x" << hex << in->SearchString[0] << dec << endl;
    cerr << "  bitTest is 0x" << hex << bitTest << dec << endl;
#endif

    switch (in->COP1)
    {
      case COMPARE_LT:
      case COMPARE_LE:
        for (i = 0; i <= bitTest; i++)
        {
          if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
            continue;

          ADD_ELEMENT(i, 5, (i < bitTest ? 1 : 0));
        }

        break;

      case COMPARE_GT:
      case COMPARE_GE:
        for (i = bitTest; i < 32; i++)
        {
          if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
            continue;

          ADD_ELEMENT(i, 5, (i > bitTest ? 1 : 0));
        }

        break;

      case COMPARE_EQ:
        if (treePtr[bitTest].fbo == 0 && treePtr[bitTest].sbid == 0 && treePtr[bitTest].entry == 0)
          break;

        ADD_ELEMENT(bitTest, 5, 0);
        break;

      case COMPARE_NE:
        for (i = 0; i < 32; i++)
        {
          if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
            continue;

          ADD_ELEMENT(i, 5, (i != bitTest ? 1 : 0));
        }

        break;

      default:
        MessageLog logger(LoggingID(28));
        logging::Message::Args colWidth;
        Message msg(34);

        colWidth.add(in->COP1);
        colWidth.add("indexWalk_1");
        msg.format(colWidth);
        logger.logDebugMessage(msg);
        return;
    }
  }
  // This is the general case where we're working within a bit test group
  else
  {
    GET_GROUP_SIZE();
#ifdef VERBOSE
    cerr << "  search string is 0x" << hex << in->SearchString[0] << dec << endl;
#endif

    for (i = 0; i < bitTestGroupSize; i++)
    {
      bool lastStage;

      // skip holes
      if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
        continue;

      if (treePtr[i].bitCompare == 0)
      {
        GET_BITTEST(bitTest, in->SearchString[0], 5);
      }
      else
      {
        GET_BITTEST(bitTest, in->SearchString[0], 10);
      }

      cmp = compare(treePtr[i].bitTest, bitTest, in->COP1, lastStage);

      if (cmp > 0)
      {
        ADD_ELEMENT(i, (treePtr[i].bitCompare == 0 ? 5 : 10), (cmp == 2 ? 1 : 0));
      }
    }
  }
}

inline int PrimitiveProcessor::compare(int val1, int val2, uint8_t COP, bool lastStage) throw()
{
  switch (COP)
  {
    case COMPARE_LT:
      if (val1 < val2)
        return 2;

      if (val1 == val2 && !lastStage)
        return 1;

      return 0;

    case COMPARE_LE:
      if (val1 < val2)
        return 2;

      if (val1 == val2)
        return 1;

      return 0;

    case COMPARE_GT:
      if (val1 > val2)
        return 2;

      if (val1 == val2 && !lastStage)
        return 1;

      return 0;

    case COMPARE_GE:
      if (val1 > val2)
        return 2;

      if (val1 == val2)
        return 1;

      return 0;

    case COMPARE_EQ:
      if (val1 == val2)
        return 1;

      return 0;

    case COMPARE_NE:
      if (val1 != val2)
        return 2;

      if (!lastStage)
        return 1;

      return 0;

    default:
      MessageLog logger(LoggingID(28));
      logging::Message::Args colWidth;
      Message msg(34);

      colWidth.add(COP);
      colWidth.add("compare");
      msg.format(colWidth);
      logger.logDebugMessage(msg);
      return false;
  }
}

void PrimitiveProcessor::indexWalk_2(const IndexWalkHeader* in, vector<IndexWalkHeader*>* out) throw()
{
  uint16_t bitTest1, bitTest2;
  uint8_t* niceBlock;
  int blockOffset, bitTestGroupSize, cmp[2], i;
  WriteEngine::IdxBitTestEntry* treePtr;
  IndexWalkHeader* element;
  int convertToSingleOp;
  bool lastStage, setState;

  IDXWALK_INIT();

  if (in->SubBlock == 1 && in->Shift == 0)  // assume this is the first lookup step
  {
    // which happens in the direct pointer block
    bitTest1 = in->SearchString[0] >> (in->SSlen - 5);
    bitTest2 = in->SearchString[1] >> (in->SSlen - 5);
#ifdef VERBOSE
    cerr << "  first iteration.  SearchString[0]=0x" << hex << in->SearchString[0] << " bittest=0x"
         << bitTest1 << " SearchString[1]=0x" << in->SearchString[1] << " bittest=0x" << bitTest2 << dec
         << endl;
#endif

    for (i = 0; i < 32; i++)
    {
      if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
        continue;

      setState = false;
      convertToSingleOp = -1;
      cmp[0] = compare(i, bitTest1, in->COP1, false);
      cmp[1] = compare(i, bitTest2, in->COP2, false);

      switch (in->BOP)
      {
        case BOP_OR:
          if (cmp[0] == 2 || cmp[1] == 2)
          {
            setState = true;
            goto add2;
          }

          // 					if (cmp[0] == 1 || cmp[1] == 1)
          // 						goto add2;

          /* XXXPAT: clean up this logic */

          if (cmp[0] == 1 && cmp[1] == 1)
            goto add2;

          if (cmp[0] == 1)
          {
            convertToSingleOp = 0;
            ADD_ELEMENT_WITH_CONVERT(i, 5, 0);
            goto skip2;
          }
          else if (cmp[1] == 1)
          {
            convertToSingleOp = 1;
            ADD_ELEMENT_WITH_CONVERT(i, 5, 0);
            goto skip2;
          }

          break;

        /* XXXPAT:  Need to verify the logic for AND.
                observations: if control reaches this point, then in the previous iteration the
                decision must have been 1, which implies equality for every previous comparison.

                If one of the comparisons returns 0, then there are no entries in this subtree
                        in the result set and it can stop here.
                else, if both of the comparisons return 2, then the whole subtree is in the result set.
                else, if only one of the comparisons returns 2, then that comparison will be 2 for
                        every comparison made down this subtree, and it is equivalent to being
                        (cmp1 && true).  In this case, we repackage the query as a single argument version
                        (using the comparison operator that was 1) to be processed by indexwalk_1().
                else, the comparisons are both 1, meaning we have equality so far and can't decide on
                        this subtree yet.
        */
        case BOP_AND:
          if (cmp[0] == 0 || cmp[1] == 0)
            break;
          else if (cmp[0] == 2 && cmp[1] == 2)
            setState = true;
          else if (cmp[0] == 2)
            convertToSingleOp = 1;
          else if (cmp[1] == 2)
            convertToSingleOp = 0;

          goto add2;

        default:
          MessageLog logger(LoggingID(28));
          logging::Message::Args colWidth;
          Message msg(39);

          colWidth.add(in->BOP);
          colWidth.add("indexwalk_2");
          msg.format(colWidth);
          logger.logDebugMessage(msg);
          return;
      }

      continue;
    add2:
      ADD_ELEMENT_WITH_CONVERT(i, 5, (setState ? 1 : 0));
    skip2:;
    }
  }
  else  // the general case
  {
    GET_GROUP_SIZE();

#ifdef VERBOSE
    cerr << "  SearchString[0]=0x" << hex << in->SearchString[0] << " SearchString[1]=0x"
         << in->SearchString[1] << dec << endl;
#endif

    for (i = 0; i < bitTestGroupSize; i++)
    {
      if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
        continue;

      setState = false;
      lastStage = false;
      convertToSingleOp = -1;

      if (treePtr[i].bitCompare == 0)
      {
        GET_BITTEST(bitTest1, in->SearchString[0], 5);
        GET_BITTEST(bitTest2, in->SearchString[1], 5);
      }
      else
      {
        GET_BITTEST(bitTest1, in->SearchString[0], 10);
        GET_BITTEST(bitTest2, in->SearchString[1], 10);
      }

      cmp[0] = compare(treePtr[i].bitTest, bitTest1, in->COP1, lastStage);
      cmp[1] = compare(treePtr[i].bitTest, bitTest2, in->COP2, lastStage);

      switch (in->BOP)
      {
        case BOP_OR:
          if (cmp[0] == 2 || cmp[1] == 2)
          {
            setState = true;
            goto add3;
          }

          // 					if (cmp[0] == 1 || cmp[1] == 1)
          // 						goto add3;

          /* XXXPAT: clean up this logic */

          if (cmp[0] == 1 && cmp[1] == 1)
            goto add3;

          if (cmp[0] == 1)
          {
            convertToSingleOp = 0;
            ADD_ELEMENT_WITH_CONVERT(i, (treePtr[i].bitCompare == 0 ? 5 : 10), 0);
            goto skip3;
          }
          else if (cmp[1] == 1)
          {
            convertToSingleOp = 1;
            ADD_ELEMENT_WITH_CONVERT(i, (treePtr[i].bitCompare == 0 ? 5 : 10), 0);
            goto skip3;
          }

          break;

        /* XXXPAT:  Need to verify the logic for AND.
                        observations: if control reaches this point, then in the previous iteration the
                        decision must have been 1, which implies equality for every previous comparison.

                        If one of the comparisons returns 0, then there are no entries in this subtree
                                in the result set and it can stop here.
                        else, if both of the comparisons return 2, then the whole subtree is in the result
           set. else, if only one of the comparisons returns 2, then that comparison will be 2 for every
           comparison made down this subtree, and it is equivalent to being (cmp1 && true).  In this case, we
           repackage the query as a single argument version (using the comparison operator that was 1) to be
           processed by indexwalk_1(). else, the comparisons are both 1, meaning we have equality so far and
           can't decide on this subtree yet.
                */
        case BOP_AND:
          if (cmp[0] == 0 || cmp[1] == 0)
            break;
          else if (cmp[0] == 2 && cmp[1] == 2)
            setState = true;
          else if (cmp[0] == 2)
            convertToSingleOp = 1;
          else if (cmp[1] == 2)
            convertToSingleOp = 0;

          goto add3;

        default:
          MessageLog logger(LoggingID(28));
          logging::Message::Args colWidth;
          Message msg(39);

          colWidth.add(in->BOP);
          colWidth.add("indexWalk_2");
          msg.format(colWidth);
          logger.logDebugMessage(msg);
          return;
      }

      continue;
    add3:
      ADD_ELEMENT_WITH_CONVERT(i, (treePtr[i].bitCompare == 0 ? 5 : 10), (setState ? 1 : 0));
    skip3:;
    }
  }
}

void PrimitiveProcessor::indexWalk_many(const IndexWalkHeader* in, vector<IndexWalkHeader*>* out) throw()
{
  uint16_t bitTest;
  uint8_t* niceBlock;
  int blockOffset, bitTestGroupSize, i;
  WriteEngine::IdxBitTestEntry* treePtr;
  IndexWalkHeader* element;
  bool lastStage;
  vector<uint64_t>::const_iterator it;
  struct
  {
    int action;
    vector<uint64_t>* searchStrings;
  } nextIteration[32];

  IDXWALK_INIT();

  /*
      Here's the high-level algorithm for this function:
      1) Iterate over the bit test tree entries (index is i)
              1a) if it is not a match, set nextIteration[i].action = 0 (searchStrings = NULL)
              1b) if it is a match which determines the whole subtree goes in the result set action = 2
     (searchStrings = NULL) 1c) if it's a match that determines the result is somewhere further down the tree,
     set action = 1, stuff the matching search string into nextIteration[i].searchStrings. 2) Iterate over the
     nextIteration structures 2a) if action == 0 do nothing 2b) if action == 1, make an intermediate
     IndexWalkHeader configured s.t. it makes sense given the # of search strings that match on that subtree
              2c) if action == 2,
                              make an intermediate IndexWalkHeader with state = 1 so the whole subtree will be
     included. 2d) if the current LBID is also the next LBID, recurse at p_IdxWalk() otherwise, add it to the
     result set to return.

      An implementation that is definitely simpler and which may be faster
      in some circumstances:
      1) if BOP is OR, COP is =, so split the query into one single-op
              query for each search string.
      2) if BOP is AND, COP is !=, so grab every indexed value and eliminate
              the entries in the argument list.  (Need to store the index values somewhere).
      *** 3) Generalize the index_many case; get rid of the NVALS=[1,2] cases.
                      - need to find a way to avoid dynamic mem allocation.
  */

  if (in->SubBlock == 1 && in->Shift == 0)  // direct pointer block
  {
    bitTestGroupSize = 32;
#ifdef VERBOSE
    cerr << "  first iteration using many search strings" << endl;
#endif

    switch (in->BOP)
    {
      case BOP_OR:

        // according to the design doc, it's safe to assume COP1 is '='
        for (i = 0; i < bitTestGroupSize; i++)
        {
          nextIteration[i].action = 0;
          nextIteration[i].searchStrings = NULL;
        }

        for (it = in->SearchStrings->begin(); it != in->SearchStrings->end(); it++)
        {
          bitTest = *it >> (in->SSlen - 5);
#ifdef VERBOSE
          cerr << "  search string=0x" << hex << *it << " bittest=0x" << bitTest << dec << endl;
#endif

          if (treePtr[bitTest].fbo == 0 && treePtr[bitTest].sbid == 0 && treePtr[bitTest].entry == 0)
            continue;

          if (nextIteration[bitTest].action == 0)
          {
            nextIteration[bitTest].searchStrings = new vector<uint64_t>();
            nextIteration[bitTest].action = 1;
          }

          nextIteration[bitTest].searchStrings->push_back(*it);
        }

        break;

      case BOP_AND:

        // safe to assume COP1 is '!='
        /*  Here's the logic here....
                With a set of 0 search strings, the result set is the entire index.
                With a set of 1 search strings, recurse the subtree containing that string, return every other
           subtree. So, for every search string, we flag the respective subtree as one that has to be
           recursed.
        */

        for (i = 0; i < bitTestGroupSize; i++)
        {
          if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
            nextIteration[i].action = 0;
          else
            nextIteration[i].action = 2;

          nextIteration[i].searchStrings = NULL;
        }

        for (it = in->SearchStrings->begin(); it != in->SearchStrings->end(); it++)
        {
          bitTest = *it >> (in->SSlen - 5);
#ifdef VERBOSE
          cerr << "  search string=0x" << hex << *it << " bittest=0x" << bitTest << dec << endl;
#endif

          if (nextIteration[bitTest].action == 2)
          {
            nextIteration[bitTest].searchStrings = new vector<uint64_t>();
            nextIteration[bitTest].action = 1;
          }

          nextIteration[bitTest].searchStrings->push_back(*it);
        }

        break;

      default:
        MessageLog logger(LoggingID(28));
        logging::Message::Args colWidth;
        Message msg(39);

        colWidth.add(in->BOP);
        msg.format(colWidth);
        logger.logDebugMessage(msg);
        return;
    }
  }
  else  // The general case of being at a node in the middle of the tree
  {
    GET_GROUP_SIZE();

    switch (in->BOP)
    {
      case BOP_OR:

        // COP1 == '='
        for (i = 0; i < bitTestGroupSize; i++)
        {
          nextIteration[i].action = 0;
          nextIteration[i].searchStrings = NULL;
        }

        for (i = 0; i < bitTestGroupSize; i++)
        {
          if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
            continue;

          for (it = in->SearchStrings->begin(); it != in->SearchStrings->end(); it++)
          {
#ifdef VERBOSE
            cerr << "  search string=0x" << hex << *it << dec << endl;
#endif

            if (treePtr[i].bitCompare == 0)
            {
              GET_BITTEST(bitTest, *it, 5);
            }
            else
            {
              GET_BITTEST(bitTest, *it, 10);
            }

            if (bitTest == treePtr[i].bitTest)
            {
              if (nextIteration[i].action == 0)
              {
                nextIteration[i].searchStrings = new vector<uint64_t>();
                nextIteration[i].action = 1;
              }

              nextIteration[i].searchStrings->push_back(*it);
            }
          }
        }

        break;

      case BOP_AND:

        // COP1 == '!='
        for (i = 0; i < bitTestGroupSize; i++)
        {
          nextIteration[i].action = 2;
          nextIteration[i].searchStrings = NULL;
        }

        for (i = 0; i < bitTestGroupSize; i++)
        {
          if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
          {
            nextIteration[i].action = 0;
            continue;
          }

          for (it = in->SearchStrings->begin(); it != in->SearchStrings->end(); it++)
          {
#ifdef VERBOSE
            cerr << "  search string=0x" << hex << *it << dec << endl;
#endif

            if (treePtr[i].bitCompare == 0)
            {
              GET_BITTEST(bitTest, *it, 5);
            }
            else
            {
              GET_BITTEST(bitTest, *it, 10);
            }

            // note: at the last stage, matches are left with action = 2 and NULL searchStrings
            if (!lastStage && bitTest == treePtr[i].bitTest)
            {
              if (nextIteration[i].action == 2)
              {
                nextIteration[i].searchStrings = new vector<uint64_t>();
                nextIteration[i].action = 1;
              }

              nextIteration[i].searchStrings->push_back(*it);
            }
            else if (lastStage && bitTest == treePtr[i].bitTest)
              nextIteration[i].action = 0;
          }
        }

        break;

      default:
        MessageLog logger(LoggingID(28));
        logging::Message::Args colWidth;
        Message msg(39);

        colWidth.add(in->BOP);
        colWidth.add("indexWalk_many");
        msg.format(colWidth);
        logger.logDebugMessage(msg);
        return;
    }
  }

  for (i = 0; i < bitTestGroupSize; i++)
  {
    if (nextIteration[i].action > 0)
    {
      element = new IndexWalkHeader();
      memcpy(element, in, sizeof(IndexWalkHeader));
      element->ism.Command = INDEX_WALK_RESULTS;
      element->Shift += (treePtr[i].bitCompare ? 10 : 5);
      element->LBID = treePtr[i].fbo;
      element->SubBlock = treePtr[i].sbid;
      element->SBEntry = treePtr[i].entry;
#ifdef VERBOSE
      cerr << "  (inline _many) creating a result from subblock entry " << i
           << " with Shift=" << (int)element->Shift << " LBID=" << element->LBID
           << " Subblock=" << (int)element->SubBlock << " Subblock entry=" << (int)element->SBEntry
           << " State=0" << endl;
#endif

      if (nextIteration[i].action == 1)
      {
        element->NVALS = nextIteration[i].searchStrings->size();

        if (nextIteration[i].searchStrings->size() > 2)
          element->SearchStrings = nextIteration[i].searchStrings;
        else if (nextIteration[i].searchStrings->size() == 2)
        {
          element->SearchString[0] = nextIteration[i].searchStrings->at(0);
          element->SearchString[1] = nextIteration[i].searchStrings->at(1);
          element->COP2 = element->COP1;
          delete nextIteration[i].searchStrings;
        }
        else  // size == 1
        {
          element->SearchString[0] = nextIteration[i].searchStrings->at(0);
          delete nextIteration[i].searchStrings;
        }
      }
      else  // action == 2
        element->State = 1;

      if (element->LBID == in->LBID && element->Shift < element->SSlen)
      {
#ifdef VERBOSE
        cerr << "  recursing..." << endl;
#endif
        p_IdxWalk(element, out);

        if (element->State == 0 && element->NVALS > 2)
          delete element->SearchStrings;

        delete element;
      }
      else
        out->push_back(element);
    }
  }
}

void PrimitiveProcessor::grabSubTree(const IndexWalkHeader* in, vector<IndexWalkHeader*>* out) throw()
{
  uint8_t* niceBlock;
  int blockOffset, bitTestGroupSize, i;
  WriteEngine::IdxBitTestEntry* treePtr;
  IndexWalkHeader* element;

  IDXWALK_INIT();
  GET_GROUP_SIZE();

  for (i = 0; i < bitTestGroupSize; i++)
  {
    if (treePtr[i].fbo == 0 && treePtr[i].sbid == 0 && treePtr[i].entry == 0)
      continue;

    ADD_ELEMENT(i, (treePtr[i].bitCompare ? 10 : 5), 1);
  }
}

void PrimitiveProcessor::p_IdxWalk(const IndexWalkHeader* in, vector<IndexWalkHeader*>* out) throw()
{
#ifdef VERBOSE
  cerr << "p_IdxWalk()" << endl;
  cerr << "  COP1=" << (int)in->COP1 << endl;
  cerr << "  COP2=" << (int)in->COP2 << endl;
  cerr << "  BOP=" << (int)in->BOP << endl;
  cerr << "  Shift=" << (int)in->Shift << endl;
  cerr << "  SSlen=" << (int)in->SSlen << endl;
  cerr << "  LBID=" << (int)in->LBID << endl;
  cerr << "  Subblock=" << (int)in->SubBlock << endl;
  cerr << "  SBEntry=" << (int)in->SBEntry << endl;
  cerr << "  NVALS=" << (int)in->NVALS << endl;
#endif

#ifdef PRIM_DEBUG

  if (in->Shift >= in->SSlen)
    throw logic_error("p_IdxWalk: called on a completed search");

#endif

  if (in->State == 1)
    grabSubTree(in, out);
  else
    switch (in->NVALS)
    {
      case 1: indexWalk_1(in, out); break;

      case 2: indexWalk_2(in, out); break;

      default: indexWalk_many(in, out); break;
    }

#ifdef VERBOSE
  cerr << "/p_IdxWalk()" << endl;
#endif
}

/*
 * Following the headers for both the request and result will be at variable
 * length list of the IndexListParam structure, where the type field is used to
 * indicate whether the Index List section is a header (0), sub-block (4), or
 * a block (5).
 */
void PrimitiveProcessor::p_IdxList(const IndexListHeader* rqst, IndexListHeader* rslt, int mode)
{
  uint8_t* listPtr;
  IndexListParam* linkList;
  IndexListEntry *listEntry, *rsltList, *sizeEntry = 0;
  int listOfst, listType, ridCt, i, j, originalRidCt;
  unsigned entryNumber, lastEntry = 0;
  int subblk_sz = WriteEngine::SUBBLOCK_TOTAL_BYTES;
  int entry_sz = WriteEngine::NEXT_PTR_BYTES;

#ifdef VERBOSE
  cerr << "p_IdxList()" << endl;
#endif

  memcpy(rslt, rqst, sizeof(IndexListHeader));
  rslt->ism.Command = INDEX_LIST_RESULTS;
  rslt->NVALS = 0;

  listPtr = (uint8_t*)block;
  linkList = (IndexListParam*)(rqst + 1);
  rsltList = (IndexListEntry*)(rslt + 1);

  for (i = 0; i < rqst->NVALS; i++)
  {
    j = 0;
    listOfst = (linkList->sbid * subblk_sz) + (linkList->entry * entry_sz);
    entryNumber = linkList->entry;
    listEntry = (IndexListEntry*)(listPtr + listOfst);

#ifdef VERBOSE
    cerr << "  processing argument number " << i + 1 << endl;
    cerr << "    type=" << linkList->type << " LBID=" << linkList->fbo << " subblock=" << linkList->sbid
         << " entry=" << linkList->entry << endl;
#endif

    listType = linkList->type;

    if (listType == LIST_SIZE)
    {
      if (listEntry->type != LIST_SIZE)
      {
        MessageLog logger(LoggingID(28));
        Message msg(40);
        logger.logDebugMessage(msg);
#ifdef VERBOSE
        cerr << "PrimitiveProcessor::p_IdxList: was told to parse a header, but the given pointer does not "
                "point to one.  sbid="
             << linkList->sbid << " entry=" << linkList->entry << endl;
#endif
        throw runtime_error("p_IdxList: not a header");
      }

      ridCt = listEntry->value;
      originalRidCt = ridCt;
#ifdef VERBOSE
      uint64_t* tmp = (uint64_t*)&listEntry[1];
      cerr << "    ridCount=" << ridCt << " key value=0x" << hex << *tmp << dec << endl;
#endif
      listEntry += 2;  // Skip size and key values
      entryNumber += 2;
      lastEntry = linkList->entry + 4;
    }

    else
    {
      if (listType == LLP_SUBBLK)
      {
        sizeEntry = listEntry + WriteEngine::LIST_SUB_LLP_POS;
        lastEntry = linkList->entry + 32;
      }
      else if (listType == LLP_BLK)
      {
        sizeEntry = listEntry + WriteEngine::LIST_BLOCK_LLP_POS;
        lastEntry = linkList->entry + (mode == 0 ? 1024 : 1023);
        // ignore the size entry for blocks if mode == 1
      }
      else
        cerr << "p_IdxList: bad pointer type " << listType << endl;

      ridCt = sizeEntry->ridCt;  // XXXPAT: make sure these are the right bits
      originalRidCt = ridCt;
#ifdef VERBOSE
      cerr << "    ridCount for this " << (listType == LLP_SUBBLK ? "subblock: " : "block: ") << ridCt
           << endl;
#endif
    }

    // 		while (ridCt > 0 && entryNumber < lastEntry) {
    while (entryNumber < lastEntry)
    {
      switch (listEntry->type)
      {
        case LLP_SUBBLK:
        case LLP_BLK:

          /* the second to last entry of a subblock
          can now be a HWM disguised as a continuation ptr. */
          if (mode && entryNumber == static_cast<unsigned>(linkList->entry + 30) && listType == LLP_SUBBLK)
            break;

#ifdef VERBOSE
          ilptmp = reinterpret_cast<IndexListParam*>(listEntry);

          cerr << "   found a continuation pointer: LBID=" << ilptmp->fbo << " subblock=" << ilptmp->sbid
               << " subblock entry=" << ilptmp->entry << endl;
#endif
          *rsltList = *listEntry;
          rslt->NVALS++;
          rsltList++;
          break;

        /*
        #ifdef VERBOSE
                                                cerr << "   reached the end of the list" << endl;
                                                if (listEntry != sizeEntry && listType != LIST_SIZE)
                                                        cerr << "   ERROR: there's a list pointer in the
        middle of the list" << endl; else if (listType != LIST_SIZE) cerr << "   Possible error: the pointer
        clause executed for a subblock or block" << endl; #endif endOfList = true; break;
        */
        case RID:
#ifdef VERBOSE
// 					cerr << "    returning rid " << listEntry->value << endl;
#endif
          *rsltList = *listEntry;
          rslt->NVALS++;
          rsltList++;
          ridCt--;
          j++;
          break;

        case LIST_SIZE:
        case NOT_IN_USE:
        case EMPTY_LIST_PTR:
        case EMPTY_PTR:
        case PARENT: break;

        default:
#ifdef VERBOSE
          IndexListParam* tmp = reinterpret_cast<IndexListParam*>(listEntry);
          cerr << "PrimitiveProcessor::p_IdxList: invalid list entry type" << endl;
          cerr << "   Entry contents: type=" << listEntry->type << " value/RID=" << listEntry->value << endl;
          cerr << "   (if a pointer, fbo=" << tmp->fbo << " subblock=" << tmp->sbid << " entry=" << tmp->entry
               << ")" << endl;
          cerr << "   Location of the entry: lbid=" << linkList->fbo
               << " subblock=" << linkList->sbid + entryNumber / 32 << " entry=" << entryNumber % 32 << endl;
          cerr << "   Input parameter indicates the start of the list is sbid=" << linkList->sbid
               << " entry=" << linkList->entry << endl;
          cerr << "   Type of search: "
               << (linkList->type == LIST_SIZE ? "header"
                                               : (linkList->type == LLP_SUBBLK
                                                      ? "subblock"
                                                      : (linkList->type == LLP_BLK ? "block" : "unknown..?")))
               << endl;
          cerr << "   Processed " << entryNumber - linkList->entry << " entries for this request so far."
               << endl;
          cerr << "   Rid count read from the list is " << originalRidCt << endl;
#endif
          MessageLog logger(LoggingID(28));
          Message msg(40);
          logger.logDebugMessage(msg);
          throw runtime_error("Bad index list entry, see stderr");
          return;
      }

      listEntry++;
      entryNumber++;
    }

#ifdef VERBOSE
    cerr << "  RIDs in result generated from input arg #" << i + 1 << ": " << j << endl;
#endif

    linkList++;
  }

  return;
}

}  // namespace primitives