
#include "cpp/varcontainer.h"
#include "varcontainer_io.h"
#include "netcdf.h"
#include <set>
#include <sstream>
#include <fstream>
#include "fpreproc/f77name.h"
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>

// #ifndef NC_UBYTE
#define NOHDF 1
// #endif

// defined in portlib
extern "C" int F77NAME(crename)(const char*, const char*) ;
extern "C" void F77NAME(get_fortran_const)(lp_32* truth, lp_32* lies) ;

const size_t VarContainer::N_START_SIZE=500 ;
const size_t VarContainer::N_START_MULT=10 ;
const int    VarContainer::STORE_AXIS_COLON_ARRAY = 0 ;

VarContainer::VarContainer(string name, size_t n_start_size) : 
  cname(name), 
  igate("I32",n_start_size), 
  fgate("F32",n_start_size), 
  dgate("F64",n_start_size), 
  lgate("L32",n_start_size),
  cgate("Char",n_start_size),
  bgate("Bytes",n_start_size),
#ifdef VARCONTAINER_MAP
  zool(),
#else
  zool(N_START_MULT*n_start_size),
#endif
  logical_true(lp_32_TRUE), 
  logical_false(lp_32_FALSE), 
  matt(), 
  patt(0) 
{
  cname = fwrap_att::despace0(name) ;
  F77NAME(get_fortran_const)(&logical_true, &logical_false) ;
} ;

VarContainer::VarContainer(string name) : 
  cname(name), 
  igate("I32",N_START_SIZE), 
  fgate("F32",N_START_SIZE), 
  dgate("F64",N_START_SIZE), 
  lgate("L32",N_START_SIZE),
  cgate("Char",N_START_SIZE),
  bgate("Bytes",N_START_SIZE),
#ifdef VARCONTAINER_MAP
  zool(),
#else
  zool(N_START_MULT*N_START_SIZE),
#endif
  logical_true(lp_32_TRUE), 
  logical_false(lp_32_FALSE), 
  matt(), 
  patt(0) 
{
  cname = fwrap_att::despace0(name) ;
  F77NAME(get_fortran_const)(&logical_true, &logical_false) ;
} ;

VarContainer::~VarContainer() {
  //cout << "in VarContainer::~VarContainer() for " << cname << endl ;
} ;

string VarContainer::typeWrite(varType t) {
  switch(t) {
  case I32:
    return "I32" ;
  case F32:
    return "F32" ;
  case F64:
    return "F64" ;
  case L32:
    return "L32" ;
  case CHAR:
    return "Char" ;
  case BYTES:
    return "Bytes" ;
  case NONE:
    return "None" ;
  default:
    return "Uknown" ;
  } ;
} ;

bool VarContainer::has_key(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator i = zool.find(name) ;

  return (i!=zool.end()) ;
} ;

void VarContainer::remove(string aname) {
  patt=0 ;

  string name = fwrap_att::despace0(aname) ;

  KeyMaster::iterator i = zool.find(name) ;
  if (i==zool.end()) return ;                      // do nothing

  iPair&  j = i->second ;
  varType t = j.first ; 
  size_t  k = j.second ;
 
  switch(t) {
  case I32:
    igate.remove(k,name) ; break ;
  case F32:
    fgate.remove(k,name) ; break ;
  case F64:
    dgate.remove(k,name) ; break ;
  case L32:
    lgate.remove(k,name) ; break ;
  case CHAR:
    cgate.remove(k,name) ; break ;
  case BYTES:
    bgate.remove(k,name) ; break ;
  default:
    throw ContainerError(cname,"?remove: logic error, unknown container type") ;
  } ;
  
  zool.erase(i) ;
} ;


void VarContainer::clear() {
  patt=0 ;
  matt.clear() ;   // remove attributes

  igate.clear() ;
  fgate.clear() ;
  dgate.clear() ;
  lgate.clear() ;
  cgate.clear() ;
  bgate.clear() ;
  zool.clear() ;
} ;

list<string> VarContainer::names(varType t) const {
  list<string> ls ;
  for (KeyMaster::const_iterator i=zool.begin() ; i!=zool.end() ; ++i) {
    varType it = i->second.first ;
    if (t==NONE||it==t) ls.push_back(i->first) ;
  } ;
  return ls ;
} ;

int VarContainer::size(varType t) const {
  size_t isize = 0 ;
  switch(t) {
  case I32:
    isize = igate.size() ; break ;
  case F32:
    isize = fgate.size() ; break ;
  case F64:
    isize = dgate.size() ; break ;
  case L32:
    isize = lgate.size() ; break ;
  case CHAR:
    isize = cgate.size() ; break ;
  case BYTES:
    isize = bgate.size() ; break ;
  case NONE:
    isize = igate.size()+fgate.size()+dgate.size()+lgate.size()
           +cgate.size()+bgate.size() ; break ;
  default:
    throw ContainerError(cname,"?size: logic error, unknown container type") ;
  } ;
  return static_cast<int>(isize) ;
} ;

ostream& operator<<(ostream& s, const VarContainer& v) {
  s << " ------------------ VarContainer ----------------" << endl ;
  s << " Name: " << v.cname << endl << endl ;
  s << " Number of Character: " << v.cgate.size() << endl ;
  s << " Number of Bytes:     " << v.bgate.size() << endl ;
  s << " Number of Logical*4: " << v.lgate.size() << endl ;
  s << " Number of Integer*4: " << v.igate.size() << endl ;
  s << " Number of Real*4:    " << v.fgate.size() << endl ;
  s << " Number of Real*8:    " << v.dgate.size() << endl ;
  s << endl ;
  string lead("   & ") ;
  
  list<size_t> ii = v.cgate.indices() ;
  for (list<size_t>::const_iterator k=ii.begin() ; k!=ii.end() ; ++k) {
    size_t i = *k ;
    s << " Character " << v.cgate.getName(i) ;
    const acpPtr_char x = v.cgate.getPoint(i) ;
    if (x->rank()!=0) {
      const Shape& shape = x->pshape() ;
      s << "[" ;
      for (size_t j=0 ; j<x->rank() ; j++) {
        if (j!=0) s<<"," ;
        s << shape[j] ;
      } ;
      s << "]" ;
    } ;
    s << endl ;
    const fwrap_map_att* p = x->att() ;
    if (p->size()>0) p->write(s,lead) ;
  } ;
  
  ii = v.bgate.indices() ;
  for (list<size_t>::const_iterator k=ii.begin() ; k!=ii.end() ; ++k) {
    size_t i = *k ;
    s << " Bytes     " << v.bgate.getName(i) ;
    const abpPtr_bytes x = v.bgate.getPoint(i) ;
    if (x->rank()!=0) {
      const Shape& shape = x->pshape() ;
      s << "[" ;
      for (size_t j=0 ; j<x->rank() ; j++) {
        if (j!=0) s<<"," ;
        s << shape[j] ;
      } ;
      s << "]" ;
    } ;
    s << endl ;
    const fwrap_map_att* p = x->att() ;
    if (p->size()>0) p->write(s,lead) ;
  } ;
  
  ii = v.lgate.indices() ;
  for (list<size_t>::const_iterator k=ii.begin() ; k!=ii.end() ; ++k) {
    size_t i = *k ;
    s << " Logical*4 " << v.lgate.getName(i)  ;
    const alpPtr_32 x = v.lgate.getPoint(i) ;
    if (x->rank()!=0) {
      const Shape& shape = x->pshape() ;
      s << "[" ;
      for (size_t j=0 ; j<x->rank() ; j++) {
        if (j!=0) s<<"," ;
        s << shape[j] ;
      } ;
      s << "]" ;
    } ;
    s << endl ;
    const fwrap_map_att* p = x->att() ;
    if (p->size()>0) p->write(s,lead) ;
  } ;
  
  ii = v.igate.indices() ;
  for (list<size_t>::const_iterator k=ii.begin() ; k!=ii.end() ; ++k) {
    size_t i = *k ;
    s << " Integer*4 " << v.igate.getName(i) ;
    const aipPtr_32 x = v.igate.getPoint(i) ;
    if (x->rank()!=0) {
      const Shape& shape = x->pshape() ;
      s << "[" ;
      for (size_t j=0 ; j<x->rank() ; j++) {
        if (j!=0) s<<"," ;
        s << shape[j] ;
      } ;
      s << "]" ;
    } ;
    s << endl ;
    const fwrap_map_att* p = x->att() ;
    if (p->size()>0) p->write(s,lead) ;
  } ;
  
  ii = v.fgate.indices() ;
  for (list<size_t>::const_iterator k=ii.begin() ; k!=ii.end() ; ++k) {
    size_t i = *k ;
    s << " Real*4    " << v.fgate.getName(i) ;
    const afpPtr_32 x = v.fgate.getPoint(i) ;
    if (x->rank()!=0) {
      const Shape& shape = x->pshape() ;
      s << "[" ;
      for (size_t j=0 ; j<x->rank() ; j++) {
        if (j!=0) s<<"," ;
        s << shape[j] ;
      } ;
      s << "]" ;
    } ;
    s << endl ;
    const fwrap_map_att* p = x->att() ;
    if (p->size()>0) p->write(s,lead) ;
  }   ;
  
  ii = v.dgate.indices() ;
  for (list<size_t>::const_iterator k=ii.begin() ; k!=ii.end() ; ++k) {
    size_t i = *k ;
    s << " Real*8    " << v.dgate.getName(i) ;
    const afpPtr_64 x = v.dgate.getPoint(i) ;
    if (x->rank()!=0) {
      const Shape& shape = x->pshape() ;
      s << "[" ;
      for (size_t j=0 ; j<x->rank() ; j++) {
        if (j!=0) s<<"," ;
        s << shape[j] ;
      } ;
      s << "]" ;
    } ;
    s << endl ;
    const fwrap_map_att* p = x->att() ;
    if (p->size()>0) p->write(s,lead) ;
  }   ;
  
  if (v.matt.size()!=0) {
    s << endl ;
    s << " Global Attributes" << endl ;
    v.matt.write(s,string(" & ")) ;
  } ;
  s << " ---------------- end VarContainer --------------" << endl ;
  
  return s ;
} ;


VarContainer::varType VarContainer::getType(string aname) const {
  string name = fwrap_att::despace0(aname) ;

  KeyMaster::const_iterator i = zool.find(name) ;
  if (i==zool.end())throw KeyNotFound(cname, name)  ;

  return i->second.first ; 
} ;

size_t VarContainer::getRank(string aname) const {
  string name = fwrap_att::despace0(aname) ;

  KeyMaster::const_iterator i = zool.find(name) ;
  if (i==zool.end()) throw KeyNotFound(cname, name) ;

  const iPair& j=i->second ;
  varType t=j.first ; 
  size_t  k=j.second ;
 
  switch(t) {
  case I32:
    return igate.getPoint(k,name)->rank() ;
  case F32:
    return fgate.getPoint(k,name)->rank() ; 
  case F64:
    return dgate.getPoint(k,name)->rank() ; 
  case L32:
    return lgate.getPoint(k,name)->rank() ; 
  case CHAR:
    return cgate.getPoint(k,name)->rank() ; 
  case BYTES:
    return bgate.getPoint(k,name)->rank() ; 
  default:
    throw ContainerError(cname,"?getRank(): logic error, unrecognized type") ;
  } ;
} ;

const Shape& VarContainer::getShape(string aname) const {
  string name = fwrap_att::despace0(aname) ;

  KeyMaster::const_iterator i = zool.find(name) ;
  if (i==zool.end()) throw KeyNotFound(cname, name) ;

  const iPair& j=i->second ;
  varType t=j.first ; 
  size_t  k=j.second ;
 
  switch(t) {
  case I32:
    return igate.getPoint(k,name)->shape() ; 
  case F32:
    return fgate.getPoint(k,name)->shape() ; 
  case F64:
    return dgate.getPoint(k,name)->shape() ; 
  case L32:
    return lgate.getPoint(k,name)->shape() ; 
  case CHAR:
    return cgate.getPoint(k,name)->shape() ; 
  case BYTES:
    return bgate.getPoint(k,name)->shape() ; 
  default:
    throw ContainerError(cname,"?getShape(): logic error, unrecognized type") ;
  } ;
} ;
  
void VarContainer::getInfo(string aname, bool& has_key, varType& type, size_t& rank, Shape& shape) const {
  string name = fwrap_att::despace0(aname) ;

  has_key=false ;  // make sure this gets set

  KeyMaster::const_iterator i = zool.find(name) ;
  if (i==zool.end()) return ;

  has_key  = true ;
  const iPair& j = i->second ;
  type     = j.first ; 
  size_t k = j.second ;

  switch(type) {
  case I32:
    {
      aipPtr_32 q1 = igate.getPoint(k,name) ;
      rank = q1->rank() ;  if (shape.size()<rank) shape.resize(rank) ;
      const Shape& s1 = q1->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s1[i] ;
      break ;
    }
  case F32:
    {
      afpPtr_32 q2 = fgate.getPoint(k,name) ;
      rank = q2->rank() ; if (shape.size()<rank) shape.resize(rank) ;
      const Shape& s2 = q2->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s2[i] ;
      break ;
    }
  case F64:
    {
      afpPtr_64 q3 = dgate.getPoint(k,name) ;
      rank = q3->rank() ; if (shape.size()<rank) shape.resize(rank) ;
      const Shape& s3 = q3->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s3[i] ;
      break ;
    }
  case L32:
    {
      alpPtr_32 q4 = lgate.getPoint(k,name) ;
      rank = q4->rank() ; if (shape.size()<rank) shape.resize(rank) ;
      const Shape& s4 = q4->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s4[i] ;
      break ;
    }
  case CHAR:
    {
      acpPtr_char q5 = cgate.getPoint(k,name) ;
      rank = q5->rank() ; if (shape.size()<rank) shape.resize(rank) ;
      const Shape& s5 = q5->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s5[i] ;
      break ;
    }
  case BYTES:
    {
      abpPtr_bytes q6 = bgate.getPoint(k,name) ;
      rank = q6->rank() ; if (shape.size()<rank) shape.resize(rank) ;
      const Shape& s6 = q6->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s6[i] ;
      break ;
    }
  default:
    throw ContainerError(cname,"?getInfo(): logic error, unrecognized type") ;
  } ;
} ;

void VarContainer::add0(string aname, varType type, const Shape& shape) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;

  switch(type) {
  case I32:
    {
      aipPtr_32 i = aipPtr_32::build(shape) ; 
      patt = i->att() ;
      i->assign(0) ;
      zool[name]=iPair(I32,igate.add(name,i)) ;
      break ;
    }
  case F32:
    {
      afpPtr_32 i = afpPtr_32::build(shape) ;
      patt = i->att() ;
      i->assign(0.f) ;
      zool[name]=iPair(F32,fgate.add(name,i)) ;
      break ;
    }
  case F64:
    {
      afpPtr_64 i = afpPtr_64::build(shape) ;
      patt = i->att() ;
      i->assign(0.) ;
      zool[name]=iPair(F64,dgate.add(name,i)) ;
      break ;
    }
  case L32:
    {
      alpPtr_32 i = alpPtr_32::build(shape) ;
      patt = i->att() ;
      i->assign(logical_false) ;
      zool[name]=iPair(L32,lgate.add(name,i)) ;
      break ;
    }
  case CHAR:
    {
      acpPtr_char i = acpPtr_char::build(shape) ;
      patt = i->att() ;
      i->assign(' ') ;
      zool[name]=iPair(CHAR,cgate.add(name,i)) ;
      break ;
    }
  case BYTES:
    {
      abpPtr_bytes i = abpPtr_bytes::build(shape) ;
      patt = i->att() ;
      zool[name]=iPair(BYTES,bgate.add(name,i)) ;
      break ;
    }
  default:
    throw ContainerError(cname,"?getInfo(): logic error, unrecognized type") ;
  } ;
} ;

  
// ------------ ContainerError ------------
VarContainer::ContainerError::ContainerError(string container, string info) throw() {
  description = "VarContainer::ContainerError: in "+container+", "+info ;
};

VarContainer::ContainerError::ContainerError(const ContainerError& k) throw() : description(k.description) {} ;

VarContainer::ContainerError& VarContainer::ContainerError::operator=(const ContainerError& k) throw() {
  if (&k!=this) {
    description = k.description ;
  }
  return *this ;
} ;

VarContainer::ContainerError::~ContainerError() throw() { } ;

const char* VarContainer::ContainerError::what() const throw() {
  return description.c_str() ;
} ;

const string  VarContainer::ContainerError::whats() const throw() {
  return description ;
} ;

// ------------ KeyNotFound ------------
VarContainer::KeyNotFound::KeyNotFound(string container, string varname, int vtype) throw() :
  ContainerError(container," ") { 
  string s="" ;
  if (vtype==-3) {
    s = "byte array " ;
  } 
  else if (vtype==-2) {
    s = "character " ;
  }
  else if (vtype==-1) {
    s="logical " ;
  }
  else if (vtype>0) {
    s="numerical " ;
  }
  else {
    s="unknown " ;
  }
  description = "VarContainer::KeyNotFound: in "+container+", container does not contain the "
    +s+"variable '"+varname+"'" ;
};

VarContainer::KeyNotFound::KeyNotFound(string container, string varname, varType need, varType got) throw() :
  ContainerError(container," ") { 
  description = "VarContainer::KeyNotFound: container "+container+" contains the variable '"
    +varname+"' as the type "+typeWrite(got)+" but needed the type "+typeWrite(need) ;
};

VarContainer::KeyNotFound::KeyNotFound(const KeyNotFound& k) throw() : ContainerError(k){} ;

VarContainer::KeyNotFound& VarContainer::KeyNotFound::operator=(const KeyNotFound& k) throw() {
  if (&k!=this) {
    ContainerError::operator=(k) ;
  }
  return *this ;
} ;

