#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include <asf.h>
#include "asf_tiff.h"

#include <gsl/gsl_math.h>
#include <proj_api.h>

#include "asf_jpeg.h"
#include <png.h>
#include "envi.h"

#include "dateUtil.h"
#include <time.h>
#include "matrix.h"
#include <asf_nan.h>
#include <asf_endian.h>
#include <asf_meta.h>
#include <asf_export.h>
#include <asf_raster.h>
#include <float_image.h>
#include <spheroids.h>
#include <typlim.h>
#include <hdf5.h>

#define RES 16
#define MAX_PTS 256

void h5_att_double(hid_t data, hid_t space, char *name, double value)
{
  hid_t attr = H5Acreate(data, name, H5T_NATIVE_DOUBLE, space, H5P_DEFAULT,
			 H5P_DEFAULT);
  H5Awrite(attr, H5T_NATIVE_DOUBLE, &value);
  H5Aclose(attr);
}

void h5_att_float(hid_t data, hid_t space, char *name, float value)
{
  hid_t attr = H5Acreate(data, name, H5T_NATIVE_FLOAT, space, H5P_DEFAULT,
			 H5P_DEFAULT);
  H5Awrite(attr, H5T_NATIVE_FLOAT, &value);
  H5Aclose(attr);
}

void h5_att_int(hid_t data, hid_t space, char *name, int value)
{
  hid_t attr = H5Acreate(data, name, H5T_NATIVE_INT, space, H5P_DEFAULT,
			 H5P_DEFAULT);
  H5Awrite(attr, H5T_NATIVE_INT, &value);
  H5Aclose(attr);
}

void h5_att_float2(hid_t data, hid_t space, char *name, float *value)
{
  hid_t attr = H5Acreate(data, name, H5T_NATIVE_FLOAT, space, H5P_DEFAULT,
			 H5P_DEFAULT);
  H5Awrite(attr, H5T_NATIVE_FLOAT, value);
  H5Aclose(attr);
}

void h5_att_str(hid_t data, hid_t space, char *name, char *value)
{
  hid_t str = H5Tcopy(H5T_C_S1);
  H5Tset_size(str, strlen(value));
  hid_t attr = H5Acreate(data, name, str, space, H5P_DEFAULT, H5P_DEFAULT);
  H5Awrite(attr, str, value);
  H5Aclose(attr);
}

