require 'narray'
require 'narray_miss'
require 'numru/hdfeos5raw'
#
# HE5 クラスに関して
#
module NumRu
  class HE5
    def HE5.open(filename,mode="r")
      call_create=false              # false-> he5_open; true->he5_create
      case(mode)
      when "r"                       # read only
        mode="H5F_ACC_RDONLY"
      when "w","w+"                  # overwrite if exits
	call_create=true
	mode="H5F_ACC_TRUNC"
      when "a","a+","r+"             # append if exits
        if( File.exists?(filename))
          call_create=true
          mode="H5F_ACC_RDWR"
        else
          call_create=true           #(nonexsitent --> create)
          mode="H5F_ACC_CREAT"
        end
      else
        raise HE5Error, "Mode #{mode} is not supported"
      end
      he5_open(filename,mode)
    end
    
    class << HE5
      alias  new  open
    end
    
    def HE5.create(filename)
      open(filename,"w")
    end
# check Swath
    def has_swath?
      chkswath
    end
# check swath name
    def swath(swathname)
      swattach(swathname)
    end
# list swath name
    def swath_names
      rtn_val = chkswathname
      if(rtn_val.is_a?(String))
        names=[]
        rtn_val.split(",").each{|d|
          names.push(d)
        }
        names
      else
        rtn_val
      end
    end
# create swath file
    def create_swath(swathname)
      swcreate(swathname)
    end
# check Grid
    def has_grid?
      chkgrid
    end
# check grid name
    def grid(gridname)
      gdattach(gridname)
    end
# list grid name
    def grid_names
      rtn_val = chkgridname
      if(rtn_val.is_a?(String))
        names=[]
        rtn_val.split(",").each{|d|
          names.push(d)
        }
        names
      else
        rtn_val
      end
    end
# create grid file
    def create_grid(gridname, xdimsize, ydimsize, upleftpt, lowrightpt)
      gdcreate(gridname, xdimsize, ydimsize, upleftpt, lowrightpt)
    end
# check Point
    def has_point?
      chkpoint
    end
# check point name
    def point(pointname)
      ptattach(pointname)
    end
# list point name
    def point_names
      rtn_val = chkpointname
      if(rtn_val.is_a?(String))
        names=[]
        rtn_val.split(",").each{|d|
          names.push(d)
        }
        names
      else
        rtn_val
      end
    end
# create point file
    def create_point(pointname)
      ptcreate(pointname)
    end
# check ZonalMean
    def has_za?
      chkza
    end
# check zonal name
    def zonal(zonalname)
      zaattach(zonalname)
    end
# list zonal name
    def zonal_names
      rtn_val = chkzaname
      if(rtn_val.is_a?(String))
        names=[]
        rtn_val.split(",").each{|d|
          names.push(d)
        }
        names
      else
        rtn_val
      end
    end
# create zonal file
    def create_zonal(zonalname)
      ptcreate(zonalname)
    end
# return file path
    def inspect
      "HE5:"+ path
    end
  end
###############################
#
# HE5Swath クラスに関して
#
  class HE5Sw

    class << HE5Sw
       def create(file, sname)
         if(file.is_a?(String))
           file = HE5.open(file, "w")
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.create_swath(sname)
       end

       def open(file, sname)
         if(file.is_a?(String))
           file = HE5.open(file, "r")
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.swath(sname)
       end
    end
# detach swath field
    def detach
      swdetach
    end
    alias closed detach
# close swath field & close HDF-EOS5 file
    def close
      swdetach
      self.file.close
    end
# count Dimension Field
    def ndims
      count, strbufsize = nentries('HE5_HDFE_NENTDIM')
      return count
    end
# count Map Field
    def nmaps
      count, strbufsize = nentries('HE5_HDFE_NENTMAP')
      return count
    end
# count Index Map Field
    def nidxmaps
      count, strbufsize = nentries('HE5_HDFE_NENTIMAP')
      return count
    end
# count Geo Location Field 
    def ngeos
      count, strbufsize = nentries('HE5_HDFE_NENTGFLD')
      return count
    end
# count Data Field 
    def nvars
      count, strbufsize = nentries('HE5_HDFE_NENTDFLD')
      return count
    end
# define HE5 Swath Field Object ( Geo localtion Field )
    def def_geo(name, datatype, dims, maxdim=nil, merge=0)
      if datatype.is_a?(Numeric)
        datatype = char_typecode(datatype)
      end
      maxdim = "NULL" if maxdim == nil
      defgeofield(name, dims, maxdim, datatype, 0)
    end
# define HE5 Swath Field Object ( Data Field )
    def def_var(name, datatype, dims, maxdim=nil, merge=0)
      if datatype.is_a?(Numeric)
        datatype = char_typecode(datatype)
      end
      maxdim = "NULL" if maxdim == nil
      defdatafield(name, dims, maxdim, datatype, merge)
    end
# set HE5 Swath Field Object ( Geo location Field )
    def geo(geoname)
       setfield(geoname)
    end
# set HE5 Swath Field Object ( Data Field )
    def var(varname)
       setfield(varname)
    end
# return HE5 Swath Field Object
    def geos( names=nil )   # return all if names==nil
       if names == nil
	  geos = (0..ngeos()-1).collect{|geoid| get_geo(geoid)}
       else
	  raise TypeError, "names is not an array" if ! names.is_a?(Array)
	  geos = names.collect{|name| geo(name)}
	  raise ArgumentError, "One or more dimensions do not exist" if geos.include?(nil)
       end
       return geos
    end
# return HE5 Swath Field Object
    def vars( names=nil )   # return all if names==nil
       if names == nil
	  vars = (0..nvars()-1).collect{|varid| get_var(varid)}
       else
	  raise TypeError, "names is not an array" if ! names.is_a?(Array)
	  vars = names.collect{|name| var(name)}
	  raise ArgumentError, "One or more variables do not exist" if vars.include?(nil)
       end
       return vars
    end
# return Dimension Name list
    def dim_names
      ndims, dimlist, sizes, ntype = inqdims('HE5_HDFE_NENTDIM')
      return dimlist.split(/,/)
    end
# return Map Name list
    def map_names
      nmaps, maplist, sizes, ntype = inqmaps('HE5_HDFE_NENTMAP')
      return maplist.split(/,/)
    end
# return Index Map Name list
    def idxmap_names
      nidxmpas, idxmaplist, sizes, ntype = inqidxmaps('HE5_HDFE_NENTIMAP')
      return idxmaplist.split(/,/)
    end
# return Geo location Field Name list
    def geo_names
      nflds, fieldlist, sizes, ntype = inqgeofields('HE5_HDFE_NENTGFLD')
      return fieldlist.split(/,/)
    end
# return Data Field Name list
    def var_names
      nflds, fieldlist, sizes, ntype = inqdatafields('HE5_HDFE_NENTDFLD')
      return fieldlist.split(/,/)
    end