VarContainer::KeyNotFound::~KeyNotFound() throw() { } ;

// ------------ IOError ------------
VarContainer::IOError::IOError(string container, string info) throw() :
  ContainerError(container,info) { 
  description = "VarContainer::IOError: in " + container + ", " + info;
};

VarContainer::IOError::IOError(const IOError& k) throw() : ContainerError(k){} ;

VarContainer::IOError& VarContainer::IOError::operator=(const IOError& k) throw() {
  if (&k!=this) {
    ContainerError::operator=(k) ;
  }
  return *this ;
} ;

VarContainer::IOError::~IOError() throw() { } ;

// ------------ storage ------------
void VarContainer::IntStore::need(size_t nsize) {
  if (dsize<nsize) {
    size_t nnew = (4*nsize)/3 ;
    int*   dnew = new int[nnew] ;
    
    int* p = dnew ;
    int* q = data ;
    for (size_t i=0 ; i<dsize ; i++) *p++ = *q++ ;
    
    delete[] data ;
    dsize = nnew ;     // a little bit extra space
    data  = dnew ;
  }  
}          // insure there is this much space

void VarContainer::ByteStore::need(size_t nsize) {
  if (dsize<nsize) {
    size_t nnew = (4*nsize)/3 ;
    Byte*  dnew = new Byte[nnew] ;
    
    Byte* p = dnew ;
    Byte* q = data ;
    for (size_t i=0 ; i<dsize ; i++) *p++ = *q++ ;
    
    delete[] data ;
    dsize = nnew ;     // a little bit extra space
    data  = dnew ;
  } 
}         // insure there is this much space

void VarContainer::DoubleStore::need(size_t nsize) {
  if (dsize<nsize) {
    size_t  nnew = (4*nsize)/3 ;
    double* dnew = new double[nnew] ;
    
    double* p = dnew ;
    double* q = data ;
    for (size_t i=0 ; i<dsize ; i++) *p++ = *q++ ;
    
    delete[] data ;
    dsize = nnew ;     // a little bit extra space
    data  = dnew ;
  } 
}         // insure there is this much space

VarContainer::StringStore::StringStore(size_t initsize) : ssize(0), sdata(0), csize(0), cdata(0) {
  ssize = initsize ;
  sdata = new string[ssize] ;
  csize = 25*initsize ;
  cdata = new char[csize] ;
} ;

VarContainer::StringStore::~StringStore() {
  delete[] sdata ;
  delete[] cdata ;
} ;

void VarContainer::StringStore::need(size_t nsize) {
  if (ssize<nsize) {
    delete[] sdata ;
    ssize = (4*nsize)/3 ;
    sdata = new string[ssize] ;
  }
} ;

void VarContainer::StringStore::cneed(size_t nsize) {
  if (csize<nsize) {
    delete[] cdata ;
    csize = (4*nsize)/3 ;
    cdata = new char[csize] ;
  }
} ;

const char VarContainer::StringStore::CHAR_TERM  = 1 ; // not 0 because java netcdf cuts off strings at 0
const char VarContainer::StringStore::CHAR_LAST  = 0 ; // last character will be a 0 so a string array of length 1 is just a string
const char VarContainer::StringStore::CHAR_EMPTY = 2 ;
const char VarContainer::StringStore::CHAR_SUB   = 32 ;

size_t VarContainer::StringStore::stringtochar(size_t isize) {
  if (isize>ssize) isize=ssize ;
  if (isize==0) {
    cneed(2) ;
    cdata[0] = CHAR_EMPTY ;      // flags the string as an empty array
    cdata[1] = 0 ;
    return 2 ;
  }

  size_t n = isize ;             // total number of chars needed, isize for terminating 0
  for (size_t i=0 ; i<isize ; i++) {
    n += sdata[i].length() ;
  }
  cneed(n) ;
  char* c = cdata ;
  for (size_t i=0 ; i<isize ; i++) {
    size_t k = sdata[i].length() ;
    sdata[i].copy(c,k) ;
    for (size_t j=0 ; j<k ; j++) {
      if ((*c)==CHAR_TERM || (*c)==CHAR_LAST || (*c)==CHAR_EMPTY) *c=CHAR_SUB ;
      c++ ;
    }
    *c++ = (i==(isize-1)) ? CHAR_LAST : CHAR_TERM ;
  }
  return n ;
} ;

size_t VarContainer::StringStore::chartostring(size_t isize) {
  if (csize<isize) isize=csize ;
  if (isize==0) {                 // consider an empty string as a scalar empty string
    need(1) ;
    sdata[0] = string("") ;
    return 1 ;
  }
  
  if (isize>=1 && cdata[0]==CHAR_EMPTY) return 0 ;  // special empty array flag

  size_t n = 0 ;      // number of strings needed
  for (int i=0 ; i<isize ; i++) {
    if (cdata[i]==CHAR_TERM || cdata[i]==CHAR_LAST || i==(isize-1)) n++ ;
  } ;
  need(n) ;
  
  size_t m  = 0 ;       // index of string being built
  char*  c0 = cdata ;   // pointer to beginning of new string in cdata
  size_t r  = 0 ;       // number of chars to store in next string
  for (int i=0 ; i<isize ; i++) {
    if (cdata[i]==CHAR_TERM || cdata[i]==CHAR_LAST) {
      sdata[m++] = string(c0,r) ;
      r=0 ; c0 = cdata+i+1 ;          // normal termination
    }
    else if (i==(isize-1)) {
      sdata[m++] = string(c0,r+1) ;   // hit when last char is not 0
    }
    else {
      r++ ;
    }
  } ;
  return n ;
} ;


// ------------ Integer ------------
ip_32 VarContainer::getScalarInt32(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;

  const iPair& j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case I32:
    {
      const aipPtr_32 i = igate.getPoint(k,name) ;
      if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
      return *(i->fptr()) ;
    } 
  default:
    throw KeyNotFound(cname,name,I32,t) ;
  } 
} ;

ip_32 VarContainer::getDefScalarInt32(string aname, ip_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) return def ;

  const iPair&  j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case I32:
    {
      const aipPtr_32 i = igate.getPoint(k,name) ;
      if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
      return *(i->fptr()) ;
    } 
  default:
    throw KeyNotFound(cname,name,I32,t) ;    
  } 
} ;

void  VarContainer::set(string aname, ip_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;

  iPair&  j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case I32:
    {
      aipPtr_32 i = igate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      *(i->fptr())=d ;
      return ;
    } 
  case F32:
    {
      afpPtr_32 i = fgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      *(i->fptr())=static_cast<fp_32>(d) ;
      return ;
    } 
  case F64:
    {
      afpPtr_64 i = dgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      *(i->fptr())=static_cast<fp_64>(d) ;
      return ;
    } 
  default:
    throw KeyNotFound(cname,name,1) ;
  } 
  throw ContainerError(cname, "variable "+name+" is not a scalar") ;
} ;

void  VarContainer::add(string aname, ip_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  aipPtr_32 i = aipPtr_32::build() ;  // build a new scalar
  patt = i->att() ;
  *(i->fptr()) = d ;
  zool[name]=iPair(I32,igate.add(name,i)) ;
} ;

aipPtr_32 VarContainer::getInt32(string aname, const aipPtr_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,1) ;
    return def ;
  }
  if (ii->second.first!=I32) throw KeyNotFound(cname,name,I32,ii->second.first) ;
  return igate.getPoint(ii->second.second) ;
} ;

void VarContainer::get(string aname, aip_32& out, aipPtr_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,1) ; 
    fwrapCopy(*def,out) ;
  }
  else {
    if (ii->second.first!=I32) throw KeyNotFound(cname,name,I32,ii->second.first) ;
    fwrapCopy(*(igate.getPoint(ii->second.second)),out) ;
  } ;
} ;

void VarContainer::get(string name, aipPtr_32 out, aipPtr_32 def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  get(name,*out,def) ;
} ;

void VarContainer::set(string aname, const aip_32& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;
  iPair&  j = ii->second ;
  varType t = j.first ; 
  size_t  k = j.second ;
  switch (t) {
  case I32:
    fwrapCopy(d,*(igate.getPoint(k,name))) ; break ;
  case F32:
    fwrapCopy(d,*(fgate.getPoint(k,name))) ; break ;
  case F64:
    fwrapCopy(d,*(dgate.getPoint(k,name))) ; break ;
  default:
    throw KeyNotFound(cname,name,1) ;
  } ;
} ;

void VarContainer::set(string name, const aipPtr_32 d) {
  patt=0 ;
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  set(name,*d) ;
} ;

void VarContainer::add(string aname, aipPtr_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  if (d.isnull()) throw ContainerError(cname, "attempt to add a null pointer to the container") ;
  patt = d->att() ;
  zool[name]=iPair(I32,igate.add(name,d)) ;
} ;

void VarContainer::replaceInfo(string name, varType type, size_t k, size_t& rank, Shape& shape) const {
  switch(type) {
  case I32:
    {
      aipPtr_32 q1 = igate.getPoint(k,name) ;
      rank = q1->rank() ;
      if (rank>shape.size()) throw ContainerError(cname,string("rank is too large for a replace, ")+name) ;
      const Shape& s1 = q1->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s1[i] ;
      break ;
    }
  case F32:
    {
      afpPtr_32 q2 = fgate.getPoint(k,name) ;
      rank = q2->rank() ;
      if (rank>shape.size()) throw ContainerError(cname,string("rank is too large for a replace, ")+name) ;
      const Shape& s2 = q2->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s2[i] ;
      break ;
    }
  case F64:
    {
      afpPtr_64 q3 = dgate.getPoint(k,name) ;
      rank = q3->rank() ;
      if (rank>shape.size()) throw ContainerError(cname,string("rank is too large for a replace, ")+name) ;
      const Shape& s3 = q3->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s3[i] ;
      break ;
    }
  case L32:
    {
      alpPtr_32 q4 = lgate.getPoint(k,name) ;
      rank = q4->rank() ;
      if (rank>shape.size()) throw ContainerError(cname,string("rank is too large for a replace, ")+name) ;
      const Shape& s4 = q4->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s4[i] ;
      break ;
    }
  case CHAR:
    {
      acpPtr_char q5 = cgate.getPoint(k,name) ;
      rank = q5->rank() ;
      if (rank>shape.size()) throw ContainerError(cname,string("rank is too large for a replace, ")+name) ;
      const Shape& s5 = q5->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s5[i] ;
      break ;
    }
  case BYTES:
    {
      abpPtr_bytes q6 = bgate.getPoint(k,name) ;
      rank = q6->rank() ;
      if (rank>shape.size()) throw ContainerError(cname,string("rank is too large for a replace, ")+name) ;
      const Shape& s6 = q6->shape() ; 
      for (size_t i=0 ; i<rank ; i++) shape[i]=s6[i] ;
      break ;
    }
  default:
    throw ContainerError(cname,"?replaceInfo(): logic error, unrecognized type") ;
  } ;
} ;

void VarContainer::replaceRemove(KeyMaster::iterator& i, string name, varType t, size_t k) {
  patt=0 ;
  switch(t) {
  case I32:
    igate.remove(k,name) ; break ;
  case F32:
    fgate.remove(k,name) ; break ;
  case F64:
    dgate.remove(k,name) ; break ;
  case L32:
    lgate.remove(k,name) ; break ;
  case CHAR:
    cgate.remove(k,name) ; break ;
  case BYTES:
    bgate.remove(k,name) ; break ;
  default:
    throw ContainerError(cname,"?replaceRemove: logic error, unknown container type") ;
  } ;
  
  zool.erase(i) ;
} ;

bool VarContainer::replace(string aname, int ioption, aipPtr_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (d.isnull()) throw ContainerError(cname, "attempted a replace() with a null pointer") ;
  if (d->rank()>GATEKEEPER_MAX_RANK) throw ContainerError(cname,"rank of variable is > GATEKEEPR_MAX_RANK") ;
  if (ioption<0 || ioption>3) throw ContainerError(cname, "?replace(): bad option number, require 0<=ioption<=3") ;
  KeyMaster::iterator i = zool.find(name) ;
  bool copyable=false ;                             // ends up true when a copy is possible
  if (i!=zool.end()) {
    iPair&  j = i->second ;             // variable with name already exists
    varType t = j.first ; 
    size_t  k = j.second ;

    size_t       drank  = d->rank() ;   // argument rank,shape
    const Shape& dshape = d->shape() ;

    size_t rank ;                       // existing variable rank,shape
    Shape  shape(GATEKEEPER_MAX_RANK) ;

    replaceInfo(name,t,k,rank,shape) ;  // get rank and shape of existing variable

    copyable = (ioption!=0) 
      && ( ((ioption==1)&&(t==I32)) || ((ioption>=2)&&(t==I32)) ) 
      && (rank==drank) ;
    if (copyable) {
      // check shape
      if (ioption==3) {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]>=dshape[i]) ;
      }
      else {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]==dshape[i]) ;
      } ;
    } ;

    if (copyable) {
      if (t==I32) {
        aipPtr_32 f = igate.getPoint(k,name) ;       // existing variable
        if (ioption==4) {
          // this option is removed because fortran will do its own back copy after this one causing
          // the restoration of the argument data.
          *(d->att()) = *(f->att()) ;                // replace argument attributes with existing attributes
          fwrapCopy(*f,*d) ;                         // back copy
          replaceRemove(i,name, t, k) ;              // remove existing variable
          patt = d->att() ;
          zool[name]=iPair(I32,igate.add(name,d)) ;  // add argument
        }
        else {
          fwrapCopy(*d,*f) ;
          patt = f->att() ;
        } ;
      }
      else {
        throw ContainerError(cname, "?replace(): logic error, wrong type encountered") ;
      } ;  
      return false ;     // a copy was made
    } 
    else {
      // variable exists but can not be copied
      if (ioption==4) throw ContainerError(cname, string("?replace(ioption==4): mismatch in argument's type,rank,shape with the")
                                           +" existing variable called, "+name) ;
      replaceRemove(i,name, t, k) ;
    } ;
  } ;
  if (!copyable) {
    // add to container, does not exist at this point
    patt = d->att() ;
    zool[name]=iPair(I32,igate.add(name,d)) ;
    return true ;
  }
  throw ContainerError(cname,"replace(): logic error, reached end of replace()") ;
} ;

// ------------ Float32 ------------
fp_32 VarContainer::getScalarFloat32(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;

  const iPair& j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F32:
    {
      const afpPtr_32 i = fgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return *(i->fptr()) ;
    } 
  case F64:
    {
      const afpPtr_64 i = dgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_32>(*(i->fptr())) ;
    } 
  case I32:
    {
      const aipPtr_32 i = igate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_32>(*(i->fptr())) ;
    } 
  default:
    throw KeyNotFound(cname,name,F32,t) ;
  } 
  throw ContainerError(cname, "variable "+name+" is not a scalar") ;
} ;

fp_32 VarContainer::getDefScalarFloat32(string aname, fp_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) return def ;

  const iPair& j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F32:
    {
      const afpPtr_32 i = fgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return *(i->fptr()) ;
    } 
  case F64:
    {
      const afpPtr_64 i = dgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_32>(*(i->fptr())) ;
    } 
  case I32:
    {
      const aipPtr_32 i = igate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_32>(*(i->fptr())) ;
    } 
  default:
    throw KeyNotFound(cname,name,F32,t) ;
  } 
  throw ContainerError(cname, "variable "+name+" is not a scalar") ;
} ;

void VarContainer::set(string aname, fp_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;

  iPair&  j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F32:
    {
      afpPtr_32 i = fgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      *(i->fptr())=d ;
      return ;
    } 
  case F64:
    {
      afpPtr_64 i = dgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      *(i->fptr())=static_cast<fp_64>(d) ;
      return ;
    } 
  case I32:
    {
      throw KeyNotFound(cname,name,F32,t) ;
    } 
  default:
    throw KeyNotFound(cname,name,1) ;
  } 
  throw ContainerError(cname, "variable "+name+" is not a scalar") ;
} ;

void VarContainer::add(string aname, fp_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  afpPtr_32 i = afpPtr_32::build() ;  // build a new scalar
  patt = i->att() ;
  *(i->fptr()) = d ;
  zool[name]=iPair(F32,fgate.add(name,i)) ;
} ;

afpPtr_32 VarContainer::getFloat32(string aname, afpPtr_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,1) ;
    return def ;
  }
  if (ii->second.first!=F32) throw KeyNotFound(cname,name,F32,ii->second.first) ;
  return fgate.getPoint(ii->second.second) ;
} ;

void VarContainer::get(string aname, afp_32& out, afpPtr_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,1) ; 
    fwrapCopy(*def,out) ;
    return ;
  } ;

  const iPair& j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F32:
    {
      fwrapCopy(*(fgate.getPoint(k,name)),out) ;
      break ;
    }
  case F64:
    {
      fwrapCopy(*(dgate.getPoint(k,name)),out) ;
      break ;
    }
  case I32:
    {
      fwrapCopy(*(igate.getPoint(k,name)),out) ;
      break ;
    } ;
  default:
    KeyNotFound(cname,name,1) ;
  } ;
} ;

void VarContainer::get(string name, afpPtr_32 out, afpPtr_32 def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  get(name,*out,def) ;
} ;

void VarContainer::set(string aname, const afp_32& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;
  iPair&  j = ii->second ;
  varType t = j.first ; 
  size_t  k = j.second ;
  switch (t) {
  case F32:
    fwrapCopy(d,*(fgate.getPoint(k,name))) ; break ;
  case F64:
    fwrapCopy(d,*(dgate.getPoint(k,name))) ; break ;
  default:
    throw KeyNotFound(cname,name,F32,t) ;
  } ;
} ;

void VarContainer::set(string name, const afpPtr_32 d) {
  patt=0 ;
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  set(name,*d) ;
} ;

