//******************************************************************************
//  Description: Test cases for MIDAS Utility routines (UTILITIES)
//
//  Author : Neon Ngo
//  Version: $Id: test_utilities.cc,v 1.23 2015/08/04 19:39:48 ntn Exp $
//
//******************************************************************************
#include <primitive.h>
#include <sstream>        // for ostringstream
//#define _DEBUG_NXM_TEST // uncomment to enable test framework debugging
#include "nxm_test.hh"    // XBC C++ test framework
#include <qmessages.h>    // included only to make sure qmessages.h and
                          //   generated: qmessdecl.h, qmessdef.hh builds cleanly
#include <xmtime.h>       // included only to make sure xmtime.h build cleanly
#include <bitset>         // since 3.5.2 for testing bug 2773

// functions declarations (keep in alphabetical order)
void test_m_between();
void test_m_d2a();
void test_m_element();
void test_m_head_m_tail_m_root_m_ext();
void test_m_lowercase();
void test_m_nelements();
void test_m_now_m_pause(real_4 pause_secs);
void test_m_nsearch();
void test_m_promote_format();
void test_m_range();
void test_m_spa();
void test_m_trims();
void test_m_word();
void test_m_startswith();
void test_m_endswith();
void test_m_nformat();
void test_m_addsubr();
void test_m_addt6subr();
void test_m_addcomp();


// global variables
bool_4 _debug; // to enabled additional debugging messages

//******************************************************************************
// main C++ host primitive entry point
//******************************************************************************
void mainroutine () {
  real_8 pause;
  int_4  niter;

  pause  = m_get_dswitch_def("PAUSE", 0.3);
  niter  = m_get_switch_def("ITER",1);
  _debug = m_get_sswitch_def("DEBUG", FALSE);
//cout << "DEBUG: [" << Mu->id << "] test_utilities: pause=" << pause << " debug=" << _debug << endl;

  nxm_assert_init("DEBUG","STATUS");

  m_sync();

  while (!Mc->break_ && (niter-- > 0)) {
    test_m_now_m_pause(pause);
  }

  test_m_promote_format();
  test_m_lowercase();
  test_m_spa();
  test_m_head_m_tail_m_root_m_ext();
  test_m_trims();
  test_m_between();
  test_m_d2a();
  test_m_nelements();
  test_m_element();
  test_m_word();
  test_m_nsearch();
  test_m_range();
  test_m_startswith();
  test_m_endswith();
  test_m_nformat();
  test_m_addsubr(); // since 3.5.2
  test_m_addt6subr(); // since 3.5.2
  test_m_addcomp(); // since 3.5.2

  nxm_assert_auto_report();
}

//******************************************************************************
// test case for m_now (BUG 2272) and m_pause (BUG 2271)
//******************************************************************************
void test_m_now_m_pause (real_4 pause_secs) {
  real_8 wsecs_before = -1;
  real_8 fsecs_before = -1;
  real_8 wsecs_after  = 0;
  real_8 fsecs_after  = 0;
  ostringstream ostr; // output stringstream for debug messages
  ostr.precision(14); // set floating point precision for this outstream (to display up to picoseconds, 10E-12 (12) + 2 for "0.")

  m_now(wsecs_before, fsecs_before); // get time before pausing
  if (_debug) {
    ostr.str(""); // clear/empty the ostringstream buffer
    ostr << "m_now() before: wsecs=" << wsecs_before << " fsecs=" << fsecs_before
         << "  pausing for " << pause_secs << " seconds...";
    m_info(ostr.str());
  }
  nxm_assert("m_now() before wsec", (wsecs_before >  0));
  nxm_assert("m_now() before fsec", (fsecs_before >= 0));

  m_pause(pause_secs);

  m_now(wsecs_after, fsecs_after);   // get time after pausing
  if (_debug) {
    ostr.str(""); // clear/empty the ostringstream buffer
    ostr << "m_now() after : wsecs=" << wsecs_after << " fsecs=" << fsecs_after;
    m_info(ostr.str());
  }
  nxm_assert("m_now() after wsec", (wsecs_after >  0));
  nxm_assert("m_now() after fsec", (fsecs_after >= 0));

  real_8 tolerance = pause_secs/100;
  real_8 elpasedTime = (wsecs_after - wsecs_before) + (fsecs_after - fsecs_before) + tolerance;
  nxm_assert("m_pause(.) elpased time", (elpasedTime >= pause_secs));
} // end test_m_now_m_pause()