# set HE5 Swath Field Object ( Geo localtion Field )
    def get_geo(geoid)
      nflds, fieldlist, sizes, ntype = inqgeofields('HE5_HDFE_NENTGFLD')
      fieldname=fieldlist.split(",")[geoid]
      return geo(fieldname)
    end
# set HE5 Swath Field Object ( Data Field )
    def get_var(varid)
      nflds, fieldlist, sizes, ntype = inqdatafields('HE5_HDFE_NENTDFLD')
      fieldname=fieldlist.split(",")[varid]
      return var(fieldname)
    end
    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end
    def att_names
      nattrs, attrnames, strbufsize = inqattrs()
      return attrnames.split(/,/)
    end
    def get_grpatt(grpattname)
      if grpatt_names.include?(grpattname)
        get_grpatt_(grpattname)
      else
        nil
      end
    end
    def grpatt_names
      nattrs, attrnames, strbufsize = inqgrpattrs()
      return attrnames.split(/,/)
    end

    def char_typecode(typecode)
      case typecode
      when 1
       ctype = "short"
      when 2 
       ctype = "sint"
      when 3 
       ctype = "int"
      when 4 
       ctype = "sfloat"
      when 5 
       ctype = "float"
      else
       raise TypeError, "not match"
      end
      ctype
    end

    def inspect
      'HE5Sw:'+name
    end
  end