void VarContainer::add(string aname, afpPtr_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  if (d.isnull()) throw ContainerError(cname, "attempt to add a null pointer to the container") ;
  patt = d->att() ;
  zool[name]=iPair(F32,fgate.add(name,d)) ;
} ;

bool VarContainer::replace(string aname, int ioption, afpPtr_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (d.isnull()) throw ContainerError(cname, "attempted a replace() with a null pointer") ;
  if (d->rank()>GATEKEEPER_MAX_RANK) throw ContainerError(cname,"rank of variable is > GATEKEEPR_MAX_RANK") ;
  if (ioption<0 || ioption>3) throw ContainerError(cname, "?replace(): bad option number, require 0<=ioption<=3") ;
  KeyMaster::iterator i = zool.find(name) ;
  bool copyable=false ;                             // ends up true when a copy is possible
  if (i!=zool.end()) {
    iPair&  j = i->second ;             // variable with name already exists
    varType t = j.first ; 
    size_t  k = j.second ;

    size_t       drank  = d->rank() ;   // argument rank,shape
    const Shape& dshape = d->shape() ;

    size_t rank ;                       // existing variable rank,shape
    Shape  shape(GATEKEEPER_MAX_RANK) ;

    replaceInfo(name,t,k,rank,shape) ;  // get rank and shape of existing variable

    copyable = (ioption!=0) 
      && ( ((ioption==1)&&(t==F32)) || ((ioption>=2)&&(t==F32||t==F64)) ) 
      && (rank==drank) ;
    if (copyable) {
      // check shape
      if (ioption==3) {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]>=dshape[i]) ;
      }
      else {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]==dshape[i]) ;
      } ;
    } ;

    if (copyable) {
      if (t==F32) {
        afpPtr_32 f = fgate.getPoint(k,name) ;       // existing variable
        fwrapCopy(*d,*f) ;
        patt = f->att() ;
      }
      else if (t==F64) {
        afpPtr_64 f = dgate.getPoint(k,name) ;       // existing variable
        fwrapCopy(*d,*f) ;
        patt = f->att() ;
      }
      else {
        throw ContainerError(cname, "?replace(): logic error, wrong type encountered") ;
      } ;  
      return false ;     // a copy was made
    } 
    else {
      // variable exists but can not be copied
      replaceRemove(i,name, t, k) ;
    } ;
  } ;
  if (!copyable) {
    // add to container, does not exist at this point
    patt = d->att() ;
    zool[name]=iPair(F32,fgate.add(name,d)) ;
    return true ;
  }
  throw ContainerError(cname,"replace(): logic error, reached end of replace()") ;
} ;

// ------------ Float64 ------------
fp_64 VarContainer::getScalarFloat64(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;

  const iPair&  j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F64:
    {
      const afpPtr_64 i = dgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return *(i->fptr()) ;
    } 
  case F32:
    {
      const afpPtr_32 i = fgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_64>(*(i->fptr())) ;
    } 
  case I32:
    {
      const aipPtr_32 i = igate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_64>(*(i->fptr())) ;
    } 
  default:
    throw KeyNotFound(cname,name,F64,t) ;
  } 
  throw ContainerError(cname, "variable "+name+" is not a scalar") ;
} ;

fp_64 VarContainer::getDefScalarFloat64(string aname, fp_64 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) return def ;

  const iPair&  j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F64:
    {
      const afpPtr_64 i = dgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return *(i->fptr()) ;
    } 
  case F32:
    {
      const afpPtr_32 i = fgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_64>(*(i->fptr())) ;
    } 
  case I32:
    {
      const aipPtr_32 i = igate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      return static_cast<fp_64>(*(i->fptr())) ;
    } 
  default:
    throw KeyNotFound(cname,name,F64,t) ;
  } 
  throw ContainerError(cname, "variable "+name+" is not a scalar") ;
} ;

void VarContainer::set(string aname, fp_64 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;

  iPair&  j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F64:
    {
      afpPtr_64 i = dgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      *(i->fptr())=d ;
      return ;
    } 
  case F32:
    {
      afpPtr_32 i = fgate.getPoint(k,name) ;
      if (i->rank()!=0) break ;
      *(i->fptr())=static_cast<fp_32>(d) ;
      return ;
    } 
  case I32:
    {
      throw KeyNotFound(cname,name,F64,t) ;
    } 
  default:
    throw KeyNotFound(cname,name,1) ;
  } 
  throw ContainerError(cname, "variable "+name+" is not a scalar") ;
} ;

void VarContainer::add(string aname, fp_64 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  afpPtr_64 i = afpPtr_64::build() ;  // build a new scalar
  patt = i->att() ;
  *(i->fptr()) = d ;
  zool[name]=iPair(F64,dgate.add(name,i)) ;
} ;

afpPtr_64 VarContainer::getFloat64(string aname, afpPtr_64 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,1) ;
    return def ;
  }
  if (ii->second.first!=F64) throw KeyNotFound(cname,name,F64,ii->second.first) ;
  return dgate.getPoint(ii->second.second) ;
} ;

void VarContainer::get(string aname, afp_64& out, afpPtr_64 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,1) ; 
    fwrapCopy(*def,out) ;
    return ;
  } ;

  const iPair&  j=ii->second ;
  varType t=j.first ; 
  size_t  k=j.second ;

  switch(t) {
  case F64:
    {
      fwrapCopy(*(dgate.getPoint(k,name)),out) ;
      break ;
    }
  case F32:
    {
      fwrapCopy(*(fgate.getPoint(k,name)),out) ;
      break ;
    }
  case I32:
    {
      fwrapCopy(*(igate.getPoint(k,name)),out) ;
      break ;
    } ;
  default:
    KeyNotFound(cname,name,1) ;
  } ;
} ;

void VarContainer::get(string name, afpPtr_64 out, afpPtr_64 def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  get(name,*out,def) ;
} ;

void VarContainer::set(string aname, const afp_64& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator ii = zool.find(name) ;
  if (ii==zool.end()) throw KeyNotFound(cname,name,1) ;
  iPair&  j = ii->second ;
  varType t = j.first ; 
  size_t  k = j.second ;
  switch (t) {
  case F64:
    fwrapCopy(d,*(dgate.getPoint(k,name))) ; break ;
  case F32:
    fwrapCopy(d,*(fgate.getPoint(k,name))) ; break ;
  default:
    throw KeyNotFound(cname,name,F64,t) ;
  } ;
} ;

void VarContainer::set(string name, const afpPtr_64 d) {
  patt=0 ;
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  set(name,*d) ;
} ;

void VarContainer::add(string aname, afpPtr_64 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  if (d.isnull()) throw ContainerError(cname, "attempt to add a null pointer to the container") ;
  patt = d->att() ;
  zool[name]=iPair(F64,dgate.add(name,d)) ;
} ;

bool VarContainer::replace(string aname, int ioption, afpPtr_64 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (d.isnull()) throw ContainerError(cname, "attempted a replace() with a null pointer") ;
  if (d->rank()>GATEKEEPER_MAX_RANK) throw ContainerError(cname,"rank of variable is > GATEKEEPR_MAX_RANK") ;
  if (ioption<0 || ioption>3) throw ContainerError(cname, "?replace(): bad option number, require 0<=ioption<=3") ;
  KeyMaster::iterator i = zool.find(name) ;
  bool copyable=false ;                             // ends up true when a copy is possible
  if (i!=zool.end()) {
    iPair&  j = i->second ;             // variable with name already exists
    varType t = j.first ; 
    size_t  k = j.second ;

    size_t       drank  = d->rank() ;   // argument rank,shape
    const Shape& dshape = d->shape() ;

    size_t rank ;                       // existing variable rank,shape
    Shape  shape(GATEKEEPER_MAX_RANK) ;

    replaceInfo(name,t,k,rank,shape) ;  // get rank and shape of existing variable

    copyable = (ioption!=0) 
      && ( ((ioption==1)&&(t==F64)) || ((ioption>=2)&&(t==F32||t==F64)) ) 
      && (rank==drank) ;
    if (copyable) {
      // check shape
      if (ioption==3) {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]>=dshape[i]) ;
      }
      else {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]==dshape[i]) ;
      } ;
    } ;

    if (copyable) {
      if (t==F32) {
        afpPtr_32 f = fgate.getPoint(k,name) ;       // existing variable
        fwrapCopy(*d,*f) ;
        patt = f->att() ;
      }
      else if (t==F64) {
        afpPtr_64 f = dgate.getPoint(k,name) ;       // existing variable
        fwrapCopy(*d,*f) ;
        patt = f->att() ;
      }
      else {
        throw ContainerError(cname, "?replace(): logic error, wrong type encountered") ;
      } ;  
      return false ;     // a copy was made
    } 
    else {
      // variable exists but can not be copied
      replaceRemove(i,name, t, k) ;
    } ;
  } ;
  if (!copyable) {
    // add to container, does not exist at this point
    patt = d->att() ;
    zool[name]=iPair(F64,dgate.add(name,d)) ;
    return true ;
  }
  throw ContainerError(cname,"replace(): logic error, reached end of replace()") ;
} ;

// ------------ Logical ------------
lp_32 VarContainer::getScalarLogical32(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=L32) throw KeyNotFound(cname,name,-1) ;
  const alpPtr_32 i = lgate.getPoint(ii->second.second) ;
  if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
  return *(i->fptr()) ;
} ;

lp_32 VarContainer::getDefScalarLogical32(string aname, lp_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    return def ;
  }
  else {
    if (ii->second.first!=L32) throw KeyNotFound(cname,name,L32,ii->second.first) ;
    const alpPtr_32 i = lgate.getPoint(ii->second.second) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    return *(i->fptr()) ;
  } ;
} ;

void  VarContainer::setLogical(string aname, lp_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()&&i1->second.first==L32) {
    alpPtr_32 i = lgate.getPoint(i1->second.second,name) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    *(i->fptr()) = (d!=0) ? logical_true : logical_false ;
    return ;
  }
  throw KeyNotFound(cname,name,-1) ;
} ;

void  VarContainer::addLogical(string aname, lp_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  alpPtr_32 i = alpPtr_32::build() ;  // build a new scalar
  patt = i->att() ;
  *(i->fptr()) = (d!=0) ? logical_true : logical_false ;
  zool[name]=iPair(L32,lgate.add(name,i)) ;
} ;


bool VarContainer::getScalarLogicalBool(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=L32) throw KeyNotFound(cname,name,-1) ;
  const alpPtr_32 i = lgate.getPoint(ii->second.second,name) ;
  if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
  return static_cast<bool>(*(i->fptr())) ;
} ;

bool VarContainer::getDefScalarLogicalBool(string aname, bool def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    return def ;
  }
  else {
    if (ii->second.first!=L32) throw KeyNotFound(cname,name,L32,ii->second.first) ;
    const alpPtr_32 i = lgate.getPoint(ii->second.second,name) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    return static_cast<bool>(*(i->fptr())) ;
  } ;
} ;

void  VarContainer::setLogical(string aname, bool d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()&&i1->second.first==L32) {
    alpPtr_32 i = lgate.getPoint(i1->second.second,name) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    *(i->fptr()) = d ? logical_true : logical_false ;
    return ;
  }
  throw KeyNotFound(cname,name,-1) ;
} ;

void  VarContainer::addLogical(string aname, bool d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  alpPtr_32 i = alpPtr_32::build() ;  // build a new scalar
  patt = i->att() ;
  *(i->fptr()) = d ? logical_true : logical_false ;
  zool[name]=iPair(L32,lgate.add(name,i)) ;
} ;


alpPtr_32 VarContainer::getLogical32(string aname, const alpPtr_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-1) ;
    return def ;
  }
  if (ii->second.first!=L32) throw KeyNotFound(cname,name,L32,ii->second.first) ;
  return lgate.getPoint(ii->second.second) ;
} ;

void VarContainer::getLogical(string aname, alp_32& out, alpPtr_32 def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-1) ; 
    fwrapCopy(*def,out) ;
  }
  else {
    if (ii->second.first!=L32) throw KeyNotFound(cname,name,L32,ii->second.first) ;
    fwrapCopy(*(lgate.getPoint(ii->second.second,name)),out) ;
  } ;
} ;

void VarContainer::getLogical(string name, alpPtr_32 out, alpPtr_32 def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  getLogical(name,*out,def) ;
} ;

void VarContainer::setLogical(string aname, const alp_32& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()) {
    if (i1->second.first!=L32) throw KeyNotFound(cname,name,L32,i1->second.first) ;
    fwrapCopy_bool(d,*(lgate.getPoint(i1->second.second)),logical_true,logical_false) ;
    return ;
  }
  throw KeyNotFound(cname,name,-1) ;
} ;

void VarContainer::setLogical(string name, const alpPtr_32 d) {
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  setLogical(name,*d) ;
} ;

void VarContainer::addLogical(string aname, alpPtr_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  if (d.isnull()) throw ContainerError(cname, "attempt to add a null pointer to the container") ;
  patt = d->att() ;
  zool[name]=iPair(L32,lgate.add(name,d)) ;
} ;

void VarContainer::getLogical(string aname, alp_bool& out, alpPtr_bool def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-1) ; 
    fwrapCopy(*def,out) ;
  }
  else {
    if (ii->second.first!=L32) throw KeyNotFound(cname,name,L32,ii->second.first) ;
    fwrapCopy(*(lgate.getPoint(ii->second.second,name)),out) ;  // usese static_cast<bool>
  } ;
} ;

void VarContainer::getLogical(string name, alpPtr_bool out, alpPtr_bool def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  getLogical(name,*out,def) ;
} ;

void VarContainer::setLogical(string aname, const alp_bool& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()) {
    if (i1->second.first!=L32) throw KeyNotFound(cname,name,L32,i1->second.first) ;
    fwrapCopy_bool(d,*(lgate.getPoint(i1->second.second,name)),logical_true,logical_false) ;
    return ;
  }
  throw KeyNotFound(cname,name,-1) ;
} ;

void VarContainer::setLogical(string name, const alpPtr_bool d) {
  patt=0 ;
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  setLogical(name,*d) ;
} ;

bool VarContainer::replaceLogical(string aname, int ioption, alpPtr_32 d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (d.isnull()) throw ContainerError(cname, "attempted a replaceLogical() with a null pointer") ;
  if (d->rank()>GATEKEEPER_MAX_RANK) throw ContainerError(cname,"rank of variable is > GATEKEEPR_MAX_RANK") ;
  if (ioption<0 || ioption>3) throw ContainerError(cname, "?replaceLogical(): bad option number, require 0<=ioption<=3") ;
  KeyMaster::iterator i = zool.find(name) ;
  bool copyable=false ;                             // ends up true when a copy is possible
  if (i!=zool.end()) {
    iPair&  j = i->second ;             // variable with name already exists
    varType t = j.first ; 
    size_t  k = j.second ;

    size_t       drank  = d->rank() ;   // argument rank,shape
    const Shape& dshape = d->shape() ;

    size_t rank ;                       // existing variable rank,shape
    Shape  shape(GATEKEEPER_MAX_RANK) ;

    replaceInfo(name,t,k,rank,shape) ;  // get rank and shape of existing variable

    copyable = (ioption!=0) 
      && ( ((ioption==1)&&(t==L32)) || ((ioption>=2)&&(t==L32)) ) 
      && (rank==drank) ;
    if (copyable) {
      // check shape
      if (ioption==3) {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]>=dshape[i]) ;
      }
      else {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]==dshape[i]) ;
      } ;
    } ;

    if (copyable) {
      if (t==L32) {
        alpPtr_32 f = lgate.getPoint(k,name) ;       // existing variable
        fwrapCopy(*d,*f) ;
        patt = f->att() ;
      }
      else {
        throw ContainerError(cname, "?replaceLogical(): logic error, wrong type encountered") ;
      } ;  
      return false ;     // a copy was made
    } 
    else {
      // variable exists but can not be copied
      replaceRemove(i,name, t, k) ;
    } ;
  } ;
  if (!copyable) {
    // add to container, does not exist at this point
    patt = d->att() ;
    zool[name]=iPair(L32,lgate.add(name,d)) ;
    return true ;
  }
  throw ContainerError(cname,"replaceLogical(): logic error, reached end of replaceLogical()") ;
} ;

bool VarContainer::replaceLogical(string name, int ioption, alpPtr_bool d) {
  const Shape& dshape = d->shape() ;
  alpPtr_32 dd = alpPtr_32::build(dshape) ;
  fwrapCopy_bool(*d, *dd,logical_true,logical_false) ;
  const fwrap_map_att* datt = d->att() ;
  dd->att()->operator=(*datt) ;
  return replaceLogical(name, ioption, dd) ;
}

// ------------ character ------------
cp_char VarContainer::getScalarChar(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=CHAR) throw KeyNotFound(cname,name,-2) ;
  const acpPtr_char i = cgate.getPoint(ii->second.second) ;
  if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
  return *(i->fptr()) ;
} ;

cp_char VarContainer::getDefScalarChar(string aname, cp_char def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    return def ;
  }
  else {
    if (ii->second.first!=CHAR) throw KeyNotFound(cname,name,CHAR,ii->second.first) ;
    const acpPtr_char i = cgate.getPoint(ii->second.second) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    return *(i->fptr()) ;
  } ;
} ;

void  VarContainer::set(string aname, cp_char d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()&&i1->second.first==CHAR) {
    acpPtr_char i = cgate.getPoint(i1->second.second,name) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    *(i->fptr()) = d ;
    return ;
  }
  throw KeyNotFound(cname,name,-2) ;
} ;

void  VarContainer::add(string aname, cp_char d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  acpPtr_char i = acpPtr_char::build() ;  // build a new scalar
  patt = i->att() ;
  *(i->fptr()) = d ;
  zool[name]=iPair(CHAR,cgate.add(name,i)) ;
} ;