//******************************************************************************
// test case for m_promote_format (BUG 2498)
//******************************************************************************
void test_m_promote_format () {
  const string expFormat = "CF";
  string format = m_promote_format("CI", "SF", "SL");
  nxm_assert_eqs("m_promote_format(..) output format", expFormat, format);
}

//******************************************************************************
// test case for m_lowercase (BUG 2498) and xmlibc.c f2jstr (BUG 2501)
//******************************************************************************
void test_m_lowercase () {
  const string lowercase = "this long mixed cased string should get lowercased! 123456789012345__";
  string       teststr   = "This Long Mixed Cased string SHOULD get LOWERCASED! 123456789012345__";
  m_lowercase(teststr);
  nxm_assert_eqs("m_lowercase(.) output", lowercase, teststr);

  // test case for f2jstr on input string longer than 160 chars (BUG 2501)
  const string expLongStr = lowercase + lowercase + lowercase;
  string       longstr    = teststr   + teststr   + teststr;
  nxm_assert("m_lowercase(.) input long string >= 160 chars", (longstr.length() >= 160));
  m_lowercase(longstr);
  nxm_assert_eqs("m_lowercase(.) output of long string >= 160 chars", expLongStr, longstr);
}

//******************************************************************************
// test case for m_spa (BUG 2523)
//******************************************************************************
void test_m_spa () {
  int    expSPA[]  = { 1,    2,    3,    4,    9,    10,   16,   1};
  string formats[] = {"SB", "CI", "VL", "QX", "MF", "XD", "TB", "UI"};
  int    size      = sizeof(expSPA) / sizeof(int); // no easy way to get size of array

  for (int ii=0; ii<size; ii++) {
    string format = formats[ii];
    int result = m_spa(format);
    nxm_assert_eq("m_spa(.) "+format, expSPA[ii], result);
  }
}

//******************************************************************************
// test case for m_head, m_tail, m_root, and m_ext (BUG 1374), since 3.1.2
//******************************************************************************
void test_m_head_m_tail_m_root_m_ext () {
  const string fname = "/tmp/path/to/filename.ext";
  const string expectedHead = "/tmp/path/to/";
  const string expectedTail = "filename.ext";
  const string expectedRoot = "filename";
  const string expectedExt  = "ext";
  string teststr;
  int_4 outlen;

  outlen = m_head(fname, teststr);
  nxm_assert_eq("m_head(.) outstr len", expectedHead.length(), outlen);
  nxm_assert_eqs("m_head(.) outstr equals ", expectedHead, teststr);

  outlen = m_tail(fname, teststr);
  nxm_assert_eq("m_tail(.) outstr len", expectedTail.length(), outlen);
  nxm_assert_eqs("m_tail(.) outstr equals", expectedTail, teststr);

  outlen = m_root(fname, teststr);
  nxm_assert_eq("m_root(.) outstr len", expectedRoot.length(), outlen);
  nxm_assert_eqs("m_root(.) outstr equals", expectedRoot, teststr);

  outlen = m_ext(fname, teststr);
  nxm_assert_eq("m_ext(.) outstr len", expectedExt.length(), outlen);
  nxm_assert_eqs("m_ext(.) outstr equals", expectedExt, teststr);
}

//******************************************************************************
// test case for m_trims (BUG 1374), since 3.1.2
//******************************************************************************
void test_m_trims () {
  string outStr = "test";
  const string substr1        = "This is a ";
  const string substr2        = " testcase.";
  const string expectedOutStr = " m_trims";
  string teststr              = "This is a m_trims testcase.";
  int_4 outlen;

  outlen = m_trims(teststr, outStr, substr1, substr2);
  nxm_assert_eq("m_trims(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_trims(.) outstr equals ", expectedOutStr, outStr);
}

