/*
  Copyright (C) 2007 Thomas Jahns <Thomas.Jahns@gmx.net>

  Permission to use, copy, modify, and distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

#include <time.h>
#include <sys/time.h>

#include "core/assert_api.h"
#include "core/bitpackstring.h"
#include "core/error_api.h"
#include "core/ensure_api.h"
#include "core/log.h"
#include "core/ma_api.h"
#include "core/yarandom_api.h"

enum {
/*   MAX_RND_NUMS = 10, */
  MAX_RND_NUMS_@uOpType@ = 100000,
};

static inline int
icmp_@uOpType@(@uOpType@ a, @uOpType@ b)
{
  if (a > b)
    return 1;
  else if (a < b)
    return -1;
  else /* if (a == b) */
    return 0;
}

/**
 * \brief bit count reference
 * @param v count the number of bits set in v
 */
static inline int
genBitCount_@uOpType@(@uOpType@ v)
{
  unsigned c; /* c accumulates the total bits set in v */
  for (c = 0; v; c++)
    v &= v - 1; /* clear the least significant bit set */
  return c;
}

#define freeResourcesAndReturn(retval) \
  do {                                 \
    gt_free(numBitsList);              \
    gt_free(randSrc);                  \
    gt_free(randCmp);                  \
    gt_free(bitStore);                 \
    gt_free(bitStoreCopy);             \
    return retval;                     \
  } while (0)