string VarContainer::getScalarString(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=CHAR) throw KeyNotFound(cname,name,-2) ;
  const acpPtr_char i = cgate.getPoint(ii->second.second,name) ;
  if (i->rank()!=0 && i->rank()!=1) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
  acp_string s = acp_string() ;      // create a new wrapped scalar string
  fwrapCharCopy((*i),s,s.shape()) ;
  return s() ;                       // return a copy of the string in s
} ;

string VarContainer::getDefScalarString(string aname, string def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    return def ;
  }
  else {
    if (ii->second.first!=CHAR) throw KeyNotFound(cname,name,CHAR,ii->second.first) ;
    const acpPtr_char i = cgate.getPoint(ii->second.second,name) ;
    if (i->rank()!=0 && i->rank()!=1) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    acp_string s = acp_string() ;
    fwrapCharCopy((*i),s,s.shape()) ;  // s.shape() has length 0
    return s() ;                       // return a copy of the string in s
  } ;
} ;

void  VarContainer::set(string aname, string d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()&&i1->second.first==CHAR) {
    acpPtr_char i = cgate.getPoint(i1->second.second,name) ;
    if (i->rank()!=0 && i->rank()!=1) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    acp_string s = acp_string() ;
    s()=d ;
    fwrapCharCopy(s,(*i),s.shape()) ;
    return ;
  }
  throw KeyNotFound(cname,name,-2) ;
} ;

void  VarContainer::add(string aname, string d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  Shape c(1) ;
  c[0] = d.size() ;
  acpPtr_char i = acpPtr_char::build(c) ;  // build a new one dimensional character array
  acp_string  s = acp_string() ;
  s()=d ;
  fwrapCharCopy(s,(*i),s.shape()) ;
  patt = i->att() ;
  zool[name]=iPair(CHAR,cgate.add(name,i)) ;
} ;


acpPtr_char VarContainer::getChar(string aname, acpPtr_char def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-2) ;
    return def ;
  }
  if (ii->second.first!=CHAR) throw KeyNotFound(cname,name,CHAR,ii->second.first) ;
  return cgate.getPoint(ii->second.second) ;
} ;

void VarContainer::get(string aname, acp_char& out, acpPtr_char def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-2) ; 
    fwrapCopy_char(*def,out) ;
  }
  else {
    if (ii->second.first!=CHAR) throw KeyNotFound(cname,name,CHAR,ii->second.first) ;
    fwrapCopy_char(*(cgate.getPoint(ii->second.second,name)),out) ;
  } ;
} ;

void VarContainer::get(string name, acpPtr_char out, acpPtr_char def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  get(name,*out,def) ;
} ;

void VarContainer::set(string aname, const acp_char& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()) {
    if (i1->second.first!=CHAR) throw KeyNotFound(cname,name,CHAR,i1->second.first) ;
    fwrapCopy_char(d,*(cgate.getPoint(i1->second.second))) ;
    return ;
  }
  throw KeyNotFound(cname,name,-2) ;
} ;

void VarContainer::set(string name, const acpPtr_char d) {
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  set(name,*d) ;
} ;

void VarContainer::add(string aname, acpPtr_char d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  if (d.isnull()) throw ContainerError(cname, "attempt to add a null pointer to the container") ;
  patt = d->att() ;
  zool[name]=iPair(CHAR,cgate.add(name,d)) ;
} ;

void VarContainer::get(string aname, acp_string& out, acpPtr_string def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-2) ; 
    fwrapCopy_char(*def,out) ;
  }
  else {
    if (ii->second.first!=CHAR) throw KeyNotFound(cname,name,CHAR,ii->second.first) ;
    fwrapCopy_char(*(cgate.getPoint(ii->second.second,name)),out) ; 
  } ;
} ;

void VarContainer::get(string name, acpPtr_string out, acpPtr_string def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  get(name,*out,def) ;
} ;

void VarContainer::set(string aname, const acp_string& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()) {
    if (i1->second.first!=CHAR) throw KeyNotFound(cname,name,CHAR,i1->second.first) ;
    fwrapCopy_char(d,*(cgate.getPoint(i1->second.second,name))) ;
    return ;
  }
  throw KeyNotFound(cname,name,-2) ;
} ;

void VarContainer::set(string name, const acpPtr_string d) {
  patt=0 ;
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  set(name,*d) ;
} ;

bool VarContainer::replace(string aname, int ioption, acpPtr_char d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (d.isnull()) throw ContainerError(cname, "attempted a replace() with a null pointer") ;
  if (d->rank()>GATEKEEPER_MAX_RANK) throw ContainerError(cname,"rank of variable is > GATEKEEPR_MAX_RANK") ;
  if (ioption<0 || ioption>3) throw ContainerError(cname, "?replace(): bad option number, require 0<=ioption<=3") ;
  KeyMaster::iterator i = zool.find(name) ;
  bool copyable=false ;                             // ends up true when a copy is possible
  if (i!=zool.end()) {
    iPair&  j = i->second ;             // variable with name already exists
    varType t = j.first ; 
    size_t  k = j.second ;

    size_t       drank  = d->rank() ;   // argument rank,shape
    const Shape& dshape = d->shape() ;

    size_t rank ;                       // existing variable rank,shape
    Shape  shape(GATEKEEPER_MAX_RANK) ;

    replaceInfo(name,t,k,rank,shape) ;  // get rank and shape of existing variable

    copyable = (ioption!=0) 
      && ( ((ioption==1)&&(t==CHAR)) || ((ioption>=2)&&(t==CHAR)) ) 
      && (rank==drank) ;
    if (copyable) {
      // check shape
      if (ioption==3) {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]>=dshape[i]) ;
      }
      else {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]==dshape[i]) ;
      } ;
    } ;

    if (copyable) {
      if (t==CHAR) {
        acpPtr_char f = cgate.getPoint(k,name) ;       // existing variable
        fwrapCopy_char(*d,*f) ;
        patt = f->att() ;
      }
      else {
        throw ContainerError(cname, "?replace(): logic error, wrong type encountered") ;
      } ;  
      return false ;     // a copy was made
    } 
    else {
      // variable exists but can not be copied
      replaceRemove(i,name, t, k) ;
    } ;
  } ;
  if (!copyable) {
    // add to container, does not exist at this point
    patt = d->att() ;
    zool[name]=iPair(CHAR,cgate.add(name,d)) ;
    return true ;
  }
  throw ContainerError(cname,"replace(): logic error, reached end of character replace()") ;
} ;

bool VarContainer::replace(string name, int ioption, acpPtr_string d) {
  const Shape& dshape = d->shape() ;
  
  Shape ddshape(dshape.size()+1) ;
  ddshape[0] = fwrapCharMaxLength(*d) ;
  if (ddshape[0]==0) ddshape[0]=1 ;
  for (size_t i=0 ; i<dshape.size() ; i++) ddshape[i+1]=dshape[i] ;
  acpPtr_char dd = acpPtr_char::build(ddshape) ;
  
  fwrapCopy_char(*d, *dd) ;
  const fwrap_map_att* datt = d->att() ;
  dd->att()->operator=(*datt) ;
  return replace(name, ioption, dd) ;
}

// ------------ Bytes ------------
Bytes VarContainer::getScalarBytes(string aname) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=BYTES) throw KeyNotFound(cname,name,-3) ;
  const abpPtr_bytes i = bgate.getPoint(ii->second.second) ;
  if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
  return *(i->fptr()) ;
} ;

Bytes VarContainer::getDefScalarBytes(string aname, Bytes def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    return def ;
  }
  else {
    if (ii->second.first!=BYTES) throw KeyNotFound(cname,name,BYTES,ii->second.first) ;
    const abpPtr_bytes i = bgate.getPoint(ii->second.second) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    return *(i->fptr()) ;
  } ;
} ;

void  VarContainer::set(string aname, Bytes d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()&&i1->second.first==BYTES) {
    abpPtr_bytes i = bgate.getPoint(i1->second.second,name) ;
    if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
    *(i->fptr()) = d ;
    return ;
  }
  throw KeyNotFound(cname,name,-3) ;
} ;

void  VarContainer::add(string aname, Bytes d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  abpPtr_bytes i = abpPtr_bytes::build() ;  // build a new scalar
  patt = i->att() ;
  *(i->fptr()) = d ;
  zool[name]=iPair(BYTES,bgate.add(name,i)) ;
} ;

void  VarContainer::getScalarBytesToFile(string aname, string filename) {
  string fname = fwrap_att::despace0(filename) ;

  struct stat buffer ;
  bool exist = stat(fname.c_str(), &buffer)==0 ;
  if (exist) {
    if ((buffer.st_mode & S_IFMT) != S_IFREG) {
      throw ContainerError(cname,"file "+fname+" is not a regular file -- can not delete") ;
    }
    unlink(fname.c_str()) ;               // remove existing file
  }

  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=BYTES) throw KeyNotFound(cname,name,-3) ;

  const abpPtr_bytes i = bgate.getPoint(ii->second.second) ;
  if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
  
  // now do stuff
  ofstream fs(fname.c_str(), ios_base::binary) ;
  
  const Bytes& v = *(i->fptr()) ;  // vector<Byte>
  size_t n = v.size() ;
  for (size_t k=0 ; k<n ; k++) fs.put(v[k]) ;
  
  fs.close() ;
}

void  VarContainer::setScalarBytesFromFile(string aname, string filename) {
  patt=0 ;

  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=BYTES) throw KeyNotFound(cname,name,-3) ;

  abpPtr_bytes i = bgate.getPoint(ii->second.second,name) ;
  if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;

  string fname = fwrap_att::despace0(filename) ;

  struct stat buffer ;
  bool exist = stat(fname.c_str(), &buffer)==0 ;
  if ((!exist)||(buffer.st_mode & S_IFMT)==S_IFDIR) {
    throw ContainerError(cname, "could not open a regular file called "+fname) ;          
  }
  
  // fetch and store
  ifstream fs(fname.c_str(), ios_base::binary) ;
  
  off_t  nexpect = buffer.st_size ;
  size_t nextra  = nexpect + 100 ;
  
  ByteStore fbuf(nextra) ;

  size_t ngot = 0 ;  
   
  while(true) {
    char c ;
    fs.get(c) ; 
    if (fs.eof()) break ;
    fbuf.need(ngot+1) ;
    *(fbuf.ptr()+ngot++) = c ;
  }
  
  fs.close() ;
  
  Bytes& d = *(i->fptr()) ;
  d.resize(ngot) ;
  for (size_t k=0 ; k<ngot ; k++) d[k]=fbuf.ptr()[k] ;
}

void  VarContainer::addScalarBytesFromFile(string aname, string filename) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;

  string fname = fwrap_att::despace0(filename) ;

  struct stat buffer ;
  bool exist = stat(fname.c_str(), &buffer)==0 ;
  if ((!exist)||(buffer.st_mode & S_IFMT)==S_IFDIR) {
    throw ContainerError(cname, "could not open a regular file called "+fname) ;          
  }
  
  ifstream fs(fname.c_str(), ios_base::binary) ;
  
  off_t  nexpect = buffer.st_size ;
  size_t nextra  = nexpect + 100 ;
  
  ByteStore fbuf(nextra) ;

  size_t ngot = 0 ;  
   
  while(true) {
    char c ;
    fs.get(c) ;
    if (fs.eof()) break ;
    fbuf.need(ngot+1) ;
    *(fbuf.ptr()+ngot++) = c ;
  }
  
  fs.close() ;
  
  abpPtr_bytes i = abpPtr_bytes::build() ;  // build a new scalar
  patt = i->att() ;
  Bytes& d = *(i->fptr()) ; 
  d.resize(ngot) ;
  for (size_t k=0 ; k<ngot ; k++) d[k]=fbuf.ptr()[k] ;
  
  zool[name]=iPair(BYTES,bgate.add(name,i)) ;
}

bool  VarContainer::replaceScalarBytesFromFile(string aname, int ioption, string filename) {
  string fname = fwrap_att::despace0(filename) ;

  struct stat buffer ;
  bool exist = stat(fname.c_str(), &buffer)==0 ;
  if ((!exist)||(buffer.st_mode & S_IFMT)==S_IFDIR) {
    throw ContainerError(cname, "could not open a regular file called "+fname) ;          
  }
  
  ifstream fs(fname.c_str(), ios_base::binary) ;
  
  off_t  nexpect = buffer.st_size ;
  size_t nextra  = nexpect + 100 ;
  
  ByteStore fbuf(nextra) ;

  size_t ngot = 0 ;  
   
  while(true) {
    char c ;
    fs.get(c) ; 
    if (fs.eof()) break ;
    fbuf.need(ngot+1) ;
    *(fbuf.ptr()+ngot++) = c ;
  }
  
  fs.close() ;

  abpPtr_bytes p = abpPtr_bytes::build() ;
  Bytes& d = *(p->fptr()) ;
  
  d.resize(ngot) ;
  for (size_t k=0 ; k<ngot ; k++) d[k]=fbuf.ptr()[k] ;

  return replace(aname, ioption, p) ;
}
  
size_t  VarContainer::sizeScalarBytes(string aname) {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()||ii->second.first!=BYTES) throw KeyNotFound(cname,name,-3) ;
  const abpPtr_bytes i = bgate.getPoint(ii->second.second) ;
  if (i->rank()!=0) throw ContainerError(cname, "variable "+name+" is not a scalar") ;
  return i->fptr()->size() ;
}

abpPtr_bytes VarContainer::getBytes(string aname, abpPtr_bytes def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-3) ;
    return def ;
  }
  if (ii->second.first!=BYTES) throw KeyNotFound(cname,name,BYTES,ii->second.first) ;
  return bgate.getPoint(ii->second.second) ;
} ;

void VarContainer::get(string aname, abp_bytes& out, abpPtr_bytes def) const {
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::const_iterator ii = zool.find(name) ;
  if (ii==zool.end()) {
    if (def.isnull()) throw KeyNotFound(cname,name,-3) ; 
    fwrapCopy(*def,out) ;
  }
  else {
    if (ii->second.first!=BYTES) throw KeyNotFound(cname,name,BYTES,ii->second.first) ;
    fwrapCopy(*(bgate.getPoint(ii->second.second,name)),out) ;
  } ;
} ;

void VarContainer::get(string name, abpPtr_bytes out, abpPtr_bytes def) const {
  if (out.isnull()) throw ContainerError(cname, "null fwrapPtr in get()"); 
  get(name,*out,def) ;
} ;

void VarContainer::set(string aname, const abp_bytes& d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  KeyMaster::iterator i1 = zool.find(name) ;
  if (i1!=zool.end()) {
    if (i1->second.first!=BYTES) throw KeyNotFound(cname,name,BYTES,i1->second.first) ;
    fwrapCopy(d,*(bgate.getPoint(i1->second.second))) ;
    return ;
  }
  throw KeyNotFound(cname,name,-3) ;
} ;

void VarContainer::set(string name, const abpPtr_bytes d) {
  if (d.isnull()) throw ContainerError(cname, "null fwrafpPtr in set()"); 
  set(name,*d) ;
} ;

void VarContainer::add(string aname, abpPtr_bytes d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (has_key(name)) throw ContainerError(cname, "variable "+name+" is already defined") ;
  if (d.isnull()) throw ContainerError(cname, "attempt to add a null pointer to the container") ;
  patt = d->att() ;
  zool[name]=iPair(BYTES,bgate.add(name,d)) ;
} ;


bool VarContainer::replace(string aname, int ioption, abpPtr_bytes d) {
  patt=0 ;
  string name = fwrap_att::despace0(aname) ;
  if (d.isnull()) throw ContainerError(cname, "attempted a replace() with a null pointer") ;
  if (d->rank()>GATEKEEPER_MAX_RANK) throw ContainerError(cname,"rank of variable is > GATEKEEPR_MAX_RANK") ;
  if (ioption<0 || ioption>3) throw ContainerError(cname, "?replace(): bad option number, require 0<=ioption<=3") ;
  KeyMaster::iterator i = zool.find(name) ;
  bool copyable=false ;                             // ends up true when a copy is possible
  if (i!=zool.end()) {
    iPair&  j = i->second ;             // variable with name already exists
    varType t = j.first ; 
    size_t  k = j.second ;

    size_t       drank  = d->rank() ;   // argument rank,shape
    const Shape& dshape = d->shape() ;

    size_t rank ;                       // existing variable rank,shape
    Shape  shape(GATEKEEPER_MAX_RANK) ;

    replaceInfo(name,t,k,rank,shape) ;  // get rank and shape of existing variable

    copyable = (ioption!=0) 
      && ( ((ioption==1)&&(t==BYTES)) || ((ioption>=2)&&(t==BYTES)) ) 
      && (rank==drank) ;
    if (copyable) {
      // check shape
      if (ioption==3) {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]>=dshape[i]) ;
      }
      else {
        for (size_t i=0 ; i<rank ; i++) copyable = copyable&&(shape[i]==dshape[i]) ;
      } ;
    } ;

    if (copyable) {
      if (t==BYTES) {
        abpPtr_bytes f = bgate.getPoint(k,name) ;       // existing variable
        fwrapCopy(*d,*f) ;
        patt = f->att() ;
      }
      else {
        throw ContainerError(cname, "?replace(): logic error, wrong type encountered") ;
      } ;  
      return false ;     // a copy was made
    } 
    else {
      // variable exists but can not be copied
      replaceRemove(i,name, t, k) ;
    } ;
  } ;
  if (!copyable) {
    // add to container, does not exist at this point
    patt = d->att() ;
    zool[name]=iPair(BYTES,bgate.add(name,d)) ;
    return true ;
  }
  throw ContainerError(cname,"replace(): logic error, reached end of Bytes replace()") ;
} ;