//******************************************************************************
// test case for m_between (BUG 1374), since 3.1.2
//******************************************************************************
void test_m_between () {
  string outStr         = "";
  string teststr        = "This is a string";
  int_4 outlen;
  int_4 min             = 1;
  int_4 max             = 7;
  string expectedOutStr = "This is";

  outlen = m_between(teststr, outStr, min, max);
  nxm_assert_eq("m_between(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_between(.) outstr equals ", expectedOutStr, outStr);

  outlen = m_between(teststr, outStr, max, min);
  nxm_assert_eq("m_between(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_between(.) outstr equals ", expectedOutStr, outStr);

  min = 4;
  max = -100;
  expectedOutStr = "This";

  outlen = m_between(teststr, outStr, min, max);
  nxm_assert_eq("m_between(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_between(.) outstr equals ", expectedOutStr, outStr);

  outlen = m_between(teststr, outStr, max, min);
  nxm_assert_eq("m_between(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_between(.) outstr equals ", expectedOutStr, outStr);

  min = -5;
  max = 0;
  expectedOutStr = "string";

  outlen = m_between(teststr, outStr, min, max);
  nxm_assert_eq("m_between(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_between(.) outstr equals ", expectedOutStr, outStr);

  outlen = m_between(teststr, outStr, max, min);
  nxm_assert_eq("m_between(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_between(.) outstr equals ", expectedOutStr, outStr);
}

//******************************************************************************
// since 3.1.2 for testing BUG 2606
//******************************************************************************
void test_m_d2a () {
  const double dval   =  0.123456;
  const string expStr = "0.1234560";
  string ostr;
  int_4 len = m_d2a(dval, ostr);
  nxm_assert_eq("m_d2a(.) outstr len", expStr.length(), len);
  nxm_assert_eqs("m_d2a(.) outstr equals ", expStr, ostr);
}

//******************************************************************************
// test case for m_nelements (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_nelements () {
  string delim                        = "<SPACE>";
  string teststr                      = "This is a m_nelements testcase.";
  const int_4 n_elem_true_delim_space = 5;
  int_4 n_elem                        = m_nelements(teststr, delim);
  nxm_assert_eq("m_nelements(.) n_elem", n_elem_true_delim_space, n_elem);

  delim                              = "e";
  const int_4 n_elem_true_delim_char = 6;
  n_elem                             = m_nelements(teststr, delim);
  nxm_assert_eq("m_nelements(.) n_elem", n_elem_true_delim_char, n_elem);

  delim                                = "m_n";
  const int_4 n_elem_true_delim_string = 2;
  n_elem                               = m_nelements(teststr, delim);
  nxm_assert_eq("m_nelements(.) n_elem", n_elem_true_delim_string, n_elem);
}

//******************************************************************************
// test case for m_element (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_element () {
  string delim                  = "<SPACE>";
  string teststr                = "This, is, a m_element, testcase.";
  string outstr;
  int_4 elem_n                  = 4;
  const string expectedOutstr   = "m_element,";
  int_4 outstr_len              = m_element(teststr, outstr, elem_n, delim);
  nxm_assert_eq("m_element(.) outstr len", expectedOutstr.length(), outstr_len);
  nxm_assert_eqs("m_element(.) outstr equals ", expectedOutstr, outstr);

  elem_n                              = 4;
  const string expectedOutstr_comma   = " testcase.";
  outstr_len                          = m_element(teststr, outstr, elem_n, "");
  nxm_assert_eq("m_element(.) outstr len", expectedOutstr_comma.length(), outstr_len);
  nxm_assert_eqs("m_element(.) outstr equals ", expectedOutstr_comma, outstr);

  elem_n                             = 100;
  const string expectedOutstr_over;
  outstr_len                         = m_element(teststr, outstr, elem_n, delim);
  nxm_assert_eq("m_element(.) outstr len", expectedOutstr_over.length(), outstr_len);
  nxm_assert_eqs("m_element(.) outstr equals ", expectedOutstr_over, outstr);

  elem_n                                   = 100;
  const string expectedOutstr_comma_over;
  outstr_len                               = m_element(teststr, outstr, elem_n, "");
  nxm_assert_eq("m_element(.) outstr len", expectedOutstr_comma_over.length(), outstr_len);
  nxm_assert_eqs("m_element(.) outstr equals ", expectedOutstr_comma_over, outstr);
}