###############################
#
# HE5SwField クラスに関して
#
  class HE5SwField
    
    MissValAtts = ["MissingValue","_FillValue"]

    class << HE5SwField

       def create(file, sname,fldname)
         if(file.is_a?(String))
           swid = HE5Sw.create(file,sname)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.var(fldname)
       end

       def open(file, sname, fldname)
         if(file.is_a?(String))
	   swid = HE5Sw.open(file, sname)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
	 swid.var(fldname)
       end
    end

    def ntype
      rank, dims, ntype, dimlist = fieldinfo()
      return ntype
    end
    alias typecode ntype

    def shape
      rank, dims, ntype, dimlist = fieldinfo()
      return dims[-1..0].to_a   # NArray --> reverced --> Array
    end

    alias shape_current shape

    def shape_ul0
      sh = shape_current
      dim_names.each_with_index do |dnm,i|
        if dnm == "Unlim"
          sh[i] = 0
        end
      end
      sh
    end

    def ndims
      rank, dims, ntype, dimlist = fieldinfo()
      return rank
    end
    alias rank ndims

    def dim(dimid)
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_val(dimid)
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_names
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",").reverse
    end

    def name=
      raise "name= not supported"
    end

    def natts
      nattrs, attrname, strbufsize = inqlocattrs()
      return nattrs
    end

    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end

    def att_names
      nattrs, attrnames, strbufsize = inqlocattrs()
      return attrnames.split(/,/)
    end

    def each_att
      attlist=Array.new
      attnames=att_names()
      attnum = natts()
      attnames.each{|attname|
        list=Array.new
        attrval=att(attname)
        list.push(attname, attrval)
        attlist.push(list)
      }
      attlist
    end

    def put_att(name,value,atttype=nil)
       count = Array.new
       count[0] = value.size
       writelocattr(name,atttype,count,value)
    end

    def attr
      @attr
    end

    def simple_put(var,hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          put_vars_char([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="sint"
          put_vars_short([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="int"
          put_vars_int([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="sfloat"
          put_vars_float([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="float"
          put_vars_double([0,0,0,0,0,0,0,0], nil, nil, var)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      elsif hash.key?("index")==true ||  hash.key?("start")==true
        h_sta = hash["start"]
        endq = hash.key?("end")
        strq = hash.key?("stride")
        if endq == false && strq == false
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, nil, nil, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, nil, nil, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, nil, nil, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, nil, nil, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, nil, nil, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == true && strq == false
 	  h_end = hash["end"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, nil, h_end, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, nil, h_end, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, nil, h_end, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, nil, h_end, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, nil, h_end, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == false && strq == true
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, h_str, nil, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, h_str, nil, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, h_str, nil, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, h_str, nil, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, h_str, nil, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        else endq == true && strq == true
	  h_end = hash["end"]
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, h_str, h_end, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, h_str, h_end, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, h_str, h_end, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, h_str, h_end, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, h_str, h_end, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        end
      else
        raise ArgumentError,"{'start'}=>{ARRAY} or {'index'}=>{ARRAY} is needed"
      end
    end

    def put_with_miss(data, *args)
      if data.is_a?( NArrayMiss )
        simple_put(data.to_na, *args)
      else
	simple_put(data, *args)
      end
    end
    alias put put_with_miss

    def simple_get(hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          get_vars_char([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="sint"
          get_vars_short([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="int"
          get_vars_int([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="sfloat"
          get_vars_float([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="float"
          get_vars_double([0,0,0,0,0,0,0,0], nil, nil)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      elsif hash.key?("index")==true ||  hash.key?("start")==true
        h_sta = hash["start"]
        endq = hash.key?("end")
        strq = hash.key?("stride")
        if endq == false && strq == false
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, nil, nil)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, nil, nil)
          elsif self.ntype=="int"
            get_vars_int(h_sta, nil, nil)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, nil, nil)
          elsif self.ntype=="float"
            get_vars_double(h_sta, nil, nil)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == true && strq == false
 	  h_end = hash["end"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, nil, h_end)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, nil, h_end)
          elsif self.ntype=="int"
            get_vars_int(h_sta, nil, h_end)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, nil, h_end)
          elsif self.ntype=="float"
            get_vars_double(h_sta, nil, h_end)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == false && strq == true
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, h_str, nil)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, h_str, nil)
          elsif self.ntype=="int"
            get_vars_int(h_sta, h_str, nil)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, h_str, nil)
          elsif self.ntype=="float"
            get_vars_double(h_sta, h_str, nil)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        else endq == true && strq == true
	  h_end = hash["end"]
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, h_str, h_end)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, h_str, h_end)
          elsif self.ntype=="int"
            get_vars_int(h_sta, h_str, h_end)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, h_str, h_end)
          elsif self.ntype=="float"
            get_vars_double(h_sta, h_str, h_end)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        end
      else
        raise ArgumentError,"{'start'}=>{ARRAY} or {'index'}=>{ARRAY} is needed"
      end
    end

    def get_with_miss(*args)
      na = simple_get(*args)
      mv = nil
      MissValAtts.each do |nm|
        mv = get_att(nm)
        break if !mv.nil?
      end
      if mv.nil?
        na
      else
        NArrayMiss.to_nam_no_dup( na, (na.ne(mv[0])) )
      end
    end
    alias get get_with_miss

    def __rubber_expansion( args )
      if (id = args.index(false))  # substitution into id
        # false is incuded
        alen = args.length
        if args.rindex(false) != id
	  raise ArguemntError,"only one rubber dimension is permitted"
        elsif alen > rank+1
  	  raise ArgumentError, "too many args"
        end
        ar = ( id!=0 ? args[0..id-1] : [] )
        args = ar + [true]*(rank-alen+1) + args[id+1..-1]
      elsif args.length == 0   # to support empty [], []=
        args = [true]*rank
      end
      args
    end
    private :__rubber_expansion

    def [](*a)
      if a.length == 0
        return self.get
      end
      a = __rubber_expansion(a)
      first = Array.new
      last = Array.new
      stride = Array.new
      set_stride = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	first.push(i)
	last.push(i)
	stride.push(1)
      elsif(i.is_a?(Range))
	first.push(i.first)
	last.push(i.exclude_end? ? i.last-1 : i.last)
	stride.push(1)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise TypeError, "Hash argument must be {a_Range, step}"
	end
	first.push(r.first) 
	last.push(r.exclude_end? ? r.last-1 : r.last)
	stride.push(s)
	set_stride = true
      elsif(i.is_a?(TrueClass))
	first.push(0)
	last.push(-1)
        stride.push(1)
      elsif( i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  na_tmp = self[*a_new]
	  if n==0 then
	    k = at
	    if at > 0
	      a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	    end
	    shape_tmp = na_tmp.shape
	    shape_tmp[k] = i.length
	    na = na_tmp.class.new(na_tmp.typecode,*shape_tmp)
	    index_tmp = Array.new(shape_tmp.length,true)
	  end
	  index_tmp[k] = n..n
	  na[*index_tmp] = na_tmp
        end
	return na
      else
	 raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }

      if(set_stride)
        na = self.get({"start"=>first, "end"=>last, "stride"=>stride})
      else
        na = self.get({"start"=>first, "end"=>last})
      end
      shape = na.shape
      (a.length-1).downto(0){ |i|
	 shape.delete_at(i) if a[i].is_a?(Fixnum)
      }
      na.reshape!( *shape )
      na
    end

    def []=(*a)
      val = a.pop
      a = __rubber_expansion(a)
      first = Array.new
      last = Array.new
      stride = Array.new
      set_stride = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	first.push(i)
	last.push(i)
	stride.push(1)
      elsif(i.is_a?(Range))
	first.push(i.first)
	last.push(i.exclude_end? ? i.last-1 : i.last)
	stride.push(1)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise ArgumentError, "Hash argument must be {first..last, step}"
	end
	first.push(r.first) 
	last.push(r.exclude_end? ? r.last-1 : r.last)
	stride.push(s)
	set_stride = true
      elsif(i.is_a?(TrueClass))
	first.push(0)
	last.push(-1)
	stride.push(1)
      elsif(i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	val = NArray.to_na(val) if val.is_a?(Array)
	rank_of_subset = a.dup.delete_if{|v| v.is_a?(Fixnum)}.length
	if val.rank != rank_of_subset
	   raise "rank of the rhs (#{val.rank}) is not equal to the rank "+
                 "of the subset specified by #{a.inspect} (#{rank_of_subset})"
	end
	k = at
	a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	if i.length != val.shape[k]
	   raise "length of the #{k+1}-th dim of rhs is incorrect "+
                 "(#{i.length} for #{val.shape[k]})"
	end
	index_tmp = Array.new(val.rank,true) if !val.is_a?(Numeric) #==>Array-like
        for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  if !val.is_a?(Numeric) then
	    index_tmp[k] = n..n
	    self[*a_new] = val[*index_tmp]
	  else
	    self[*a_new] = val
	  end
	end
        first.push(val.first)
        last.push(val.last)
	return self
      else
	raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }
      if(set_stride)
	self.put(val, {"start"=>first, "end"=>last, "stride"=>stride})
      else
	self.put(val, {"start"=>first, "end"=>last})
      end
    end

    def inspect
      'HE5SwField:'+swath.file.path+'?var='+name
    end
  end
###############################
#
# HE5Grid クラスに関して
#
  class HE5Gd

    class << HE5Gd
       def create(file, gname, xdimsize, ydimsize, upleftpt, lowrightpt)
         if(file.is_a?(String))
           file = HE5.open(file, "w")
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.create_grid(gname, xdimsize, ydimsize, upleftpt, lowrightpt)
       end

       def open(file, gname)
         if(file.is_a?(String))
           file = HE5.open(file, "r")
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.grid(gname)
       end
    end
# detach grid field
    def detach
      gddetach
    end
    alias closed detach
# close grid field & close HDF-EOS5 file
    def close
      gddetach
      self.file.close
    end
# count Dimension Field
    def ndims
      count, strbufsize = nentries('HE5_HDFE_NENTDIM')
      return count
    end
# count Data Field 
    def nvars
      count, strbufsize = nentries('HE5_HDFE_NENTDFLD')
      return count
    end
# define HE5 Grid Field Object ( Data Field )
    def def_var(name, datatype, dims, maxdim=nil, merge=0)
      if datatype.is_a?(Numeric)
        datatype = char_typecode(datatype)
      end
      maxdim = "NULL" if maxdim == nil
      deffield(name, dims, maxdim, datatype, merge)
    end
# set HE5 Grid Field Object ( Data Field )
    def var(varname)
       setfield(varname)
    end
# return HE5 Grid Field Object
    def vars( names=nil )   # return all if names==nil
       if names == nil
	  vars = (0..nvars()-1).collect{|varid| get_var(varid)}
       else
	  raise TypeError, "names is not an array" if ! names.is_a?(Array)          
	  vars = names.collect{|name| var(name)}
	  raise ArgumentError, "One or more variables do not exist" if vars.include?(nil)
       end
       return vars
    end
# return Dimension Name list
    def dim_names
      ndims, dimlist, sizes, ntype = inqdims('HE5_HDFE_NENTDIM')
      return dimlist.split(/,/)
    end
# return Data Field Name list
    def var_names
      nflds, fieldlist, sizes, ntype = inqfields('HE5_HDFE_NENTDFLD')
      return fieldlist.split(/,/)
    end
# set HE5 Grid Field Object ( Data Field )
    def get_var(varid)
      nflds, fieldlist, sizes, ntype = inqfields('HE5_HDFE_NENTDFLD')
      fieldname=fieldlist.split(",")[varid]
      return var(fieldname)
    end
    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end
    def att_names
      nattrs, attrnames, strbufsize = inqattrs()
      return attrnames.split(/,/)
    end
    def get_grpatt(grpattname)
      if grpatt_names.include?(grpattname)
        get_grpatt_(grpattname)
      else
        nil
      end
    end
    def grpatt_names
      nattrs, attrnames, strbufsize = inqgrpattrs()
      return attrnames.split(/,/)
    end

    def char_typecode(typecode)
      case typecode
      when 1
       ctype = "short"
      when 2 
       ctype = "sint"
      when 3 
       ctype = "int"
      when 4 
       ctype = "sfloat"
      when 5 
       ctype = "float"
      else
       raise TypeError, "not match"
      end
      ctype
    end

    def inspect
      'HE5Gd:'+name
    end
  end
###############################
#
# HE5GdField クラスに関して
#
  class HE5GdField
    
    MissValAtts = ["MissingValue","_FillValue"]

    class << HE5GdField

       def create(file, gname, fldname, xdimsize, ydimsize, upleftpt, lowrightpt)
         if(file.is_a?(String))
           gdid = HE5Gd.create(file, gname, xdimsize, ydimsize, upleftpt, lowrightpt)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.var(fldname)
       end

       def open(file, gname, fldname)
         if(file.is_a?(String))
	   gdid = HE5Gd.open(file, gname)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
	 gdid.var(fldname)
       end
    end

    def ntype
      rank, dims, ntype, dimlist = fieldinfo()
      return ntype
    end
    alias typecode ntype

    def shape
      rank, dims, ntype, dimlist = fieldinfo()
      return dims[-1..0].to_a   # NArray --> reverced --> Array
    end

    alias shape_current shape

    def shape_ul0
      sh = shape_current
      dim_names.each_with_index do |dnm,i|
        if dnm == "Unlim"
          sh[i] = 0
        end
      end
      sh
    end

    def ndims
      rank, dims, ntype, dimlist = fieldinfo()
      return rank
    end
    alias rank ndims

    def dim(dimid)
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_val(dimid)
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_names
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",").reverse
    end

    def name=
      raise "name= not supported"
    end

    def natts
      nattrs, attrname, strbufsize = inqlocattrs()
      return nattrs
    end

    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end

    def att_names
      nattrs, attrnames, strbufsize = inqlocattrs()
      return attrnames.split(/,/)
    end

    def each_att
      attlist=Array.new
      attnames=att_names()
      attnum = natts()
      attnames.each{|attname|
        list=Array.new
        attrval=att(attname)
        list.push(attname, attrval)
        attlist.push(list)
      }
      attlist
    end

    def put_att(name,value,atttype=nil)
       count = Array.new
       count[0] = value.size
       writelocattr(name,atttype,count,value)
    end

    def attr
      @attr
    end

    def simple_put(var,hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          put_vars_char([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="sint"
          put_vars_short([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="int"
          put_vars_int([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="sfloat"
          put_vars_float([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="float"
          put_vars_double([0,0,0,0,0,0,0,0], nil, nil, var)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      elsif hash.key?("index")==true ||  hash.key?("start")==true
        h_sta = hash["start"]
        endq = hash.key?("end")
        strq = hash.key?("stride")
        if endq == false && strq == false
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, nil, nil, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, nil, nil, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, nil, nil, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, nil, nil, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, nil, nil, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == true && strq == false
 	  h_end = hash["end"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, nil, h_end, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, nil, h_end, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, nil, h_end, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, nil, h_end, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, nil, h_end, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == false && strq == true
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, h_str, nil, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, h_str, nil, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, h_str, nil, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, h_str, nil, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, h_str, nil, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        else endq == true && strq == true
	  h_end = hash["end"]
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, h_str, h_end, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, h_str, h_end, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, h_str, h_end, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, h_str, h_end, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, h_str, h_end, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        end
      else
        raise ArgumentError,"{'start'}=>{ARRAY} or {'index'}=>{ARRAY} is needed"
      end
    end
    alias put simple_put

    def simple_get(hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          get_vars_char([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="sint"
          get_vars_short([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="int"
          get_vars_int([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="sfloat"
          get_vars_float([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="float"
          get_vars_double([0,0,0,0,0,0,0,0], nil, nil)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      elsif hash.key?("index")==true ||  hash.key?("start")==true
        h_sta = hash["start"]
        endq = hash.key?("end")
        strq = hash.key?("stride")
        if endq == false && strq == false
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, nil, nil)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, nil, nil)
          elsif self.ntype=="int"
            get_vars_int(h_sta, nil, nil)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, nil, nil)
          elsif self.ntype=="float"
            get_vars_double(h_sta, nil, nil)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == true && strq == false
 	  h_end = hash["end"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, nil, h_end)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, nil, h_end)
          elsif self.ntype=="int"
            get_vars_int(h_sta, nil, h_end)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, nil, h_end)
          elsif self.ntype=="float"
            get_vars_double(h_sta, nil, h_end)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == false && strq == true
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, h_str, nil)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, h_str, nil)
          elsif self.ntype=="int"
            get_vars_int(h_sta, h_str, nil)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, h_str, nil)
          elsif self.ntype=="float"
            get_vars_double(h_sta, h_str, nil)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        else endq == true && strq == true
	  h_end = hash["end"]
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, h_str, h_end)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, h_str, h_end)
          elsif self.ntype=="int"
            get_vars_int(h_sta, h_str, h_end)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, h_str, h_end)
          elsif self.ntype=="float"
            get_vars_double(h_sta, h_str, h_end)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        end
      else
        raise ArgumentError,"{'start'}=>{ARRAY} or {'index'}=>{ARRAY} is needed"
      end
    end

    def get_with_miss(*args)
      na = simple_get(*args)
      mv = nil
      MissValAtts.each do |nm|
        mv = get_att(nm)
        break if !mv.nil?
      end
      if mv.nil?
        na
      else
        NArrayMiss.to_nam_no_dup( na, (na.ne(mv[0])) )
      end
    end
    alias get get_with_miss

    def __rubber_expansion( args )
      if (id = args.index(false))  # substitution into id
        # false is incuded
        alen = args.length
        if args.rindex(false) != id
	  raise ArguemntError,"only one rubber dimension is permitted"
        elsif alen > rank+1
  	  raise ArgumentError, "too many args"
        end
        ar = ( id!=0 ? args[0..id-1] : [] )
        args = ar + [true]*(rank-alen+1) + args[id+1..-1]
      elsif args.length == 0   # to support empty [], []=
        args = [true]*rank
      end
      args
    end
    private :__rubber_expansion

    def [](*a)
      if a.length == 0
        return self.get
      end
      a = __rubber_expansion(a)
      first = Array.new
      last = Array.new
      stride = Array.new
      set_stride = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	first.push(i)
	last.push(i)
	stride.push(1)
      elsif(i.is_a?(Range))
	first.push(i.first)
	last.push(i.exclude_end? ? i.last-1 : i.last)
	stride.push(1)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise TypeError, "Hash argument must be {a_Range, step}"
	end
	first.push(r.first) 
	last.push(r.exclude_end? ? r.last-1 : r.last)
	stride.push(s)
	set_stride = true
      elsif(i.is_a?(TrueClass))
	first.push(0)
	last.push(-1)
        stride.push(1)
      elsif( i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  na_tmp = self[*a_new]
	  if n==0 then
	    k = at
	    if at > 0
	      a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	    end
	    shape_tmp = na_tmp.shape
	    shape_tmp[k] = i.length
	    na = na_tmp.class.new(na_tmp.typecode,*shape_tmp)
	    index_tmp = Array.new(shape_tmp.length,true)
	  end
	  index_tmp[k] = n..n
	  na[*index_tmp] = na_tmp
        end
	return na
      else
	 raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }

      if(set_stride)
        na = self.get({"start"=>first, "end"=>last, "stride"=>stride})
      else
        na = self.get({"start"=>first, "end"=>last})
      end
      shape = na.shape
      (a.length-1).downto(0){ |i|
	 shape.delete_at(i) if a[i].is_a?(Fixnum)
      }
      na.reshape!( *shape )
      na
    end

    def []=(*a)
      val = a.pop
      a = __rubber_expansion(a)
      first = Array.new
      last = Array.new
      stride = Array.new
      set_stride = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	first.push(i)
	last.push(i)
	stride.push(1)
      elsif(i.is_a?(Range))
	first.push(i.first)
	last.push(i.exclude_end? ? i.last-1 : i.last)
	stride.push(1)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise ArgumentError, "Hash argument must be {first..last, step}"
	end
	first.push(r.first) 
	last.push(r.exclude_end? ? r.last-1 : r.last)
	stride.push(s)
	set_stride = true
      elsif(i.is_a?(TrueClass))
	first.push(0)
	last.push(-1)
	stride.push(1)
      elsif(i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	val = NArray.to_na(val) if val.is_a?(Array)
	rank_of_subset = a.dup.delete_if{|v| v.is_a?(Fixnum)}.length
	if val.rank != rank_of_subset
	   raise "rank of the rhs (#{val.rank}) is not equal to the rank "+
                 "of the subset specified by #{a.inspect} (#{rank_of_subset})"
	end
	k = at
	a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	if i.length != val.shape[k]
	   raise "length of the #{k+1}-th dim of rhs is incorrect "+
                 "(#{i.length} for #{val.shape[k]})"
	end
	index_tmp = Array.new(val.rank,true) if !val.is_a?(Numeric) #==>Array-like
        for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  if !val.is_a?(Numeric) then
	    index_tmp[k] = n..n
	    self[*a_new] = val[*index_tmp]
	  else
	    self[*a_new] = val
	  end
	end
	return self
      else
	raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }

      if(set_stride)
	self.put(val, {"start"=>first, "end"=>last, "stride"=>stride})
      else
	self.put(val, {"start"=>first, "end"=>last})
      end
    end

    def inspect
      'HE5GdField:'+grid.file.path+'?var='+name
    end
  end
###############################
#
# HE5Pt クラスに関して
#
  class HE5Pt

    class << HE5Pt
       def create(file, pname)
         if(file.is_a?(String))
           file = HE5.open(file, "w")
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.create_point(pname)
       end

       def open(file, pname)
         if(file.is_a?(String))
           file = HE5.open(file, "a")  # "r" の場合、simple plot でエラーになるため
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.point(pname)
       end
    end
# detach point field
    def detach
      ptdetach
    end
    alias closed detach
# close point field & close HDF-EOS5 file
    def close
      ptdetach
      self.file.close
    end
# define HE5 Point Object
    def def_level(levelname, rank, fieldlist, dimlist, datatypelist)
      if datatypelist.is_a?(Numeric)
        datatype = char_typecode(datatypelist)
      elsif(datatype.length > 1)
        datatypelist.each{|dtype|
          dtype=char_typecode(dtype) if dtype.is_a?(Numeric)
          datatype.push(dtype)
        }
      end
      deflevel(levelname, rank.length, rank, fieldlist, fieldlist,  dimlist, datatype)
    end
# define HE5 Point Linkage Object
    def def_link(fwd_name, bkw_name, key)
      deflinkage(fwd_name, bkw_name, key)
    end
# set HE5 Point Field Object ( Field )
    def var(fieldname, levelname=nil)
       if levelname == nil
          setfield(fieldname)
       else
          fieldlist = chkfield(levelname)
          if fieldlist.include?(fieldname)
             setfield_level(fieldname,levelname)
          else
	     raise ArgumentError, "Level do not exist this fieldname"
          end
       end
    end
# return HE5 Point Field Object
    def vars( names=nil )   # return all if names==nil
       if names == nil
	  vars = (0..nvars()-1).collect{|varid| get_var(varid)}
       elsif(!file.is_a?(HE5))
	  raise TypeError, "names is not an array" if ! names.is_a?(Array)
	  vars = names.collect{|name| var(name)}
	  raise ArgumentError, "One or more variables do not exist" if vars.include?(nil)
       end
       return vars
    end
# return Data Field Name list
    def var_names
      nflds, fieldlist = nfields()
      return fieldlist.split(/,/)
    end
# set HE5 Point Field Object ( Data Field )
    def get_var(varid)
      nflds, fieldlist = nfields()
      fieldname=fieldlist.split(",")[varid]
      return var(fieldname)
    end
    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end
    def att_names
      nattrs, attrnames, strbufsize = inqattrs()
      return attrnames.split(/,/)
    end
    def get_grpatt(grpattname)
      if grpatt_names.include?(grpattname)
        get_grpatt_(grpattname)
      else
        nil
      end
    end
    def grpatt_names
      nattrs, attrnames, strbufsize = inqgrpattrs()
      return attrnames.split(/,/)
    end

    def char_typecode(typecode)
      case typecode
      when 1
       ctype = "short"
      when 2 
       ctype = "sint"
      when 3 
       ctype = "int"
      when 4 
       ctype = "sfloat"
      when 5 
       ctype = "float"
      else
       raise TypeError, "not match"
      end
      ctype
    end

    def inspect
      'HE5Pt:'+name
    end
  end
###############################
#
# HE5PtField クラスに関して
#
  class HE5PtField
    
    MissValAtts = ["MissingValue","_FillValue"]

    class << HE5PtField

       def create(file, pname,fldname)
         if(file.is_a?(String))
           ptid = HE5Pt.create(file, pname)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.var(fldname)
       end

       def open(file, pname, fldname)
         if(file.is_a?(String))
	   ptid = HE5Pt.open(file, pname)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
	 ptid.var(fldname)
       end
    end

    def ntype
      rank, dims, ntype, dimlist = fieldinfo()
      return ntype
    end
    alias typecode ntype

    def shape
      rank, dims, ntype, dimlist = fieldinfo()
      return dims[-1..0].to_a   # NArray --> reverced --> Array
    end

    alias shape_current shape

    def shape_ul0
      sh = shape_current
      dim_names.each_with_index do |dnm,i|
        if dnm == "Unlim"
          sh[i] = 0
        end
      end
      sh
    end

    def ndims
      rank, dims, ntype, dimlist = fieldinfo()
      return rank
    end
    alias rank ndims

    def dim(dimid)
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_val(dimid)
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_names
      rank, dims, ntype, dimlist = fieldinfo()
      return dimlist.split(",").reverse
    end

    def name=
      raise "name= not supported"
    end

    def natts
      nattrs, attrname, strbufsize = inqlocattrs()
      return nattrs
    end

    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end

    def att_names
      nattrs, attrnames, strbufsize = inqlocattrs()
      return attrnames.split(/,/)
    end

    def each_att
      attlist=Array.new
      attnames=att_names()
      attnum = natts()
      attnames.each{|attname|
        list=Array.new
        attrval=att(attname)
        list.push(attname, attrval)
        attlist.push(list)
      }
      attlist
    end

    def put_att(name,value,atttype=nil)
       count = Array.new
       count[0] = value.size
       writelocattr(name,atttype,count,value)
    end

    def attr
      @attr
    end

    def simple_put(var,hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          put_vars_char([0,0,0,0,0,0,0,0], var)
        elsif self.ntype=="sint"
          put_vars_short([0,0,0,0,0,0,0,0], var)
        elsif self.ntype=="int"
          put_vars_int([0,0,0,0,0,0,0,0], var)
        elsif self.ntype=="sfloat"
          put_vars_float([0,0,0,0,0,0,0,0], var)
        elsif self.ntype=="float"
          put_vars_double([0,0,0,0,0,0,0,0], var)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      elsif hash == "count"
        count = hash["count"]
        if self.ntype == "char" || self.ntype=="byte"
          put_vars_char(count, var)
        elsif self.ntype=="sint"
          put_vars_short(count, var)
        elsif self.ntype=="int"
          put_vars_int(count, var)
        elsif self.ntype=="sfloat"
          put_vars_float(count, var)
        elsif self.ntype=="float"
          put_vars_double(count, var)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      else
        raise ArgumentError,"{'count'}=>{ARRAY} is needed"
      end
    end
    alias put  simple_put

    def simple_get(hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          get_vars_char()
        elsif self.ntype=="sint"
          get_vars_short()
        elsif self.ntype=="int"
          get_vars_int()
        elsif self.ntype=="sfloat"
          get_vars_float()
        elsif self.ntype=="float"
          get_vars_double()
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      else
        raise ArgumentError,"hash isn't needed"
      end
    end

    def get_with_miss(*args)
      na = simple_get(*args)
      mv = nil
      MissValAtts.each do |nm|
        mv = get_att(nm)
        break if !mv.nil?
      end
      if mv.nil?
        na
      else
        NArrayMiss.to_nam_no_dup( na, (na.ne(mv[0])) )
      end
    end
    alias get get_with_miss

    def __rubber_expansion( args )
      if (id = args.index(false))  # substitution into id
        # false is incuded
        alen = args.length
        if args.rindex(false) != id
	  raise ArguemntError,"only one rubber dimension is permitted"
        elsif alen > rank+1
  	  raise ArgumentError, "too many args"
        end
        ar = ( id!=0 ? args[0..id-1] : [] )
        args = ar + [true]*(rank-alen+1) + args[id+1..-1]
      elsif args.length == 0   # to support empty [], []=
        args = [true]*rank
      end
      args
    end
    private :__rubber_expansion

    def [](*a)
      if a.length == 0
        return self.get
      end
      a = __rubber_expansion(a)
      count = Array.new
      set_count = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	count.push(i)
      elsif(i.is_a?(Range))
	count.push(i.first)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise TypeError, "Hash argument must be {a_Range, step}"
	end
	count.push(r.first) 
	set_count = true
      elsif(i.is_a?(TrueClass))
	count.push(0)
      elsif( i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  na_tmp = self[*a_new]
	  if n==0 then
	    k = at
	    if at > 0
	      a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	    end
	    shape_tmp = na_tmp.shape
	    shape_tmp[k] = i.length
	    na = na_tmp.class.new(na_tmp.typecode,*shape_tmp)
	    index_tmp = Array.new(shape_tmp.length,true)
	  end
	  index_tmp[k] = n..n
	  na[*index_tmp] = na_tmp
        end
	return na
      else
	 raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }

      if(set_count)
        na = self.get({"count"=>count})
      else
        na = self.get()
      end
      shape = na.shape
      (a.length-1).downto(0){ |i|
	 shape.delete_at(i) if a[i].is_a?(Fixnum)
      }
      na.reshape!( *shape )
      na
    end

    def []=(*a)
      val = a.pop
      a = __rubber_expansion(a)
      count = Array.new
      set_count = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	count.push(i)
      elsif(i.is_a?(Range))
	count.push(i.first)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise ArgumentError, "Hash argument must be {first..last, step}"
	end
	count.push(r.first) 
	set_stride = true
      elsif(i.is_a?(TrueClass))
	count.push(0)
      elsif(i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	val = NArray.to_na(val) if val.is_a?(Array)
	rank_of_subset = a.dup.delete_if{|v| v.is_a?(Fixnum)}.length
	if val.rank != rank_of_subset
	   raise "rank of the rhs (#{val.rank}) is not equal to the rank "+
                 "of the subset specified by #{a.inspect} (#{rank_of_subset})"
	end
	k = at
	a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	if i.length != val.shape[k]
	   raise "length of the #{k+1}-th dim of rhs is incorrect "+
                 "(#{i.length} for #{val.shape[k]})"
	end
	index_tmp = Array.new(val.rank,true) if !val.is_a?(Numeric) #==>Array-like
        for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  if !val.is_a?(Numeric) then
	    index_tmp[k] = n..n
	    self[*a_new] = val[*index_tmp]
	  else
	    self[*a_new] = val
	  end
	end
	return self
      else
	raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }

      if(set_count)
	self.put(val, {"count"=>count})
      else
	self.put(val)
      end
    end

    def inspect
      'HE5PtField:'+point.file.path+'?var='+name
    end
  end

###############################
#
# HE5Za クラスに関して
#
  class HE5Za

    class << HE5Za
       def create(file, zname)
         if(file.is_a?(String))
           file = HE5.open(file, "w")
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.create_zonal(zname)
       end

       def open(file, zname)
         if(file.is_a?(String))
           file = HE5.open(file, "r")
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.zonal(zname)
       end
    end
# detach zonal field
    def detach
      zadetach
    end
    alias closed detach
# close zonal field & close HDF-EOS5 file
    def close
      zadetach
      self.file.close
    end
# count Dimension Field
    def ndims
      count, strbufsize = nentries('HE5_HDFE_NENTDIM')
      return count
    end
# count Data Field 
    def nvars
      count, strbufsize = nentries('HE5_HDFE_NENTDFLD')
      return count
    end
# define HE5 Zonal Field Object ( Data Field )
    def def_var(name, datatype, dims, maxdim=nil)
      if datatype.is_a?(Numeric)
        datatype = char_typecode(datatype)
      end
      maxdim = "NULL" if maxdim == nil
      define(name, dims, maxdim, datatype)
    end
# set HE5 Zonal Field Object ( Data Field )
    def var(varname)
       setfield(varname)
    end
# return HE5 Zonal Field Object
    def vars( names=nil )   # return all if names==nil
       if names == nil
	  vars = (0..nvars()-1).collect{|varid| get_var(varid)}
       else
	  raise TypeError, "names is not an array" if ! names.is_a?(Array)
	  vars = names.collect{|name| var(name)}
	  raise ArgumentError, "One or more variables do not exist" if vars.include?(nil)
       end
       return vars
    end
# return Dimension Name list
    def dim_names
      ndims, dimlist, sizes, ntype = inqdims('HE5_HDFE_NENTDIM')
      return dimlist.split(/,/)
    end
# return Data Field Name list
    def var_names
      nflds, fieldlist, sizes, ntype = inquire('HE5_HDFE_NENTDFLD')
      return fieldlist.split(/,/)
    end
# set HE5 Zonal Field Object ( Data Field )
    def get_var(varid)
      nflds, fieldlist, sizes, ntype = inquire('HE5_HDFE_NENTDFLD')
      fieldname=fieldlist.split(",")[varid]
      return var(fieldname)
    end
    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end
    def att_names
      nattrs, attrnames, strbufsize = inqattrs()
      return attrnames.split(/,/)
    end
    def get_grpatt(grpattname)
      if grpatt_names.include?(grpattname)
        get_grpatt_(grpattname)
      else
        nil
      end
    end
    def grpatt_names
      nattrs, attrnames, strbufsize = inqgrpattrs()
      return attrnames.split(/,/)
    end

    def char_typecode(typecode)
      case typecode
      when 1
       ctype = "short"
      when 2 
       ctype = "sint"
      when 3 
       ctype = "int"
      when 4 
       ctype = "sfloat"
      when 5 
       ctype = "float"
      else
       raise TypeError, "not match"
      end
      ctype
    end

    def inspect
      'HE5Gd:'+name
    end
  end
###############################
#
# HE5ZaField クラスに関して
#
  class HE5ZaField
    
    MissValAtts = ["MissingValue","_FillValue"]

    class << HE5ZaField

       def create(file, zname,fldname)
         if(file.is_a?(String))
           zaid = HE5Za.create(file, zname)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
         file.var(fldname)
       end

       def open(file, zname, fldname)
         if(file.is_a?(String))
	   zaid = HE5Za.open(file, zname)
         elsif(!file.is_a?(HE5))
           raise TypeError,
                "1st arg must be a HDF-EOS5 (file object) or a String (path)"
         end
	 zaid.var(fldname)
       end
    end

    def ntype
      rank, dims, ntype, dimlist = fieldinfo()
      return ntype
    end
    alias typecode ntype

    def shape
      rank, dims, ntype, dimlist =fieldinfo
      return dims[-1..0].to_a   # NArray --> reverced --> Array
    end

    alias shape_current shape

    def shape_ul0
      sh = shape_current
      dim_names.each_with_index do |dnm,i|
        if dnm == "Unlim"
          sh[i] = 0
        end
      end
      sh
    end

    def ndims
      rank, dims, ntype, dimlist =fieldinfo()
      return rank
    end
    alias rank ndims

    def dim(dimid)
      rank, dims, ntype, dimlist =fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_val(dimid)
      rank, dims, ntype, dimlist =fieldinfo()
      return dimlist.split(",")[dimid]
    end

    def dim_names
      rank, dims, ntype, dimlist =fieldinfo()
      return dimlist.split(",").reverse
    end

    def name=
      raise "name= not supported"
    end

    def natts
      nattrs, attrname, strbufsize = inqlocattrs()
      return nattrs
    end

    def get_att(attname)
      if att_names.include?(attname)
        get_att_(attname)
      else
        nil
      end
    end

    def att_names
      nattrs, attrnames, strbufsize = inqlocattrs()
      return attrnames.split(/,/)
    end

    def each_att
      attlist=Array.new
      attnames=att_names()
      attnum = natts()
      attnames.each{|attname|
        list=Array.new
        attrval=att(attname)
        list.push(attname, attrval)
        attlist.push(list)
      }
      attlist
    end

    def put_att(name,value,atttype=nil)
       count = Array.new
       count[0] = value.size
       writelocattr(name,atttype,count,value)
    end

    def attr
      @attr
    end

    def simple_put(var,hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          put_vars_char([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="sint"
          put_vars_short([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="int"
          put_vars_int([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="sfloat"
          put_vars_float([0,0,0,0,0,0,0,0], nil, nil, var)
        elsif self.ntype=="float"
          put_vars_double([0,0,0,0,0,0,0,0], nil, nil, var)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      elsif hash.key?("index")==true ||  hash.key?("start")==true
        h_sta = hash["start"]
        endq = hash.key?("end")
        strq = hash.key?("stride")
        if endq == false && strq == false
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, nil, nil, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, nil, nil, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, nil, nil, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, nil, nil, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, nil, nil, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == true && strq == false
 	  h_end = hash["end"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, nil, h_end, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, nil, h_end, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, nil, h_end, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, nil, h_end, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, nil, h_end, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == false && strq == true
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, h_str, nil, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, h_str, nil, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, h_str, nil, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, h_str, nil, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, h_str, nil, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        else endq == true && strq == true
	  h_end = hash["end"]
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            put_vars_char(h_sta, h_str, h_end, var)
          elsif self.ntype=="sint"
            put_vars_short(h_sta, h_str, h_end, var)
          elsif self.ntype=="int"
            put_vars_int(h_sta, h_str, h_end, var)
          elsif self.ntype=="sfloat"
            put_vars_float(h_sta, h_str, h_end, var)
          elsif self.ntype=="float"
            put_vars_double(h_sta, h_str, h_end, var)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        end
      else
        raise ArgumentError,"{'start'}=>{ARRAY} or {'index'}=>{ARRAY} is needed"
      end
    end
    alias put  simple_put

    def simple_get(hash=nil)
      if hash == nil
        if self.ntype == "char" || self.ntype=="byte"
          get_vars_char([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="sint"
          get_vars_short([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="int"
          get_vars_int([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="sfloat"
          get_vars_float([0,0,0,0,0,0,0,0], nil, nil)
        elsif self.ntype=="float"
          get_vars_double([0,0,0,0,0,0,0,0], nil, nil)
        else
          raise ArgumentError, "variable type isn't supported in HDF-EOS5"
        end
      elsif hash.key?("index")==true ||  hash.key?("start")==true
        h_sta = hash["start"]
        endq = hash.key?("end")
        strq = hash.key?("stride")
        if endq == false && strq == false
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, nil, nil)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, nil, nil)
          elsif self.ntype=="int"
            get_vars_int(h_sta, nil, nil)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, nil, nil)
          elsif self.ntype=="float"
            get_vars_double(h_sta, nil, nil)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == true && strq == false
 	  h_end = hash["end"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, nil, h_end)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, nil, h_end)
          elsif self.ntype=="int"
            get_vars_int(h_sta, nil, h_end)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, nil, h_end)
          elsif self.ntype=="float"
            get_vars_double(h_sta, nil, h_end)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        elsif endq == false && strq == true
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, h_str, nil)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, h_str, nil)
          elsif self.ntype=="int"
            get_vars_int(h_sta, h_str, nil)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, h_str, nil)
          elsif self.ntype=="float"
            get_vars_double(h_sta, h_str, nil)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        else endq == true && strq == true
	  h_end = hash["end"]
	  h_str = hash["stride"]
          if self.ntype == "char" || self.ntype=="byte"
            get_vars_char(h_sta, h_str, h_end)
          elsif self.ntype=="sint"
            get_vars_short(h_sta, h_str, h_end)
          elsif self.ntype=="int"
            get_vars_int(h_sta, h_str, h_end)
          elsif self.ntype=="sfloat"
            get_vars_float(h_sta, h_str, h_end)
          elsif self.ntype=="float"
            get_vars_double(h_sta, h_str, h_end)
          else
            raise ArgumentError, "variable type isn't supported in HDF-EOS5"
          end
        end
      else
        raise ArgumentError,"{'start'}=>{ARRAY} or {'index'}=>{ARRAY} is needed"
      end
    end

    def get_with_miss(*args)
      na = simple_get(*args)
      mv = nil
      MissValAtts.each do |nm|
        mv = get_att(nm)
        break if !mv.nil?
      end
      if mv.nil?
        na
      else
        NArrayMiss.to_nam_no_dup( na, (na.ne(mv[0])) )
      end
    end
    alias get get_with_miss

    def __rubber_expansion( args )
      if (id = args.index(false))  # substitution into id
        # false is incuded
        alen = args.length
        if args.rindex(false) != id
	  raise ArguemntError,"only one rubber dimension is permitted"
        elsif alen > rank+1
  	  raise ArgumentError, "too many args"
        end
        ar = ( id!=0 ? args[0..id-1] : [] )
        args = ar + [true]*(rank-alen+1) + args[id+1..-1]
      elsif args.length == 0   # to support empty [], []=
        args = [true]*rank
      end
      args
    end
    private :__rubber_expansion

    def [](*a)
      if a.length == 0
        return self.get
      end
      a = __rubber_expansion(a)
      first = Array.new
      last = Array.new
      stride = Array.new
      set_stride = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	first.push(i)
	last.push(i)
	stride.push(1)
      elsif(i.is_a?(Range))
	first.push(i.first)
	last.push(i.exclude_end? ? i.last-1 : i.last)
	stride.push(1)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise TypeError, "Hash argument must be {a_Range, step}"
	end
	first.push(r.first) 
	last.push(r.exclude_end? ? r.last-1 : r.last)
	stride.push(s)
	set_stride = true
      elsif(i.is_a?(TrueClass))
	first.push(0)
	last.push(-1)
        stride.push(1)
      elsif( i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  na_tmp = self[*a_new]
	  if n==0 then
	    k = at
	    if at > 0
	      a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	    end
	    shape_tmp = na_tmp.shape
	    shape_tmp[k] = i.length
	    na = na_tmp.class.new(na_tmp.typecode,*shape_tmp)
	    index_tmp = Array.new(shape_tmp.length,true)
	  end
	  index_tmp[k] = n..n
	  na[*index_tmp] = na_tmp
        end
	return na
      else
	 raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }

      if(set_stride)
        na = self.get({"start"=>first, "end"=>last, "stride"=>stride})
      else
        na = self.get({"start"=>first, "end"=>last})
      end
      shape = na.shape
      (a.length-1).downto(0){ |i|
	 shape.delete_at(i) if a[i].is_a?(Fixnum)
      }
      na.reshape!( *shape )
      na
    end

    def []=(*a)
      val = a.pop
      a = __rubber_expansion(a)
      first = Array.new
      last = Array.new
      stride = Array.new
      set_stride = false
      a.each{|i|
      if(i.is_a?(Fixnum))
	first.push(i)
	last.push(i)
	stride.push(1)
      elsif(i.is_a?(Range))
	first.push(i.first)
	last.push(i.exclude_end? ? i.last-1 : i.last)
	stride.push(1)
      elsif(i.is_a?(Hash))
	r = (i.to_a[0])[0]
        s = (i.to_a[0])[1]
        if ( !( r.is_a?(Range) ) || ! ( s.is_a?(Integer) ) )
	    raise ArgumentError, "Hash argument must be {first..last, step}"
	end
	first.push(r.first) 
	last.push(r.exclude_end? ? r.last-1 : r.last)
	stride.push(s)
	set_stride = true
      elsif(i.is_a?(TrueClass))
	first.push(0)
	last.push(-1)
	stride.push(1)
      elsif(i.is_a?(Array) || i.is_a?(NArray))
	a_new = a.dup
	at = a.index(i)
	i = NArray.to_na(i) if i.is_a?(Array)
	val = NArray.to_na(val) if val.is_a?(Array)
	rank_of_subset = a.dup.delete_if{|v| v.is_a?(Fixnum)}.length
	if val.rank != rank_of_subset
	   raise "rank of the rhs (#{val.rank}) is not equal to the rank "+
                 "of the subset specified by #{a.inspect} (#{rank_of_subset})"
	end
	k = at
	a[0..at-1].each{|x| if x.is_a?(Fixnum) then k -= 1 end}
	if i.length != val.shape[k]
	   raise "length of the #{k+1}-th dim of rhs is incorrect "+
                 "(#{i.length} for #{val.shape[k]})"
	end
	index_tmp = Array.new(val.rank,true) if !val.is_a?(Numeric) #==>Array-like
        for n in 0..i.length-1
	  a_new[at] = i[n]..i[n]
	  if !val.is_a?(Numeric) then
	    index_tmp[k] = n..n
	    self[*a_new] = val[*index_tmp]
	  else
	    self[*a_new] = val
	  end
	end
	return self
      else
	raise TypeError, "argument must be Fixnum, Range, Hash, TrueClass, Array, or NArray"
      end
      }

      if(set_stride)
	self.put(val, {"start"=>first, "end"=>last, "stride"=>stride})
      else
	self.put(val, {"start"=>first, "end"=>last})
      end
    end

    def inspect
      'HE5ZaField:'+za.file.path+'?var='+name
    end
  end
end