// ----------------- attributes --------------------
fwrap_map_att* VarContainer::att(string vname) {
  patt=0 ;
  string name = fwrap_att::despace(vname) ;

  if (name.length()==0) { 
    patt=&matt ; 
    return patt ; 
  } ;

  KeyMaster::iterator i = zool.find(name) ;
  if (i==zool.end()) return 0 ;

  iPair&  j=i->second ;
  varType t=j.first ; 
  size_t  k=j.second ;
 
  switch(t) {
  case I32:
    patt = igate.getPoint(k,name)->att() ; break ;
  case F32:
    patt = fgate.getPoint(k,name)->att() ; break ;
  case F64:
    patt = dgate.getPoint(k,name)->att() ; break ;
  case L32:
    patt = lgate.getPoint(k,name)->att() ; break ;
  case CHAR:
    patt = cgate.getPoint(k,name)->att() ; break ;
  case BYTES:
    patt = bgate.getPoint(k,name)->att() ; break ;
  default:
    throw ContainerError(cname,"?att(): logic error, unrecognized type") ;
  } ;
  return patt ;
} ;

const fwrap_map_att* VarContainer::att(string vname) const {
  patt=0 ;
  const fwrap_map_att* catt=0 ;
  string name = fwrap_att::despace(vname) ;

  if (name.length()==0) { 
    catt=&matt ; 
    patt=const_cast<fwrap_map_att*>(catt) ; 
    return catt ; 
  } ;

  KeyMaster::const_iterator i = zool.find(name) ;
  if (i==zool.end()) return 0 ;

  const iPair&  j=i->second ;
  varType t=j.first ; 
  size_t  k=j.second ;
 
  switch(t) {
  case I32:
    catt = igate.getPoint(k,name)->att() ; break ;
  case F32:
    catt = fgate.getPoint(k,name)->att() ;  break ;
  case F64:
    catt = dgate.getPoint(k,name)->att() ;  break ;
  case L32:
    catt = lgate.getPoint(k,name)->att() ;  break ;
  case CHAR:
    catt = cgate.getPoint(k,name)->att() ;  break ;
  case BYTES:
    catt = bgate.getPoint(k,name)->att() ;  break ;
  default:
    throw ContainerError(cname,"?att(): logic error, unrecognized type") ;
  } ;
  patt=const_cast<fwrap_map_att*>(catt) ; 
  return catt ; 

} ;

fwrap_map_att* VarContainer::att() { 
  patt=&matt ; 
  return patt ;
} ;

const fwrap_map_att* VarContainer::att() const { 
  patt=const_cast<fwrap_map_att*>(&matt) ; 
  return &matt ;
} ;

// -------------------- netcdf --------------------


int VarContainer::nc_get_var_ip32(int ncidp, int varid, ip_32* p) {
  return nc_get_var_int(ncidp,varid,p) ;
} ;
int VarContainer::nc_get_var_fp32(int ncidp, int varid, fp_32* p) {
  return nc_get_var_float(ncidp,varid,p) ;
} ;
int VarContainer::nc_get_var_fp64(int ncidp, int varid, fp_64* p) {
  return nc_get_var_double(ncidp,varid,p) ;
} ;
int VarContainer::nc_get_var_lp32(int ncidp, int varid, lp_32* p) {
  return nc_get_var_int(ncidp,varid,p) ;
} ;
int VarContainer::nc_get_var_char(int ncidp, int varid, cp_char* p) {
  return nc_get_var_text(ncidp,varid,p) ;
} ;

int VarContainer::nc_put_var_ip32(int ncidp, int varid, const ip_32* p) {
  return nc_put_var_int(ncidp,varid,p) ;
} ;
int VarContainer::nc_put_var_fp32(int ncidp, int varid, const fp_32* p) {
  return nc_put_var_float(ncidp,varid,p) ;
} ;
int VarContainer::nc_put_var_fp64(int ncidp, int varid, const fp_64* p) {
  return nc_put_var_double(ncidp,varid,p) ;
} ;
int VarContainer::nc_put_var_lp32(int ncidp, int varid, const lp_32* p) {
  return nc_put_var_int(ncidp,varid,p) ;
} ;
int VarContainer::nc_put_var_char(int ncidp, int varid, const cp_char* p) {
  return nc_put_var_text(ncidp,varid,p) ;
} ;

int VarContainer::nc_put_var_bytes(int ncidp, int svarid, int dvarid, size_t np, const Bytes* p) {
  // first fill the sizes array and put in netcdf file
  unsigned long long sv[np] ;  // sizes of each Bytes vector
  
  size_t tv = 0 ;  // total number of Byte(s) in all the vectors
  
  const Bytes* q = p ;
  for (size_t i=0 ; i<np ; i++) {
    size_t s = (q++)->size() ;  
    sv[i] = s ;
    tv   += s ;
  }
  
#ifdef NOHDF
  int isv[np] ;
  for (size_t i=0 ; i<np ; i++) isv[i]=static_cast<int>(sv[i]) ;
  int ier = nc_put_var_int(ncidp,svarid,isv) ;
#else
  int ier = nc_put_var_ulonglong(ncidp,svarid,sv) ;
#endif
  if (ier!=NC_NOERR) return ier ;
  
  // now fill the <name>_BYTES_ with all of the Byte data
  ByteStore bs(tv) ;    // don't use the stack as this could get big
  Byte* b = bs.ptr() ;
  
  q = p ;
  for (size_t i=0 ; i<np ; i++) {   // loop over Bytes = vector<Byte>
    const Bytes& v = *(q++) ;
    size_t       s = v.size() ; 
         
    for (size_t k=0 ; k<s ; k++) *b++ = v[k] ;   // loop over Byte(s) in each vector and move to storage
  }
  
#ifdef NOHDF
  ier = nc_put_var_uchar(ncidp,dvarid,bs.ptr()) ;
#else
  ier = nc_put_var_ubyte(ncidp,dvarid,bs.ptr()) ;
#endif  
  return ier ;
} ;

int VarContainer::nc_get_var_bytes(int ncidp, int svarid, int dvarid, size_t np, size_t nb, Bytes* p) {
  // first fill the sizes array and put in netcdf file
  unsigned long long sv[np] ;  // sizes of each Bytes vector

#ifdef NOHDF
  int isv[np] ;
  int ier = nc_get_var_int(ncidp, svarid, isv) ;
  if (ier==NC_NOERR) {
    for (size_t i=0 ; i<np ; i++) {
      sv[i]=0 ;
      sv[i]=static_cast<unsigned long long>(isv[i]) ;
    }
  }
#else
  int ier = nc_get_var_ulonglong(ncidp, svarid, sv) ;
#endif

  if (ier!=NC_NOERR) return ier ;
  
  size_t tv = 0 ;  // total number of Byte(s) in all the vectors
  
  Bytes* q = p ;
  for (size_t i=0 ; i<np ; i++) {
    size_t s = sv[i] ;  
    (q++)->resize(s) ;   // set size of vector<Byte>
    tv += s ;
  }
  
  if (tv != nb) return NC_EDIMSIZE ;  // well this was a surprise
    
  // now fill the <name>_BYTES_ with all of the Byte data
  ByteStore bs(tv) ;    // don't use the stack as this could get big
  Byte* b = bs.ptr() ;
 
#ifdef NOHDF
  ier = nc_get_var_uchar(ncidp, dvarid, b) ;
#else
  ier = nc_get_var_ubyte(ncidp, dvarid, b) ;
#endif

  if (ier!=NC_NOERR) return ier ;
   
  q = p ;
  for (size_t i=0 ; i<np ; i++) {   // loop over Bytes = vector<Byte>
    Bytes& v = *(q++) ;
    size_t s = v.size() ; 
         
    for (size_t k=0 ; k<s ; k++) v[k] = *b++  ;   // loop over Byte(s) in each vector and move from storage
  }
  
  return NC_NOERR ;
} ;

string VarContainer::dimname(int i) const {
  ostringstream s ;
  if (i<0) {
    s << "attempt to define an illegal dimension size = " << i ;
    throw ContainerError(cname, s.str()) ;
  } ;
  s << "dim_" << i ;
  return s.str() ;
} ;

const char* VarContainer::dnames[] = { ":0", ":1", ":2", ":3", ":4", ":5", ":6" } ;

void VarContainer::ncerror(int serror, string extra) const {
  if (serror != NC_NOERR) {
    string s = nc_strerror(serror) ;
    throw ContainerError(cname, s+extra) ;
  } ;
} ;

void VarContainer::ncaddatt(int ncidp, int varid, const fwrap_map_att* a, string ctype) const {
  if (a->size()==0 && ctype.length()==0) return ;
  list<string> sa = a->keys() ;  // grab all the attribute names

  IntStore    ist(4) ;   // grab some memory for transferring attribute arrays
  DoubleStore dst(4) ;
  StringStore sst(4) ;

  for (list<string>::const_iterator p = sa.begin() ; p!=sa.end() ; ++p) {  // loop over names
    const fwrap_att*   q     = a->get(*p) ;
    fwrap_att::attInfo ainfo = q->get_info() ;
    
    fwrap_att::att_type t  = ainfo.first ;       // attribute type
    size_t              sz = ainfo.second ;      // attribute array size

    string r = fwrap_att::despace(*p) ;   // name to store in cdf file  
    if (r.length()==0) continue ;         // empty name
    if (r[0] == ':')   continue ;         // the name of a dimension ":0",":1",... or the : array
    
    switch (t){
    case fwrap_att::itype:
      {
	const fwrap_int_att* iptr = dynamic_cast<const fwrap_int_att*>(q) ;
	if (iptr==0) throw ContainerError(cname,string("unexpected bad cast of attribute to int, name = ")+r) ;
	ist.need(sz) ;
	iptr->fill_int(ist.ptr(),sz) ;
	ncerror(nc_put_att_int(ncidp,varid,r.c_str(),NC_INT,sz,ist.ptr())) ;
      }
      break ;
    case fwrap_att::dtype:
      {      
	const fwrap_double_att* dptr = dynamic_cast<const fwrap_double_att*>(q) ;
	if (dptr==0) throw ContainerError(cname,string("unexpected bad cast of attribute to double, name = ")+r) ;
	dst.need(sz) ;
	dptr->fill_double(dst.ptr(),sz) ;
	ncerror(nc_put_att_double(ncidp,varid,r.c_str(),NC_DOUBLE,sz,dst.ptr())) ;
      }
      break ;
    case fwrap_att::stype:
      {
	const fwrap_string_att* sptr = dynamic_cast<const fwrap_string_att*>(q) ;
	if (sptr==0) throw ContainerError(cname,string("unexpected bad cast of attribute to string, name = ")+r) ;
	if (sz==1) {        
	  string ss = sptr->get_string() ;    
	  ncerror(nc_put_att_text(ncidp,varid,r.c_str(),ss.length(),ss.c_str())) ;  // write scalar without terminating 0
	}
	else {
	  sst.need(sz) ;
	  sptr->fill_string(sst.ptr(),sz) ;
	  sz = sst.stringtochar(sz) ;                                      // convert to char* with string separated by 0
	  ncerror(nc_put_att_text(ncidp,varid,r.c_str(),sz,sst.cptr())) ;
	}
      }
      break ;
    } ;
  } ; 

  // add ctype
  if (ctype.length()>0) {
    const char* rr = "ctype" ;
    ncerror(nc_put_att_text(ncidp,varid,rr,ctype.length(),ctype.c_str())) ;
  } ;
} 


bool VarContainer::ncinc(const fwrap_map_att* a, const list<string>& yes, const list<string>& no) {
  if (!yes.empty()) {
    // --- make sure one of the yes attribute exists as a nonzero scalar int ---
    bool found = false ;

    for (list<string>::const_iterator p=yes.begin() ; p!=yes.end() ; ++p) {
      const fwrap_att* q = a->get(*p) ;

      if (q==0 || q->get_type()!=fwrap_att::itype || q->get_size()!=1) continue ;  // missing yes attribute as an integer scalar

      const fwrap_int_att* qi = dynamic_cast<const fwrap_int_att*>(q) ;

      if (qi!=0 && qi->get_int()!=0) {
	found=true ; break ;
      }                      
    }
    if (!found) return false ;
  }

  if (!no.empty()) {
    // --- exclude if a no attribute exists as a nonzero scalar int ---

    for (list<string>::const_iterator p=no.begin() ; p!=no.end() ; ++p) {
      const fwrap_att* q = a->get(*p) ;

      if (q==0 || q->get_type()!=fwrap_att::itype || q->get_size()!=1) continue ;  // missing no attribute as an integer scalar

      const fwrap_int_att* qi = dynamic_cast<const fwrap_int_att*>(q) ;
      if (qi!=0 && qi->get_int()!=0) return false ;                       // attribute value exists as nonzero so exclude         
    }
  }

  return true ;
}

int VarContainer::nckeep(const fwrap_map_att* a, const list<string>& keep, const list<string>& protect)  {
  if (!protect.empty()) {
    for (list<string>::const_iterator p=protect.begin() ; p!=protect.end() ; ++p) {
      const fwrap_att* q = a->get(*p) ;

      if (q==0 || q->get_type()!=fwrap_att::itype || q->get_size()!=1) continue ;  // missing keep attribute as an integer scalar

      const fwrap_int_att* qi = dynamic_cast<const fwrap_int_att*>(q) ;

      if (qi!=0 && qi->get_int()!=0) return 2 ;
    }
  }

  if (!keep.empty()) {
    for (list<string>::const_iterator p=keep.begin() ; p!=keep.end() ; ++p) {
      const fwrap_att* q = a->get(*p) ;

      if (q==0 || q->get_type()!=fwrap_att::itype || q->get_size()!=1) continue ;  // missing keep attribute as an integer scalar

      const fwrap_int_att* qi = dynamic_cast<const fwrap_int_att*>(q) ;

      if (qi!=0 && qi->get_int()!=0) return 1 ;
    }
  }
  
  return 0 ;
}