//******************************************************************************
// test case for m_word (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_word () {
  string teststr              = "This is a m_word testcase.";
  string substring            = "hi";
  string outstr;
  const string expectedOutstr = "This";
  int_4 outstr_len            = m_word(teststr, outstr, substring);
  nxm_assert_eq("m_word(.) outstr len", expectedOutstr.length(), outstr_len);
  nxm_assert_eqs("m_word(.) outstr equals ", expectedOutstr, outstr);
}

//******************************************************************************
// test case for m_nsearch (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_nsearch () {
  string teststr          = "This is a m_nsearch testcase.";
  string substring        = "a";
  int_4 occur             = 3;
  const int expectedIndex = 25;
  int_4 index             = m_nsearch(teststr, substring, occur);
  nxm_assert_eq("m_nsearch(.) index", expectedIndex, index);

  occur                           = 7;
  const int expectedIndexNotFound = -1;
  index                           = m_nsearch(teststr, substring, occur);
  nxm_assert_eq("m_nsearch(.) index", expectedIndexNotFound, index);

  substring                       = "nonexistent";
  occur                           = 1;
  index                           = m_nsearch(teststr, substring, occur);
  nxm_assert_eq("m_nsearch(.) index", expectedIndexNotFound, index);
}

//******************************************************************************
// test case for m_range (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_range () {
  string outStr         = "";
  string teststr        = "This is a string";
  int_4 outlen;
  int_4 min             = 1;
  int_4 max             = 7;
  string expectedOutStr = "This is";

  outlen = m_range(teststr, outStr, min, max);
  nxm_assert_eq("m_range(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_range(.) outstr equals ", expectedOutStr, outStr);

  expectedOutStr = "";
  outlen = m_range(teststr, outStr, max, min);
  nxm_assert_eq("m_range(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_range(.) outstr equals ", expectedOutStr, outStr);

  min = 4;
  max = -100;
  expectedOutStr = "";

  outlen = m_range(teststr, outStr, min, max);
  nxm_assert_eq("m_range(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_range(.) outstr equals ", expectedOutStr, outStr);

  outlen = m_range(teststr, outStr, max, min);
  nxm_assert_eq("m_range(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_range(.) outstr equals ", expectedOutStr, outStr);

  min = -5;
  max = 0;
  expectedOutStr = "string";

  outlen = m_range(teststr, outStr, min, max);
  nxm_assert_eq("m_range(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_range(.) outstr equals ", expectedOutStr, outStr);

  expectedOutStr = "";
  outlen = m_range(teststr, outStr, max, min);
  nxm_assert_eq("m_range(.) outstr len", expectedOutStr.length(), outlen);
  nxm_assert_eqs("m_range(.) outstr equals ", expectedOutStr, outStr);
}

//******************************************************************************
// test case for m_startswith (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_startswith () {
  string prefix     = "This";
  string teststr    = "This is a m_startswith testcase.";
  int_4 expectedOut = 1;
  int_4 out         = m_startswith(teststr, prefix);
  nxm_assert_eq("m_startswith(.)", expectedOut, out);

  prefix            = "testcase";
  expectedOut       = 0;
  out               = m_startswith(teststr, prefix);
  nxm_assert_eq("m_startswith(.)", expectedOut, out);

  prefix            = "this";
  expectedOut       = 0;
  out               = m_startswith(teststr, prefix);
  nxm_assert_eq("m_startswith(.)", expectedOut, out);
}