int
gt_bitPackString@iTypeTag@_unit_test(GtError *err)
{
  BitString bitStore = NULL;
  BitString bitStoreCopy = NULL;
  @uOpType@ *randSrc = NULL; /*< create random ints here for input as bit
                                *  store */
  @uOpType@ *randCmp = NULL; /*< used for random ints read back */
  unsigned *numBitsList = NULL;
  size_t i, numRnd;
  BitOffset offsetStart, offset;
  int had_err = 0;
  offset = offsetStart = random()%(sizeof (@uOpType@) * CHAR_BIT);
  numRnd = random() % (MAX_RND_NUMS_@uOpType@ + 1);
  gt_log_log("offset=" GT_WU ", numRnd=" GT_WU "\n",
          (GtUword)offsetStart, (GtUword)numRnd);
  {
    BitOffset numBits = sizeof (@uOpType@) * CHAR_BIT * numRnd + offsetStart;
    randSrc = gt_malloc(sizeof (@uOpType@)*numRnd);
    bitStore = gt_malloc(bitElemsAllocSize(numBits) * sizeof (BitElem));
    bitStoreCopy = gt_calloc(bitElemsAllocSize(numBits), sizeof (BitElem));
    randCmp = gt_malloc(sizeof (@uOpType@)*numRnd);
  }
  /* first test unsigned types */
  gt_log_log("gt_bsStore@uTypeTag@/gt_bsGet@uTypeTag@: ");
  for (i = 0; i < numRnd; ++i)
  {
#if @bitSize@ > 32 && LONG_BIT < @bitSize@
    @uOpType@ v = randSrc[i] = (@uOpType@)random() << 32 | random();
#else /* @bitSize@ > 32 && LONG_BIT < @bitSize@ */
    @uOpType@ v = randSrc[i] = random();
#endif /* @bitSize@ > 32 && LONG_BIT < @bitSize@ */
    int bits = gt_required@uTypeTag@Bits(v);
    gt_bsStore@uTypeTag@(bitStore, offset, bits, v);
    offset += bits;
  }
  offset = offsetStart;
  for (i = 0; i < numRnd; ++i)
  {
    @uOpType@ v = randSrc[i];
    int bits = gt_required@uTypeTag@Bits(v);
    @uOpType@ r = gt_bsGet@uTypeTag@(bitStore, offset, bits);
    gt_ensure(r == v);
    if (had_err)
    {
      gt_log_log("Expected %"@uOpPRI@", got %"@uOpPRI@", i = " GT_WU "\n",
              v, r, (GtUword)i);
      freeResourcesAndReturn(had_err);
    }
    offset += bits;
  }
  gt_log_log("passed\n");
  if (numRnd > 0)
  {
    @uOpType@ v = randSrc[0], r = 0;
    unsigned numBits = gt_required@uTypeTag@Bits(v);
    BitOffset i = offsetStart + numBits;
    @uOpType@ mask = ~(@uOpType@)0;
    if (numBits < @bitSize@)
      mask = ~(mask << numBits);
    gt_log_log("bsSetBit, gt_bsClearBit, bsToggleBit, gt_bsGetBit: ");
    while (v)
    {
      int lowBit = v & 1;
      v >>= 1;
      gt_ensure(lowBit == (r = gt_bsGetBit(bitStore, --i)));
      if (had_err)
      {
        gt_log_log("Expected %d, got %d, i = "GT_LLU"\n",
                lowBit, (int)r, (GtUint64)i);
        freeResourcesAndReturn(had_err);
      }
    }
    i = offsetStart + numBits;
    gt_bsClear(bitStoreCopy, offsetStart, numBits, random()&1);
    v = randSrc[0];
    while (i)
    {
      int lowBit = v & 1;
      v >>= 1;
      if (lowBit)
        bsSetBit(bitStoreCopy, --i);
      else
        gt_bsClearBit(bitStoreCopy, --i);
    }
    v = randSrc[0];
    r = gt_bsGet@uTypeTag@(bitStoreCopy, offsetStart, numBits);
    gt_ensure(r == v);
    if (had_err)
    {
      gt_log_log("Expected %"@uOpPRI@", got %"@uOpPRI@"\n", v, r);
      freeResourcesAndReturn(had_err);
    }
    for (i = 0; i < numBits; ++i)
      bsToggleBit(bitStoreCopy, offsetStart + i);
    r = gt_bsGet@uTypeTag@(bitStoreCopy, offsetStart, numBits);
    gt_ensure(r == (v = (~v & mask)));
    if (had_err)
    {
      gt_log_log("Expected %"@uOpPRI@", got %"@uOpPRI@"\n", v, r);
      freeResourcesAndReturn(had_err);
    }
    gt_log_log("passed\n");
  }
  if (numRnd > 1)
  {
    gt_log_log("gt_bsCompare: ");
    {
      @uOpType@ v0 = randSrc[0];
      int bits0 = gt_required@uTypeTag@Bits(v0);
      @uOpType@ r0;
      offset = offsetStart;
      r0 = gt_bsGet@uTypeTag@(bitStore, offset, bits0);
      for (i = 1; i < numRnd; ++i)
      {
        @uOpType@ v1 = randSrc[i];
        int bits1 = gt_required@uTypeTag@Bits(v1);
        @uOpType@ r1 = gt_bsGet@uTypeTag@(bitStore, offset + bits0, bits1);
        int result = -2;   /*< -2 is not a return value of gt_bsCompare, thus
                            *   if it is displayed, there was an earlier
                            *   error. */
        gt_ensure(r0 == v0 && r1 == v1);
        gt_ensure(icmp_@uOpType@(v0, v1) ==
               (result = gt_bsCompare(bitStore, offset, bits0,
                                   bitStore, offset + bits0, bits1)));
        if (had_err)
        {
          gt_log_log("Expected v0 %s v1, got v0 %s v1,\n for v0=%"
                  @uOpPRI@" and v1=%"@uOpPRI@",\n"
                  "i = " GT_WU ", bits0=%u, bits1=%u\n",
                  (v0 > v1?">":(v0 < v1?"<":"==")),
                  (result > 0?">":(result < 0?"<":"==")), v0, v1,
                  (GtUword)i, bits0, bits1);
          freeResourcesAndReturn(had_err);
        }
        offset += bits0;
        bits0 = bits1;
        v0 = v1;
        r0 = r1;
      }
    }
    gt_log_log("passed\n");
  }
  gt_log_log("gt_bsStoreUniform@uTypeTag@Array/gt_bsGet@uTypeTag@: ");
  if (numRnd > 0) {
    unsigned numBits = random()%@bitSize@ + 1;
    @uOpType@ mask = ~(@uOpType@)0;
    if (numBits < @bitSize@)
      mask = ~(mask << numBits);
    offset = offsetStart;
    gt_bsStoreUniform@uTypeTag@Array(bitStore, offset, numBits, numRnd,
                                     randSrc);
    for (i = 0; i < numRnd; ++i)
    {
      @uOpType@ v = randSrc[i] & mask;
      @uOpType@ r = gt_bsGet@uTypeTag@(bitStore, offset, numBits);
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@uOpPRI@", got %"@uOpPRI@",\n"
                "i = " GT_WU ", bits=%u\n", v, r, (GtUword)i, numBits);
        freeResourcesAndReturn(had_err);
      }
      offset += numBits;
    }
    gt_log_log("passed\n");
    gt_log_log("gt_bsStoreUniform@uTypeTag@Array/"
               "gt_bsGetUniform@uTypeTag@Array: ");
    gt_bsGetUniform@uTypeTag@Array(bitStore, offset = offsetStart,
                               numBits, numRnd, randCmp);
    for (i = 0; i < numRnd; ++i)
    {
      @uOpType@ v = randSrc[i] & mask;
      @uOpType@ r = randCmp[i];
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log( "Expected %"@uOpPRI@", got %"@uOpPRI@",\n"
                " i = " GT_WU ", bits=%u\n",
                v, r, (GtUword)i, numBits);
        freeResourcesAndReturn(had_err);
      }
    }
    if (numRnd > 1)
    {
      @uOpType@ v = randSrc[0] & mask;
      @uOpType@ r = 0;
      gt_bsGetUniform@uTypeTag@Array(bitStore, offsetStart,
                                 numBits, 1, &r);
      if (r != v)
      {
        gt_log_log("Expected %"@uOpPRI@", got %"@uOpPRI@","
                " one value extraction\n",
                v, r);
        freeResourcesAndReturn(had_err);
      }
    }
    gt_log_log(" passed\n");
  }
  /* int types */
  gt_log_log("gt_bsStore@iTypeTag@/gt_bsGet@iTypeTag@: ");
  for (i = 0; i < numRnd; ++i)
  {
    @iOpType@ v = (@iOpType@)randSrc[i];
    unsigned bits = gt_required@iTypeTag@Bits(v);
    gt_bsStore@iTypeTag@(bitStore, offset, bits, v);
    offset += bits;
  }
  offset = offsetStart;
  for (i = 0; i < numRnd; ++i)
  {
    @iOpType@ v = randSrc[i];
    unsigned bits = gt_required@iTypeTag@Bits(v);
    @iOpType@ r = gt_bsGet@iTypeTag@(bitStore, offset, bits);
    gt_ensure(r == v);
    if (had_err)
    {
      gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@",\n"
                  "i = " GT_WU ", bits=%u\n",
                  v, r, (GtUword)i, bits);
      freeResourcesAndReturn(had_err);
    }
    offset += bits;
  }
  gt_log_log("passed\n");
  gt_log_log("gt_bsStoreUniform@iTypeTag@Array/gt_bsGet@iTypeTag@: ");
  if (numRnd > 0) {
    unsigned numBits = random()%@bitSize@ + 1;
    @iOpType@ mask = ~(@iOpType@)0;
    if (numBits < @bitSize@)
      mask = ~(mask << numBits);
    offset = offsetStart;
    gt_bsStoreUniform@iTypeTag@Array(bitStore, offset, numBits, numRnd,
                                (@iOpType@ *)randSrc);
    for (i = 0; i < numRnd; ++i)
    {
      @iOpType@ m = (@iOpType@)1 << (numBits - 1);
      @iOpType@ v = (@iOpType@)((randSrc[i] & mask) ^ m) - m;
      @iOpType@ r = gt_bsGet@iTypeTag@(bitStore, offset, numBits);
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@",\n"
                    "i = " GT_WU ", numBits=%u\n",
                    v, r, (GtUword)i, numBits);
        freeResourcesAndReturn(had_err);
      }
      offset += numBits;
    }
    gt_log_log("passed\n");
    gt_log_log("gt_bsStoreUniform@iTypeTag@Array/"
               "gt_bsGetUniform@iTypeTag@Array: ");
    gt_bsGetUniform@iTypeTag@Array(bitStore, offset = offsetStart,
                              numBits, numRnd, (@iOpType@ *)randCmp);
    for (i = 0; i < numRnd; ++i)
    {
      @iOpType@ m = (@iOpType@)1 << (numBits - 1);
      @iOpType@ v = (@iOpType@)((randSrc[i] & mask) ^ m) - m;
      @iOpType@ r = randCmp[i];
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@", i = " GT_WU "\n",
                v, r, (GtUword)i);
        freeResourcesAndReturn(had_err);
      }
    }
    if (numRnd > 0)
    {
      @iOpType@ m = (@iOpType@)1 << (numBits - 1);
      @iOpType@ v = (@iOpType@)((randSrc[0] & mask) ^ m) - m;
      @iOpType@ r = 0;
      gt_bsGetUniform@iTypeTag@Array(bitStore, offsetStart,
                                numBits, 1, &r);
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@
                ", one value extraction\n",
                v, r);
        freeResourcesAndReturn(had_err);
      }
    }
    gt_log_log("passed\n");
  }

  gt_log_log("gt_bsStoreNonUniform@uTypeTag@Array/gt_bsGet@uTypeTag@: ");
  if (numRnd != 0) {
    BitOffset bitsTotal = 0;
    numBitsList = gt_malloc(sizeof (unsigned) * numRnd);
    for (i = 0; i < numRnd; ++i)
      bitsTotal += (numBitsList[i] = random()%@bitSize@ + 1);
    offset = offsetStart;
    gt_bsStoreNonUniform@uTypeTag@Array(bitStore, offset, numRnd, bitsTotal,
                                     numBitsList, randSrc);
    for (i = 0; i < numRnd; ++i)
    {
      unsigned numBits = numBitsList[i];
      @uOpType@ mask = (numBits < @bitSize@)?
        ~((~(@uOpType@)0) << numBits):~(@uOpType@)0;
      @uOpType@ v = randSrc[i] & mask;
      @uOpType@ r = gt_bsGet@uTypeTag@(bitStore, offset, numBits);
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@uOpPRI@", got %"@uOpPRI@",\n"
                "i = " GT_WU ", bits=%u\n",
                v, r, (GtUword)i, numBits);
        freeResourcesAndReturn(had_err);
      }
      offset += numBits;
    }
    gt_log_log("passed\n");
    gt_log_log("gt_bsStoreNonUniform@uTypeTag@Array/"
            "gt_bsGetNonUniform@uTypeTag@Array: ");
    gt_bsGetNonUniform@uTypeTag@Array(bitStore, offset = offsetStart,
                                   numRnd, bitsTotal, numBitsList, randCmp);
    for (i = 0; i < numRnd; ++i)
    {
      unsigned numBits = numBitsList[i];
      @uOpType@ mask = (numBits < @bitSize@)?
        ~((~(@uOpType@)0) << numBits):~(@uOpType@)0;
      @uOpType@ v = randSrc[i] & mask,
        r = randCmp[i];
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log( "Expected %"@uOpPRI@", got %"@uOpPRI@",\n"
                " i = " GT_WU ", bits=%u\n",
                v, r, (GtUword)i, numBits);
        freeResourcesAndReturn(had_err);
      }
    }
    if (numRnd > 1)
    {
      unsigned numBits = numBitsList[0];
      @uOpType@ mask = (numBits < @bitSize@)?
        ~((~(@uOpType@)0) << numBits):~(@uOpType@)0;
      @uOpType@ v = randSrc[0] & mask;
      @uOpType@ r = 0;
      gt_bsGetNonUniform@uTypeTag@Array(bitStore, offsetStart, 1, numBits,
                                     numBitsList, &r);
      if (r != v)
      {
        gt_log_log("Expected %"@uOpPRI@", got %"@uOpPRI@", "
                " one value extraction\n",
                v, r);
        freeResourcesAndReturn(had_err);
      }
    }
    gt_log_log(" passed\n");
    gt_free(numBitsList);
    numBitsList = NULL;
  }
  gt_log_log("bsNonStoreUniform@iTypeTag@Array/gt_bsGet@iTypeTag@: ");
  if (numRnd != 0) {
    BitOffset bitsTotal = 0;
    numBitsList = gt_malloc(sizeof (unsigned) * numRnd);
    for (i = 0; i < numRnd; ++i)
      bitsTotal += (numBitsList[i] = random()%@bitSize@ + 1);
    offset = offsetStart;
    gt_bsStoreNonUniform@iTypeTag@Array(bitStore, offset, numRnd, bitsTotal,
                                     numBitsList, (@iOpType@ *)randSrc);
    for (i = 0; i < numRnd; ++i)
    {
      unsigned numBits = numBitsList[i];
      @iOpType@ mask = (numBits < @bitSize@)
        ? ~((~(@iOpType@)0) << numBits) : ~(@iOpType@)0;
      @iOpType@ m = (@iOpType@)1 << (numBits - 1);
      @iOpType@ v = (@iOpType@)((randSrc[i] & mask) ^ m) - m;
      @iOpType@ r = gt_bsGet@iTypeTag@(bitStore, offset, numBits);
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@",\n"
                    "i = " GT_WU ", numBits=%u\n",
                    v, r, (GtUword)i, numBits);
        freeResourcesAndReturn(had_err);
      }
      offset += numBits;
    }
    gt_log_log("passed\n");
    gt_log_log("gt_bsStoreNonUniform@iTypeTag@Array/"
            "gt_bsGetNonUniform@iTypeTag@Array: ");
    gt_bsGetNonUniform@iTypeTag@Array(bitStore, offset = offsetStart, numRnd,
                                   bitsTotal, numBitsList,
                                   (@iOpType@ *)randCmp);
    for (i = 0; i < numRnd; ++i)
    {
      unsigned numBits = numBitsList[i];
      @iOpType@ mask = (numBits < @bitSize@)
        ? ~((~(@iOpType@)0) << numBits) : ~(@iOpType@)0;
      @iOpType@ m = (@iOpType@)1 << (numBits - 1);
      @iOpType@ v = (@iOpType@)((randSrc[i] & mask) ^ m) - m;
      @iOpType@ r = randCmp[i];
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@", i = " GT_WU "\n",
                v, r, (GtUword)i);
        freeResourcesAndReturn(had_err);
      }
    }
    if (numRnd > 0)
    {
      unsigned numBits = numBitsList[0];
      @iOpType@ mask = (numBits < @bitSize@)
        ? ~((~(@iOpType@)0) << numBits) : ~(@iOpType@)0;
      @iOpType@ m = (@iOpType@)1 << (numBits - 1);
      @iOpType@ v = (@iOpType@)((randSrc[0] & mask) ^ m) - m;
      @iOpType@ r = 0;
      gt_bsGetNonUniform@iTypeTag@Array(bitStore, offsetStart,
                                     1, numBits, numBitsList, &r);
      gt_ensure(r == v);
      if (had_err)
      {
        gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@
                ", one value extraction\n",
                v, r);
        freeResourcesAndReturn(had_err);
      }
    }
    gt_log_log("passed\n");
    gt_free(numBitsList);
    numBitsList = NULL;
  }

  if (numRnd > 0)
  {
    gt_log_log("gt_bsCopy: ");
    {
      /* first decide how many of the values to use and at which to start */
      size_t numValueCopies, copyStart;
      BitOffset numCopyBits = 0, destOffset;
      unsigned numBits = random()%@bitSize@ + 1;
      @uOpType@ mask = ~(@uOpType@)0;
      if (numBits < @bitSize@)
        mask = ~(mask << numBits);
      if (random()&1)
      {
        numValueCopies = random()%(numRnd + 1);
        copyStart = random()%(numRnd - numValueCopies + 1);
      }
      else
      {
        copyStart = random() % numRnd;
        numValueCopies = random()%(numRnd - copyStart) + 1;
      }
      gt_assert(copyStart + numValueCopies <= numRnd);
      offset = offsetStart + (BitOffset)copyStart * numBits;
      gt_bsStoreUniform@uTypeTag@Array(bitStore, offset, numBits,
                                       numValueCopies, randSrc);
      destOffset = random()%(offsetStart + @bitSize@
                             * (BitOffset)(numRnd - numValueCopies) + 1);
      numCopyBits = (BitOffset)numBits * numValueCopies;
      /* the following gt_bsCopy should be equivalent to:
       * gt_bsStoreUniform@uTypeTag@Array(bitStoreCopy, destOffset,
       *                              numBits, numValueCopies, randSrc); */
      gt_bsCopy(bitStore, offset, bitStoreCopy, destOffset, numCopyBits);
      gt_ensure(
                gt_bsCompare(bitStore, offset, numCopyBits,
                             bitStoreCopy, destOffset, numCopyBits) == 0);
      if (had_err)
      {
        gt_log_log("Expected equality on bitstrings\n"
                    "offset = "GT_LLU", destOffset = "GT_LLU","
                    " numCopyBits="GT_LLU"\n",
                    (GtUint64)offset,
                    (GtUint64)destOffset,
                    (GtUint64)numCopyBits);
        /* FIXME: implement bitstring output function */
        freeResourcesAndReturn(had_err);
      }
      gt_log_log("passed\n");
    }
  }
  if (numRnd > 0)
  {
    gt_log_log("gt_bsClear: ");
    {
      /* first decide how many of the values to use and at which to start */
      size_t numResetValues, resetStart;
      BitOffset numResetBits = 0;
      unsigned numBits = random()%@bitSize@ + 1;
      int bitVal = random()&1;
      @iOpType@ cmpVal = bitVal?-1:0;
      @uOpType@ mask = ~(@uOpType@)0;
      if (numBits < @bitSize@)
        mask = ~(mask << numBits);
      if (random()&1)
      {
        numResetValues = random()%(numRnd + 1);
        resetStart = random()%(numRnd - numResetValues + 1);
      }
      else
      {
        resetStart = random() % numRnd;
        numResetValues = random()%(numRnd - resetStart) + 1;
      }
      gt_assert(resetStart + numResetValues <= numRnd);
      offset = offsetStart;
      gt_bsStoreUniform@iTypeTag@Array(bitStore, offset, numBits, numRnd,
                                    (@iOpType@ *)randSrc);
      numResetBits = (BitOffset)numBits * numResetValues;
      gt_bsClear(bitStore, offset + (BitOffset)resetStart * numBits,
              numResetBits, bitVal);
      {
        @iOpType@ m = (@iOpType@)1 << (numBits - 1);
        for (i = 0; i < resetStart; ++i)
        {
          @iOpType@ v = (@iOpType@)((randSrc[i] & mask) ^ m) - m;
          @iOpType@ r = gt_bsGet@iTypeTag@(bitStore, offset, numBits);
          gt_ensure(r == v);
          if (had_err)
          {
            gt_log_log( "Expected %"@dOpPRI@", got %"@dOpPRI@",\n"
                     "i = " GT_WU ", numBits=%u\n",
                     v, r, (GtUword)i, numBits);
            freeResourcesAndReturn(had_err);
          }
          offset += numBits;
        }
        for (; i < resetStart + numResetValues; ++i)
        {
          @iOpType@ r = gt_bsGet@iTypeTag@(bitStore, offset, numBits);
          gt_ensure(r == cmpVal);
          if (had_err)
          {
            gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@",\n"
                    "i = " GT_WU ", numBits=%u\n",
                    cmpVal, r, (GtUword)i, numBits);
            freeResourcesAndReturn(had_err);
          }
          offset += numBits;
        }
        for (; i < numRnd; ++i)
        {
          @iOpType@ v = (@iOpType@)((randSrc[i] & mask) ^ m) - m;
          @iOpType@ r = gt_bsGet@iTypeTag@(bitStore, offset, numBits);
          gt_ensure(r == v);
          if (had_err)
          {
            gt_log_log("Expected %"@dOpPRI@", got %"@dOpPRI@",\n"
                    "i = " GT_WU ", numBits=%u\n",
                    v, r, (GtUword)i, numBits);
            freeResourcesAndReturn(had_err);
          }
          offset += numBits;
        }
      }
    }
    gt_log_log("passed\n");
  }
  if (numRnd > 0)
  {
    gt_log_log("gt_bs1BitsCount: ");
    {
      /* first decide how many of the values to use and at which to start */
      size_t numCountValues, countStart;
      BitOffset numCountBits = 0, bitCountRef = 0, bitCountCmp;
      unsigned numBits = random()%@bitSize@ + 1;
      @uOpType@ mask = ~(@uOpType@)0;
      if (numBits < @bitSize@)
        mask = ~(mask << numBits);
      if (random()&1)
      {
        numCountValues = random()%(numRnd + 1);
        countStart = random()%(numRnd - numCountValues + 1);
      }
      else
      {
        countStart = random() % numRnd;
        numCountValues = random()%(numRnd - countStart) + 1;
      }
      gt_assert(countStart + numCountValues <= numRnd);
      offset = offsetStart;
      gt_bsStoreUniform@uTypeTag@Array(bitStore, offset, numBits, numRnd,
                                       randSrc);
      numCountBits = (BitOffset)numBits * numCountValues;
      bitCountCmp = gt_bs1BitsCount(bitStore,
                                 offset + (BitOffset)countStart * numBits,
                                 numCountBits);
      for (i = countStart; i < countStart + numCountValues; ++i)
      {
        @uOpType@ v = (@uOpType@)randSrc[i] & mask;
        bitCountRef += genBitCount_@uOpType@(v);
      }
      gt_ensure(bitCountRef == bitCountCmp);
      if (had_err)
      {
        gt_log_log("Expected "GT_LLU", got "GT_LLU",\n"
                "numBits=%u\n", (GtUint64)bitCountRef,
                (GtUint64)bitCountCmp, numBits);
        freeResourcesAndReturn(had_err);
      }
      offset += numBits;
    }
    gt_log_log("passed\n");
  }
  freeResourcesAndReturn(had_err);
}
/*
vim: ft=c
 */