void VarContainer::ncsave(string filename, int option, list<string> yes, list<string> no) const {
  if (option<0 || option>2) throw ContainerError(cname, "bad option index") ;

  string dname = fwrap_att::despace0(filename) ;           // final netcdf file name without spaces
  int    cmode = option==1 ? NC_NOCLOBBER : NC_CLOBBER ;
  string sname = option==2 ? dname+"_temp" : dname  ;      // netcdf file name to write to create
  string sterr = string(", for file ")+filename ;

  // create
  int ncidp ;   // integer id returned by netcdf
  
#ifndef NOHDF
  cmode = cmode | NC_NETCDF4 ;
#else
  cmode = cmode | NC_64BIT_OFFSET ; 
#endif
  
  ncerror(nc_create(sname.c_str(), cmode, &ncidp), sterr) ;
  
  try {
    // dimensions
    map<string,int> dims ;  // map from dimension name to netcdf dimension index
    map<string,int> vims ;  // map from variable name to netcdf variable index
    
    bool    hk ;  // has_key from getInfo
    varType tp ;  // type from getInfo
    size_t  rk ;  // rank from getInfo
    
    Shape   sh(GATEKEEPER_MAX_RANK) ;  // shape from getInfo
    int     did[GATEKEEPER_MAX_RANK] ; // netcdf dimension ids

    sh[0] = 0 ;  // make sure defined
    
    // ----------- loop over integer variables -----------
    list<size_t> iindices = igate.indices() ;
    list<size_t> ikndices = list<size_t>() ;      // indices actually included

    for (list<size_t>::const_iterator k=iindices.begin() ; k!=iindices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = igate.getName(ik) ;      // variable name
      const aipPtr_32& p = igate.getPoint(ik) ;     // fwrap pointer
      const fwrap_map_att* a = p->att() ;           // attributes for variable
      
      if (! ncinc(a,yes,no)) continue ;    // attributes exclude this variable
      if (p->size()==0)      continue ;    // netcdf can not handle a dimension with zero size
      ikndices.push_back(ik) ;

      size_t n = p->rank() ;               // variable rank
      if (n>GATEKEEPER_MAX_RANK) {
        throw ContainerError(cname, "can not handle rank>GATEKEEPR_MAX_RANK in ncsave()") ;
      }
      
      const fwrap_string_att* cc = dynamic_cast<const fwrap_string_att*>(a->get(":")) ;  // look for ":" array attribute
      
      // loop over dimensions in Fortran ordering
      for (size_t i=0 ; i<n ; i++) {
        size_t ic=(n-1)-i ;           // C dimension index
        size_t d = p->size(i) ;       // size of this dimension
        
        // get dimension name
        string dn = dimname(d) ;                                                                        // name of this dimension for netcdf file 
        string qn = (cc!=0 && cc->get_size()>i) ? fwrap_att::despace(cc->getx_string(i)) : string("") ; // dimension name from attribute
        
        if (qn.length()<=0) {
            const fwrap_string_att* c = dynamic_cast<const fwrap_string_att*>(a->get(dnames[i])) ; // look for attribute ":0",":1",..
            if (c!=0 && c->get_size()==1) {
                qn = fwrap_att::despace(c->get_string()) ;   // found dimension attribute, lookup name qn as a variable
            }
        }
        
        if (qn.length()>0) {
            getInfo(qn, hk, tp, rk, sh) ;
            if (hk && (rk==1) && (sh[0]==d)) dn=qn ;   // found the dimension qn defined as a variable name
        } ;
        
        // get the netcdf dimension id or create a new one
        int id = 0 ;
        map<string,int>::iterator ii = dims.find(dn) ;
        if (ii!=dims.end()) {
          id = ii->second ;
        }
        else {
          //cout << "defining dimension " << dn << endl ;
          ncerror(nc_def_dim(ncidp,dn.c_str(),d,&id), sterr) ;  // create a new dimension
          dims[dn] = id ;                                       // store id locally associated with variable name
        } ;
        did[ic] = id ;
      } ;
      
      // define variable
      int iv = 0  ;                   // holds netcdf variable id
      //cout << "defining variable " << s << endl ;
      ncerror(nc_def_var(ncidp,s.c_str(),NC_INT,n,did,&iv), sterr) ;
      vims[s] = iv ;                  // store variable id associated with variable name
      
      // attributes
      ncaddatt(ncidp, iv, a, "I32") ;
    } ;
    
    // -----------loop over float variables -----------
    list<size_t> findices = fgate.indices() ;
    list<size_t> fkndices = list<size_t>() ;      // indices actually included

    for (list<size_t>::const_iterator k=findices.begin() ; k!=findices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = fgate.getName(ik) ;      // variable name
      const afpPtr_32& p = fgate.getPoint(ik) ;     // fwrap pointer
      const fwrap_map_att* a = p->att() ;  // attributes for variable
      
      if (! ncinc(a,yes,no)) continue ;    // attributes exclude this variable
      if (p->size()==0)      continue ;    // netcdf can not handle a dimension with zero size
      fkndices.push_back(ik) ;

      size_t n = p->rank() ;               // variable rank
      if (n>GATEKEEPER_MAX_RANK) {
        throw ContainerError(cname, "can not handle rank>GATEKEEPR_MAX_RANK in ncsave()") ;
      }
      const fwrap_string_att* cc = dynamic_cast<const fwrap_string_att*>(a->get(":")) ;  // look for ":" array attribute
      
      // loop over dimensions
      for (size_t i=0 ; i<n ; i++) {
        size_t ic=(n-1)-i ;           // C dimension index
        size_t d = p->size(i) ;       // size of this dimension
        
        // get dimension name
        string dn = dimname(d) ;                                                                        // name of this dimension for netcdf file 
        string qn = (cc!=0 && cc->get_size()>i) ? fwrap_att::despace(cc->getx_string(i)) : string("") ; // dimension name from attribute
        
        if (qn.length()<=0) {
            const fwrap_string_att* c = dynamic_cast<const fwrap_string_att*>(a->get(dnames[i])) ; // look for attribute ":0",":1",..
            if (c!=0 && c->get_size()==1) {
                qn = fwrap_att::despace(c->get_string()) ;   // found dimension attribute, lookup name qn as a variable
            }
        }
        
        if (qn.length()>0) {
            getInfo(qn, hk, tp, rk, sh) ;
            if (hk && (rk==1) && (sh[0]==d)) dn=qn ;   // found the dimension qn defined as a variable name
        } ;
        
        // get the netcdf dimension id or create a new one
        int id = 0 ;
        map<string,int>::iterator ii = dims.find(dn) ;
        if (ii!=dims.end()) {
          id = ii->second ;
        }
        else {
          //cout << "defining dimension " << dn << endl ;
          ncerror(nc_def_dim(ncidp,dn.c_str(),d,&id), sterr) ;  // create a new dimension
          dims[dn] = id ;                                       // store id locally associated with variable name
        } ;
        did[ic] = id ;
      } ;
      
      // define variable
      int iv = 0  ;                   // holds netcdf variable id
      //cout << "defining variable " << s << endl ;
      ncerror(nc_def_var(ncidp,s.c_str(),NC_FLOAT,n,did,&iv), sterr) ;
      vims[s] = iv ;                  // store variable id associated with variable name
      
      // attributes
      ncaddatt(ncidp, iv, a, "F32") ;
    } ;
    
    // ----------- loop over double variables -----------
    list<size_t> dindices = dgate.indices() ;
    list<size_t> dkndices = list<size_t>() ;      // indices actually included

    for (list<size_t>::const_iterator k=dindices.begin() ; k!=dindices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = dgate.getName(ik) ;      // variable name
      const afpPtr_64& p = dgate.getPoint(ik) ;     // fwrap pointer
      const fwrap_map_att* a = p->att() ;  // attributes for variable
      
      if (! ncinc(a,yes,no)) continue ;    // attributes exclude this variable
      if (p->size()==0)      continue ;    // netcdf can not handle a dimension with zero size
      dkndices.push_back(ik) ;

      size_t n = p->rank() ;               // variable rank
      if (n>GATEKEEPER_MAX_RANK) {
        throw ContainerError(cname, "can not handle rank>GATEKEEPR_MAX_RANK in ncsave()") ;
      }
      
      const fwrap_string_att* cc = dynamic_cast<const fwrap_string_att*>(a->get(":")) ;  // look for ":" array attribute
      
      // loop over dimensions
      for (size_t i=0 ; i<n ; i++) {
        size_t ic=(n-1)-i ;           // C dimension index
        size_t d = p->size(i) ;       // size of this dimension
        
        // get dimension name
        string dn = dimname(d) ;                                                                        // name of this dimension for netcdf file 
        string qn = (cc!=0 && cc->get_size()>i) ? fwrap_att::despace(cc->getx_string(i)) : string("") ; // dimension name from attribute
        
        if (qn.length()<=0) {
            const fwrap_string_att* c = dynamic_cast<const fwrap_string_att*>(a->get(dnames[i])) ; // look for attribute ":0",":1",..
            if (c!=0 && c->get_size()==1) {
                qn = fwrap_att::despace(c->get_string()) ;   // found dimension attribute, lookup name qn as a variable
            }
        }
        
        if (qn.length()>0) {
            getInfo(qn, hk, tp, rk, sh) ;
            if (hk && (rk==1) && (sh[0]==d)) dn=qn ;   // found the dimension qn defined as a variable name
        } ;
         
        // get the netcdf dimension id or create a new one
        int id = 0 ;
        map<string,int>::iterator ii = dims.find(dn) ;
        if (ii!=dims.end()) {
          id = ii->second ;
        }
        else {
          //cout << "defining dimension " << dn << endl ;
          ncerror(nc_def_dim(ncidp,dn.c_str(),d,&id), sterr) ;  // create a new dimension
          dims[dn] = id ;                                       // store id locally associated with variable name
        } ;
        did[ic] = id ;
      } ;
      
      // define variable
      int iv = 0  ;                   // holds netcdf variable id
      //cout << "defining variable " << s << endl ;
      ncerror(nc_def_var(ncidp,s.c_str(),NC_DOUBLE,n,did,&iv), sterr) ;
      vims[s] = iv ;                  // store variable id associated with variable name
      
      // attributes
      ncaddatt(ncidp, iv, a, "F64") ;
    } ;
    
    // ----------- loop over logical variables -----------
    list<size_t> lindices = lgate.indices() ;
    list<size_t> lkndices = list<size_t>() ;      // indices actually included

    for (list<size_t>::const_iterator k=lindices.begin() ; k!=lindices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = lgate.getName(ik) ;      // variable name
      const alpPtr_32& p = lgate.getPoint(ik) ;     // fwrap pointer
      const fwrap_map_att* a = p->att() ;           // attributes for variable
      
      if (! ncinc(a,yes,no)) continue ;    // attributes exclude this variable
      if (p->size()==0)      continue ;    // netcdf can not handle a dimension with zero size
      lkndices.push_back(ik) ;

      size_t n = p->rank() ;               // variable rank
      if (n>GATEKEEPER_MAX_RANK) {
        throw ContainerError(cname, "can not handle rank>GATEKEEPR_MAX_RANK in ncsave()") ;
      }
      
      const fwrap_string_att* cc = dynamic_cast<const fwrap_string_att*>(a->get(":")) ;  // look for ":" array attribute
      
      // loop over dimensions
      for (size_t i=0 ; i<n ; i++) {
        size_t ic=(n-1)-i ;           // C dimension index
        size_t d = p->size(i) ;       // size of this dimension
        
        // get dimension name
        string dn = dimname(d) ;                                                                        // name of this dimension for netcdf file 
        string qn = (cc!=0 && cc->get_size()>i) ? fwrap_att::despace(cc->getx_string(i)) : string("") ; // dimension name from attribute
        
        if (qn.length()<=0) {
            const fwrap_string_att* c = dynamic_cast<const fwrap_string_att*>(a->get(dnames[i])) ; // look for attribute ":0",":1",..
            if (c!=0 && c->get_size()==1) {
                qn = fwrap_att::despace(c->get_string()) ;   // found dimension attribute, lookup name qn as a variable
            }
        }
                
        if (qn.length()>0) {
            getInfo(qn, hk, tp, rk, sh) ;
            if (hk && (rk==1) && (sh[0]==d)) dn=qn ;   // found the dimension qn defined as a variable name
        } ;
        
        // get the netcdf dimension id or create a new one
        int id = 0 ;
        map<string,int>::iterator ii = dims.find(dn) ;
        if (ii!=dims.end()) {
          id = ii->second ;
        }
        else {
          //cout << "defining dimension " << dn << endl ;
          ncerror(nc_def_dim(ncidp,dn.c_str(),d,&id), sterr) ;  // create a new dimension
          dims[dn] = id ;                                       // store id locally associated with variable name
        } ;
        did[ic] = id ;
      } ;
      
      // define variable
      int iv = 0  ;                   // holds netcdf variable id
      //cout << "defining variable " << s << endl ;
      ncerror(nc_def_var(ncidp,s.c_str(),NC_INT,n,did,&iv), sterr) ;
      vims[s] = iv ;                  // store variable id associated with variable name
      
      // attributes
      ncaddatt(ncidp, iv, a, "L32") ;
    } ;
    
    // ----------- loop over character variables -----------
    list<size_t> cindices = cgate.indices() ;
    list<size_t> ckndices = list<size_t>() ;      // indices actually included

    for (list<size_t>::const_iterator k=cindices.begin() ; k!=cindices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = cgate.getName(ik) ;      // variable name
      const acpPtr_char&   p = cgate.getPoint(ik) ; // fwrap pointer
      const fwrap_map_att* a = p->att() ;           // attributes for variable
      
      if (! ncinc(a,yes,no)) continue ;    // attributes exclude this variable
      if (p->size()==0)      continue ;    // netcdf can not handle a dimension with zero size
      ckndices.push_back(ik) ;

      size_t n = p->rank() ;               // variable rank
      if (n>GATEKEEPER_MAX_RANK) {
        throw ContainerError(cname, "can not handle rank>GATEKEEPR_MAX_RANK in ncsave()") ;
      }
      
      const fwrap_string_att* cc = dynamic_cast<const fwrap_string_att*>(a->get(":")) ;  // look for ":" array attribute
      
      // loop over dimensions
      for (size_t i=0 ; i<n ; i++) {
        size_t ic=(n-1)-i ;           // C dimension index
        size_t d = p->size(i) ;       // size of this dimension
        
        // get dimension name
        string dn = dimname(d) ;                                                                        // name of this dimension for netcdf file 
        string qn = (cc!=0 && cc->get_size()>i) ? fwrap_att::despace(cc->getx_string(i)) : string("") ; // dimension name from attribute
        
        if (qn.length()<=0) {
            const fwrap_string_att* c = dynamic_cast<const fwrap_string_att*>(a->get(dnames[i])) ; // look for attribute ":0",":1",..
            if (c!=0 && c->get_size()==1) {
                qn = fwrap_att::despace(c->get_string()) ;   // found dimension attribute, lookup name qn as a variable
            }
        }
                
        if (qn.length()>0) {
            getInfo(qn, hk, tp, rk, sh) ;
            if (hk && (rk==1) && (sh[0]==d)) dn=qn ;   // found the dimension qn defined as a variable name
        } ;
        
        // get the netcdf dimension id or create a new one
        int id = 0 ;
        map<string,int>::iterator ii = dims.find(dn) ;
        if (ii!=dims.end()) {
          id = ii->second ;
        }
        else {
          //cout << "defining dimension " << dn << endl ;
          ncerror(nc_def_dim(ncidp,dn.c_str(),d,&id), sterr) ;  // create a new dimension
          dims[dn] = id ;                                       // store id locally associated with variable name
        } ;
        did[ic] = id ;
      } ;
      
      // define variable
      int iv = 0  ;                   // holds netcdf variable id
      //cout << "defining variable " << s << endl ;
      ncerror(nc_def_var(ncidp,s.c_str(),NC_CHAR,n,did,&iv), sterr) ;
      vims[s] = iv ;                  // store variable id associated with variable name
      
      // attributes
      ncaddatt(ncidp, iv, a, "Char") ;
    } ;
    
    // ----------- loop over Bytes variables -----------
    // these are different since each value is a byte array with a variable size.
    // So the 64 bit sizes of the variables will be stored in the netcdf file under the
    // variable name <name> and an additional one dimensional variable called <name>_BYTES_
    // will contain the concatenation of all the byte data.
    //
    list<size_t> bindices = bgate.indices() ;
    list<size_t> bkndices = list<size_t>() ;      // indices actually included

    for (list<size_t>::const_iterator k=bindices.begin() ; k!=bindices.end() ; ++k) {
      size_t ik = *k ;
      string s  = bgate.getName(ik) ;      // variable name
      
      const abpPtr_bytes&  p = bgate.getPoint(ik) ; // fwrap pointer
      const fwrap_map_att* a = p->att() ;           // attributes for variable
      
      if (! ncinc(a,yes,no)) continue ;    // attributes exclude this variable
      if (p->size()==0)      continue ;    // netcdf can not handle a dimension with zero size
      bkndices.push_back(ik) ;

      size_t n = p->rank() ;               // variable rank
      if (n>GATEKEEPER_MAX_RANK) {
        throw ContainerError(cname, "can not handle rank>GATEKEEPR_MAX_RANK in ncsave()") ;
      }
      
      const fwrap_string_att* cc = dynamic_cast<const fwrap_string_att*>(a->get(":")) ;  // look for ":" array attribute
      
      // loop over dimensions
      for (size_t i=0 ; i<n ; i++) {
        size_t ic=(n-1)-i ;           // C dimension index
        size_t d = p->size(i) ;       // size of this dimension
        
        // get dimension name
        string dn = dimname(d) ;                                                                        // name of this dimension for netcdf file 
        string qn = (cc!=0 && cc->get_size()>i) ? fwrap_att::despace(cc->getx_string(i)) : string("") ; // dimension name from attribute
        
        if (qn.length()<=0) {
            const fwrap_string_att* c = dynamic_cast<const fwrap_string_att*>(a->get(dnames[i])) ; // look for attribute ":0",":1",..
            if (c!=0 && c->get_size()==1) {
                qn = fwrap_att::despace(c->get_string()) ;   // found dimension attribute, lookup name qn as a variable
            }
        }
                
        if (qn.length()>0) {
            getInfo(qn, hk, tp, rk, sh) ;
            if (hk && (rk==1) && (sh[0]==d)) dn=qn ;   // found the dimension qn defined as a variable name
        } ;
        
        // get the netcdf dimension id or create a new one
        int id = 0 ;
        map<string,int>::iterator ii = dims.find(dn) ;
        if (ii!=dims.end()) {
          id = ii->second ;
        }
        else {
          //cout << "defining dimension " << dn << endl ;
          ncerror(nc_def_dim(ncidp,dn.c_str(),d,&id), sterr) ;  // create a new dimension
          dims[dn] = id ;                                       // store id locally associated with variable name
        } ;
        did[ic] = id ;
      } ;
      
      // define variable containing the sizes of each BYTES
      int iv = 0  ;                   // holds netcdf variable id
      //cout << "defining variable " << s << endl ;
#ifdef NOHDF
      ncerror(nc_def_var(ncidp,s.c_str(),NC_INT,n,did,&iv), sterr) ;
#else
      ncerror(nc_def_var(ncidp,s.c_str(),NC_UINT64,n,did,&iv), sterr) ;
#endif
      vims[s] = iv ;                  // store variable id associated with variable name
      
      // attributes
      ncaddatt(ncidp, iv, a, "Bytes") ;
      
      // define variable containing all of the BYTE data
      s = s+"_BYTES_" ;          // name of one dimensional array to hold all of the data
      
      size_t nb  = p->size() ;   // total number of Bytes arrays
      size_t tnb = 0 ;           // total number of Byte(s) in all of the arrays
      const Bytes* bp = p->fptr() ;
      //cout << s << endl ;
      for (size_t i=0 ; i<nb ; i++) {
        //cout << "i=" << i << ", size=" << bp->size() << endl ;
        tnb += (bp++)->size() ;
      }
      //cout << "total = " << tnb << endl ;
      
      string dn = dimname(tnb) ; // dim_<size> name for dimension
      
      map<string,int>::iterator ii = dims.find(dn) ;
      int id=0 ;
      if (ii!=dims.end()) {
        id = ii->second ;
      }
      else {
        //cout << "defining dimension " << dn << endl ;
        ncerror(nc_def_dim(ncidp,dn.c_str(),tnb,&id), sterr) ;  // create a new dimension
        dims[dn] = id ;                                         // store id locally associated with variable name
      } ;
      
      did[0] = id ;
      iv = 0  ;                   // holds netcdf variable id
      //cout << "defining variable " << s << endl ;
#ifdef NOHDF
      ncerror(nc_def_var(ncidp,s.c_str(),NC_BYTE,1,did,&iv), sterr) ;
#else
      ncerror(nc_def_var(ncidp,s.c_str(),NC_UBYTE,1,did,&iv), sterr) ;
#endif
      vims[s] = iv ;                  // store variable id associated with variable name      
    } ;
    
    // now the global attributes
    ncaddatt(ncidp, NC_GLOBAL, &matt, string()) ;
    
    //
    // ----------- enter data mode -----------
    // ready to put in data
    ncerror(nc_enddef(ncidp), sterr) ;
    
    // ----------- loop over integer variables -----------
    for (list<size_t>::const_iterator k=ikndices.begin() ; k!=ikndices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = igate.getName(ik) ;      // variable name
      const aipPtr_32& p = igate.getPoint(ik) ;     // fwrap pointer
      
      int ivar = vims[s] ;
      
      ncerror(nc_put_var_ip32(ncidp,ivar,p->fptr()), sterr) ;
    } ;
    
    // ----------- loop over float variables -----------
    for (list<size_t>::const_iterator k=fkndices.begin() ; k!=fkndices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = fgate.getName(ik) ;      // variable name
      const afpPtr_32& p = fgate.getPoint(ik) ;     // fwrap pointer
      
      int ivar = vims[s] ;
      
      ncerror(nc_put_var_fp32(ncidp,ivar,p->fptr()), sterr) ;
    } ;
    
    // ----------- loop over double variables -----------
    for (list<size_t>::const_iterator k=dkndices.begin() ; k!=dkndices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = dgate.getName(ik) ;      // variable name
      const afpPtr_64& p = dgate.getPoint(ik) ;     // fwrap pointer
      
      int ivar = vims[s] ;
      
      ncerror(nc_put_var_fp64(ncidp,ivar,p->fptr()), sterr) ;
    } ;
    
    // ----------- loop over logical variables -----------
    for (list<size_t>::const_iterator k=lkndices.begin() ; k!=lkndices.end() ; ++k) {
      size_t          ik = *k ;
      string           s = lgate.getName(ik) ;      // variable name
      const alpPtr_32& p = lgate.getPoint(ik) ;     // fwrap pointer
      
      int ivar = vims[s] ;
      
      ncerror(nc_put_var_lp32(ncidp,ivar,p->fptr()), sterr) ;
    } ;
    
    // ----------- loop over character variables -----------
    for (list<size_t>::const_iterator k=ckndices.begin() ; k!=ckndices.end() ; ++k) {
      size_t            ik = *k ;
      string             s = cgate.getName(ik) ;      // variable name
      const acpPtr_char& p = cgate.getPoint(ik) ;     // fwrap pointer
      
      int ivar = vims[s] ;
      
      ncerror(nc_put_var_char(ncidp,ivar,p->fptr()), sterr) ;
    } 
    
    // ----------- loop over Bytes variables -----------
    for (list<size_t>::const_iterator k=bkndices.begin() ; k!=bkndices.end() ; ++k) {
      size_t             ik = *k ;
      string              s = bgate.getName(ik) ;      // variable name
      const abpPtr_bytes& p = bgate.getPoint(ik) ;     // fwrap pointer
      
      size_t np = p->size() ;  // total number of byte vectors
      
      int svar = vims[s] ;
      int dvar = vims[s+"_BYTES_"] ;
      
      ncerror(nc_put_var_bytes(ncidp,svar,dvar,np,p->fptr()), sterr) ;
    } 
  }  
  catch (...) {
    nc_close(ncidp) ;        // close file in case of fatal error and rethrow
    throw ;
  } ;
  
  // close
  ncerror(nc_close(ncidp), sterr) ;

  if (option==2) {
    if (F77NAME(crename)(sname.c_str(),dname.c_str())) { 
      throw ContainerError(cname, "error renaming file from "+sname+" to "+dname+" in ncsave()") ;
    } ;
  } ;
} ;