//******************************************************************************
// test case for m_endswith (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_endswith () {
  string suffix     = "testcase.";
  string teststr    = "This is a m_endswith testcase.";
  int_4 expectedOut = 1;
  int_4 out         = m_endswith(teststr, suffix);
  nxm_assert_eq("m_endswith(.)", expectedOut, out);

  suffix            = "This";
  expectedOut       = 0;
  out               = m_endswith(teststr, suffix);
  nxm_assert_eq("m_endswith(.)", expectedOut, out);

  suffix            = "TESTCASE.";
  expectedOut       = 0;
  out               = m_endswith(teststr, suffix);
  nxm_assert_eq("m_endswith(.)", expectedOut, out);
}

//******************************************************************************
// test case for m_nformat (BUG 2806), since 3.5.0  Author: Adam Weisz
//******************************************************************************
void test_m_nformat () {
  string instr          = "3.141592653589793238462643383";
  string formstr        = "(F5.4)";
  string outstr;
  string expectedOutstr = "3.1416";
  int_4 expectedOut     = 6;
  int_4 out             = m_nformat(instr, formstr, outstr);
  nxm_assert_eq("m_nformat(.)", expectedOut, out);
  nxm_assert_eqs("m_nformat(.)", expectedOutstr, outstr);

  formstr        = "(I3)";
  expectedOutstr = "3";
  expectedOut    = 1;
  out            = m_nformat(instr, formstr, outstr);
  nxm_assert_eq("m_nformat(.)", expectedOut, out);
  nxm_assert_eqs("m_nformat(.)", expectedOutstr, outstr);

  formstr        = "(I3.3)";
  expectedOutstr = "003";
  expectedOut    = 3;
  out            = m_nformat(instr, formstr, outstr);
  nxm_assert_eq("m_nformat(.)", expectedOut, out);
  nxm_assert_eqs("m_nformat(.)", expectedOutstr, outstr);

  instr          = "1234567890";
  formstr        = "(E4.3E2)";
  expectedOutstr = "1.235E+09";
  expectedOut    = 9;
  out            = m_nformat(instr, formstr, outstr);
  nxm_assert_eq("m_nformat(.)", expectedOut, out);
  nxm_assert_eqs("m_nformat(.)", expectedOutstr, outstr);

  formstr        = "";
  expectedOutstr = "1,234,567,890";
  expectedOut    = 13;
  out            = m_nformat(instr, formstr, outstr);
  nxm_assert_eq("m_nformat(.)", expectedOut, out);
  nxm_assert_eqs("m_nformat(.)", expectedOutstr, outstr);

  instr          = "2025792684";
  formstr        = "STD";
  expectedOutstr = "2014:03:12::16:11:24.000";
  expectedOut    = 24;
  out            = m_nformat(instr, formstr, outstr);
  nxm_assert_eq("m_nformat(.)", expectedOut, out);
  nxm_assert_eqs("m_nformat(.)", expectedOutstr, outstr);
}

struct test_3111_6111_struct { // since 3.5.2 for BUG 2773
  int_1           sb;
  std::bitset<8>  sp;
  std::bitset<16> mp;
  std::bitset<8>  vp;
  int_1           sn;
  int_2           vn;
};