void h5_value_double(hid_t file, char *group, char *name,
		     double value, char *long_name, char *units)
{
  char meta[100];
  sprintf(meta, "%s/%s", group, name);
  hid_t h5_space = H5Screate(H5S_SCALAR);
  hid_t h5_data = H5Dcreate(file, meta, H5T_NATIVE_DOUBLE, h5_space,
			    H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  H5Dwrite(h5_data, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
  h5_att_str(h5_data, h5_space, "long_name", long_name);
  if (units && strlen(units) > 0)
    h5_att_str(h5_data, h5_space, "units", units);
  H5Dclose(h5_data);
  H5Sclose(h5_space);
}

void h5_value_float(hid_t file, char *group, char *name,
		    float value, char *long_name, char *units)
{
  char meta[100];
  sprintf(meta, "%s/%s", group, name);
  hid_t h5_space = H5Screate(H5S_SCALAR);
  hid_t h5_data = H5Dcreate(file, meta, H5T_NATIVE_FLOAT, h5_space,
			    H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  H5Dwrite(h5_data, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
  h5_att_str(h5_data, h5_space, "long_name", long_name);
  if (units && strlen(units) > 0)
    h5_att_str(h5_data, h5_space, "units", units);
  H5Dclose(h5_data);
  H5Sclose(h5_space);
}

void h5_value_int(hid_t file, char *group, char *name,
		  int value, char *long_name, char *units)
{
  char meta[100];
  sprintf(meta, "%s/%s", group, name);
  hid_t h5_space = H5Screate(H5S_SCALAR);
  hid_t h5_data = H5Dcreate(file, meta, H5T_NATIVE_INT, h5_space,
			    H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  H5Dwrite(h5_data, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
  h5_att_str(h5_data, h5_space, "long_name", long_name);
  if (units && strlen(units) > 0)
    h5_att_str(h5_data, h5_space, "units", units);
  H5Dclose(h5_data);
  H5Sclose(h5_space);
}

void h5_value_str(hid_t file, char *group, char *name,
		  char *value, char *long_name, char *units)
{
  char meta[100];
  sprintf(meta, "%s/%s", group, name);
  hid_t h5_space = H5Screate(H5S_SCALAR);
  hid_t h5_str = H5Tcopy(H5T_C_S1);
  H5Tset_size(h5_str, strlen(value));
  hid_t h5_data = H5Dcreate(file, meta, h5_str, h5_space,
			 H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  H5Dwrite(h5_data, h5_str, H5S_ALL, H5S_ALL, H5P_DEFAULT, value);
  h5_att_str(h5_data, h5_space, "long_name", long_name);
  if (units && strlen(units) > 0)
    h5_att_str(h5_data, h5_space, "units", units);
  H5Dclose(h5_data);
  H5Sclose(h5_space);
}

h5_t *initialize_h5_file(const char *output_file_name, meta_parameters *md)
{
  hid_t h5_file, h5_datagroup, h5_metagroup, h5_data, h5_proj;
  hid_t h5_array, h5_string, h5_time, h5_lat, h5_lon, h5_xgrid, h5_ygrid;
  int ii, kk, complex=FALSE, projected=FALSE;
  char *spatial_ref=NULL, *datum=NULL, *spheroid=NULL;
  char dataset[50], group[50], band[5], str_attr[50], tmp[50];
  double lfValue;

  // Convenience variables
  meta_general *mg = md->general;
  meta_sar *ms = md->sar;
  meta_state_vectors *mo = md->state_vectors;
  meta_projection *mp = md->projection;

  // Check whether data is map projected
  if (mp && mp->type != SCANSAR_PROJECTION)
    //asfPrintError("Image is map projected. Wrong initialization function!\n");
    projected = TRUE;

  // Initialize the HDF pointer structure
  h5_t *h5 = (h5_t *) MALLOC(sizeof(h5_t));
  int band_count = mg->band_count;
  h5->var_count = band_count;
  h5->var = (hid_t *) MALLOC(sizeof(hid_t)*h5->var_count);

  // Check for complex data
  if (mg->image_data_type == COMPLEX_IMAGE)
    complex = TRUE;

  // Create new HDF5 file
  h5_file =
    H5Fcreate(output_file_name, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
  h5->file = h5_file;

  // Create data space
  int samples = mg->sample_count;
  int lines = mg->line_count;
  hsize_t dims[2] = { lines, samples };
  hsize_t cdims[2] = { 100, samples };
  hsize_t rdims[2] = { 1, 2 };
  h5_array = H5Screate_simple(2, dims, NULL);
  h5->space = h5_array;
  h5_string = H5Screate(H5S_SCALAR);
  hid_t h5_range = H5Screate(H5S_SIMPLE);
  H5Sset_extent_simple(h5_range, 2, rdims, NULL);

  // Create data structure
  char **band_name = extract_band_names(mg->bands, band_count);
  hid_t h5_plist = H5Pcreate(H5P_DATASET_CREATE);
  H5Pset_chunk(h5_plist, 2, cdims);
  H5Pset_deflate(h5_plist, 6);

  // Create a data group
  sprintf(group, "/data");
  h5_datagroup = H5Gcreate(h5_file, group, H5P_DEFAULT, H5P_DEFAULT,
			   H5P_DEFAULT);
  // Projection information
  if (projected) {
    sprintf(group, "/data/projection");
    h5_proj = H5Gcreate(h5_file, group, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);

    if (mp->type == UNIVERSAL_TRANSVERSE_MERCATOR) {

      h5_att_str(h5_proj, h5_string, "grid_mapping_name",
		 "transverse_mercator");
      h5_att_double(h5_proj, h5_string, "scale_factor_at_central_meridian",
		    mp->param.utm.scale_factor);
      h5_att_double(h5_proj, h5_string, "longitude_of_central_meridian",
		    mp->param.utm.lon0);
      h5_att_double(h5_proj, h5_string, "latitude_of_projection_origin",
		    mp->param.utm.lat0);
      h5_att_double(h5_proj, h5_string, "false_easting",
		    mp->param.utm.false_easting);
      h5_att_double(h5_proj, h5_string, "false_northing",
		    mp->param.utm.false_northing);
      h5_att_str(h5_proj, h5_string, "projection_x_coordinate", "xgrid");
      h5_att_str(h5_proj, h5_string, "projection_y_coordinate", "ygrid");
      h5_att_str(h5_proj, h5_string, "units", "meters");
      h5_att_double(h5_proj, h5_string, "grid_boundary_top_projected_y",
		    mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      h5_att_double(h5_proj, h5_string, "grid_boundary_bottom_projected_y",
		    lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      h5_att_double(h5_proj, h5_string, "grid_boundary_right_projected_x",
		    lfValue);
      h5_att_double(h5_proj, h5_string, "grid_boundary_left_projected_x",
		    mp->startX);
      spatial_ref = (char *) MALLOC(sizeof(char)*1024);
      datum = (char *) datum_toString(mp->datum);
      spheroid = (char *) spheroid_toString(mp->spheroid);
      double flat = mp->re_major/(mp->re_major - mp->re_minor);
      sprintf(spatial_ref, "PROJCS[\"%s_UTM_Zone_%d%c\",GEOGCS[\"GCS_%s\",DATUM[\"D_%s\",SPHEROID[\"%s\",%.1lf,%-16.11g]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"False_Easting\",%.1lf],PARAMETER[\"False_Northing\",%.1lf],PARAMETER[\"Central_Meridian\",%.1lf],PARAMETER[\"Scale_Factor\",%.4lf],PARAMETER[\"Latitude_Of_Origin\",%.1lf],UNIT[\"Meter\",1]]",
	      spheroid, mp->param.utm.zone, mp->hem, spheroid, datum,
	      spheroid, mp->re_major, flat, mp->param.utm.false_easting,
	      mp->param.utm.false_northing, mp->param.utm.lon0,
	      mp->param.utm.scale_factor, mp->param.utm.lat0);
      h5_att_str(h5_proj, h5_string, "spatial_ref", spatial_ref);
      sprintf(str_attr, "+proj=utm +zone=%d", mp->param.utm.zone);
      if (mg->center_latitude < 0)
	strcat(str_attr, " +south");
      h5_att_str(h5_proj, h5_string, "proj4text", str_attr);
      h5_att_int(h5_proj, h5_string, "zone", mp->param.utm.zone);
      h5_att_double(h5_proj, h5_string, "semimajor_radius", mp->re_major);
      h5_att_double(h5_proj, h5_string, "semiminor_radius", mp->re_minor);
      sprintf(str_attr, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      h5_att_str(h5_proj, h5_string, "GeoTransform", str_attr);
    }
    else if (mp->type == POLAR_STEREOGRAPHIC) {

      h5_att_str(h5_proj, h5_string, "grid_mapping_name",
		 "polar_stereographic");
      h5_att_double(h5_proj, h5_string, "straight_vertical_longitude_from_pole",
		    mp->param.ps.slon);
      h5_att_double(h5_proj, h5_string, "latitude_of_projection_origin",
		    mp->param.ps.slat);
      h5_att_double(h5_proj, h5_string, "scale_factor_at_projection_origin",
		    1.0);
      h5_att_double(h5_proj, h5_string, "false_easting",
		    mp->param.ps.false_easting);
      h5_att_double(h5_proj, h5_string, "false_northing",
		    mp->param.ps.false_northing);
      h5_att_str(h5_proj, h5_string, "projection_x_coordinate", "xgrid");
      h5_att_str(h5_proj, h5_string, "projection_y_coordinate", "ygrid");
      h5_att_str(h5_proj, h5_string, "units", "meters");
      h5_att_double(h5_proj, h5_string, "grid_boundary_top_projected_y",
		    mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      h5_att_double(h5_proj, h5_string, "grid_boundary_bottom_projected_y",
		    lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      h5_att_double(h5_proj, h5_string, "grid_boundary_right_projected_x",
		    lfValue);
      h5_att_double(h5_proj, h5_string, "grid_boundary_left_projected_x",
		    mp->startX);
      spatial_ref = (char *) MALLOC(sizeof(char)*1024);
      double flat = mp->re_major/(mp->re_major - mp->re_minor);
      sprintf(spatial_ref, "PROJCS[\"Stereographic_North_Pole\",GEOGCS[\"unnamed ellipse\",DATUM[\"D_unknown\",SPHEROID[\"Unknown\",%.3lf,%-16.11g]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0002247191011236]],PROJECTION[\"Stereographic_North_Pole\"],PARAMETER[\"standard_parallel_1\",%.4lf],PARAMETER[\"central_meridian\",%.4lf],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",%.1lf],PARAMETER[\"false_northing\",%.1lf],UNIT[\"Meter\",1, AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"3411\"]]",
	      mp->re_major, flat, mp->param.ps.slat, mp->param.ps.slon,
	      mp->param.ps.false_easting, mp->param.ps.false_northing);
      h5_att_str(h5_proj, h5_string, "spatial_ref", spatial_ref);
      if (mp->param.ps.is_north_pole)
	sprintf(str_attr, "+proj=stere +lat_0=90.0000 +lat_ts=%.4lf "
		"+lon_0=%.4lf +k=1 +x_0=%.3lf +y_0=%.3lf +a=%.3lf +b=%.3lf "
		"+units=m +no_defs", mp->param.ps.slat, mp->param.ps.slon,
		mp->param.ps.false_easting, mp->param.ps.false_northing,
		mp->re_major, mp->re_minor);
      else
	sprintf(str_attr, "+proj=stere +lat_0=-90.0000 +lat_ts=%.4lf "
		"+lon_0=%.4lf +k=1 +x_0=%.3lf +y_0=%.3lf +a=%.3lf +b=%.3lf "
		"+units=m +no_defs", mp->param.ps.slat, mp->param.ps.slon,
		mp->param.ps.false_easting, mp->param.ps.false_northing,
		mp->re_major, mp->re_minor);
      h5_att_str(h5_proj, h5_string, "proj4text", str_attr);
      h5_att_double(h5_proj, h5_string, "semimajor_radius", mp->re_major);
      h5_att_double(h5_proj, h5_string, "semiminor_radius", mp->re_minor);
      sprintf(str_attr, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      h5_att_str(h5_proj, h5_string, "GeoTransform", str_attr);
    }
    else if (mp->type == ALBERS_EQUAL_AREA) {

      h5_att_str(h5_proj, h5_string, "grid_mapping_name",
		 "albers_conical_equal_area");
      h5_att_double(h5_proj, h5_string, "standard_parallel_1",
		    mp->param.albers.std_parallel1);
      h5_att_double(h5_proj, h5_string, "standard_parallel_2",
		    mp->param.albers.std_parallel2);
      h5_att_double(h5_proj, h5_string, "longitude_of_central_meridian",
		    mp->param.albers.center_meridian);
      h5_att_double(h5_proj, h5_string, "latitude_of_projection_origin",
		    mp->param.albers.orig_latitude);
      h5_att_double(h5_proj, h5_string, "false_easting",
		    mp->param.albers.false_easting);
      h5_att_double(h5_proj, h5_string, "false_northing",
		    mp->param.albers.false_northing);
      h5_att_str(h5_proj, h5_string, "projection_x_coordinate", "xgrid");
      h5_att_str(h5_proj, h5_string, "projection_y_coordinate", "ygrid");
      h5_att_str(h5_proj, h5_string, "units", "meters");
      h5_att_double(h5_proj, h5_string, "grid_boundary_top_projected_y",
		    mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      h5_att_double(h5_proj, h5_string, "grid_boundary_bottom_projected_y",
		    lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      h5_att_double(h5_proj, h5_string, "grid_boundary_right_projected_x",
		    lfValue);
      h5_att_double(h5_proj, h5_string, "grid_boundary_left_projected_x",
		    mp->startX);
      spatial_ref = (char *) MALLOC(sizeof(char)*1024);
      datum = (char *) datum_toString(mp->datum);
      spheroid = (char *) spheroid_toString(mp->spheroid);
      double flat = mp->re_major/(mp->re_major - mp->re_minor);
      sprintf(spatial_ref, "PROJCS[\"Albers_Equal_Area_Conic\",GEOGCS[\"GCS_%s\",DATUM[\"D_%s\",SPHEROID[\"%s\",%.3lf,%-16.11g]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199432955]],PROJECTION[\"Albers\"],PARAMETER[\"False_Easting\",%.3lf],PARAMETER[\"False_Northing\",%.3lf],PARAMETER[\"Central_Meridian\",%.4lf],PARAMETER[\"Standard_Parallel_1\",%.4lf],PARAMETER[\"Standard_Parallel_2\",%.4lf],PARAMETER[\"Latitude_Of_Origin\",%.4lf],UNIT[\"Meter\",1]]",
	      datum, datum, spheroid, mp->re_major, flat,
	      mp->param.albers.false_easting, mp->param.albers.false_northing,
	      mp->param.albers.center_meridian, mp->param.albers.std_parallel1,
	      mp->param.albers.std_parallel2, mp->param.albers.orig_latitude);
      h5_att_str(h5_proj, h5_string, "spatial_ref", spatial_ref);
      sprintf(str_attr, "+proj=aea +lat_1=%.4lf +lat_2=%.4lf +lat_0=%.4lf "
	      "+lon_0=%.4lf +x_0=%.3lf +y_0=%.3lf",
	      mp->param.albers.std_parallel1, mp->param.albers.std_parallel2,
	      mp->param.albers.orig_latitude, mp->param.albers.center_meridian,
	      mp->param.albers.false_easting, mp->param.albers.false_northing);
      h5_att_str(h5_proj, h5_string, "proj4text", str_attr);
      h5_att_double(h5_proj, h5_string, "semimajor_radius", mp->re_major);
      h5_att_double(h5_proj, h5_string, "semiminor_radius", mp->re_minor);
      sprintf(str_attr, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      h5_att_str(h5_proj, h5_string, "GeoTransform", str_attr);
    }
    else if (mp->type == LAMBERT_CONFORMAL_CONIC) {

      h5_att_str(h5_proj, h5_string, "grid_mapping_name",
		 "lambert_conformal_conic");
      h5_att_double(h5_proj, h5_string, "standard_parallel_1",
		    mp->param.lamcc.plat1);
      h5_att_double(h5_proj, h5_string, "standard_parallel_2",
		    mp->param.lamcc.plat2);
      h5_att_double(h5_proj, h5_string, "longitude_of_central_meridian",
		    mp->param.lamcc.lon0);
      h5_att_double(h5_proj, h5_string, "latitude_of_projection_origin",
		    mp->param.lamcc.lat0);
      h5_att_double(h5_proj, h5_string, "false_easting",
		    mp->param.lamcc.false_easting);
      h5_att_double(h5_proj, h5_string, "false_northing",
		    mp->param.lamcc.false_northing);
      h5_att_str(h5_proj, h5_string, "projection_x_coordinate", "xgrid");
      h5_att_str(h5_proj, h5_string, "projection_y_coordinate", "ygrid");
      h5_att_str(h5_proj, h5_string, "units", "meters");
      h5_att_double(h5_proj, h5_string, "grid_boundary_top_projected_y",
		    mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      h5_att_double(h5_proj, h5_string, "grid_boundary_bottom_projected_y",
		    lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      h5_att_double(h5_proj, h5_string, "grid_boundary_right_projected_x",
		    lfValue);
      h5_att_double(h5_proj, h5_string, "grid_boundary_left_projected_x",
		    mp->startX);
      spatial_ref = (char *) MALLOC(sizeof(char)*1024);
      datum = (char *) datum_toString(mp->datum);
      spheroid = (char *) spheroid_toString(mp->spheroid);
      double flat = mp->re_major/(mp->re_major - mp->re_minor);
      sprintf(spatial_ref, "PROJCS[\"Lambert_Conformal_Conic\",GEOGCS[\"GCS_%s\",DATUM[\"D_%s\",SPHEROID[\"%s\",%.3lf,%-16.11g]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199432955]],PROJECTION[\"Lambert_Conformal_Conic\"],PARAMETER[\"False_Easting\",%.3lf],PARAMETER[\"False_Northing\",%.3lf],PARAMETER[\"Central_Meridian\",%.4lf],PARAMETER[\"Standard_Parallel_1\",%.4lf],PARAMETER[\"Standard_Parallel_2\",%.4lf],PARAMETER[\"Latitude_Of_Origin\",%.4lf],UNIT[\"Meter\",1]]",
	      datum, datum, spheroid, mp->re_major, flat,
	      mp->param.lamcc.false_easting, mp->param.lamcc.false_northing,
	      mp->param.lamcc.lon0, mp->param.lamcc.plat1,
	      mp->param.lamcc.plat2, mp->param.lamcc.lat0);
      h5_att_str(h5_proj, h5_string, "spatial_ref", spatial_ref);
      sprintf(str_attr, "+proj=lcc +lat_1=%.4lf +lat_2=%.4lf +lat_0=%.4lf "
	      "+lon_0=%.4lf +x_0=%.3lf +y_0=%.3lf",
	      mp->param.lamcc.plat1, mp->param.lamcc.plat2,
	      mp->param.lamcc.lat0, mp->param.lamcc.lon0,
	      mp->param.lamcc.false_easting, mp->param.lamcc.false_northing);
      h5_att_str(h5_proj, h5_string, "proj4text", str_attr);
      h5_att_double(h5_proj, h5_string, "semimajor_radius", mp->re_major);
      h5_att_double(h5_proj, h5_string, "semiminor_radius", mp->re_minor);
      sprintf(str_attr, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      h5_att_str(h5_proj, h5_string, "GeoTransform", str_attr);
    }
    else if (mp->type == LAMBERT_AZIMUTHAL_EQUAL_AREA) {

      h5_att_str(h5_proj, h5_string, "grid_mapping_name",
		 "lambert_azimuthal_equal_area");
      h5_att_double(h5_proj, h5_string, "longitude_of_projection_origin",
		    mp->param.lamaz.center_lon);
      h5_att_double(h5_proj, h5_string, "latitude_of_projection_origin",
		    mp->param.lamaz.center_lat);
      h5_att_double(h5_proj, h5_string, "false_easting",
		    mp->param.lamaz.false_easting);
      h5_att_double(h5_proj, h5_string, "false_northing",
		    mp->param.lamaz.false_northing);
      h5_att_str(h5_proj, h5_string, "projection_x_coordinate", "xgrid");
      h5_att_str(h5_proj, h5_string, "projection_y_coordinate", "ygrid");
      h5_att_str(h5_proj, h5_string, "units", "meters");
      h5_att_double(h5_proj, h5_string, "grid_boundary_top_projected_y",
		    mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      h5_att_double(h5_proj, h5_string, "grid_boundary_bottom_projected_y",
		    lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      h5_att_double(h5_proj, h5_string, "grid_boundary_right_projected_x",
		    lfValue);
      h5_att_double(h5_proj, h5_string, "grid_boundary_left_projected_x",
		    mp->startX);
      spatial_ref = (char *) MALLOC(sizeof(char)*1024);
      datum = (char *) datum_toString(mp->datum);
      spheroid = (char *) spheroid_toString(mp->spheroid);
      double flat = mp->re_major/(mp->re_major - mp->re_minor);
      sprintf(spatial_ref, "PROJCS[\"Lambert_Azimuthal_Equal_Area\",GEOGCS[\"GCS_%s\",DATUM[\"D_%s\",SPHEROID[\"%s\",%.3lf,%-16.11g]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199432955]],PROJECTION[\"Lambert_Conformal_Conic\"],PARAMETER[\"False_Easting\",%.3lf],PARAMETER[\"False_Northing\",%.3lf],PARAMETER[\"Central_Meridian\",%.4lf],PARAMETER[\"Latitude_Of_Origin\",%.4lf],UNIT[\"Meter\",1]]",
	      datum, datum, spheroid, mp->re_major, flat,
	      mp->param.lamaz.false_easting, mp->param.lamaz.false_northing,
	      mp->param.lamaz.center_lon, mp->param.lamaz.center_lat);
      h5_att_str(h5_proj, h5_string, "spatial_ref", spatial_ref);
      sprintf(str_attr, "+proj=laea +lat_0=%.4lf +lon_0=%.4lf +x_0=%.3lf "
	      "+y_0=%.3lf",
	      mp->param.lamaz.center_lat, mp->param.lamaz.center_lon,
	      mp->param.lamaz.false_easting, mp->param.lamaz.false_northing);
      h5_att_str(h5_proj, h5_string, "proj4text", str_attr);
      h5_att_double(h5_proj, h5_string, "semimajor_radius", mp->re_major);
      h5_att_double(h5_proj, h5_string, "semiminor_radius", mp->re_minor);
      sprintf(str_attr, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      h5_att_str(h5_proj, h5_string, "GeoTransform", str_attr);
    }
  }

  for (ii=0; ii<band_count; ii++) {

    // Create data set
    strncpy(band, band_name[ii], 2);
    band[2] = '\0';
    sprintf(dataset, "/data/%s_AMPLITUDE_IMAGE", band);
    h5_data = H5Dcreate(h5_file, dataset, H5T_NATIVE_FLOAT, h5_array,
			H5P_DEFAULT, h5_plist, H5P_DEFAULT);
    h5->var[ii] = h5_data;

    // Add attributes (from CF convention)
    sprintf(str_attr, "%s", mg->sensor);
    if (mg->image_data_type < 9)
      strcat(str_attr, " radar backscatter");
    if (mg->radiometry >= r_SIGMA_DB && mg->radiometry <= r_GAMMA_DB)
      strcat(str_attr, " in dB");
    h5_att_str(h5_data, h5_string, "long_name", str_attr);
    h5_att_str(h5_data, h5_string, "cell_methods", "area: backscatter value");
    h5_att_str(h5_data, h5_string, "units", "1");
    h5_att_str(h5_data, h5_string, "units_description",
	       "unitless normalized radar cross-section");
    if (mg->radiometry >= r_SIGMA && mg->radiometry <= r_GAMMA)
      strcat(str_attr, " stored as powerscale");
    else if (mg->radiometry >= r_SIGMA_DB && mg->radiometry <= r_GAMMA_DB)
      strcat(str_attr, " stored as dB=10*log10(*)");
    h5_att_float(h5_data, h5_string, "_FillValue", -999);
    h5_att_str(h5_data, h5_string, "coordinates", "longitude latitude");
    if (projected)
      h5_att_str(h5_data, h5_string, "grid_mapping", "projection");

    // Close up
    H5Dclose(h5_data);
  }

  // Extra bands - Time
  asfPrintStatus("Storing band 'time' ...\n");
  float serial_date = seconds_from_str(mg->acquisition_date);
  sprintf(dataset, "/data/time");
  h5_time = H5Dcreate(h5_file, dataset, H5T_NATIVE_FLOAT, h5_string,
		      H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  H5Dwrite(h5_time, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
	   &serial_date);
  h5_att_str(h5_time, h5_string, "units", "seconds since 1900-01-01T00:00:00Z");
  h5_att_str(h5_time, h5_string, "references", "scene center time");
  h5_att_str(h5_time, h5_string, "standard_name", "time");
  h5_att_str(h5_time, h5_string, "axis", "T");
  h5_att_str(h5_time, h5_string, "long_name", "serial date");
  H5Dclose(h5_time);

  // Extra bands - Longitude
  int nl = mg->line_count;
  int ns = mg->sample_count;
  long pixel_count = mg->line_count * mg->sample_count;
  double *value = (double *) MALLOC(sizeof(double)*MAX_PTS);
  double *l = (double *) MALLOC(sizeof(double)*MAX_PTS);
  double *s = (double *) MALLOC(sizeof(double)*MAX_PTS);
  double line, sample, lat, lon, first_value;
  float *lons = (float *) MALLOC(sizeof(float)*pixel_count);
  asfPrintStatus("Generating band 'longitude' ...\n");
  meta_get_latLon(md, 0, 0, 0.0, &lat, &lon);
  if (lon < 0.0)
    first_value = lon + 360.0;
  else
    first_value = lon;
  asfPrintStatus("Calculating grid for quadratic fit ...\n");
  for (ii=0; ii<RES; ii++) {
    for (kk=0; kk<RES; kk++) {
      line = ii * nl / RES;
      sample = kk * ns / RES;
      meta_get_latLon(md, line, sample, 0.0, &lat, &lon);
      l[ii*RES+kk] = line;
      s[ii*RES+kk] = sample;
      if (lon < 0.0)
	value[ii*RES+kk] = lon + 360.0;
      else
	value[ii*RES+kk] = lon;
    }
    asfLineMeter(ii, nl);
  }
  quadratic_2d q = find_quadratic(value, l, s, MAX_PTS);
  q.A = first_value;
  for (ii=0; ii<nl; ii++) {
    for (kk=0; kk<ns; kk++) {
      lons[ii*ns+kk] = (float)
	(q.A + q.B*ii + q.C*kk + q.D*ii*ii + q.E*ii*kk + q.F*kk*kk +
	 q.G*ii*ii*kk + q.H*ii*kk*kk + q.I*ii*ii*kk*kk + q.J*ii*ii*ii +
	 q.K*kk*kk*kk) - 360.0;
      if (lons[ii*ns+kk] < -180.0)
	lons[ii*ns+kk] += 360.0;
    }
    asfLineMeter(ii, nl);
  }
  asfPrintStatus("Storing band 'longitude' ...\n");
  sprintf(dataset, "/data/longitude");
  h5_lon = H5Dcreate(h5_file, dataset, H5T_NATIVE_FLOAT, h5_array,
		     H5P_DEFAULT, h5_plist, H5P_DEFAULT);
  H5Dwrite(h5_lon, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, lons);
  h5_att_str(h5_lon, h5_string, "units", "degrees_east");
  h5_att_str(h5_lon, h5_string, "long_name", "longitude");
  h5_att_str(h5_lon, h5_string, "standard_name", "longitude");
  float valid_range[2] = { -180.0, 180.0 };
  h5_att_float2(h5_lon, h5_range, "valid_range", valid_range);
  h5_att_float(h5_lon, h5_string, "_FillValue", -999);
  H5Dclose(h5_lon);
  FREE(lons);

  // Extra bands - Latitude
  float *lats = (float *) MALLOC(sizeof(float)*pixel_count);
  asfPrintStatus("Generating band 'latitude' ...\n");
  meta_get_latLon(md, 0, 0, 0.0, &lat, &lon);
  first_value = lat + 180.0;
  asfPrintStatus("Calculating grid for quadratic fit ...\n");
  for (ii=0; ii<RES; ii++) {
    for (kk=0; kk<RES; kk++) {
      line = ii * nl / RES;
      sample = kk * ns / RES;
      meta_get_latLon(md, line, sample, 0.0, &lat, &lon);
      l[ii*RES+kk] = line;
      s[ii*RES+kk] = sample;
      value[ii*RES+kk] = lat + 180.0;
    }
    asfLineMeter(ii, nl);
  }
  q = find_quadratic(value, l, s, MAX_PTS);
  q.A = first_value;
  for (ii=0; ii<nl; ii++) {
    for (kk=0; kk<ns; kk++) {
      if (mg->orbit_direction == 'A')
	lats[(nl-ii-1)*ns+kk] = (float)
	  (q.A + q.B*ii + q.C*kk + q.D*ii*ii + q.E*ii*kk + q.F*kk*kk +
	   q.G*ii*ii*kk + q.H*ii*kk*kk + q.I*ii*ii*kk*kk + q.J*ii*ii*ii +
	   q.K*kk*kk*kk) - 180.0;
      else
	lats[ii*ns+kk] = (float)
	  (q.A + q.B*ii + q.C*kk + q.D*ii*ii + q.E*ii*kk + q.F*kk*kk +
	   q.G*ii*ii*kk + q.H*ii*kk*kk + q.I*ii*ii*kk*kk + q.J*ii*ii*ii +
	   q.K*kk*kk*kk) - 180.0;
    }
    asfLineMeter(ii, nl);
  }
  asfPrintStatus("Storing band 'latitude' ...\n");
  sprintf(dataset, "/data/latitude");
  h5_lat = H5Dcreate(h5_file, dataset, H5T_NATIVE_FLOAT, h5_array,
		     H5P_DEFAULT, h5_plist, H5P_DEFAULT);
  H5Dwrite(h5_lat, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, lats);
  h5_att_str(h5_lat, h5_string, "units", "degrees_north");
  h5_att_str(h5_lat, h5_string, "long_name", "latitude");
  h5_att_str(h5_lat, h5_string, "standard_name", "latitude");
  valid_range[0] = -90.0;
  valid_range[1] = 90.0;
  h5_att_float2(h5_lat, h5_range, "valid_range", valid_range);
  h5_att_float(h5_lat, h5_string, "_FillValue", -999);
  H5Dclose(h5_lat);
  FREE(lats);

  if (projected) {
    // Extra bands - ygrid
    float *ygrids = (float *) MALLOC(sizeof(float)*pixel_count);
    for (ii=0; ii<nl; ii++) {
      for (kk=0; kk<ns; kk++)
	ygrids[ii*ns+kk] = mp->startY + kk*mp->perY;
      asfLineMeter(ii, nl);
    }
    asfPrintStatus("Storing band 'ygrid' ...\n");
    sprintf(dataset, "/data/ygrid");
    h5_ygrid = H5Dcreate(h5_file, dataset, H5T_NATIVE_FLOAT, h5_array,
			 H5P_DEFAULT, h5_plist, H5P_DEFAULT);
    H5Dwrite(h5_ygrid, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, ygrids);
    h5_att_str(h5_ygrid, h5_string, "units", "meters");
    h5_att_str(h5_ygrid, h5_string, "long_name",
	       "projection_grid_y_coordinates");
    h5_att_str(h5_ygrid, h5_string, "standard_name",
	       "projection_y_coordinates");
    h5_att_str(h5_ygrid, h5_string, "axis", "Y");
    H5Dclose(h5_ygrid);
    FREE(ygrids);

    // Extra bands - xgrid
    float *xgrids = (float *) MALLOC(sizeof(float)*pixel_count);
    for (ii=0; ii<nl; ii++) {
      for (kk=0; kk<ns; kk++)
	xgrids[ii*ns+kk] = mp->startX + kk*mp->perX;
      asfLineMeter(ii, nl);
    }
    asfPrintStatus("Storing band 'xgrid' ...\n");
    sprintf(dataset, "/data/xgrid");
    h5_xgrid = H5Dcreate(h5_file, dataset, H5T_NATIVE_FLOAT, h5_array,
			 H5P_DEFAULT, h5_plist, H5P_DEFAULT);
    H5Dwrite(h5_xgrid, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, xgrids);
    h5_att_str(h5_xgrid, h5_string, "units", "meters");
    h5_att_str(h5_xgrid, h5_string, "long_name",
	       "projection_grid_x_coordinates");
    h5_att_str(h5_xgrid, h5_string, "standard_name",
	       "projection_x_coordinates");
    h5_att_str(h5_xgrid, h5_string, "axis", "X");
    H5Dclose(h5_xgrid);
    FREE(xgrids);
  }

  H5Gclose(h5_datagroup);

  // Adding global attributes
  hid_t h5_global = H5Gopen(h5_file, "/", H5P_DEFAULT);
  h5_att_str(h5_global, h5_string, "institution", "Alaska Satellite Facility");
  sprintf(str_attr, "%s %s %s image", mg->sensor, mg->sensor_name, mg->mode);
  h5_att_str(h5_global, h5_string, "title", str_attr);
  if (mg->image_data_type == AMPLITUDE_IMAGE)
    strcpy(str_attr, "SAR backcatter image");
  h5_att_str(h5_global, h5_string, "source", str_attr);
  h5_att_str(h5_global, h5_string, "original_file", mg->basename);
  ymd_date ymd;
  hms_time hms;
  parse_date(mg->acquisition_date, &ymd, &hms);
  if (strcmp_case(mg->sensor, "RSAT-1") == 0)
    sprintf(str_attr, "Copyright Canadian Space Agency, %d", ymd.year);
  else if (strncmp_case(mg->sensor, "ERS", 3) == 0)
    sprintf(str_attr, "Copyright European Space Agency, %d", ymd.year);
  else if (strcmp_case(mg->sensor, "JERS-1") == 0 ||
	   strcmp_case(mg->sensor, "ALOS") == 0)
    sprintf(str_attr, "Copyright Japan Aerospace Exploration Agency , %d",
	    ymd.year);
  h5_att_str(h5_global, h5_string, "comment", str_attr);
  h5_att_str(h5_global, h5_string, "reference",
	     "Documentation available at: www.asf.alaska.edu");
  time_t t;
  struct tm *timeinfo;
  time(&t);
  timeinfo = gmtime(&t);
  sprintf(str_attr, "%s", asctime(timeinfo));
  chomp(str_attr);
  strcat(str_attr, ", UTC: H5 File created.");
  h5_att_str(h5_global, h5_string, "history", str_attr);
  H5Gclose(h5_global);

  // Metadata
  sprintf(group, "/metadata");
  h5_metagroup = H5Gcreate(h5_file, group, H5P_DEFAULT, H5P_DEFAULT,
			   H5P_DEFAULT);

  // Metadata - General block
  h5_value_str(h5_file, group, "general_name", mg->basename, "file_name", NULL);
  h5_value_str(h5_file, group, "general_sensor", mg->sensor,
	       "imaging satellite", NULL);
  h5_value_str(h5_file, group, "general_sensor_name", mg->sensor_name,
	       "imaging sensor", NULL);
  h5_value_str(h5_file, group, "general_mode", mg->mode, "imaging mode", NULL);
  h5_value_str(h5_file, group, "general_processor", mg->processor,
	       "name and version of processor", NULL);
  h5_value_str(h5_file, group, "general_data_type",
	       data_type2str(mg->data_type), "type of samples (e.g. REAL64)",
	       NULL);
  h5_value_str(h5_file, group, "general_image_data_type",
	       image_data_type2str(mg->image_data_type),
	       "image data type (e.g. AMPLITUDE_IMAGE)", NULL);
  h5_value_str(h5_file, group, "general_radiometry",
	       radiometry2str(mg->radiometry), "radiometry (e.g. SIGMA)", NULL);
  // FIXME: UDUNITS seconds since ...
  h5_value_str(h5_file, group, "general_acquisition_date", mg->acquisition_date,
	       "acquisition date of image", NULL);
  h5_value_int(h5_file, group, "general_orbit", mg->orbit,
	       "orbit number of image", NULL);
  if (mg->orbit_direction == 'A')
    strcpy(str_attr, "Ascending");
  else
    strcpy(str_attr, "Descending");
  h5_value_str(h5_file, group, "general_orbit_direction", str_attr,
	       "orbit direction", NULL);
  h5_value_int(h5_file, group, "general_frame", mg->frame,
	       "frame number of image", NULL);
  h5_value_int(h5_file, group, "general_band_count", mg->band_count,
	       "number of bands in image", NULL);
  h5_value_str(h5_file, group, "general_bands", mg->bands,
	       "bands of the sensor", NULL);
  h5_value_int(h5_file, group, "general_line_count", mg->line_count,
	       "number of lines in image", NULL);
  h5_value_int(h5_file, group, "general_sample_count", mg->sample_count,
	       "number of samples in image", NULL);
  h5_value_int(h5_file, group, "general_start_line", mg->start_line,
	       "first line relative to original image", NULL);
  h5_value_int(h5_file, group, "general_start_sample", mg->start_sample,
	       "first sample relative to original image", NULL);
  h5_value_double(h5_file, group, "general_x_pixel_size", mg->x_pixel_size,
		  "range pixel size", "m");
  h5_value_double(h5_file, group, "general_y_pixel_size", mg->y_pixel_size,
		  "azimuth pixel size", "m");
  h5_value_double(h5_file, group, "general_center_latitude",
		  mg->center_latitude, "approximate image center latitude",
		  "degrees_north");
  h5_value_double(h5_file, group, "general_center_longitude",
		  mg->center_longitude, "approximate image center longitude",
		  "degrees_east");
  h5_value_double(h5_file, group, "general_re_major", mg->re_major,
		  "major (equator) axis of earth", "m");
  h5_value_double(h5_file, group, "general_re_minor", mg->re_minor,
		  "minor (polar) axis of earth", "m");
  h5_value_double(h5_file, group, "general_bit_error_rate", mg->bit_error_rate,
		  "fraction of bits which are in error", NULL);
  h5_value_int(h5_file, group, "general_missing_lines", mg->missing_lines,
	       "number of missing lines in image", NULL);
  h5_value_float(h5_file, group, "general_no_data", mg->no_data,
		 "value indicating no data for a pixel", NULL);

  if (ms) {
    if (ms->image_type == 'S')
      strcpy(str_attr, "slant range");
    else if (ms->image_type == 'G')
      strcpy(str_attr, "ground range");
    else if (ms->image_type == 'P')
      strcpy(str_attr, "projected");
    else if (ms->image_type == 'R')
      strcpy(str_attr, "georeferenced");
    h5_value_str(h5_file, group, "sar_image_type", str_attr, "image type",
		 NULL);
    if (ms->look_direction == 'R')
      strcpy(str_attr, "right");
    else if (ms->look_direction == 'L')
      strcpy(str_attr, "left");
    h5_value_str(h5_file, group, "sar_look_direction", str_attr,
		 "SAR satellite look direction", NULL);
    h5_value_int(h5_file, group, "sar_look_count", ms->look_count,
		 "number of looks to take from SLC", NULL);
    h5_value_int(h5_file, group, "sar_multilook", ms->multilook,
		 "multilooking flag", NULL);
    h5_value_int(h5_file, group, "sar_deskewed", ms->deskewed,
		 "zero doppler deskew flag", NULL);
    h5_value_int(h5_file, group, "sar_original_line_count",
		 ms->original_line_count, "number of lines in original image",
		 NULL);
    h5_value_int(h5_file, group, "sar_original_sample_count",
		 ms->original_sample_count,
		 "number of samples in original image", NULL);
    h5_value_double(h5_file, group, "sar_line_increment",
		    ms->line_increment, "line increment for sampling", NULL);
    h5_value_double(h5_file, group, "sar_sample_increment",
		    ms->sample_increment, "sample increment for sampling",
		    NULL);
    h5_value_double(h5_file, group, "sar_range_time_per_pixel",
		    ms->range_time_per_pixel, "time per pixel in range", "s");
    h5_value_double(h5_file, group, "sar_azimuth_time_per_pixel",
		    ms->azimuth_time_per_pixel, "time per pixel in azimuth",
		    "s");
    h5_value_double(h5_file, group, "sar_slant_range_first_pixel",
		    ms->slant_range_first_pixel, "slant range to first pixel",
		    "m");
    h5_value_double(h5_file, group, "sar_slant_shift", ms->slant_shift,
		    "error correction factor in slant range", "m");
    h5_value_double(h5_file, group, "sar_time_shift", ms->time_shift,
		    "error correction factor in time", "s");
    h5_value_double(h5_file, group, "sar_wavelength", ms->wavelength,
		    "SAR carrier wavelength", "m");
    h5_value_double(h5_file, group, "sar_pulse_repetition_frequency",
		    ms->prf, "pulse repetition frequency", "Hz");
    h5_value_double(h5_file, group, "sar_earth_radius", ms->earth_radius,
		    "earth radius at image center", "m");
    h5_value_double(h5_file, group, "sar_satellite_height",
		    ms->satellite_height,
		    "satellite height from earth's center", "m");
    h5_value_double(h5_file, group, "sar_range_doppler_centroid",
		    ms->range_doppler_coefficients[0], "range doppler centroid",
		    "Hz");
    // FIXME: UDUNITS unit_description
    h5_value_double(h5_file, group, "sar_range_doppler_linear",
		    ms->range_doppler_coefficients[1],
		    "range doppler per range pixel", "Hz/pixel");
    // FIXME: UDUNITS unit description
    h5_value_double(h5_file, group, "sar_range_doppler_quadratic",
		    ms->range_doppler_coefficients[2],
		    "range doppler per range pixel square", "Hz/pixel^2");
    h5_value_double(h5_file, group, "sar_azimuth_doppler_centroid",
		    ms->azimuth_doppler_coefficients[0],
		    "azimuth doppler centroid", "Hz");
    h5_value_double(h5_file, group, "sar_azimuth_doppler_linear",
		    ms->azimuth_doppler_coefficients[1],
		    "azimuth doppler per azimuth pixel", "Hz/pixel");
    h5_value_double(h5_file, group, "sar_azimuth_doppler_quadratic",
		    ms->azimuth_doppler_coefficients[2],
		    "azimuth doppler per azimuth pixel square", "Hz/pixel^2");
  }

  if (mo) {
    int vector_count = mo->vector_count;
    h5_value_int(h5_file, group, "orbit_year", mo->year,
		 "year of image start", NULL);
    h5_value_int(h5_file, group, "orbit_day_of_year", mo->julDay,
		 "day of year at image start", NULL);
    h5_value_double(h5_file, group, "orbit_second_of_day", mo->second,
		    "second of day at image start", "s");
    h5_value_int(h5_file, group, "orbit_vector_count", vector_count,
		 "number of state vectors", NULL);
    for (ii=0; ii<vector_count; ii++) {
      sprintf(tmp, "orbit_vector[%d]_time", ii+1);
      h5_value_double(h5_file, group, tmp, mo->vecs[ii].time,
		      "time relative to image start", "s");
      sprintf(tmp, "orbit_vector[%d]_position_x", ii+1);
      h5_value_double(h5_file, group, tmp, mo->vecs[ii].vec.pos.x,
		      "x coordinate, earth-fixed", "m");
      sprintf(tmp, "orbit_vector[%d]_position_y", ii+1);
      h5_value_double(h5_file, group, tmp, mo->vecs[ii].vec.pos.y,
		      "y coordinate, earth-fixed", "m");
      sprintf(tmp, "orbit_vector[%d]_position_z", ii+1);
      h5_value_double(h5_file, group, tmp, mo->vecs[ii].vec.pos.z,
		      "z coordinate, earth-fixed", "m");
      sprintf(tmp, "orbit_vector[%d]_velocity_x", ii+1);
      h5_value_double(h5_file, group, tmp, mo->vecs[ii].vec.vel.x,
		      "x velocity, earth-fixed", "m/s");
      sprintf(tmp, "orbit_vector[%d]_velocity_y", ii+1);
      h5_value_double(h5_file, group, tmp, mo->vecs[ii].vec.vel.y,
		      "y velocity, earth-fixed", "m/s");
      sprintf(tmp, "orbit_vector[%d]_velocity_z", ii+1);
      h5_value_double(h5_file, group, tmp, mo->vecs[ii].vec.vel.z,
		      "z velocity, earth-fixed", "m/s");
    }
  }
  H5Gclose(h5_metagroup);
  H5Sclose(h5_string);
  H5Sclose(h5_array);

  // Write ASF metadata to XML file
  char *output_file =
    (char *) MALLOC(sizeof(char)*(strlen(output_file_name)+5));
  sprintf(output_file, "%s.xml", output_file_name);
  meta_write_xml(md, output_file);
  FREE(output_file);

  return h5;
}

void finalize_h5_file(h5_t *hdf)
{
  H5Fclose(hdf->file);

  // Clean up
  FREE(hdf->var);
  FREE(hdf);
}


/*
 *   Copyright (c) 2007, Michael Lehn
 *
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *   1) Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2) Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in
 *      the documentation and/or other materials provided with the
 *      distribution.
 *   3) Neither the name of the FLENS development group nor the names of
 *      its contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef FLENS_FLENS_H
#define FLENS_FLENS_H 1

#define ADDRESS(x) reinterpret_cast<const void *>(&x)

#ifndef ASSERT
#define ASSERT(x) assert(x)
#endif //ASSERT

#include <array.h>
#include <bandstorage.h>
#include <blas.h>
#include <blas_flens.h>
#include <cg.h>
#include <complex_helper.h>
#include <crs.h>
#include <densevector.h>
#include <evalclosure.h>
#include <fullstorage.h>
#include <generalmatrix.h>
#include <generic_blas.h>
#include <refcounter.h>
#include <lapack.h>
#include <lapack_flens.h>
#include <listinitializer.h>
#include <matvec.h>
#include <matvecclosures.h>
#include <matvecio.h>
#include <matvecoperations.h>
#include <multigrid.h>
#include <packedstorage.h>
#include <range.h>
#include <snapshot.h>
#include <storage.h>
#include <sparsematrix.h>
#include <sparse_blas.h>
#include <sparse_blas_flens.h>
#include <symmetricmatrix.h>
#include <traits.h>
#include <triangularmatrix.h>
#include <underscore.h>
#include <uplo.h>

#endif // FLENS_FLENS_H


#ifndef S3D_CV_IMAGE_OPERATION_INPUT_OUTPUT_ADAPTER_H
#define S3D_CV_IMAGE_OPERATION_INPUT_OUTPUT_ADAPTER_H

#include <s3d/multiview/stan_results.h>

#include <opencv2/core/mat.hpp>

#include <gsl/gsl>

namespace s3d {
namespace image_operation {

class ImageOperations;

class InputOutputAdapter {
public:
  InputOutputAdapter(gsl::not_null<ImageOperations*> imageOperations);

  void setInputImages(const cv::Mat& leftImage, const cv::Mat& rightImage);
  std::tuple<cv::Mat&, cv::Mat&> getOutputImages();
  bool applyAllOperations();
  void copyInputToOutputs();
  bool canApplyOperations();

  StanResults results;

private:
  cv::Mat inputLeftImage{};
  cv::Mat inputRightImage{};
  cv::Mat outputLeftImage{};
  cv::Mat outputRightImage{};

  ImageOperations* operations_{};
};

} // namespace s3d
} // namespace image_operation

#endif // S3D_CV_IMAGE_OPERATION_INPUT_OUTPUT_ADAPTER_H


#pragma once
#include <mylibs/container.hpp> // pl::Vector
#include <initializer_list> // std::initializer_list
#include "FoodImpl.h" // fa::FoodWithAmount
#include "NoteImpl.h" // fa::NoteImpl
#include "PlanEntry.h" // fa::PlanEntryDynamicDelegator
#include "Date.h" // fa::Date
#include "GlobalHeader.h" // fa::OStream
#include <utility> // std::forward
#include <mylibs/utility.hpp> // pl::forEachArgument
#include <gsl.h> // gsl::not_null

namespace fa {
    namespace daily_plan_detail {
        template <class Type>
        using Container = pl::Vector<Type>;
    } // END of namespace daily_plan_detail

    class DailyPlanImpl final {
    public:
        using this_type = DailyPlanImpl;
        using DailyPlanFood = FoodWithAmount;
        using DailyPlanNote = NoteImpl;
        using Interface = PlanEntryInterface;
        using FoodUniqueOwner = pl::PolymorphicUniqueOwner<Interface, PlanEntryDynamicDelegator>;
        using NoteUniqueOwner = pl::PolymorphicUniqueOwner<Interface, PlanEntryDynamicDelegator>;
        using FoodCont = daily_plan_detail::Container<FoodUniqueOwner>;
        using NoteCont = daily_plan_detail::Container<NoteUniqueOwner>;

        //! put FoodWithAmounts and NoteImpls in here.
        template <class ...Args>
        explicit DailyPlanImpl(Args &&...args) : date_{ Date::today() } {
            pl::forEachArgument([this](auto &&e) {
                                    initCont(std::forward<decltype(e)>(e));
                                }, std::forward<Args>(args)...);
        }

        Date getDate() const;
        void setDate(Date const &);
        void setDate(Date &&);
        NutrientTable getAllNutrients() const;
        NutrientTable getMacroNutrients() const;
        NutrientTable getMicroNutrients() const;
        double getMacroPercentage() const;
        double getMicroPercentage() const;
        double getKcalPercentage() const;
        double getOverallPercentage() const;
        void addEntry(FoodWithAmount const &);
        void addEntry(FoodWithAmount &&);
        void addEntry(NoteImpl const &);
        void addEntry(NoteImpl &&);

        pl::View<FoodCont const> getFoods() const;
        pl::View<FoodCont> getFoods();
        pl::View<NoteCont const> getNotes() const;
        pl::View<NoteCont> getNotes();
        daily_plan_detail::Container<gsl::not_null<Interface const *>> getEntries() const;
        daily_plan_detail::Container<gsl::not_null<Interface *>> getEntries();
        daily_plan_detail::Container<gsl::not_null<DailyPlanFood *>> getFoodsDownCasted();
        daily_plan_detail::Container<gsl::not_null<DailyPlanFood const *>> getFoodsDownCasted() const;
        daily_plan_detail::Container<gsl::not_null<DailyPlanNote *>> getNotesDownCasted();
        daily_plan_detail::Container<gsl::not_null<DailyPlanNote const *>> getNotesDownCasted() const;

        friend OStream &operator<<(OStream &, this_type const &);

    private:
        void initCont(DailyPlanFood const &dpf);

        void initCont(DailyPlanFood &&dpf);

        void initCont(DailyPlanNote const &note);

        void initCont(DailyPlanNote &&note);

        NutrientTable getNutrients(FoodCont::const_iterator b,
                                   FoodCont::const_iterator e,
                                   NutrientTable accu) const;

        FoodCont foodCont_;
        NoteCont noteCont_;
        Date date_;
    }; // END of class DailyPlanImpl
} // END of namespace fa


#pragma once

#include "Library.h"

#include <gsl/span>

#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
#include <variant>
#include <vector>

namespace CesiumGeometry {

namespace AvailabilityUtilities {
uint8_t countOnesInByte(uint8_t _byte);
uint32_t countOnesInBuffer(gsl::span<const std::byte> buffer);
} // namespace AvailabilityUtilities

struct CESIUMGEOMETRY_API ConstantAvailability {
  bool constant;
};

struct CESIUMGEOMETRY_API SubtreeBufferView {
  uint32_t byteOffset;
  uint32_t byteLength;
  uint8_t buffer;
};

typedef std::variant<ConstantAvailability, SubtreeBufferView> AvailabilityView;

struct CESIUMGEOMETRY_API AvailabilitySubtree {
  AvailabilityView tileAvailability;
  AvailabilityView contentAvailability;
  AvailabilityView subtreeAvailability;
  std::vector<std::vector<std::byte>> buffers;
};

/**
 * @brief Availability nodes wrap subtree objects and link them together to
 * form a downwardly traversable availability tree.
 */
struct CESIUMGEOMETRY_API AvailabilityNode {
  /**
   * @brief The subtree data for this node.
   *
   * If a node exists but its subtree does not exist, it indicates that the
   * subtree is known to be available and is actively in the process of loading.
   */
  std::optional<AvailabilitySubtree> subtree;

  /**
   * @brief The child nodes for this subtree node.
   */
  std::vector<std::unique_ptr<AvailabilityNode>> childNodes;

  /**
   * @brief Creates an empty instance;
   */
  AvailabilityNode() noexcept;

  /**
   * @brief Sets the loaded subtree for this availability node.
   *
   * @param subtree_ The loaded subtree to set for this node.
   * @param maxChildrenSubtrees The maximum number of children this subtree
   * could possible have if all of them happen to be available.
   */
  void setLoadedSubtree(
      AvailabilitySubtree&& subtree_,
      uint32_t maxChildrenSubtrees) noexcept;
};

struct CESIUMGEOMETRY_API AvailabilityTree {
  std::unique_ptr<AvailabilityNode> pRoot;
};

class CESIUMGEOMETRY_API AvailabilityAccessor {
public:
  AvailabilityAccessor(
      const AvailabilityView& view,
      const AvailabilitySubtree& subtree) noexcept;

  bool isBufferView() const noexcept {
    return pBufferView != nullptr && bufferAccessor;
  }

  bool isConstant() const noexcept { return pConstant != nullptr; }

  /**
   * @brief Unsafe if isConstant is false.
   */
  bool getConstant() const { return pConstant->constant; }

  /**
   * @brief Unsafe is isBufferView is false.
   */
  const gsl::span<const std::byte>& getBufferAccessor() const {
    return *bufferAccessor;
  }

  /**
   * @brief Unsafe if isBufferView is false.
   */
  const std::byte& operator[](size_t i) const {
    return bufferAccessor.value()[i];
  }

  /**
   * @brief Unsafe if isBufferView is false;
   */
  size_t size() const { return pBufferView->byteLength; }

private:
  const SubtreeBufferView* pBufferView;
  const ConstantAvailability* pConstant;
  std::optional<gsl::span<const std::byte>> bufferAccessor;
};
} // namespace CesiumGeometry


/*
 Copyright (C) 2002 M. Marques, A. Castro, A. Rubio, G. Bertsch

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

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

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

*/

#ifndef _SYMBOLS_H
#define _SYMBOLS_H

#include <gsl/gsl_complex.h>
#include "liboct_parser.h"

typedef enum{
  S_CMPLX, S_STR, S_BLOCK, S_FNCT
} symrec_type;

/* Data type for links in the chain of symbols. */
typedef struct symrec{
  char *name;                  /* name of symbol */
  symrec_type type;            /* type of symbol: complex, string, block, or function */
  int def;                     /* has this symbol been defined */
  int used;                    /* this symbol has been used before */

  int nargs;                   /* if type==S_FNCT contains the number of arguments of the function */

  union {
    gsl_complex c;             /* value of a S_CMPLX */
    char *str;                 /* value of a S_STR */
    sym_block *block;          /* to store blocks */
    gsl_complex (*fnctptr)();  /* value of a S_FNCT */
  } value;

  struct symrec *next;         /* link field */
} symrec;

/* The symbol table: a chain of struct symrec. */
extern symrec *sym_table;
extern char *reserved_symbols[];

symrec *putsym (const char *sym_name, symrec_type sym_type);
symrec *getsym (const char *sym_name);
int      rmsym (const char *sym_name);

void sym_notdef(symrec *sym);
void sym_redef(symrec *sym);
void sym_wrong_arg(symrec *sym);
void sym_init_table(void);
void sym_end_table(void);
void sym_output_table(int only_unused, int mpiv_node);
void str_tolower(char *in);
void sym_mark_table_used();
void sym_print(FILE *f, const symrec *ptr);

#endif


/*
 Copyright (C) 2002 M. Marques, A. Castro, A. Rubio, G. Bertsch

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

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

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

 $Id$
*/

#ifndef _LIB_OCT_H
#define _LIB_OCT_H

#include <gsl/gsl_complex.h>

int         parse_init   (char *file_out, int *mpiv_node);
int         parse_input  (char *file_in);
void        parse_end    (void);

int         parse_isdef  (char *name);
int         parse_int    (char *name, int def);
double      parse_double (char *name, double def);
gsl_complex parse_complex(char *name, gsl_complex def);
char       *parse_string (char *name, char *def);

/* Now comes stuff for the blocks */
typedef struct sym_block_line{
  int n;
  char **fields;
} sym_block_line;

typedef struct sym_block{
  int n;
  sym_block_line *lines;
} sym_block;

int parse_block        (char *name, sym_block **blk);
int parse_block_end    (sym_block **blk);
int parse_block_n      (sym_block *blk);
int parse_block_cols   (sym_block *blk, int l);
int parse_block_int    (sym_block *blk, int l, int col, int *r);
int parse_block_double (sym_block *blk, int l, int col, double *r);
int parse_block_complex(sym_block *blk, int l, int col, gsl_complex *r);
int parse_block_string (sym_block *blk, int l, int col, char **r);

/* from parse_exp.c */
enum pr_type {PR_NONE,PR_CMPLX, PR_STR};
typedef struct parse_result{
  union {
    gsl_complex c;
    char *s;
  } value;
  enum pr_type type;
} parse_result;

void parse_result_free(parse_result *t);

int parse_exp(char *exp, parse_result *t);

void parse_putsym_int(char *s, int i);
void parse_putsym_double(char *s, double d);
void parse_putsym_complex(char *s, gsl_complex c);

#endif


#pragma once

#include <sstream>
#include <gsl/string_span>

/*
	String tokenizer that works like the ToEE vanilla one.
*/
class Tokenizer {
public:

	explicit Tokenizer(const std::string &input) : mIn(input) {
	}

	bool NextToken();

	bool IsQuotedString() const {
		return mTokenType == TokenType::QuotedString;
	}

	bool IsNumber() const {
		return mTokenType == TokenType::Number;
	}

	bool IsIdentifier() const {
		return mTokenType == TokenType::Identifier;
	}

	bool IsIdentifier(const char *identifier) const;

	const std::string &GetTokenText() const {
		return mTokenText;
	}

	const bool& GetEnableEscapes() const {
		return mEnableEscapes;
	}

	void SetEnableEscapes(bool enableEscapes) {
		mEnableEscapes = enableEscapes;
	}

	int GetTokenInt() const {
		return mTokenInt;
	}

	float GetTokenFloat() const {
		return (float) mTokenFloat;
	}

private:
	enum class TokenType {
		Number,
		QuotedString,
		Identifier,
		Unknown
	};

	std::istringstream mIn;
	std::string mTokenText;
	TokenType mTokenType = TokenType::Unknown;
	double mTokenFloat = 0;
	int mTokenInt = 0;

	std::string mLine; // Buffer for current line
	size_t mLinePos; // Current position within mLine
	int mLineNo = 0;
	bool mEnableEscapes = true;

	bool GetLine();
	bool LineHasMoreChars() const;

	bool ReadNumber();
	bool ReadQuotedString();
	bool ReadIdentifier();

	// Character based reading on the input source
	char PeekChar();
	void SkipChar();
	char TakeChar();
	void UngetChar();

	// Seeks past any control or space characters at current line pos
	void SkipSpaceAndControl();
	// Skips past comment at current line pos to end of line
	void SkipComment();
};


#ifndef COMUTILS_H
#define    COMUTILS_H

#include "HypCommands.h" // RECORD_LENGTH

#include <gsl/gsl-lite.hpp> // gsl::span

#include <array>
#include <functional>
#include <memory>
#include <stdint.h>
#include <string>
#include <vector>

#include <QObject>
#include <QString>
QT_USE_NAMESPACE

class SerialPort;

#define val const auto

//------------------------------------------------------------------------------
class DeviceLink : public QObject
{
    Q_OBJECT

    DeviceLink()                              = delete;
    DeviceLink            (const DeviceLink&) = delete;
    DeviceLink& operator =(const DeviceLink&) = delete;

public:
    //enum class DeviceType {
    //    UNKNOWN = 0,
    //    LDU, RDU, MDU,
    //    NUM_TYPES // the last
    //};

    typedef std::function<void(const QString&)>            MsgToDisplayCbk;
    typedef std::function<void(const gsl::span<uint8_t>&)> PacketRecvdCbk;
    typedef std::function<void()>                          SeriesEndCbk;
    typedef std::function<void(bool)>                      FinishedCbk;

    DeviceLink(MsgToDisplayCbk msgCbk, PacketRecvdCbk packetCbk,
               SeriesEndCbk seriesEndCbk, FinishedCbk finishCbk);

    void DownloadRecorded(); // returns packets via PacketRecvdCbk
    void ClearRecordings();
    void UploadToDevice(const std::vector<uint8_t>& data);

    Hyperion::DeviceType GetDeviceType() const { return m_deviceType;  }
    int GetFirmwareVersionX100() const { return m_firmwareVersionX100; }

private:
    void DownloadThreadFn();
    void ClearThreadFn();
    void UploadThreadFn();

    std::unique_ptr<SerialPort> EstablishConnection(size_t& port_idx, uint8_t& handshakeReply);

    void SendMessage  (const QString& msg) const { if (m_messageCbk)     m_messageCbk(msg); }
    void ReceivePacket(const gsl::span<uint8_t>& p)const { if (m_packetRecvdCbk) m_packetRecvdCbk(p); }
    void SendFinished (bool ok)            const { if (m_finishCbk)      m_finishCbk(ok); }

    typedef std::vector<uint8_t> dataVec_t;

    template<size_t N> static bool ChecksumOk(const std::array<uint8_t, N>& data) {
        size_t sum = 0;
        for (size_t i=0;  i+1 < N;  sum += data[i++]);
        return ((sum & 0xFF) == data[N - 1]);
    }

//data:
    MsgToDisplayCbk m_messageCbk;
    PacketRecvdCbk  m_packetRecvdCbk;
    SeriesEndCbk    m_seriesEndCbk;
    FinishedCbk     m_finishCbk;

    std::vector<QString> m_portNames;

    Hyperion::DeviceType m_deviceType = Hyperion::UNKNOWN_DEVICE;
    int m_firmwareVersionX100 = 0; // unknown

//statics const:
    static const std::array<uint8_t,2> HANDSHAKE;            // "DZ"
    static const std::array<uint8_t,2> START_DOWNLOAD;       // "DK"
    static const std::array<uint8_t,1> CONTINUE_DOWNLOAD;    // 0x01
    static const std::array<uint8_t,2> CLEAR_VALUES;         // "DE" - not sure what it does...
    static const std::array<uint8_t,2> CLEAR_MEMORY;         // "DB"
    static const std::array<uint8_t,2> GET_FW_VERSION; // "DV"

    static const std::array<uint8_t,5> HANDSHAKE_REPLY;
};
//------------------------------------------------------------------------------

#endif // COMUTILS_H


/* vector/gsl_vector_uint.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_VECTOR_UINT_H__
#define __GSL_VECTOR_UINT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_uint.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  unsigned int *data;
  gsl_block_uint *block;
  int owner;
}
gsl_vector_uint;

typedef struct
{
  gsl_vector_uint vector;
} _gsl_vector_uint_view;

typedef _gsl_vector_uint_view gsl_vector_uint_view;

typedef struct
{
  gsl_vector_uint vector;
} _gsl_vector_uint_const_view;

typedef const _gsl_vector_uint_const_view gsl_vector_uint_const_view;

/* Allocation */

GSL_EXPORT gsl_vector_uint *gsl_vector_uint_alloc (const size_t n);
GSL_EXPORT gsl_vector_uint *gsl_vector_uint_calloc (const size_t n);

GSL_EXPORT gsl_vector_uint *gsl_vector_uint_alloc_from_block (gsl_block_uint * b,
                                                                const size_t offset,
                                                                const size_t n,
                                                                const size_t stride);

GSL_EXPORT gsl_vector_uint *gsl_vector_uint_alloc_from_vector (gsl_vector_uint * v,
                                                                 const size_t offset,
                                                                 const size_t n,
                                                                 const size_t stride);

GSL_EXPORT void gsl_vector_uint_free (gsl_vector_uint * v);

/* Views */

GSL_EXPORT
_gsl_vector_uint_view
gsl_vector_uint_view_array (unsigned int *v, size_t n);

GSL_EXPORT
_gsl_vector_uint_view
gsl_vector_uint_view_array_with_stride (unsigned int *base,
                                         size_t stride,
                                         size_t n);

GSL_EXPORT
_gsl_vector_uint_const_view
gsl_vector_uint_const_view_array (const unsigned int *v, size_t n);

GSL_EXPORT
_gsl_vector_uint_const_view
gsl_vector_uint_const_view_array_with_stride (const unsigned int *base,
                                               size_t stride,
                                               size_t n);

GSL_EXPORT
_gsl_vector_uint_view
gsl_vector_uint_subvector (gsl_vector_uint *v,
                            size_t i,
                            size_t n);

GSL_EXPORT
_gsl_vector_uint_view
gsl_vector_uint_subvector_with_stride (gsl_vector_uint *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_EXPORT
_gsl_vector_uint_const_view
gsl_vector_uint_const_subvector (const gsl_vector_uint *v,
                                  size_t i,
                                  size_t n);

GSL_EXPORT
_gsl_vector_uint_const_view
gsl_vector_uint_const_subvector_with_stride (const gsl_vector_uint *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_EXPORT unsigned int gsl_vector_uint_get (const gsl_vector_uint * v, const size_t i);
GSL_EXPORT void gsl_vector_uint_set (gsl_vector_uint * v, const size_t i, unsigned int x);

GSL_EXPORT unsigned int *gsl_vector_uint_ptr (gsl_vector_uint * v, const size_t i);
GSL_EXPORT const unsigned int *gsl_vector_uint_const_ptr (const gsl_vector_uint * v, const size_t i);

GSL_EXPORT void gsl_vector_uint_set_zero (gsl_vector_uint * v);
GSL_EXPORT void gsl_vector_uint_set_all (gsl_vector_uint * v, unsigned int x);
GSL_EXPORT int gsl_vector_uint_set_basis (gsl_vector_uint * v, size_t i);

GSL_EXPORT int gsl_vector_uint_fread (FILE * stream, gsl_vector_uint * v);
GSL_EXPORT int gsl_vector_uint_fwrite (FILE * stream, const gsl_vector_uint * v);
GSL_EXPORT int gsl_vector_uint_fscanf (FILE * stream, gsl_vector_uint * v);
GSL_EXPORT int gsl_vector_uint_fprintf (FILE * stream, const gsl_vector_uint * v,
                                         const char *format);

GSL_EXPORT int gsl_vector_uint_memcpy (gsl_vector_uint * dest, const gsl_vector_uint * src);

GSL_EXPORT int gsl_vector_uint_reverse (gsl_vector_uint * v);

GSL_EXPORT int gsl_vector_uint_swap (gsl_vector_uint * v, gsl_vector_uint * w);
GSL_EXPORT int gsl_vector_uint_swap_elements (gsl_vector_uint * v, const size_t i, const size_t j);

GSL_EXPORT unsigned int gsl_vector_uint_max (const gsl_vector_uint * v);
GSL_EXPORT unsigned int gsl_vector_uint_min (const gsl_vector_uint * v);
GSL_EXPORT void gsl_vector_uint_minmax (const gsl_vector_uint * v, unsigned int * min_out, unsigned int * max_out);

GSL_EXPORT size_t gsl_vector_uint_max_index (const gsl_vector_uint * v);
GSL_EXPORT size_t gsl_vector_uint_min_index (const gsl_vector_uint * v);
GSL_EXPORT void gsl_vector_uint_minmax_index (const gsl_vector_uint * v, size_t * imin, size_t * imax);

GSL_EXPORT int gsl_vector_uint_add (gsl_vector_uint * a, const gsl_vector_uint * b);
GSL_EXPORT int gsl_vector_uint_sub (gsl_vector_uint * a, const gsl_vector_uint * b);
GSL_EXPORT int gsl_vector_uint_mul (gsl_vector_uint * a, const gsl_vector_uint * b);
GSL_EXPORT int gsl_vector_uint_div (gsl_vector_uint * a, const gsl_vector_uint * b);
GSL_EXPORT int gsl_vector_uint_scale (gsl_vector_uint * a, const double x);
GSL_EXPORT int gsl_vector_uint_add_constant (gsl_vector_uint * a, const double x);

GSL_EXPORT int gsl_vector_uint_isnull (const gsl_vector_uint * v);

#ifdef HAVE_INLINE

extern inline
unsigned int
gsl_vector_uint_get (const gsl_vector_uint * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

extern inline
void
gsl_vector_uint_set (gsl_vector_uint * v, const size_t i, unsigned int x)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

extern inline
unsigned int *
gsl_vector_uint_ptr (gsl_vector_uint * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (unsigned int *) (v->data + i * v->stride);
}

extern inline
const unsigned int *
gsl_vector_uint_const_ptr (const gsl_vector_uint * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const unsigned int *) (v->data + i * v->stride);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_UINT_H__ */


static char help[] = "Broker for DYNHELICS\n";

#include <stdio.h>
#include <unistd.h>
#include <helics.h>
#include <petsc.h>

int main(int argc,char **argv)
{
  helics_broker broker;
  const char*   helicsversion;
  int            isconnected;
  char           initstring[PETSC_MAX_PATH_LEN];
  PetscInt       nfederates=0;
  PetscErrorCode ierr;

  PetscInitialize(&argc,&argv,"petscopt",help);

  helicsversion = helicsGetVersion();

  printf("BROKER: Helics version = %s\n",helicsversion);
  printf("%s",help);

  ierr = PetscOptionsGetInt(NULL,NULL,"-nfeds",&nfederates,NULL);CHKERRQ(ierr);
  if(!nfederates) {
    SETERRQ(PETSC_COMM_SELF,0,"Number of federates need to be given with option -nfeds");
  }
  ierr = PetscSNPrintf(initstring,PETSC_MAX_PATH_LEN-1,"%d",nfederates);
  ierr = PetscStrcat(initstring," --name=mainbroker");

  /* Create broker */
  broker = helicsCreateBroker("zmq","",initstring);

  isconnected = helicsBrokerIsConnected(broker);

  if(isconnected) {
    printf("BROKER: Created and connected\n");
  }

  while(helicsBrokerIsConnected(broker)) {
    usleep(1000); /* Sleep for 1 millisecond */
  }
  printf("BROKER: disconnected\n");
  helicsBrokerFree(broker);
  helicsCloseLibrary();

  printf("Helics library closed\n");
  PetscFinalize();
  return 0;
}


#ifndef __GSL_VECTOR_H__
#define __GSL_VECTOR_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <gsl/gsl_vector_complex_long_double.h>
#include <gsl/gsl_vector_complex_double.h>
#include <gsl/gsl_vector_complex_float.h>

#include <gsl/gsl_vector_long_double.h>
#include <gsl/gsl_vector_double.h>
#include <gsl/gsl_vector_float.h>

#include <gsl/gsl_vector_ulong.h>
#include <gsl/gsl_vector_long.h>

#include <gsl/gsl_vector_uint.h>
#include <gsl/gsl_vector_int.h>

#include <gsl/gsl_vector_ushort.h>
#include <gsl/gsl_vector_short.h>

#include <gsl/gsl_vector_uchar.h>
#include <gsl/gsl_vector_char.h>

#endif /* __GSL_VECTOR_H__ */


#pragma once

#include <array>

#include <gsl/span>

#include "Joystick.h"
#include "JoystickAxis.h"
#include "JoystickButton.h"
#include "JoystickProvider.h"

namespace qvr
{

class SfmlJoystick : public Joystick
{
public:
	struct ButtonState {
		bool isDown = false;
		bool wasDown = false;
	};

	struct AxisState {
		float position = 0.0f;
		float previousPosition = 0.0f;
	};

private:
	const int m_Index;

	int m_ButtonCount;
	int m_AxisCount;

	bool m_IsConnected  = false;
	bool m_WasConnected = false;

	// Copied from sf::Joystick
	enum
	{
		ButtonCount = 32, ///< Maximum number of supported buttons
		AxisCount = 8     ///< Maximum number of supported axes
	};

	std::array<ButtonState, ButtonCount> m_Buttons;
	std::array<AxisState,   AxisCount>   m_Axes;

	bool JustConnected() const {
		return m_IsConnected && !m_WasConnected;
	}
	bool JustDisconnected() const {
		return m_WasConnected && !m_IsConnected;
	}

	void UpdateButtons();
	void UpdateAxes();

public:
	SfmlJoystick(const int index) : m_Index(index) {}

	void Update();

	bool IsConnected() const {
		return m_IsConnected;
	}

	bool IsDown(const JoystickButton button) const override {
		return m_Buttons[button.GetValue()].isDown;
	}
	bool JustDown(const JoystickButton button) const override {
		return m_Buttons[button.GetValue()].isDown && !m_Buttons[button.GetValue()].wasDown;
	}
	bool JustUp(const JoystickButton button) const override {
		return m_Buttons[button.GetValue()].wasDown && !m_Buttons[button.GetValue()].isDown;
	}

	float GetPosition(const JoystickAxis axis) const override {
		return m_Axes[(int)axis].position;
	}
	float GetPreviousPosition(const JoystickAxis axis) const override {
		return m_Axes[(int)axis].previousPosition;
	}

	const gsl::span<const ButtonState> GetButtons() const {
		return gsl::make_span(m_Buttons.data(), m_ButtonCount);
	}

	const gsl::span<const AxisState> GetAxes() const {
		return gsl::make_span(m_Axes);
	}

	int GetButtonCount() const {
		return m_ButtonCount;
	}

	int GetAxisCount() const {
		return m_AxisCount;
	}
};

class SfmlJoystickSet : public JoystickProvider
{
public:
	// Worst constructor ever.
	SfmlJoystickSet()
		: m_Joysticks({ 0, 1, 2, 3, 4, 5, 6, 7 })
	{}

	void Update();

	const Joystick* GetJoystick(const JoystickIndex index) const override {
		if (m_Joysticks[index.Get()].IsConnected()) {
			return &m_Joysticks[index.Get()];
		}
		return nullptr;
	}

	static const int Count = 8; // The maximum number of supported joysticks.

private:
	std::array<SfmlJoystick, Count> m_Joysticks;
};

void LogSfmlJoystickInfo(const int index);

}


/**
 * @file coroutine/linux.h
 * @author github.com/luncliff (luncliff@gmail.com)
 * @copyright CC BY 4.0
 */
#ifndef COROUTINE_SYSTEM_WRAPPER_H
#define COROUTINE_SYSTEM_WRAPPER_H
#if !(defined(__linux__))
#error "expect Linux platform for this file"
#endif
#include <sys/epoll.h> // for Linux epoll

#include <coroutine/return.h>
#include <gsl/gsl>

/**
 * @defgroup Linux
 */

namespace coro {

/**
 * @brief RAII wrapping for epoll file descriptor
 * @ingroup Linux
 */
class epoll_owner final {
    int64_t epfd;

  public:
    /**
     * @brief create a fd with `epoll`. Throw if the function fails.
     * @see kqeueue
     * @throw system_error
     */
    epoll_owner() noexcept(false);
    /**
     * @brief close the current epoll file descriptor
     */
    ~epoll_owner() noexcept;
    epoll_owner(const epoll_owner&) = delete;
    epoll_owner(epoll_owner&&) = delete;
    epoll_owner& operator=(const epoll_owner&) = delete;
    epoll_owner& operator=(epoll_owner&&) = delete;

  public:
    /**
     * @brief bind the fd to epoll
     * @param fd
     * @param req
     * @see epoll_ctl
     * @throw system_error
     */
    void try_add(uint64_t fd, epoll_event& req) noexcept(false);

    /**
     * @brief unbind the fd to epoll
     * @param fd
     * @see epoll_ctl
     */
    void remove(uint64_t fd);

    /**
     * @brief fetch all events for the given kqeueue descriptor
     * @param wait_ms millisecond to wait
     * @param list
     * @return ptrdiff_t
     * @see epoll_wait
     * @throw system_error
     *
     * Timeout is not an error for this function
     */
    ptrdiff_t wait(uint32_t wait_ms,
                   gsl::span<epoll_event> list) noexcept(false);

  public:
    /**
     * @brief return temporary awaitable object for given event
     * @param req input for `change` operation
     * @see change
     *
     * There is no guarantee of reusage of returned awaiter object
     * When it is awaited, and `req.udata` is null(0),
     * the value is set to `coroutine_handle<void>`
     *
     * ```cpp
     * auto edge_in_async(epoll_owner& ep, int64_t fd) -> frame_t {
     *     epoll_event req{};
     *     req.events = EPOLLET | EPOLLIN | EPOLLONESHOT;
     *     req.data.ptr = nullptr;
     *     co_await ep.submit(fd, req);
     * }
     * ```
     */
    [[nodiscard]] auto submit(int64_t fd, epoll_event& req) noexcept {
        class awaiter final : public suspend_always {
            epoll_owner& ep;
            int64_t fd;
            epoll_event& req;

          public:
            constexpr awaiter(epoll_owner& _ep, int64_t _fd, epoll_event& _req)
                : ep{_ep}, fd{_fd}, req{_req} {
            }

          public:
            void await_suspend(coroutine_handle<void> coro) noexcept(false) {
                if (req.data.ptr == nullptr)
                    req.data.ptr = coro.address();
                return ep.try_add(fd, req);
            }
        };
        return awaiter{*this, fd, req};
    }
};

/**
 * @brief RAII + stateful `eventfd`
 * @see https://github.com/grpc/grpc/blob/master/src/core/lib/iomgr/is_epollexclusive_available.cc
 * @ingroup Linux
 *
 * If the object is signaled(`set`),
 * the bound `epoll_owner` will yield suspended coroutine through `epoll_event`'s user data.
 *
 * Its object can be `co_await`ed multiple times
 */
class event final {
    uint64_t state;

  public:
    event() noexcept(false);
    ~event() noexcept;
    event(const event&) = delete;
    event(event&&) = delete;
    event& operator=(const event&) = delete;
    event& operator=(event&&) = delete;

    uint64_t fd() const noexcept;
    bool is_set() const noexcept;
    void set() noexcept(false);
    void reset() noexcept(false);
};

/**
 * @brief Bind the given `event`(`eventfd`) to `epoll_owner`(Epoll)
 *
 * @param ep  epoll_owner
 * @param efd event
 * @see event
 * @return awaitable struct for the binding
 * @ingroup Linux
 */
auto wait_in(epoll_owner& ep, event& efd) {
    class awaiter : epoll_event {
        epoll_owner& ep;
        event& efd;

      public:
        /**
         * @brief Prepares one-time registration
         */
        awaiter(epoll_owner& _ep, event& _efd) noexcept
            : epoll_event{}, ep{_ep}, efd{_efd} {
            this->events = EPOLLET | EPOLLIN | EPOLLONESHOT;
        }

        bool await_ready() const noexcept {
            return efd.is_set();
        }
        /**
         * @brief Wait for `write` to given `eventfd`
         */
        void await_suspend(coroutine_handle<void> coro) noexcept(false) {
            this->data.ptr = coro.address();
            return ep.try_add(efd.fd(), *this);
        }
        /**
         * @brief Reset the given event object when resumed
         */
        void await_resume() noexcept {
            return efd.reset();
        }
    };
    return awaiter{ep, efd};
}

} // namespace coro

#endif // COROUTINE_SYSTEM_WRAPPER_H


#pragma once

#include "Core/Pointers.h"
#include "Graphics/GraphicsResource.h"
#include "Graphics/TextureInfo.h"

#include <glad/gl.h>
#include <gsl/span>

#include <vector>

class Texture;
struct Viewport;

namespace Fb
{
   enum class DepthStencilType
   {
      None,
      Depth24Stencil8,
      Depth32FStencil8
   };

   enum class Target
   {
      Framebuffer = GL_FRAMEBUFFER,
      ReadFramebuffer = GL_READ_FRAMEBUFFER,
      DrawFramebuffer = GL_DRAW_FRAMEBUFFER
   };

   enum class CubeFace
   {
      Front,
      Back,
      Top,
      Bottom,
      Left,
      Right
   };

   struct Specification
   {
      GLsizei width = 0;
      GLsizei height = 0;

      GLsizei samples = 0;
      bool cubeMap = false;

      DepthStencilType depthStencilType = DepthStencilType::Depth24Stencil8;

      gsl::span<const Tex::InternalFormat> colorAttachmentFormats;

      bool operator==(const Specification& other) const;
   };

   struct Attachments
   {
      SPtr<Texture> depthStencilAttachment;
      std::vector<SPtr<Texture>> colorAttachments;
   };

   Attachments generateAttachments(const Specification& specification);
}

namespace std
{
   template<>
   struct hash<Fb::Specification>
   {
      size_t operator()(const Fb::Specification& specification) const;
   };
}

class Framebuffer : public GraphicsResource
{
public:
   Framebuffer();
   Framebuffer(const Framebuffer& other) = delete;
   Framebuffer(Framebuffer&& other);
   ~Framebuffer();
   Framebuffer& operator=(const Framebuffer& other) = delete;
   Framebuffer& operator=(Framebuffer&& other);

private:
   void move(Framebuffer&& other);
   void release();

public:
   using SpecificationType = Fb::Specification;
   static SPtr<Framebuffer> create(const Fb::Specification& specification);
   static const char* labelSuffix();

   static void blit(Framebuffer& source, Framebuffer& destination, GLenum readBuffer, GLenum drawBuffer, GLbitfield mask, GLenum filter);
   static void bindDefault(Fb::Target target = Fb::Target::Framebuffer);

   void bind(Fb::Target target = Fb::Target::Framebuffer);
   bool isBound(Fb::Target target = Fb::Target::Framebuffer) const;

   const Fb::Attachments& getAttachments() const
   {
      return attachments;
   }

   const SPtr<Texture>& getDepthStencilAttachment() const;
   SPtr<Texture> getColorAttachment(int index) const;

   void setAttachments(Fb::Attachments newAttachments);

   bool getViewport(Viewport& viewport) const;

   bool isCubeMap() const;
   void setActiveFace(Fb::CubeFace face);

private:
   const SPtr<Texture>* getFirstValidAttachment() const;

   Fb::Attachments attachments;
};


#ifndef SIGSCANNERMEMORYDATA_HHHHH
#define SIGSCANNERMEMORYDATA_HHHHH

#include <vector>
#include <gsl/span>
#include <string>

namespace MPSig {
    class SigScannerMemoryData {
        gsl::span<const char> m_refData;
    public:
        explicit SigScannerMemoryData(gsl::span<const char> data);

        bool IsInRange(std::intptr_t addr) const;
        char* Deref(std::intptr_t addrWithOffset) const;
        std::pair<char*, bool> DerefTry(std::intptr_t addrWithOffset) const;

        gsl::span<const char> Get() const;
        std::intptr_t GetOffset() const;
    };
}

#endif


/*
 *  Copyright (C) 2021 FISCO BCOS.
 *  SPDX-License-Identifier: Apache-2.0
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 * @brief interface for TransactionReceipt
 * @file TransactionReceipt.h
 */
#pragma once

#include "ProtocolTypeDef.h"
#include <bcos-crypto/interfaces/crypto/CryptoSuite.h>
#include <bcos-utilities/FixedBytes.h>
#include <gsl/span>

namespace bcos
{
namespace protocol
{
class LogEntry;
class TransactionReceipt
{
public:
    using Ptr = std::shared_ptr<TransactionReceipt>;
    using ConstPtr = std::shared_ptr<const TransactionReceipt>;
    explicit TransactionReceipt(bcos::crypto::CryptoSuite::Ptr _cryptoSuite)
      : m_cryptoSuite(_cryptoSuite)
    {}

    virtual ~TransactionReceipt() {}

    virtual void decode(bytesConstRef _receiptData) = 0;
    virtual void encode(bytes& _encodedData) const = 0;
    virtual bytesConstRef encode(bool _onlyHashFieldData = false) const = 0;

    virtual bcos::crypto::HashType hash() const
    {
        auto hashFields = encode(true);
        return m_cryptoSuite->hash(hashFields);
    }

    virtual int32_t version() const = 0;
    virtual u256 gasUsed() const = 0;  // TODO: remove from hash
    virtual std::string_view contractAddress() const = 0;
    virtual int32_t status() const = 0;
    virtual bytesConstRef output() const = 0;
    virtual gsl::span<const LogEntry> logEntries() const = 0;
    virtual bcos::crypto::CryptoSuite::Ptr cryptoSuite() { return m_cryptoSuite; }
    virtual BlockNumber blockNumber() const = 0;

    // additional information on transaction execution, no need to be involved in the hash
    // calculation
    virtual std::string const& message() const = 0;
    virtual void setMessage(std::string const& _message) = 0;
    virtual void setMessage(std::string&& _message) = 0;

protected:
    bcos::crypto::CryptoSuite::Ptr m_cryptoSuite;
};
using Receipts = std::vector<TransactionReceipt::Ptr>;
using ReceiptsPtr = std::shared_ptr<Receipts>;
using ReceiptsConstPtr = std::shared_ptr<const Receipts>;
}  // namespace protocol
}  // namespace bcos


#ifndef buildin_40bf10d0_ba81_4f06_accf_8020d3698607_h
#define buildin_40bf10d0_ba81_4f06_accf_8020d3698607_h

#include <gslib\type.h>
#include <gslib\string.h>
#include <rathen\config.h>

__rathen_begin__

#define max_opr 4

struct oprnode
{
    int     prior;
    gchar   opr[max_opr];
    bool    unary;
};

struct oprinfo
{
    string      opr;
    bool        unary;

    oprinfo(const gchar* o, bool u): opr(o), unary(u) {}
    oprinfo(const string& s, bool u): opr(s), unary(u) {}
};

struct unary_oprinfo:
    public oprinfo
{
    unary_oprinfo(const gchar* o): oprinfo(o, true) {}
    unary_oprinfo(const string& s): oprinfo(s, true) {}
};

struct binary_oprinfo:
    public oprinfo
{
    binary_oprinfo(const gchar* o): oprinfo(o, false) {}
    binary_oprinfo(const string& s): oprinfo(s, false) {}
};

class opr_manager
{
public:
    /*
     * the operators:
       ( )
       ++ --
       - ! ~
       += -= *= /= %=
       * / %
       + -
       << >>
       < > <= >= == !=
       & | ^ && ||
       =
     */
    static const oprnode opr_list[];

private:
    opr_manager() {}

public:
    static opr_manager* get_singleton_ptr()
    {
        static opr_manager inst;
        return &inst;
    }
    const oprnode* get_unary_operator(const gchar* src) const;
    const oprnode* get_binary_operator(const gchar* src) const;
    const oprnode* get_unary_operator(const string& src) const { return get_unary_operator(src.c_str()); }
    const oprnode* get_binary_operator(const string& src) const { return get_binary_operator(src.c_str()); }
    const oprnode* get_operator(const oprinfo& info) const
    {
        return info.unary ? get_unary_operator(info.opr) :
            get_binary_operator(info.opr);
    }

    /*
     * Currently, only priority '1' was unary.
     * pass the 'opr' to this function, in case of any proper changes in the future.
     */
    bool is_unary(int prior, const gchar* opr) const { return prior == 1; }
    bool is_unary(int prior, const string& opr) const { return prior == 1; }
};

#define _opr_manager    opr_manager::get_singleton_ptr()

#define max_key 8

struct key_node
{
    int     pattern;
    gchar   key[max_key];
};

class key_manager
{
public:
    static const key_node key_list[];
    enum
    {
        /*
         * Respectively like:
         * stop;
         * exit(...);
         * else {...}
         * if(...) {...}
         */
        pat_err     = 0x00,
        pat_raw     = 0x01,
        pat_arg     = 0x02,
        pat_seg     = 0x04,
        pat_com     = 0x08,
    };

private:
    key_manager() {}

public:
    static key_manager* get_singleton_ptr()
    {
        static key_manager inst;
        return &inst;
    }
    const key_node* get_key(const gchar* src) const;
    const key_node* get_key(const string& src) const { return get_key(src.c_str()); }
    int get_key_pattern(const gchar* src) const;
    int get_key_pattern(const string& src) const { return get_key_pattern(src.c_str()); }
};

#define _key_manager    key_manager::get_singleton_ptr()

__rathen_end__

#endif