fwrap_map_att VarContainer::ncgetatt(int ncidp, int varid, int iatts) const {
  char buf[NC_MAX_NAME+1] ;     // buffer for netcdf names
  
  IntStore    ist(4) ;          // grab some memory for transferring attribute arrays
  DoubleStore dst(4) ;
  StringStore sst(4) ;

  fwrap_map_att mp ;            // hold the attributes
  
  for (int j=0 ; j<iatts ; j++) {
    ncerror(nc_inq_attname(ncidp,varid,j,buf)) ;      // get attribute name
    string aname = fwrap_att::despace(string(buf)) ;  // attribute name which can be stored
    if (aname.length()==0 || aname[0]==':') continue ;
    
    nc_type xtype ;             // netcdf type of the attribute
    size_t  len ;               // length of the attribute 
    ncerror(nc_inq_att(ncidp,varid,buf,&xtype,&len)) ;
    
    if (xtype==NC_BYTE||xtype==NC_SHORT||xtype==NC_INT) {
      ist.need(len) ;
      ncerror(nc_get_att_int(ncidp,varid,buf,ist.ptr())) ;
      mp.set(aname,ist.ptr(),len) ;
    } 
    else if (xtype==NC_DOUBLE||xtype==NC_FLOAT) {
      dst.need(len) ;
      ncerror(nc_get_att_double(ncidp,varid,buf,dst.ptr())) ;
      mp.set(aname,dst.ptr(),len) ;
    } 
    else if (xtype==NC_CHAR) {
      sst.cneed(len) ;
      ncerror(nc_get_att_text(ncidp,varid,buf,sst.cptr())) ;
      len = sst.chartostring(len) ;          // convert from char* to string*
      mp.set(aname,sst.ptr(),len) ;
    } ;
  } ;

  return mp ;
} ;

void VarContainer::ncload(string filename, int option) {
  patt=0 ;
  if (option<0 || option>5) throw ContainerError(cname, "bad option index") ;

  string sterr = string(", for file ")+filename ;
  char buf[NC_MAX_NAME+1] ;  // buffer for netcdf names

  int ncidp  ;    // netcdf file id

  ncerror(nc_open(filename.c_str(),NC_NOWRITE,&ncidp), sterr) ;

  try {
    int ndim = 0 ;  // number of dimensions
    int nvar = 0 ;  // number of variables
    int ngatt= 0 ;  // number of global attributes
    int nunl = 0 ;  // index of unlimited dimension
    
    ncerror(nc_inq(ncidp,&ndim,&nvar,&ngatt,&nunl), sterr) ;
    
    if (option==0||option==4) clear() ;    // start from a fresh container

    if (option!=2&&option!=5) matt = ncgetatt(ncidp, NC_GLOBAL, ngatt) ;  // grab global attributes
    if (option==4) {
      ncerror(nc_close(ncidp), sterr) ;
      return ;
    }

    // loop over the netcdf variables and collect names, need these to determine
    // if a name is a valid axis name 
    map<string,size_t> vims ;   // map from variable name to rank
    map<string,int>    bims ;   // map from variable name to index
    
    for (int i=0 ; i<nvar ; i++) {
      int ii=0 ;
      ncerror(nc_inq_varname(ncidp,i,buf), sterr) ;
      ncerror(nc_inq_varndims(ncidp,i,&ii), sterr) ;
      string name = fwrap_att::despace(buf) ;
      if (name.length()==0) continue ;
      vims[name] = ii ;
      bims[name] = i ;
    } ;

    string anames[GATEKEEPER_MAX_RANK] ;   // axis names
    
    // loop over the netcdf variables
    for (int i=0 ; i<nvar ; i++) {
      nc_type itype ;                    // type of the variable
      int     idims ;                    // number of dimensions
      int     kdims[NC_MAX_VAR_DIMS] ;   // dimension indices
      int     iatts ;                    // number of attributes
      
      ncerror(nc_inq_var(ncidp,i,buf,&itype,&idims,kdims,&iatts), sterr) ;
      
      if (idims>GATEKEEPER_MAX_RANK) {
        throw ContainerError(cname, string("can not handle netcdf variables with more then GATEKEEPR_MAX_RANK dimensions in file ")
                                            +filename) ;
      }
      
      string name = fwrap_att::despace(string(buf)) ;  // variable name, no spaces in the name allowed
      if (name.length()==0) continue ;                 // should not happen
#ifdef NOHDF
      if (itype == NC_BYTE)  continue ;  // holds concatenated BYTES, will be fetched when needed
#else
      if (itype == NC_UBYTE) continue ;  // holds concatenated BYTES, will be fetched when needed
#endif
      //cout << "reading variable " << name << endl ;

      bool    hk ;                       // has_key from getInfo
      varType tp ;                       // type from getInfo
      size_t  rk ;                       // rank from getInfo
      Shape   sh(GATEKEEPER_MAX_RANK) ;  // shape from getInfo
      getInfo(name, hk, tp, rk, sh) ;
      
      bool copy = false ;               // true to copy, false to create a variable
      bool aopy = true ;                // true to copy attributes
      if (option==0 || option==1) {
        if (hk) remove(name) ;
      }
      else if (option==2||option==3||option==5) {
        if (option==2&&!hk) continue ;  // only read data for existing keys for option=2
        copy = hk ;                     // option 2,3,5 -> copy over existing keys
        aopy = option!=2 ;              // option 3,5   -> copy attributes also
      } ;

      fwrap_map_att mp = ncgetatt(ncidp,i,iatts) ;  // get variable attributes

      size_t jdims = static_cast<size_t>(idims) ;   // number of dimensions
      Shape shape(jdims) ;
      for (size_t j=0 ; j<jdims ; j++) {            // loop over dimensions in C order
        size_t iq = (jdims-1)-j ;                   // fortran order
        size_t length=0 ; 
        ncerror(nc_inq_dim(ncidp,kdims[j],buf,&length), sterr) ;       // dimension name and length
        shape[iq]=length ;
        string dname = fwrap_att::despace(string(buf)) ;        // name of axis
        map<string,size_t>::iterator p = vims.find(dname) ;     // look for axis among variable names in the cdf file
        anames[iq] = (p!=vims.end() && p->second==1) ? dname : string("") ;               
        //cout << "  " << iq << " " << length << " " << dname << endl ;
      } ;
      
      if (STORE_AXIS_COLON_ARRAY!=0) {
        mp.set(":",anames,jdims) ;      // add ":" array attribute pointing to axis variable names
      }
      else {
        for (size_t j=0 ; j<jdims ; j++) mp.set(dnames[j],anames[j]) ; // add ":<n>" attribute pointing to axis variable name
      }
      
      // find type, check netcdf type then look for ctype attribute
      varType vt = NONE ;
      
      switch (itype) {
      case NC_BYTE:
      case NC_SHORT:
      case NC_INT:
        vt = I32 ; break ;
      case NC_FLOAT:
        vt = F32 ; break ;
      case NC_DOUBLE:
        vt = F64 ; break ;
      case NC_CHAR:
        vt = CHAR ; break ;
#ifndef NOHDF
      case NC_UINT64:
        vt = BYTES ; break ;
#endif
      } ;
      
      const fwrap_string_att* cat = dynamic_cast<const fwrap_string_att*>(mp.get("ctype")) ;  // get ctype string attribute
      if (cat!=0) {
        string cctype = fwrap_att::despace(cat->get_string()) ;
        if (cctype=="I32") {
          vt =  I32 ;
        }
        else if (cctype=="F32") {
          vt = F32 ;
        }
        else if (cctype == "F64") {
          vt = F64 ;
        }
        else if (cctype == "L32") {
          vt = L32 ;
        }
        else if (cctype == "Char") {
          vt = CHAR ;
        }
        else if (cctype == "Bytes") {
          vt = BYTES ;
        } ;
        mp.remove("ctype") ;    // do not carry ctype into container
        //cout << "found ctype = " << cctype << endl ;
      } ;
      //cout << "type of " << name << " is " << vt << endl ;

      if (vt==NONE) {
        cerr << "!varcontainer: unable to determine a numeric type for netcdf variable " << name << endl ;
        continue ;
      } ;
      
      // create the new variable and add to the container
      //cout << "processing variable " << name << " with type " << vt << endl ;
      if (vt==I32) {
        aipPtr_32 p = aipPtr_32::build(shape) ;              // build
        ncerror(nc_get_var_ip32(ncidp,i,p->fptr()), sterr) ; // store data
        *(p->att()) = mp ;                                   // copy attributes
        if (copy) { 
          set(name,p) ; 
          if (aopy) {
            fwrap_map_att* am = att(name) ;
            if (am!=0) (*am)=mp ;
          } ;
        } else { 
          add(name,p) ; 
        } ;
      } 
      else if (vt==F32) {
        afpPtr_32 p = afpPtr_32::build(shape) ;        // build
        ncerror(nc_get_var_fp32(ncidp,i,p->fptr()), sterr) ;  // store data
        *(p->att()) = mp ;                             // copy attributes
        if (copy) { 
          set(name,p) ; 
          if (aopy) {
            fwrap_map_att* am = att(name) ;
            if (am!=0) (*am)=mp ;
          } ;
        } 
        else { 
          add(name,p) ; 
        } ;
      }
      else if (vt==F64) {
        afpPtr_64 p = afpPtr_64::build(shape) ;               // build
        ncerror(nc_get_var_fp64(ncidp,i,p->fptr()), sterr) ;  // store data
        *(p->att()) = mp ;                                    // copy attributes
        if (copy) { 
          set(name,p) ; 
          if (aopy) {
            fwrap_map_att* am = att(name) ;
            if (am!=0) (*am)=mp ;
          } ;
        } else { 
          add(name,p) ; 
        } ;
      }
      else if (vt==L32) {
        alpPtr_32 p = alpPtr_32::build(shape) ;               // build
        ncerror(nc_get_var_lp32(ncidp,i,p->fptr()), sterr) ;  // store data
        *(p->att()) = mp ;                                    // copy attributes
        size_t n = p->pstore() ;                       // number of data items
        lp_32* q = p->fptr() ;                         // start of array
        for (size_t kq=0 ; kq<n ; q++,kq++) {
          *q = (*q) ? logical_true : logical_false ;   // set to container bool values
        } ;
        if (copy) { 
          setLogical(name,p) ; 
          if (aopy) {
            fwrap_map_att* am = att(name) ;
            if (am!=0) (*am)=mp ;
          } ;
        } 
        else { 
          addLogical(name,p) ; 
        } ;
      } 
      else if (vt==CHAR) {
        acpPtr_char p = acpPtr_char::build(shape) ;          // build
        ncerror(nc_get_var_char(ncidp,i,p->fptr()), sterr) ; // store data
        *(p->att()) = mp ;                                   // copy attributes
        if (copy) { 
          set(name,p) ; 
          if (aopy) {
            fwrap_map_att* am = att(name) ;
            if (am!=0) (*am)=mp ;
          } ;
        } else { 
          add(name,p) ; 
        } ;
      } 
      else if (vt==BYTES) {
        abpPtr_bytes p = abpPtr_bytes::build(shape) ;  // build fwrap<Bytes> destination
        
        size_t np = p->size() ;  // total number of Bytes arrays
        
        // fetch info on the netcdf variable <name>_BYTES_ holding the actual data
        map<string,int>::iterator bi = bims.find(name+"_BYTES_") ;
        if (bi==bims.end()) {
          throw ContainerError(cname,"did not find the byte array "+name+"_BYTES_ in the netcdf file") ;
        }
        int ib = bi->second ;  // netcdf id for one dimensional byte array

        ncerror(nc_inq_var(ncidp,ib,buf,&itype,&idims,kdims,&iatts), sterr) ;
#ifdef NOHDF
        if (itype != NC_BYTE) {
          throw ContainerError(cname,"netcdf variable "+name+"_BYTES_ does not have type NC_BYTE") ;
        }
#else
        if (itype != NC_UBYTE) {
          throw ContainerError(cname,"netcdf variable "+name+"_BYTES_ does not have type NC_UBYTE") ;
        }
#endif
        else if (idims != 1) {
          throw ContainerError(cname,"netcdf variable "+name+"_BYTES_ is not one dimensional") ;
        }
        
        size_t nb=0 ;  // total size of data in netcdf byte array
       
        ncerror(nc_inq_dim(ncidp,kdims[0],buf,&nb), sterr) ;  // grab nb
        
        ncerror(nc_get_var_bytes(ncidp, i, ib, np, nb, p->fptr()), sterr) ; // fetch data
        
        *(p->att()) = mp ;                                    // copy attributes
        if (copy) { 
          set(name,p) ; 
          if (aopy) {
            fwrap_map_att* am = att(name) ;
            if (am!=0) (*am)=mp ;
          } ;
        } else { 
          add(name,p) ; 
        } 
      } 
    }    // end loop over variables

  }
  catch (...) {
    nc_close(ncidp) ;        // close file in case of fatal error and rethrow
    throw ;
  } ;
  
  // close
  ncerror(nc_close(ncidp), sterr) ;
} ;

void VarContainer::share(const VarContainer& box, const list<string>& names, int option) {
  patt=0 ;
  if (option<0 || option>2) throw ContainerError(cname, "bad option index") ;

  if (&box==this) throw ContainerError(cname, "attempt to share from self") ;
  
  ostringstream sce ;

  for (list<string>::const_iterator nx = names.begin() ; nx!=names.end() ; ++nx) {
    string name = fwrap_att::despace(*nx) ;          // variable name, no spaces in the name allowed
    if (name.length()==0) continue ;                 // ignore empty names

    bool    vit ;                         // look for key, this data is from the source container    
    varType vt ;                          // type of variable
    size_t  vrank ;                       // rank
    Shape   vshape(GATEKEEPER_MAX_RANK) ; // shape

    box.getInfo(name, vit, vt, vrank, vshape);
    
    if (!vit) continue ;     // not in source container
  
    if (has_key(name)) {    // look for name in this container
        switch (option) {
        case 0:
            remove(name) ;
            break ;        
        case 1:
            continue ;   // skip this name
            break ;        
        case 2:
            sce << "the variable " << name << " is already being used" ;
            throw ContainerError(cname, sce.str()) ;
            break ;       
        default:
            throw ContainerError(cname, "logic error, unknown option") ;
        }
    }
    
    if (vt==I32) {
        add(name, box.getInt32(name)) ;
    }
    else if (vt==F32) {
        add(name, box.getFloat32(name)) ;
    }
    else if (vt==F64) {
        add(name, box.getFloat64(name)) ;    
    }
    else if (vt==L32) {
        addLogical(name, box.getLogical32(name)) ;    
    }
    else if (vt==CHAR) {
        add(name, box.getChar(name)) ;    
    }
    else if (vt==BYTES) {
        add(name, box.getBytes(name)) ;        
    }
    else {
        throw ContainerError(cname,"unknown variable type") ;
    }   
  }
}