//******************************************************************************
// test case for m_addsubr (BUG 2773), since 3.5.2
//******************************************************************************
void test_m_addsubr () {
  CPHEADER hout;
  CPHEADER hin;
  const string testfn = "ram:test3111_bug2773";
  const int numElements = 3;

  m_init(hout, testfn, "3111", "NH", 0);
  m_addsubr(hout, "BYTE",  "SB");  // 1 byte
  m_addsubr(hout, "SBIT",  "SP");  // 1 byte (holding 1 bit)
  m_addsubr(hout, "MBIT",  "MP");  // 2 bytes (holding 9 bits)
  m_addsubr(hout, "VBIT",  "VP");  // 1 byte (holding 3 bits)
//  m_addsubr(hout, "SNB",   "SN");  // 1 byte (holding 1 nibble (4 bit integer)) X-Midas doesn't support Nibbles?
//  m_addsubr(hout, "VNB",   "VN");  // 2 bytes (holding 3 nibbles) - X-Midas doesn't support Nibbles?
  m_addsubr(hout, "PAD1",    "SB"); // 1 byte (using this until X-Midas and XBC supports "N" for nibble)
  m_addsubr(hout, "PAD2",    "CB"); // 2 bytes (using this until X-Midas and XBC supports "N" for nibble)

  m_allocate(hout);

  // create and initialize test data
  test_3111_6111_struct data;
  data.sb = 7;

  data.sp.reset();
  data.sp.set(0); // turn on bit at position 0

  data.mp.reset();
  data.mp.set(1); // turn on bit at position 1
  data.mp.set(8); // turn on bit at position 8

  data.vp.reset();
  data.vp.set(2); // turn on bit at position 2

  data.sn = 0x01;

  data.vn = 0xf1;

  // write test data to file (numElements times)
  for (int ii=0; ii<numElements; ii++) {
    m_filad(hout, &data, 1);
  }
  m_close(hout);

  // open test file for reading
  m_init(hin, testfn, "3000", "", 0);
  m_inok(hin);

  nxm_assert_eq("m_addsubr(.) created type 3000 with desired number elements", numElements, (int_4) hin.size); // cast to double to int4 to get rid of compiler warning
  nxm_assert_eq("m_addsubr(.) created type 3000 with 6 sub-records", 6, hin.subrecords);
  nxm_assert_eq("m_addsubr(.) hcb.bpe (bytes per element)", 8, hin.bpe);
  nxm_assert_eq("m_addsubr(.) & m_open() hcb.first_byte", 512.0, hin.first_byte); // now initialized in m$open
  nxm_assert_eq("m_addsubr(.) & m_open() hcb.last_byte", 536.0, hin.last_byte); // now initialized in m$open

  m_close(hin);
}

//******************************************************************************
// test case for m_addt6subr (BUG 2773), since 3.5.2
//******************************************************************************
void test_m_addt6subr () {
  CPHEADER hout;
  CPHEADER hin;
  const string testfn = "ram:test6111_bug2773";
  const int numElements = 3;

  m_init(hout, testfn, "6111", "NH", 0);
  // for each subrecord min=0 max=-1 nel=1, units=1, uprefix=0)
  m_addt6subr(hout, "BYTE",  "SB", 0, -1 , 1, 1, 0);  // 1 byte
  m_addt6subr(hout, "SBIT",  "SP" , 0, -1 , 1, 1, 0);  // 1 byte (holding 1 bit)
  m_addt6subr(hout, "MBIT",  "MP" , 0, -1 , 1, 1, 0);  // 2 bytes (holding 9 bits)
  m_addt6subr(hout, "VBIT",  "VP" , 0, -1 , 1, 1, 0);  // 1 byte (holding 3 bits)
//  m_addt6subr(hout, "SNB",   "SN", 0, -1 , 1, 1, 0);  // 1 byte (holding 1 nibble (4 bit integer)) X-Midas doesn't support Nibbles?
//  m_addt6subr(hout, "VNB",   "VN", 0, -1 , 1, 1, 0);  // 2 bytes (holding 3 nibbles) - X-Midas doesn't support Nibbles?
  m_addt6subr(hout, "PAD1",    "SB", 0, -1 , 1, 1, 0); // 1 byte (using this until X-Midas and XBC supports "N" for nibble)
  m_addt6subr(hout, "PAD2",    "CB", 0, -1 , 1, 1, 0); // 2 bytes (using this until X-Midas and XBC supports "N" for nibble)

  m_allocate(hout);

  // create and initialize test data
  test_3111_6111_struct data;
  data.sb = 7;

  data.sp.reset();
  data.sp.set(0); // turn on bit at position 0

  data.mp.reset();
  data.mp.set(1); // turn on bit at position 1
  data.mp.set(8); // turn on bit at position 8

  data.vp.reset();
  data.vp.set(2); // turn on bit at position 2

  data.sn = 0x01;

  data.vn = 0xf1;

  // write test data to file (numElements times)
  for (int ii=0; ii<numElements; ii++) {
    m_filad(hout, &data, 1);
  }
  m_close(hout);

  // open test file for reading
  m_init(hin, testfn, "6000", "", 0);
  m_inok(hin);

  nxm_assert_eq("m_addt6subr(.) created type 6000 with desired number elements", numElements, (int_4) hin.size); // cast to double to int4 to get rid of compiler warning
  nxm_assert_eq("m_addt6subr(.) created type 6000 with 6 sub-records", 6, hin.subrecords);
  nxm_assert_eq("m_addt6subr(.) hcb.bpe (bytes per element)", 8, hin.bpe);
  nxm_assert_eq("m_addt6subr(.) & m_open() hcb.last_byte", 536.0, hin.last_byte); // now initialized in m$open

  m_close(hin);
}