void VarContainer::update(const VarContainer& box, int option) {
  patt=0 ;
  if (option<0 || option>5) throw ContainerError(cname, "bad option index") ;

  if (&box==this) throw ContainerError(cname, "attempt to update from self") ;
  
  if (option==0||option==4) clear() ;    // start from a fresh container
  
  const fwrap_map_att* vp = box.att() ;  // grab global attributes
  
  if (option!=2 && option!=5 && vp!=0) matt = *vp ;   // copy global attributes
  if (option==4) return ;
  
  // loop over the variables
  list<string> lnames = box.names(NONE) ;   // all variable names
  
  for (list<string>::const_iterator i=lnames.begin() ; i!=lnames.end() ; ++i) {
    string name = fwrap_att::despace(*i) ;           // variable name, no spaces in the name allowed
    if (name.length()==0) continue ;                 // should not happen
    
    bool    vit ;         // must have key, this data is from the source container
    varType vt ;          // type of variable
    size_t  vrank ;       // rank
    Shape   vshape(GATEKEEPER_MAX_RANK) ; // shape
    
    box.getInfo(name, vit, vt, vrank, vshape);
    if (!vit) throw ContainerError(cname, "logic error fetching variable info") ;
    
    Shape qshape(vrank) ;
    for (size_t i=0 ; i<vrank ; i++) qshape[i]=vshape[i] ;

    //cout << "reading variable " << name << endl ;
    
    bool    hk ;                       // has_key from getInfo
    varType tp ;                       // type from getInfo
    size_t  rk ;                       // rank from getInfo
    Shape   sh(GATEKEEPER_MAX_RANK) ;  // shape from getInfo
    getInfo(name, hk, tp, rk, sh) ;
    
    bool copy = false ;               // true to copy, false to create a variable
    bool aopy = true ;                // true to copy attributes
    if (option==0 || option==1) {
      if (hk) remove(name) ;
    }
    else if (option==2||option==3||option==5) {
      if (option==2&&!hk) continue ;  // only read data for existing keys for option=2
      copy = hk ;                     // option 2,3,5 -> copy over existing keys
      aopy = option!=2 ;              // option 3,5   -> copy attributes also
    } ;
    
    vp = box.att(name) ;              // get variable attributes
    
    // create the new variable and add to the container
    //cout << "processing variable " << name << " with type " << vt << endl ;
    if (vt==I32) {
      const aipPtr_32 p = box.getInt32(name) ;
      if (copy) { 
        set(name,p) ; 
        if (aopy) {
          fwrap_map_att* am = att(name) ;
          if (am!=0&&vp!=0) (*am)=*vp ;
        } ;
      } else { 
        aipPtr_32 q = aipPtr_32::build(qshape) ;    // make a new object
        q->assign(*p) ;                             // copy data over
        *(q->att()) = *(p->att()) ;                 // copy attributes
        add(name,q) ; 
      } ;
    } 
    else if (vt==F32) {
      const afpPtr_32 p = box.getFloat32(name) ;
      if (copy) { 
        set(name,p) ; 
        if (aopy) {
          fwrap_map_att* am = att(name) ;
          if (am!=0&&vp!=0) (*am)=*vp ;
        } ;
      } else { 
        afpPtr_32 q = afpPtr_32::build(qshape) ;    // make a new object
        q->assign(*p) ;                             // copy data over
        *(q->att()) = *(p->att()) ;                 // copy attributes
        add(name,q) ; 
      } ;
    }
    else if (vt==F64) {
      const afpPtr_64 p = box.getFloat64(name) ;
      if (copy) { 
        set(name,p) ; 
        if (aopy) {
          fwrap_map_att* am = att(name) ;
          if (am!=0&&vp!=0) (*am)=*vp ;
        } ;
      } else { 
        afpPtr_64 q = afpPtr_64::build(qshape) ;    // make a new object
        q->assign(*p) ;                             // copy data over
        *(q->att()) = *(p->att()) ;                 // copy attributes
        add(name,q) ; 
      } ;
    }
    else if (vt==L32) {
      const alpPtr_32 p = box.getLogical32(name) ;
      if (copy) { 
        setLogical(name,p) ; 
        if (aopy) {
          fwrap_map_att* am = att(name) ;
          if (am!=0&&vp!=0) (*am)=*vp ;
        } ;
      } else { 
        alpPtr_32 q = alpPtr_32::build(qshape) ;    // make a new object
        q->assign(*p) ;                             // copy data over
        *(q->att()) = *(p->att()) ;                 // copy attributes
        addLogical(name,q) ; 
      } ;
    } 
    else if (vt==CHAR) {
      const acpPtr_char p = box.getChar(name) ;
      if (copy) { 
        set(name,p) ; 
        if (aopy) {
          fwrap_map_att* am = att(name) ;
          if (am!=0&&vp!=0) (*am)=*vp ;
        } ;
      } else { 
        acpPtr_char q = acpPtr_char::build(qshape) ;    // make a new object
        q->assign(*p) ;                             // copy data over
        *(q->att()) = *(p->att()) ;                 // copy attributes
        add(name,q) ; 
      } ;
    } 
    else if (vt==BYTES) {
      const abpPtr_bytes p = box.getBytes(name) ;
      if (copy) { 
        set(name,p) ; 
        if (aopy) {
          fwrap_map_att* am = att(name) ;
          if (am!=0&&vp!=0) (*am)=*vp ;
        } ;
      } else { 
        abpPtr_bytes q = abpPtr_bytes::build(qshape) ;    // make a new object
        q->assign(*p) ;                             // copy data over
        *(q->att()) = *(p->att()) ;                 // copy attributes
        add(name,q) ; 
      } ;
    } ;
  } ;  // end loop over variables
} ;


void VarContainer::send(VarWriter* writer, list<string> names, list<string> yes, list<string> no) const {
    VarWorkerWriter w(cname,writer) ;  
    
    const char* more  = "MORE" ; // marker that there is more variable data
    const char* end   = "EROM" ; // marker for end of a variable
    const char* last  = "LAST" ; // marker that the last variable was already sent
    
    w.begin() ;   // open up stream

    try {
        bool all = names.empty() ;   // true to include all the names
    
        std::set<string> snames ;    // set of trimmed names to include, faster for searching?
        if (!all) {
            for (list<string>::const_iterator six = names.begin() ; six!=names.end() ; ++six) {
                snames.insert(fwrap_att::despace(*six)) ;
            }
        }        

        w.write(&matt) ;   // global attributes
       
        //
        // skip variables if names=["GLOBAL_ATTS"]
        //
        if (all || snames.size()!=1 || snames.find("GLOBAL_ATTS")==snames.end()) {  
            int t = 0 ;        // filled with current var_type as an int

            // ----------- loop over integer variables -----------
            t = I32 ;
            list<size_t> iindices = igate.indices() ;
    
            for (list<size_t>::const_iterator k=iindices.begin() ; k!=iindices.end() ; ++k) {
                size_t          ik = *k ;
                string           s = igate.getName(ik) ;      // variable name
                const aipPtr_32& p = igate.getPoint(ik) ;     // fwrap pointer
    
                if ((all || snames.find(s)!=snames.end()) && ncinc(p->att(), yes, no)) {
                    w.send(reinterpret_cast<const Byte*>(more), 4) ;  // mark start of variable
        
                    w.write(&s,1) ;           // 1, name
                    w.write<int>(&t,1) ;      // 1, type
                    w.writeFwrap<ip_32>(p) ;  // variable and attributes
        
                    w.send(reinterpret_cast<const Byte*>(end), 4) ;  // mark end of variable
                }
            } 

            // ----------- loop over float variables -----------
            t = F32 ;
            iindices = fgate.indices() ;
    
            for (list<size_t>::const_iterator k=iindices.begin() ; k!=iindices.end() ; ++k) {
                size_t          ik = *k ;
                string           s = fgate.getName(ik) ;      // variable name
                const afpPtr_32& p = fgate.getPoint(ik) ;     // fwrap pointer
    
                if ((all || snames.find(s)!=snames.end()) && ncinc(p->att(), yes, no)) {
                    w.send(reinterpret_cast<const Byte*>(more), 4) ;  // mark start of variable
        
                    w.write(&s,1) ;           // 1, name
                    w.write<int>(&t,1) ;      // 1, type
                    w.writeFwrap<fp_32>(p) ;  // variable and attributes
        
                    w.send(reinterpret_cast<const Byte*>(end), 4) ;  // mark end of variable
                }
            } 

            // ----------- loop over double variables -----------
            t = F64 ;
            iindices = dgate.indices() ;
    
            for (list<size_t>::const_iterator k=iindices.begin() ; k!=iindices.end() ; ++k) {
                size_t          ik = *k ;
                string           s = dgate.getName(ik) ;      // variable name
                const afpPtr_64& p = dgate.getPoint(ik) ;     // fwrap pointer
    
                if ((all || snames.find(s)!=snames.end()) && ncinc(p->att(), yes, no)) {
                    w.send(reinterpret_cast<const Byte*>(more), 4) ;  // mark start of variable
        
                    w.write(&s,1) ;           // 1, name
                    w.write<int>(&t,1) ;      // 1, type
                    w.writeFwrap<fp_64>(p) ;  // variable and attributes
        
                    w.send(reinterpret_cast<const Byte*>(end), 4) ;  // mark end of variable
                }
            } 

            // ----------- loop over logical variables -----------
            t = L32 ;
            iindices = lgate.indices() ;
    
            for (list<size_t>::const_iterator k=iindices.begin() ; k!=iindices.end() ; ++k) {
                size_t          ik = *k ;
                string           s = lgate.getName(ik) ;      // variable name
                const alpPtr_32& p = lgate.getPoint(ik) ;     // fwrap pointer
    
                if ((all || snames.find(s)!=snames.end()) && ncinc(p->att(), yes, no)) {
                    w.send(reinterpret_cast<const Byte*>(more), 4) ;  // mark start of variable
        
                    w.write(&s,1) ;           // 1, name
                    w.write<int>(&t,1) ;      // 1, type
                    w.writeFwrapLogical(p) ;  // variable and attributes
        
                    w.send(reinterpret_cast<const Byte*>(end), 4) ;  // mark end of variable
                }
            } 

            // ----------- loop over char variables -----------
            t = CHAR ;
            iindices = cgate.indices() ;
    
            for (list<size_t>::const_iterator k=iindices.begin() ; k!=iindices.end() ; ++k) {
                size_t            ik = *k ;
                string             s = cgate.getName(ik) ;      // variable name
                const acpPtr_char& p = cgate.getPoint(ik) ;     // fwrap pointer
    
                if ((all || snames.find(s)!=snames.end()) && ncinc(p->att(), yes, no)) {
                    w.send(reinterpret_cast<const Byte*>(more), 4) ;  // mark start of variable
        
                    w.write(&s,1) ;             // 1, name
                    w.write<int>(&t,1) ;        // 1, type
                    w.writeFwrap<cp_char>(p) ;  // variable and attributes
        
                    w.send(reinterpret_cast<const Byte*>(end), 4) ;  // mark end of variable
                }
            } 

            // ----------- loop over Bytes variables -----------
            t = BYTES ;
            iindices = bgate.indices() ;
    
            for (list<size_t>::const_iterator k=iindices.begin() ; k!=iindices.end() ; ++k) {
                size_t             ik = *k ;
                string              s = bgate.getName(ik) ;      // variable name
                const abpPtr_bytes& p = bgate.getPoint(ik) ;     // fwrap pointer
    
                if ((all || snames.find(s)!=snames.end()) && ncinc(p->att(), yes, no)) {
                    w.send(reinterpret_cast<const Byte*>(more), 4) ;  // mark start of variable
        
                    w.write(&s,1) ;           // 1, name
                    w.write<int>(&t,1) ;      // 1, type
                    w.writeFwrapBytes(p) ;    // variable and attributes
        
                    w.send(reinterpret_cast<const Byte*>(end), 4) ;  // mark end of variable
                }
            } 
        } 
    
        w.send(reinterpret_cast<const Byte*>(last), 4) ;  // no more data, send LAST
    
        w.done() ;      // ok, close down stream                     
    }
    catch (ContainerError& e) {
        w.error(e.whats()) ;
        throw ;
    }
    catch (...) {
        w.error("unknown VarContainer::send error") ;
        throw ;
    }
};


void VarContainer::receive(VarReader* reader, int option, list<string> keep, list<string> protect) {
    VarWorkerReader r(cname,reader) ;  
    
    const char* more  = "MORE" ; // marker that there is more variable data
    const char* end   = "EROM" ; // marker for end of a variable
    const char* last  = "LAST" ; // marker that the last variable was already sent
    patt=0 ;
    
    r.begin() ;   // open up stream
    
    try {
        if (option<0 || option>6) throw ContainerError(cname, "bad option index") ;
    
        if (option==0||option==4) clear() ;    // start from a fresh container

        if (option!=2&&option!=5) {
            matt.clear() ;
            r.read(&matt) ;       // global attributes
        }
        else {
            fwrap_map_att tmap ;  
            r.read(&tmap) ;       // read global attributes and discard
        }

        aipPtr_32    pip ;
        afpPtr_32    pfp ;
        afpPtr_64    pdp ;
        alpPtr_32    plp ;
        acpPtr_char  pcp ;
        abpPtr_bytes pbp ;
    
        char check[5] ;           // check markers
        
        while(true) {
            // -- check start marker --
            r.read<char>(check,4) ;
            check[4]=0 ;                           // null terminate
            if (strncmp(last,check,4)==0) break ;  // LAST found, no more data -- normal exit
            if (strncmp(more,check,4)!=0) {        // MORE not found, oops
                throw IOError(cname,"did not receive the expected characters MORE or LAST before reading data") ;
            }
        
            // -- get name and type of variable --
            r.expect(1,r.readString()) ;      // read count and strings
            string name = *(r.ptrString()) ;  // name of variable in stream
        
            r.expect(1,r.readInt()) ;                            // read count and integer
            varType vt = static_cast<varType>(*(r.ptrInt())) ;   // type of variable in stream
                
            // -- read variable from stream --
            switch (vt) {
            case I32:
                pip = r.readFwrap<ip_32>() ; break ;
            case F32:
                pfp = r.readFwrap<fp_32>() ; break ;
            case F64:
                pdp = r.readFwrap<fp_64>() ; break ;
            case L32:
                plp = r.readFwrapLogical(logical_true,logical_false) ; break ;
            case CHAR:
                pcp = r.readFwrap<cp_char>() ; break ;
            case BYTES:
                pbp = r.readFwrapBytes() ; break ;
            default:
                throw IOError(cname,"received unknown variable type") ;
            }    
            
            // -- check end marker --
            r.read<char>(check,4) ;
            check[4]=0 ;                          // null terminate
            if (strncmp(end,check,4)!=0) {        
                throw IOError(cname,"did not receive the expected characters EROM after reading data") ;
            }
            
            // -- process received data --
            
            if (option==4) continue ;          // not reading any variables into the container
                                               // but still need to read variables from the stream 
                                               
            // -- check if variable already exists --
            bool    hk ;                       // has_key from getInfo
            varType tp ;                       // type from getInfo
            size_t  rk ;                       // rank from getInfo
            Shape   sh(GATEKEEPER_MAX_RANK) ;  // shape from getInfo
            getInfo(name, hk, tp, rk, sh) ;
    
            fwrap_map_att* am = 0 ;  // attributes of existing variable or 0
                       
            int ikp = 0 ;            // how to treat existing variable: 0->any change, 1->keep, 2->protect 
            
            if (hk) {
                am  = att(name) ;
                ikp = nckeep(am,keep,protect) ;
            }
            
            if (ikp==2) continue ;            // existing variable/attributes are protected from changes
            
            // -- setup options --
            bool copy = false ;               // true to copy, false to create/replace a variable
            bool aopy = true ;                // true to copy over attributes
            if (option==0 || option==1) {
                if (hk && ikp==0) remove(name) ;
            }
            else if (option==2||option==3||option==5||option==6) {
                if (option==2&&!hk) continue ;  // only read data for existing keys for option=2
                copy = hk ;                     // option 2,3,5,6 -> copy into existing variables
                aopy = option!=2&&option!=6 ;   // option 3,5     -> copy attributes also
            } ;
            
            if (ikp==1) { copy=true ; aopy=false ; }  // existing variable is a keeper, copy data and don't change attributes
               
            // -- load into container --   
            switch(vt) {
            case I32:
                if (copy) { 
                    set(name,pip) ; 
                    if (aopy && am!=0) (*am)=*(pip->att()) ;  // replace attributes in existing variable
                } else { 
                    add(name,pip) ; 
                } ; break ;
            case F32:
                if (copy) { 
                    set(name,pfp) ; 
                    if (aopy && am!=0) (*am)=*(pfp->att()) ;  // replace attributes in existing variable
                } else { 
                    add(name,pfp) ; 
                } ; break ;
            case F64:
                if (copy) { 
                    set(name,pdp) ; 
                    if (aopy && am!=0) (*am)=*(pdp->att()) ;  // replace attributes in existing variable
                } else { 
                    add(name,pdp) ; 
                } ; break ;
            case L32:
                if (copy) { 
                    setLogical(name,plp) ; 
                    if (aopy && am!=0) (*am)=*(plp->att()) ;  // replace attributes in existing variable
                } else { 
                    addLogical(name,plp) ; 
                } ; break ;
            case CHAR:
                if (copy) { 
                    set(name,pcp) ; 
                    if (aopy && am!=0) (*am)=*(pcp->att()) ;  // replace attributes in existing variable
                } else { 
                    add(name,pcp) ; 
                } ; break ;
            case BYTES:
                if (copy) { 
                    set(name,pbp) ; 
                    if (aopy && am!=0) (*am)=*(pbp->att()) ;  // replace attributes in existing variable
                } else { 
                    add(name,pbp) ; 
                } ; break ;
            default:
                throw IOError(cname,"processed unknown variable type") ;
            }    
        }

        r.done() ;      // ok, close down stream                     
    }
    catch (ContainerError& e) {
        r.error(e.whats()) ;
        throw ;
    }
    catch (...) {
        r.error("unknown VarContainer::receive error") ;
        throw ;
    }    
} 