//******************************************************************************
// test case for m_addcomp (BUG 2773), since 3.5.2
//******************************************************************************
void test_m_addcomp () {
  CPHEADER hout;
  CPHEADER hin;
  const string testfn = "ram:test3111_bug2773";
  const int numElements = 3;

  m_init(hout, testfn, "3111", "NH", 0);
  // for each subrecord file_TYPE=3111 UNITS=1)
  m_addcomp(hout, "BYTE",  "SB", 3111, 1);  // 1 byte
  m_addcomp(hout, "SBIT",  "SP" , 3111, 1);;  // 1 byte (holding 1 bit)
  m_addcomp(hout, "MBIT",  "MP" , 3111, 1);  // 2 bytes (holding 9 bits)
  m_addcomp(hout, "VBIT",  "VP" , 3111, 1);  // 1 byte (holding 3 bits)
//  m_addcomp(hout, "SNB",   "SN" , 3111, 1););  // 1 byte (holding 1 nibble (4 bit integer)) X-Midas doesn't support Nibbles?
//  m_addcomp(hout, "VNB",   "VN" , 3111, 1););  // 2 bytes (holding 3 nibbles) - X-Midas doesn't support Nibbles?
  m_addcomp(hout, "PAD1",    "SB", 3111, 1); // 1 byte (using this until X-Midas and XBC supports "N" for nibble)
  m_addcomp(hout, "PAD2",    "CB", 3111, 1); // 2 bytes (using this until X-Midas and XBC supports "N" for nibble)

  m_allocate(hout);

  // create and initialize test data
  test_3111_6111_struct data;
  data.sb = 7;

  data.sp.reset();
  data.sp.set(0); // turn on bit at position 0

  data.mp.reset();
  data.mp.set(1); // turn on bit at position 1
  data.mp.set(8); // turn on bit at position 8

  data.vp.reset();
  data.vp.set(2); // turn on bit at position 2

  data.sn = 0x01;

  data.vn = 0xf1;

  // write test data to file (numElements times)
  for (int ii=0; ii<numElements; ii++) {
    m_filad(hout, &data, 1);
  }
  m_close(hout);

  // open test file for reading
  m_init(hin, testfn, "3000", "", 0);
  m_inok(hin);

  if (_debug) {
    cout << "hcb.size          = " << hin.size << endl;
    cout << "hcb.record_length = " << hin.record_length << endl;
    cout << "hcb.last_byte     = " << hin.last_byte << endl;
    cout << "hcb.last_bits     = " << hin.last_bits << endl;
    cout << "hcb.save_bits     = " << hin.save_bits << endl;
  }
  nxm_assert_eq("m_addcomp(.) created type 3000 with desired number elements", numElements, (int_4) hin.size); // cast to double to int4 to get rid of compiler warning
  nxm_assert_eq("m_addcomp(.) created type 3000 with 6 sub-records", 6, hin.subrecords);
  nxm_assert_eq("m_addcomp(.) hcb.bpe (bytes per element)", 8, hin.bpe);
  nxm_assert_eq("m_addcomp(.) & m_open() hcb.last_byte", 536.0, hin.last_byte); // now initialized in m$open

  m_close(hin);
}
