/*
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* fftw.h -- system-wide definitions */
/* $Id: fftw-int.h,v 1.39 1999/02/19 17:22:00 athena Exp $ */

#ifndef FFTW_INT_H
#define FFTW_INT_H
#include <config.h>
#include <fftw.h>

#ifdef __cplusplus
extern "C" {
#else
#endif				/* __cplusplus */

/****************************************************************************/
/*                            Private Functions                             */
/****************************************************************************/

extern fftw_twiddle *fftw_create_twiddle(int n, const fftw_codelet_desc *d);
extern void fftw_destroy_twiddle(fftw_twiddle *tw);

extern void fftw_strided_copy(int, fftw_complex *, int, fftw_complex *);
extern void fftw_executor_simple(int, const fftw_complex *, fftw_complex *,
				 fftw_plan_node *, int, int);

extern fftwnd_plan fftwnd_create_plan_aux(int rank, const int *n,
					  fftw_direction dir, int flags);
extern fftw_plan *fftwnd_new_plan_array(int rank);
extern fftw_plan *fftwnd_create_plans_generic(fftw_plan *plans,
					      int rank, const int *n,
					      fftw_direction dir, int flags);
extern fftw_plan *fftwnd_create_plans_specific(fftw_plan *plans,
					       int rank, const int *n,
					       const int *n_after,
					       fftw_direction dir, int flags,
					       fftw_complex *in, int istride,
					       fftw_complex *out, int ostride);
extern int fftwnd_work_size(int rank, const int *n, int flags, int ncopies);

extern void fftwnd_aux(fftwnd_plan p, int cur_dim,
		       fftw_complex *in, int istride,
		       fftw_complex *out, int ostride,
		       fftw_complex *work);
extern void fftwnd_aux_howmany(fftwnd_plan p, int cur_dim,
			       int howmany,
			       fftw_complex *in, int istride, int idist,
			       fftw_complex *out, int ostride, int odist,
			       fftw_complex *work);

/* wisdom prototypes */
enum fftw_wisdom_category {
     FFTW_WISDOM, RFFTW_WISDOM
};

extern int fftw_wisdom_lookup(int n, int flags, fftw_direction dir,
			      enum fftw_wisdom_category category,
			      int istride, int ostride,
			      enum fftw_node_type *type,
			      int *signature, int replace_p);
extern void fftw_wisdom_add(int n, int flags, fftw_direction dir,
			    enum fftw_wisdom_category cat,
			    int istride, int ostride,
			    enum fftw_node_type type,
			    int signature);

/* Private planner functions: */
extern double fftw_estimate_node(fftw_plan_node *p);
extern fftw_plan_node *fftw_make_node_notw(int size,
					const fftw_codelet_desc *config);
extern fftw_plan_node *fftw_make_node_real2hc(int size,
					const fftw_codelet_desc *config);
extern fftw_plan_node *fftw_make_node_hc2real(int size,
					const fftw_codelet_desc *config);
extern fftw_plan_node *fftw_make_node_twiddle(int n,
					 const fftw_codelet_desc *config,
					      fftw_plan_node *recurse,
					      int flags);
extern fftw_plan_node *fftw_make_node_hc2hc(int n,
					    fftw_direction dir,
					 const fftw_codelet_desc *config,
					    fftw_plan_node *recurse,
					    int flags);
extern fftw_plan_node *fftw_make_node_generic(int n, int size,
					      fftw_generic_codelet *codelet,
					      fftw_plan_node *recurse,
					      int flags);
extern fftw_plan_node *fftw_make_node_rgeneric(int n, int size,
					       fftw_direction dir,
					       fftw_rgeneric_codelet * codelet,
					       fftw_plan_node *recurse,
					       int flags);
extern int fftw_factor(int n);
extern fftw_plan_node *fftw_make_node(void);
extern fftw_plan fftw_make_plan(int n, fftw_direction dir,
				fftw_plan_node *root, int flags,
				enum fftw_node_type wisdom_type,
				int wisdom_signature);
extern void fftw_use_plan(fftw_plan p);
extern void fftw_use_node(fftw_plan_node *p);
extern void fftw_destroy_plan_internal(fftw_plan p);
extern fftw_plan fftw_pick_better(fftw_plan p1, fftw_plan p2);
extern fftw_plan fftw_lookup(fftw_plan *table, int n, int flags);
extern void fftw_insert(fftw_plan *table, fftw_plan this_plan, int n);
extern void fftw_make_empty_table(fftw_plan *table);
extern void fftw_destroy_table(fftw_plan *table);
extern void fftw_complete_twiddle(fftw_plan_node *p, int n);

extern fftw_plan_node *fftw_make_node_rader(int n, int size,
					    fftw_direction dir,
					    fftw_plan_node *recurse,
					    int flags);
extern fftw_rader_data *fftw_rader_top;

/****************************************************************************/
/*                           Floating Point Types                           */
/****************************************************************************/

/*
 * We use these definitions to make it easier for people to change
 * FFTW to use long double and similar types. You shouldn't have to
 * change this just to use float or double.
 */

/*
 * Change this if your floating-point constants need to be expressed
 * in a special way.  For example, if fftw_real is long double, you
 * will need to append L to your fp constants to make them of the
 * same precision.  Do this by changing "x" below to "x##L".
 */
#define FFTW_KONST(x) ((fftw_real) x)

#define FFTW_TRIG_SIN sin
#define FFTW_TRIG_COS cos
typedef double FFTW_TRIG_REAL;	/* the argument type for sin and cos */

#define FFTW_K2PI FFTW_KONST(6.2831853071795864769252867665590057683943388)

/****************************************************************************/
/*                               gcc/x86 hacks                              */
/****************************************************************************/

/*
 * gcc 2.[78].x and x86 specific hacks.  These macros align the stack
 * pointer so that the double precision temporary variables in the
 * codelets will be aligned to a multiple of 8 bytes (*way* faster on
 * pentium and pentiumpro)
 */
#ifdef __GNUC__
#ifdef __i386__
#ifdef FFTW_ENABLE_I386_HACKS
#ifndef FFTW_ENABLE_FLOAT
#define FFTW_USING_I386_HACKS
#define HACK_ALIGN_STACK_EVEN() {                        \
     if ((((long) (__builtin_alloca(0))) & 0x7)) __builtin_alloca(4);    \
}

#define HACK_ALIGN_STACK_ODD() {                         \
     if (!(((long) (__builtin_alloca(0))) & 0x7)) __builtin_alloca(4);   \
}

#ifdef FFTW_DEBUG_ALIGNMENT
#define ASSERT_ALIGNED_DOUBLE() {                        \
     double __foo;                                       \
     if ((((long) &__foo) & 0x7)) abort();                \
}
#endif

#endif
#endif
#endif
#endif

#ifndef HACK_ALIGN_STACK_EVEN
#define HACK_ALIGN_STACK_EVEN()
#endif
#ifndef HACK_ALIGN_STACK_ODD
#define HACK_ALIGN_STACK_ODD()
#endif
#ifndef ASSERT_ALIGNED_DOUBLE
#define ASSERT_ALIGNED_DOUBLE()
#endif

/****************************************************************************/
/*                                  Timers                                  */
/****************************************************************************/

/*
 * Here, you can use all the nice timers available in your machine.
 */

/*
 *
 Things you should define to include your own clock:

 fftw_time -- the data type used to store a time

 extern fftw_time fftw_get_time(void);
 -- a function returning the current time.  (We have
 implemented this as a macro in most cases.)

 extern fftw_time fftw_time_diff(fftw_time t1, fftw_time t2);
 -- returns the time difference (t1 - t2).
 If t1 < t2, it may simply return zero (although this
 is not required).  (We have implemented this as a macro
 in most cases.)

 extern double fftw_time_to_sec(fftw_time t);
 -- returns the time t expressed in seconds, as a double.
 (Implemented as a macro in most cases.)

 FFTW_TIME_MIN -- a double-precision macro holding the minimum
 time interval (in seconds) for accurate time measurements.
 This should probably be at least 100 times the precision of
 your clock (we use even longer intervals, to be conservative).
 This will determine how long the planner takes to measure
 the speeds of different possible plans.

 Bracket all of your definitions with an appropriate #ifdef so that
 they will be enabled on your machine.  If you do add your own
 high-precision timer code, let us know (at fftw@theory.lcs.mit.edu).

 Only declarations should go in this file.  Any function definitions
 that you need should go into timer.c.
 */

/*
 * define a symbol so that we know that we have the fftw_time_diff
 * function/macro (it did not exist prior to FFTW 1.2)
 */
#define FFTW_HAS_TIME_DIFF

/**********************************************
 *              SOLARIS
 **********************************************/
#if defined(HAVE_GETHRTIME)

/* we use the nanosecond virtual timer */
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

typedef hrtime_t fftw_time;

#define fftw_get_time() gethrtime()
#define fftw_time_diff(t1,t2) ((t1) - (t2))
#define fftw_time_to_sec(t) ((double) t / 1.0e9)

/*
 * a measurement is valid if it runs for at least
 * FFTW_TIME_MIN seconds.
 */
#define FFTW_TIME_MIN (1.0e-4)	/* for Solaris nanosecond timer */
#define FFTW_TIME_REPEAT 8

/**********************************************
 *        Pentium time stamp counter
 **********************************************/
#elif defined(__GNUC__) && defined(__i386__) && defined(FFTW_ENABLE_PENTIUM_TIMER)

/*
 * Use internal Pentium register (time stamp counter). Resolution
 * is 1/FFTW_CYCLES_PER_SEC seconds (e.g. 5 ns for Pentium 200 MHz).
 * (This code was contributed by Wolfgang Reimer)
 */

#ifndef FFTW_CYCLES_PER_SEC
#error "Must define FFTW_CYCLES_PER_SEC in fftw/config.h to use the Pentium cycle counter"
#endif

typedef unsigned long long fftw_time;

static __inline__ fftw_time read_tsc()
{
     struct {
	  long unsigned lo, hi;
     } counter;
     long unsigned sav_eax, sav_edx;
   __asm__("movl %%eax,%0":"=m"(sav_eax));
   __asm__("movl %%edx,%0":"=m"(sav_edx));
     __asm__("rdtsc");
   __asm__("movl %%eax,%0":"=m"(counter.lo));
   __asm__("movl %%edx,%0":"=m"(counter.hi));
   __asm__("movl %0,%%eax": : "m"(sav_eax):"eax");
   __asm__("movl %0,%%edx": : "m"(sav_edx):"edx");
     return *(fftw_time *) & counter;
}

#define fftw_get_time()  read_tsc()
#define fftw_time_diff(t1,t2) ((t1) - (t2))
#define fftw_time_to_sec(t) (((double) (t)) / FFTW_CYCLES_PER_SEC)
#define FFTW_TIME_MIN (1.0e-4)	/* for Pentium TSC register */

/************* generic systems having gettimeofday ************/
#elif defined(HAVE_GETTIMEOFDAY) || defined(HAVE_BSDGETTIMEOFDAY)
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#define FFTW_USE_GETTIMEOFDAY

typedef struct timeval fftw_time;

extern fftw_time fftw_gettimeofday_get_time(void);
extern fftw_time fftw_gettimeofday_time_diff(fftw_time t1, fftw_time t2);
#define fftw_get_time() fftw_gettimeofday_get_time()
#define fftw_time_diff(t1, t2) fftw_gettimeofday_time_diff(t1, t2)
#define fftw_time_to_sec(t) ((double)(t).tv_sec + (double)(t).tv_usec * 1.0E-6)

#ifndef FFTW_TIME_MIN
/* this should be fine on any system claiming a microsecond timer */
#define FFTW_TIME_MIN (1.0e-2)
#endif

/**********************************************
 *              MACINTOSH
 **********************************************/
#elif defined(HAVE_MAC_TIMER)

/*
 * By default, use the microsecond-timer in the Mac Time Manager.
 * Alternatively, by changing the following #if 1 to #if 0, you
 * can use the nanosecond timer available *only* on PCI PowerMacs.
 */
#ifndef HAVE_MAC_PCI_TIMER	/* use time manager */

/*
 * Use Macintosh Time Manager routines (maximum resolution is about 20
 * microseconds).
 */
typedef struct fftw_time_struct {
     unsigned long hi, lo;
} fftw_time;

extern fftw_time get_Mac_microseconds(void);

#define fftw_get_time() get_Mac_microseconds()

/* define as a function instead of a macro: */
extern fftw_time fftw_time_diff(fftw_time t1, fftw_time t2);

#define fftw_time_to_sec(t) ((t).lo * 1.0e-6 + 4294967295.0e-6 * (t).hi)

/* very conservative, since timer should be accurate to 20e-6: */
/* (although this seems not to be the case in practice) */
#define FFTW_TIME_MIN (5.0e-2)	/* for MacOS Time Manager timer */

#else				/* use nanosecond timer */

/* Use the nanosecond timer available on PCI PowerMacs. */

#include <DriverServices.h>

typedef AbsoluteTime fftw_time;
#define fftw_get_time() UpTime()
#define fftw_time_diff(t1,t2) SubAbsoluteFromAbsolute(t1,t2)
#define fftw_time_to_sec(t) (AbsoluteToNanoseconds(t).lo * 1.0e-9)

/* Extremely conservative minimum time: */
/* for MacOS PCI PowerMac nanosecond timer */
#define FFTW_TIME_MIN (5.0e-3)

#endif				/* use nanosecond timer */

/**********************************************
 *              WINDOWS
 **********************************************/
#elif defined(HAVE_WIN32_TIMER)

#include <time.h>

typedef unsigned long fftw_time;
extern unsigned long GetPerfTime(void);
extern double GetPerfSec(double ticks);

#define fftw_get_time() GetPerfTime()
#define fftw_time_diff(t1,t2) ((t1) - (t2))
#define fftw_time_to_sec(t) GetPerfSec(t)

#define FFTW_TIME_MIN (5.0e-2)	/* for Win32 timer */

/**********************************************
 *              CRAY
 **********************************************/
#elif defined(_CRAYMPP)		/* Cray MPP system */

double SECONDR(void);		/*
				 * I think you have to link with -lsci to
				 * get this
				 */

typedef double fftw_time;
#define fftw_get_time() SECONDR()
#define fftw_time_diff(t1,t2) ((t1) - (t2))
#define fftw_time_to_sec(t) (t)

#define FFTW_TIME_MIN (1.0e-1)	/* for Cray MPP SECONDR timer */

/**********************************************
 *          VANILLA UNIX/ISO C SYSTEMS
 **********************************************/
/* last resort: use good old Unix clock() */
#else

#include <time.h>

typedef clock_t fftw_time;

#ifndef CLOCKS_PER_SEC
#ifdef sun
/* stupid sunos4 prototypes */
#define CLOCKS_PER_SEC 1000000
extern long clock(void);
#else				/* not sun, we don't know CLOCKS_PER_SEC */
#error Please define CLOCKS_PER_SEC
#endif
#endif

#define fftw_get_time() clock()
#define fftw_time_diff(t1,t2) ((t1) - (t2))
#define fftw_time_to_sec(t) (((double) (t)) / CLOCKS_PER_SEC)

/*
 * ***VERY*** conservative constant: this says that a
 * measurement must run for 200ms in order to be valid.
 * You had better check the manual of your machine
 * to discover if it can do better than this
 */
#define FFTW_TIME_MIN (2.0e-1)	/* for default clock() timer */

#endif				/* UNIX clock() */

/* take FFTW_TIME_REPEAT measurements... */
#ifndef FFTW_TIME_REPEAT
#define FFTW_TIME_REPEAT 4
#endif

/* but do not run for more than TIME_LIMIT seconds while measuring one FFT */
#ifndef FFTW_TIME_LIMIT
#define FFTW_TIME_LIMIT 2.0
#endif

#ifdef __cplusplus
}				/* extern "C" */

#endif				/* __cplusplus */

#endif				/* FFTW_INT_H */


#pragma once

#include "types/type_util.h"

#include <expected.hpp>
#include <gsl/gsl>
#include <sstream>
#include <variant>

namespace util {

template<typename... Args>
std::string
concat(const Args&... args)
{
  std::stringstream ss;
  (ss << ... << args);
  return ss.str();
}

namespace detail {
template<typename T>
T
str_to(const std::string& str, std::size_t* pos, int base)
{
  static_assert(AlwaysFalse<T>::value, "try_parse_number not defined for type T!");
}

#define GEN_STR_TO_SPECIALIZATION(type, func)                                                      \
  template<>                                                                                \
  inline type str_to(const std::string& str, std::size_t* pos, int base)                           \
  {                                                                                \
    const auto parsed = func(str, pos, base);                                                      \
    if (parsed > std::numeric_limits<type>::max()) {                                               \
      throw std::out_of_range{ "Value is out of range for type " #type };                          \
    }                                                                                \
    return gsl::narrow_cast<type>(parsed);                                                         \
  }

GEN_STR_TO_SPECIALIZATION(uint8_t, std::stoul)
GEN_STR_TO_SPECIALIZATION(int8_t, std::stol)
GEN_STR_TO_SPECIALIZATION(uint16_t, std::stoul)
GEN_STR_TO_SPECIALIZATION(int16_t, std::stol)
GEN_STR_TO_SPECIALIZATION(uint32_t, std::stoul)
GEN_STR_TO_SPECIALIZATION(int32_t, std::stol)
GEN_STR_TO_SPECIALIZATION(uint64_t, std::stoull)
GEN_STR_TO_SPECIALIZATION(int64_t, std::stoll)

template<>
inline float
str_to(const std::string& str, std::size_t* pos, int)
{
  return std::stof(str, pos);
}

template<>
inline double
str_to(const std::string& str, std::size_t* pos, int)
{
  return std::stod(str, pos);
}

template<>
inline long double
str_to(const std::string& str, std::size_t* pos, int)
{
  return std::stold(str, pos);
}

}

/**
 * Wrapper around the std::stoX functions. Tries to parse the given string as a number
 * of the given type and returns a tl::expected with the resulting number of the reason
 * for failure. The arguments are the same as for the std functions, with the exception that
 * base is only valid if T is an integer type
 */
template<typename T>
tl::expected<T, std::variant<std::invalid_argument, std::out_of_range>>
try_parse_number(const std::string& str, std::size_t* pos = nullptr, int base = 10)
{
  try {
    const auto number = detail::str_to<T>(str, pos, base);
    return { number };
  } catch (const std::invalid_argument& ex) {
    return tl::make_unexpected(std::variant<std::invalid_argument, std::out_of_range>{ ex });
  } catch (const std::out_of_range& ex) {
    return tl::make_unexpected(std::variant<std::invalid_argument, std::out_of_range>{ ex });
  }
}

} // namespace util


#ifndef IAIF_H
#define IAIF_H

#include <utility>
#include <tuple>

#include <gsl/gsl_vector.h>

#include "constants.h"
#include "lpc.h"
#include "filter.h"

void computeIAIF(gsl_vector *g, gsl_vector *dg, gsl_vector *x);

#endif // IAIF_H


/******************************************************************************
* Copyright 2018 The Apollo Authors. All Rights Reserved.
*
* 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.
*****************************************************************************/
#pragma once

#include <cblas.h>

#include "modules/perception/camera/common/camera_frame.h"

namespace apollo {
namespace perception {
namespace camera {

class BaseSimilar {
 public:
  virtual bool Calc(CameraFrame *frame1,
                    CameraFrame *frame2,
                    base::Blob<float> *sim) = 0;
};

class CosineSimilar : public BaseSimilar {
 public:
  CosineSimilar() = default;

  bool Calc(CameraFrame *frame1,
            CameraFrame *frame2,
            base::Blob<float> *sim) override;
};

class GPUSimilar : public BaseSimilar {
 public:
  bool Calc(CameraFrame *frame1,
            CameraFrame *frame2,
            base::Blob<float> *sim) override;
};
}  // namespace camera
}  // namespace perception
}  // namespace apollo


/**
 * Original Author:
 * * author: Jochen K"upper
 * * created: April 2002
 *
 * author: Pierre Schnizer
 * created: December 2003
 * file: pygsl/src/simanmodule.c
 * $Id: simanmodule.c,v 1.9 2008/10/25 20:45:04 schnizer Exp $
 *
 * Jochen K"upper wrote the original version of this module. In December 2003 I
 * rewrote it. Now I only support the variable type as it is more pythonic.
 *
 */

#include <Python.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <gsl/gsl_siman.h>
#include <gsl/gsl_nan.h>
#include <pygsl/error_helpers.h>
#include <pygsl/general_helpers.h>
#include <pygsl/rng.h>
#include <pygsl/rng_helpers.h>

/*
 * Common to all objects of one problem
 * Currently the individual method pointers are not used. Instead the method
 * is resolved every time. Do you know which method is faster? Is it advicable
 * to resolve it once and store it here? Pierre
 */
typedef struct{
	/*
	  PyObject * efunc;
	  PyObject * step;
	  PyObject * metric;
	  PyObject * print;
	*/
	PyObject * rng;
	jmp_buf  buffer;
}pygsl_siman_func_t;

/*
 * The Linked list keeping the reference to the individual objects.
 *
 * Necessary as I have to use longjmp as the current implementation does
 * not allow error propagation yet!
 */
struct _pygsl_siman_t{
	pygsl_siman_func_t *func;
	PyObject * x;
	struct _pygsl_siman_t * prev;
	struct _pygsl_siman_t * next;
};
typedef struct _pygsl_siman_t pygsl_siman_t;

static const char module_doc[] = "C Implementation needed for the siman module.";
static PyObject *module = NULL;
static const char filename[] = __FILE__;

/*
 * Naming of the various methods the object has provide as callbacks.
 */
static const char EFunc_name[]  = "EFunc";
static const char Metric_name[] = "Metric";
static const char Step_name[]   = "Step";
static const char Clone_name[]  = "Clone";
static const char Print_name[]  = "Print";

/*
 * Get a callable method from a object.
 *
 * Basically it is a flat wrapper around PyObject_GetAttrString but
 * checks if the method is callable and adds a Traceback frame
 *
 * Flag usage:
 *           flag == 1 Must exist and must be callable
 *           flag == 2 If it exists it must be callable
 *
 * For the traceback frame all arguments following the module are needed.
 * If you do not know the module, you can pass a NULL pointer.
 */
static PyObject *
PyGSL_get_callable_method(PyObject *o, const char * attr, int flag, PyObject *module,
			  const char * filename, const char * func_name,
			  int lineno)
{
	PyObject * method = NULL;

	FUNC_MESS_BEGIN();
	method = PyObject_GetAttrString(o, (char *) attr);
	if(method == NULL){
		if(flag == 1){
			PyGSL_add_traceback(module, filename, func_name, lineno);
		}else if(flag == 2){
			/* Clear the error otherwise it will show up later on! */
			PyErr_Clear();
		}
		return NULL;
	}
	if(!(PyCallable_Check(method))) {
		/* I must add the method name! I must change it to an more descriptive exception! */
		PyGSL_add_traceback(module, (const char *) filename, func_name, lineno);
		PyErr_SetString(PyExc_TypeError, "Found a attribute which was not callable!"
				"XXX must add the method name!");
		return NULL;
	}
	DEBUG_MESS(2, "Found a method at %p", (void *) method);
	FUNC_MESS_END();
	return method;
}

/* This function type should return the energy of a configuration XP. */
static double
PyGSL_siman_efunc(void *xp)
{

	PyObject *result = NULL, *callback = NULL, *arglist = NULL;
	PyGSL_error_info info;
	pygsl_siman_t *x;
	int flag=GSL_EFAILED;
	double value;
	/* static char *functionname  = __FUNCTION__; */

	FUNC_MESS_BEGIN();

	assert(xp);
	x = (pygsl_siman_t *) xp;

	DEBUG_MESS(2, "Found a pygsl_siman_t at %p and a pygsl_siman_func_t at %p and x at %p",
		   (void *)x, (void *) x->func, (void *) x->x);

	assert(x);
	assert(x->func);

	callback = PyGSL_get_callable_method(x->x, EFunc_name, 1, module, filename, __FUNCTION__, __LINE__);
	if(callback == NULL)
		goto fail;

	info.callback = callback;
	info.message  = __FUNCTION__;
	info.error_description = "and the description ???";
	info.argnum = 1;

	arglist = PyTuple_New(0);
	result = PyEval_CallObject(callback, arglist);
	Py_DECREF(arglist);
	if((flag = PyGSL_CHECK_PYTHON_RETURN(result, 1, &info)) != GSL_SUCCESS){
		PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	if((flag = PyGSL_PYFLOAT_TO_DOUBLE(result, &value, &info)) != GSL_SUCCESS){
		PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	Py_DECREF(result);
	FUNC_MESS_END();
	return value;

  fail:
	FUNC_MESS("In Fail");
	Py_XDECREF(result);
	longjmp(x->func->buffer, flag);
	return GSL_NAN;
}

/*
 *  This function type should modify the configuration XP using a random step
 *  taken from the generator R, up to a maximum distance of STEP_SIZE.
 */
static void
PyGSL_siman_step(const gsl_rng *r, void *xp, double step_size)
{

	PyObject *result = NULL, *arglist = NULL, *callback = NULL;
	PyGSL_error_info info;
	pygsl_siman_t *x;
	int flag=GSL_EFAILED;

	/* static const char * functionname  = __FUNCTION__; */

	FUNC_MESS_BEGIN();
	x = (pygsl_siman_t *) xp;
	DEBUG_MESS(2, "Found x at %p", xp);

	callback = PyGSL_get_callable_method(x->x, Step_name, 1, module, filename, __FUNCTION__, __LINE__);
	if(callback == NULL)
		goto fail;

	info.callback = callback;
	info.message  = __FUNCTION__;
	info.error_description = "???";
	info.argnum = 1;

	assert(PyGSL_RNG_Check(x->func->rng));
	assert(((PyGSL_rng *) x->func->rng)->rng == r);

	/* create argument list */
	arglist = PyTuple_New(2);
	PyTuple_SET_ITEM(arglist, 0, x->func->rng);
	Py_INCREF(x->func->rng); /* Don't forget tuple is owner! */
	PyTuple_SET_ITEM(arglist, 1, PyFloat_FromDouble(step_size));

	result = PyEval_CallObject(callback, arglist);
	Py_DECREF(arglist);
	if((flag = PyGSL_CHECK_PYTHON_RETURN(result, 0, &info)) != GSL_SUCCESS){
		PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	Py_DECREF(result);
	FUNC_MESS_END();
	return;

  fail:
	FUNC_MESS("In Fail");
	Py_XDECREF(result);
	longjmp(x->func->buffer, flag);
	return;
}

/* This function type should return the distance between two configurations XP
   and YP. */
static double
PyGSL_siman_metric(void *xp, void *yp)
{

	PyObject *result = NULL, *arglist = NULL, *callback = NULL;
	PyGSL_error_info info;
	pygsl_siman_t *x, *y;
	int flag=GSL_EFAILED;
	double value;
	/* static const char * functionname = __FUNCTION__; */

	FUNC_MESS_BEGIN();
	x = (pygsl_siman_t *) xp;
	y = (pygsl_siman_t *) yp;

	DEBUG_MESS(2, "Found x at (%p,%p) and y at (%p %p)",
		   (void *) x, (void *)x->x, (void *) y, (void *) y->x);

	assert(x);
	assert(y);
	assert(x->x);
	assert(y->x);

	callback = PyGSL_get_callable_method(x->x, Metric_name, 1, module, filename, __FUNCTION__, __LINE__);
	if(callback == NULL)
		goto fail;

	info.callback = callback;
	info.message  = __FUNCTION__;
	info.error_description = "???";
	info.argnum = 1;

	arglist = PyTuple_New(1);
	PyTuple_SET_ITEM(arglist, 0, y->x);
	Py_INCREF(y->x); /* Tuple is owner! */

	result = PyEval_CallObject(callback, arglist);
	Py_XDECREF(arglist);

	if((flag = PyGSL_CHECK_PYTHON_RETURN(result, 0, &info)) != GSL_SUCCESS){
		PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	if((flag = PyGSL_PYFLOAT_TO_DOUBLE(result, &value, &info)) != GSL_SUCCESS){
		PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	Py_DECREF(result);
	FUNC_MESS_END();
	return value;

  fail:
	FUNC_MESS("In Fail");
	Py_XDECREF(result);
	longjmp(x->func->buffer, flag);
	return GSL_NAN;
}

/* This function type should print the contents of the configuration XP. */
static void
PyGSL_siman_print(void *xp)
{
	PyObject *result = NULL, *callback=NULL, *arglist=NULL;
	PyGSL_error_info info;
	pygsl_siman_t *x;
	int flag=GSL_EFAILED;
	/* static const char * functionname  = __FUNCTION__; */

	FUNC_MESS_BEGIN();
	x = (pygsl_siman_t *) xp;

	callback = PyGSL_get_callable_method(x->x, Print_name, 1, module, filename, __FUNCTION__, __LINE__);
	if(callback == NULL)
		goto fail;

	info.callback = callback;
	info.message  = __FUNCTION__;
	info.error_description = "what goes here ???";
	info.argnum = 1;

	arglist = PyTuple_New(0);
	result = PyEval_CallObject(callback, arglist);
	Py_DECREF(arglist);

	if((flag = PyGSL_CHECK_PYTHON_RETURN(result, 0, &info)) != GSL_SUCCESS){
		PyGSL_add_traceback(module, (const char*)filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	Py_DECREF(result);
	FUNC_MESS_END();
	return;

  fail:
	FUNC_MESS("In Fail");
	Py_XDECREF(result);
	longjmp(x->func->buffer, flag);
	return;

}

/*
 * As Python is generating the new objects I can not skip the reference
 * counting, but must add a reference for each object in construct and
 * depose it here and in the siman_destroy function.
 *
 * siman_solve will call siman_release_x anyway at the end to clear up
 * all objects. Necessary to non existing error propagation in the siman
 * module.
 */
static void
PyGSL_siman_copy(void *src, void *dst)
{
	PyObject *callback=NULL, *new = NULL, *arglist = NULL;
	PyGSL_error_info info;
	pygsl_siman_t *x, *y;
	int flag=GSL_EFAILED;
	/* static const char * functionname = __FUNCTION__; */

	FUNC_MESS_BEGIN();
	x = (pygsl_siman_t *) src;
	y = (pygsl_siman_t *) dst;

	DEBUG_MESS(2, "Got source at %p, Destination at %p", (void *) x, (void *) y);
	assert(x->x);
	callback = PyGSL_get_callable_method(x->x, Clone_name, 1, module, filename, __FUNCTION__, __LINE__);
	if(callback == NULL)
		goto fail;

	arglist = PyTuple_New(0);
	new = PyEval_CallObject(callback, arglist);
	Py_DECREF(arglist);

	info.callback = callback;
	info.message  = __FUNCTION__;
	info.error_description = "???";
	info.argnum = 1;

	if((flag = PyGSL_CHECK_PYTHON_RETURN(new, 1, &info)) != GSL_SUCCESS){
		PyGSL_add_traceback(module, (const char*)filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	Py_XDECREF(y->x);
	/* Py_INCREF(new);  Necessary? I don't think so. */
	y->x = new;
	FUNC_MESS_END();
	return;

  fail:
	FUNC_MESS("Fail");
	Py_XDECREF(new);
	longjmp(x->func->buffer, flag);

}

static void *
PyGSL_siman_copy_construct(void *new)
{
	pygsl_siman_t * ret, * n, *p;
	int flag = GSL_ENOMEM;

	FUNC_MESS_BEGIN();
	n   = (pygsl_siman_t *) new;

	/* The pointers next and prev are checked against NULL */
	ret = (pygsl_siman_t *) calloc(1, sizeof(pygsl_siman_t));
	DEBUG_MESS(2, "New was %p, Constructed a new object at %p", new, (void *) ret);
	if(ret == NULL){
		pygsl_error("Could not allocate the object for the linked list",
			  filename, __LINE__ - 3, GSL_ENOMEM);
		goto fail;
	}
	/* Put the Link to the old object so that I can clone when I need to copy */
	ret->x    = n->x;
	/* Eventually I will dispose the object */
	Py_INCREF(ret->x);
	/* Pointer to the func struct. This information is the same for all objects */
	ret->func = n->func;

	/* Find the first open object in the linked list ... */
	p = n;
	while(p->next != NULL){
		p = p->next;
	}
	DEBUG_MESS(2, "I found a open object at %p", (void *) p);
	/* and connect the links */
	p->next = ret;
	ret->prev = p;

	FUNC_MESS_END();
	return ret;

  fail:
	FUNC_MESS("Fail");
	longjmp(n->func->buffer, flag);
	return NULL;

}

static void
PyGSL_siman_destroy(void * old)
{
	pygsl_siman_t * o;

	FUNC_MESS_BEGIN();
	o = (pygsl_siman_t *) old;
	assert(o);

	/* fprintf(stderr, "Destroying: Previous object = %p, Next Object = %p\n",
	   (void *)o->prev, (void *)o->next); */

	/* Reconnect the linked list */
	if (o->prev && o->next){
		/* Connect the both */
		o->prev->next = o->next;
		o->next->prev = o->prev;
	}
	else if (o->prev && o->next == NULL){
		/* Prev last element. Terminate the list */
		o->prev->next = NULL;
	}
	else if (o->prev == NULL && o->next == NULL){
		/* Last Element, better to leave it */
		DEBUG_MESS(2, "I do not dispose the last element %p!", (void *) o);
		return;
	}

	/* Dispose the object */
	Py_XDECREF(o->x);
	free(o);
	FUNC_MESS_END();
}

/* Clean up of the linked list of objects */
int
PyGSL_siman_release_x(pygsl_siman_t * myargs, pygsl_siman_t * x)
{
	pygsl_siman_t *p=NULL;
	FUNC_MESS_BEGIN();

	p = myargs;
	/* fprintf(stderr, "Releasing list!\n"); */
	while(1){
		/* fprintf(stderr, "Previous object = %p, Next Object = %p\n",
		   (void *)p->prev, (void *)p->next); */
		/* Don't delete the object containing the result! */
		if(p != x){
			/* fprintf(stderr, "Deleting object at %p\n", (void *) p); */
			PyGSL_siman_destroy((void *) p);
		}
		if(p->next == NULL)
			break;
		p = p->next;
	}
	FUNC_MESS_END();
	return GSL_SUCCESS;
}

static const char pygsl_siman_solve_doc[] =
"Simulated annealing driver.\n\
\n\
Usage:\n\
result = solve(r, x0, ...)\n\
\n\
Input:\n\
    r   ... a random generator from pygsl.rng\n\
    x0  ... a configuration. It must be an object providing the following\n\
            methods:\n\
            EFunc()\n\
            Metric()\n\
            Step()\n\
            Clone()\n\
               If you want to use the print functionality you must provide\n\
               the following method:\n\
            Print()\n\
\n\
Output:\n\
    result ... a object of type x0 with the final value.\n\
\n\
Keywords:\n\
    n_tries       = 200  ... how many points to try for each step\n\
    iters_fixed_T = 10   ... how many iterations at each temperature?\n\
    step_size     = 10   ... max step size in the random walk\n\
\n\
                      parameters for the Boltzmann distribution\n\
    k             = 1.0    ... Boltzmann constant\n\
    t_initial     = 0.002  ... initial temperature\n\
    mu_t          = 1.005  ... damping factor for the temperature\n\
    t_min         = 2.0e-6\n\
\n\
    do_print      = 0      ... print the status of the annealing process\n\
                               (== 0: do not print)\n\
                               ( > 0: print)\n\
";

/* wrapper functions */
static PyObject *
PyGSL_siman_solve(PyObject *self, PyObject *args, PyObject *kw)
{
	PyObject *result = NULL;
	PyObject *efunc = NULL, *step = NULL, *metric = NULL, *clone = NULL,
		 *print = NULL, *r_o = NULL, *x_o=NULL;

	gsl_rng *rng = NULL;

	gsl_siman_print_t    a_print= PyGSL_siman_print;
	gsl_siman_params_t   params = {200, 10, 10.0, 1.0, 0.002, 1.005, 2.0e-6};

	pygsl_siman_func_t   myargs_func = {NULL};
	pygsl_siman_t        myargs = {NULL, NULL, NULL, NULL};

	/* static const char  * functionname = __FUNCTION__; */

	int flag=GSL_EFAILED, do_print=0;
	void * x0 = NULL;

	static const char * kwlist[] = {"rng", "x0", "n_tries", "iters_fixed_T", "step_size", "k",
					"t_initial", "mu_t",  "t_min", "do_print", NULL};

	FUNC_MESS_BEGIN();
	/* python arguments are (rng, x0, settings) */
	if(! PyArg_ParseTupleAndKeywords(args, kw, "OO|iidddddi",  (char **) kwlist, &r_o, &x_o,
			      &params.n_tries, &params.iters_fixed_T, &params.step_size,
			      &params.k, &params.t_initial, &params.mu_t, &params.t_min, &do_print))
		return NULL;

	/* The following methods must exist */
	efunc  = PyGSL_get_callable_method(x_o, EFunc_name,  1, module, filename, __FUNCTION__, __LINE__);
	step   = PyGSL_get_callable_method(x_o, Step_name,   1, module, filename, __FUNCTION__, __LINE__);
	metric = PyGSL_get_callable_method(x_o, Metric_name, 1, module, filename, __FUNCTION__, __LINE__);
	clone  = PyGSL_get_callable_method(x_o, Clone_name,  1, module, filename, __FUNCTION__, __LINE__);
	if( efunc == NULL || step == NULL || metric == NULL || clone == NULL){
		return NULL;
	}

	/* optional print */
	if(do_print == 0){
		a_print = NULL;
	} else {
		print  = PyGSL_get_callable_method(x_o, Print_name,  1, module, filename, __FUNCTION__, __LINE__);
		if(print == NULL){
			DEBUG_MESS(2, "Did not get a print method! print = %p", print);
			a_print = NULL;
			return NULL;
		}
	}

	rng = PyGSL_gsl_rng_from_pyobject(r_o);
	if(rng == NULL){
		return NULL;
	}

	/* initialize/assign functions */
	Py_INCREF(x_o);
	/*
	  myargs_func.efunc  = efunc;
	  myargs_func.step   = step;
	  myargs_func.metric = metric;
	  myargs_func.print  = print;
	*/
	myargs_func.rng    = r_o;

	myargs.func = &myargs_func;
	myargs.x   = x_o;
	myargs.prev = NULL;
	myargs.next = NULL;

	x0 = (void *) &myargs;
	DEBUG_MESS(2, "x0 @ %p; myargs at %p; myargs_func at %p", x0, (void *) &myargs, (void *) &myargs_func);
	DEBUG_MESS(2, "Found a pygsl_siman_t at %p and a pygsl_siman_func_t at %p",
		   (void *) x0,
		   (void *) (((pygsl_siman_t *) x0)->func));

	if((flag = setjmp(myargs_func.buffer)) == 0){
		FUNC_MESS("Starting siman");
		gsl_siman_solve(rng, x0, PyGSL_siman_efunc, PyGSL_siman_step,
				PyGSL_siman_metric, a_print, PyGSL_siman_copy,
				PyGSL_siman_copy_construct, PyGSL_siman_destroy,
				0, /* Only variable mode supported by this wrapper. */
				params);
		FUNC_MESS("End siman");
	}else{
		PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__);
		goto fail;
	}
	Py_DECREF(x_o);
	DEBUG_MESS(2, "I found x0 at %p", x0);
	result = ((pygsl_siman_t *) x0)->x;

	PyGSL_siman_release_x(&myargs, x0);
	FUNC_MESS_END();
	return result;

  fail:
	FUNC_MESS("In Fail");
	PyGSL_siman_release_x(&myargs, x0);
	Py_XDECREF(x_o);
	PyGSL_error_flag(flag);
	return NULL;
}

/* module initialization */
static PyMethodDef simanMethods[] = {
	{"solve", (PyCFunction) PyGSL_siman_solve,
	 METH_VARARGS | METH_KEYWORDS, (char *) pygsl_siman_solve_doc},
	{NULL, NULL} /* Sentinel */
};

DL_EXPORT(void) init_siman(void)
{
	PyObject *m = NULL;
	FUNC_MESS_BEGIN();
	m = Py_InitModule("_siman", simanMethods);
	module = m;
	init_pygsl();
	import_pygsl_rng();
	FUNC_MESS_END();
	return;
}

/*
 * Local Variables:
 * mode: C
 * c-file-style: "python"
 * End:
 */


#pragma once

#include "Common.h"
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/edge_list.hpp>
#include <boost/graph/graph_selectors.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/visitors.hpp>
#include <any>
#include <forward_list>
#include <functional>
#include <gsl/span>
#include <set>
#include <string_view>
#include <variant>

namespace bcos::scheduler
{
class GraphKeyLocks
{
public:
    using Ptr = std::shared_ptr<GraphKeyLocks>;
    using KeyLock = std::tuple<std::string, std::string>;
    using KeyLockView = std::tuple<std::string_view, std::string_view>;

    using ContractView = std::string_view;
    using KeyView = std::string_view;

    GraphKeyLocks() = default;
    GraphKeyLocks(const GraphKeyLocks&) = delete;
    GraphKeyLocks(GraphKeyLocks&&) = delete;
    GraphKeyLocks& operator=(const GraphKeyLocks&) = delete;
    GraphKeyLocks& operator=(GraphKeyLocks&&) = delete;

    bool batchAcquireKeyLock(std::string_view contract, gsl::span<std::string const> keyLocks,
        ContextID contextID, Seq seq);

    bool acquireKeyLock(
        std::string_view contract, std::string_view key, ContextID contextID, Seq seq);

    std::vector<std::string> getKeyLocksNotHoldingByContext(
        std::string_view contract, ContextID excludeContextID) const;

    void releaseKeyLocks(ContextID contextID, Seq seq);

    bool detectDeadLock(ContextID contextID);

    struct Vertex : public std::variant<ContextID, KeyLock>
    {
        using std::variant<ContextID, KeyLock>::variant;

        bool operator==(const KeyLockView& rhs) const
        {
            if (index() != 1)
            {
                return false;
            }

            auto view = std::make_tuple(std::string_view(std::get<0>(std::get<1>(*this))),
                std::string_view(std::get<1>(std::get<1>(*this))));
            return view == rhs;
        }
    };

private:
    struct VertexIterator
    {
        using kind = boost::vertex_property_tag;
    };
    using VertexProperty = boost::property<VertexIterator, const Vertex*>;
    struct EdgeSeq
    {
        using kind = boost::edge_property_tag;
    };
    using EdgeProperty = boost::property<EdgeSeq, int64_t>;

    using Graph = boost::adjacency_list<boost::multisetS, boost::multisetS, boost::bidirectionalS,
        VertexProperty, EdgeProperty>;
    using VertexPropertyTag = boost::property_map<Graph, VertexIterator>::const_type;
    using EdgePropertyTag = boost::property_map<Graph, EdgeSeq>::const_type;
    using VertexID = Graph::vertex_descriptor;
    using EdgeID = Graph::edge_descriptor;

    Graph m_graph;
    std::map<Vertex, VertexID, std::less<>> m_vertexes;

    VertexID touchContext(ContextID contextID);
    VertexID touchKeyLock(KeyLockView keylockView);

    void addEdge(VertexID source, VertexID target, Seq seq);
    void removeEdge(VertexID source, VertexID target, Seq seq);
};

}  // namespace bcos::scheduler

namespace std
{
inline bool operator<(const bcos::scheduler::GraphKeyLocks::Vertex& lhs,
    const bcos::scheduler::GraphKeyLocks::KeyLockView& rhs)
{
    if (lhs.index() != 1)
    {
        return true;
    }

    auto view = std::make_tuple(std::string_view(std::get<0>(std::get<1>(lhs))),
        std::string_view(std::get<1>(std::get<1>(lhs))));
    return view < rhs;
}

inline bool operator<(const bcos::scheduler::GraphKeyLocks::KeyLockView& lhs,
    const bcos::scheduler::GraphKeyLocks::Vertex& rhs)
{
    if (rhs.index() != 1)
    {
        return false;
    }

    auto view = std::make_tuple(std::string_view(std::get<0>(std::get<1>(rhs))),
        std::string_view(std::get<1>(std::get<1>(rhs))));
    return lhs < view;
}
}  // namespace std


/* This file is necessary otherwise clang instantiate the bindgen tools
 * multiple time concurrently which produce non-deterministic results. */

#include <cblas.h>
#include <lapacke.h>


#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>
#include <netcdf.h>

#define RES 16
#define MAX_PTS 256

void nc_meta_double(int group_id, char *name, char *desc, char *units,
		    double *value)
{
  int var_id;
  char *str = (char *) MALLOC(sizeof(char)*1024);
  nc_def_var(group_id, name, NC_DOUBLE, 0, 0, &var_id);
  nc_put_att_text(group_id, var_id, "long_name", strlen(desc), desc);
  if (units && strlen(units) > 0) {
    strcpy(str, units);
    nc_put_att_text(group_id, var_id, "units", strlen(str), str);
  }
  nc_put_var_double(group_id, var_id, value);
}

void nc_meta_float(int group_id, char *name, char *desc, char *units,
		   float *value)
{
  int var_id;
  char *str = (char *) MALLOC(sizeof(char)*1024);
  nc_def_var(group_id, name, NC_FLOAT, 0, 0, &var_id);
  nc_put_att_text(group_id, var_id, "long_name", strlen(desc), desc);
  if (units && strlen(units) > 0) {
    strcpy(str, units);
    nc_put_att_text(group_id, var_id, "units", strlen(str), str);
  }
  nc_put_var_float(group_id, var_id, value);
}

void nc_meta_int(int group_id, char *name, char *desc, char *units,
		 int *value)
{
  int var_id;
  char *str = (char *) MALLOC(sizeof(char)*1024);
  nc_def_var(group_id, name, NC_INT, 0, 0, &var_id);
  nc_put_att_text(group_id, var_id, "long_name", strlen(desc), desc);
  if (units && strlen(units) > 0) {
    strcpy(str, units);
    nc_put_att_text(group_id, var_id, "units", strlen(str), str);
  }
  nc_put_var_int(group_id, var_id, value);
}

void nc_meta_str(int group_id, char *name, char *desc, char *units,
		 char *value)
{
  int var_id;
  const char *str_value = (char *) MALLOC(sizeof(char)*strlen(value));
  strcpy(str_value, value);
  char *str = (char *) MALLOC(sizeof(char)*1024);
  nc_def_var(group_id, name, NC_STRING, 0, 0, &var_id);
  nc_put_att_text(group_id, var_id, "long_name", strlen(desc), desc);
  if (units && strlen(units) > 0) {
    strcpy(str, units);
    nc_put_att_text(group_id, var_id, "units", strlen(str), str);
  }
  nc_put_var_string(group_id, var_id, &str_value);
}

netcdf_t *initialize_netcdf_file(const char *output_file,
				 meta_parameters *meta)
{
  int ii, status, ncid, var_id;
  int dim_xgrid_id, dim_ygrid_id, dim_lat_id, dim_lon_id, dim_time_id;
  char *spatial_ref=NULL, *datum=NULL, *spheroid=NULL;

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

  // Assign parameters
  int projected = FALSE;
  int band_count = mg->band_count;
  int variable_count = band_count + 3;
  if (mp && mp->type != SCANSAR_PROJECTION) {
    projected = TRUE;
    variable_count += 2;
  }
  size_t line_count = mg->line_count;
  size_t sample_count = mg->sample_count;

  // Assign data type
  nc_type datatype;
  if (mg->data_type == BYTE)
    datatype = NC_CHAR;
  else if (mg->data_type == REAL32)
    datatype = NC_FLOAT;

  // Initialize the netCDF pointer structure
  netcdf_t *netcdf = (netcdf_t *) MALLOC(sizeof(netcdf_t));
  netcdf->var_count = variable_count;
  netcdf->var_id = (int *) MALLOC(sizeof(int)*variable_count);

  // Create the actual file
  status = nc_create(output_file, NC_CLOBBER|NC_NETCDF4, &ncid);
  netcdf->ncid = ncid;
  if (status != NC_NOERR)
    asfPrintError("Could not open netCDF file (%s).\n", nc_strerror(status));

  // Define dimensions
  if (projected) {
    nc_def_dim(ncid, "xgrid", sample_count, &dim_xgrid_id);
    nc_def_dim(ncid, "ygrid", line_count, &dim_ygrid_id);
  }
  else {
    status = nc_def_dim(ncid, "longitude", sample_count, &dim_lon_id);
    if (status != NC_NOERR)
      asfPrintError("Problem with longitude definition\n");
    status = nc_def_dim(ncid, "latitude", line_count, &dim_lat_id);
    if (status != NC_NOERR)
      asfPrintError("Problem with latitude definition\n");
  }
  status = nc_def_dim(ncid, "time", 1, &dim_time_id);
  if (status != NC_NOERR)
    asfPrintError("Problem with time definition\n");

  // Define projection
  char *str = (char *) MALLOC(sizeof(char)*1024);
  double lfValue;
  if (projected) {
    nc_def_var(ncid, "projection", NC_CHAR, 0, 0, &var_id);
    if (mp->type == UNIVERSAL_TRANSVERSE_MERCATOR) {

      strcpy(str, "transverse_mercator");
      nc_put_att_text(ncid, var_id, "grid_mapping_name", strlen(str), str);
      nc_put_att_double(ncid, var_id, "scale_factor_at_central_meridian",
			NC_DOUBLE, 1, &mp->param.utm.scale_factor);
      nc_put_att_double(ncid, var_id, "longitude_of_central_meridian",
			NC_DOUBLE, 1, &mp->param.utm.lon0);
      nc_put_att_double(ncid, var_id, "latitude_of_projection_origin",
			NC_DOUBLE, 1, &mp->param.utm.lat0);
      nc_put_att_double(ncid, var_id, "false_easting", NC_DOUBLE, 1,
			&mp->param.utm.false_easting);
      nc_put_att_double(ncid, var_id, "false_northing", NC_DOUBLE, 1,
			&mp->param.utm.false_northing);
      strcpy(str, "xgrid");
      nc_put_att_text(ncid, var_id, "projection_x_coordinate", strlen(str),
		      str);
      strcpy(str, "ygrid");
      nc_put_att_text(ncid, var_id, "projection_y_coordinate", strlen(str),
		      str);
      strcpy(str, "m");
      nc_put_att_text(ncid, var_id, "units", strlen(str), str);
      nc_put_att_double(ncid, var_id, "grid_boundary_top_projected_y",
			NC_DOUBLE, 1, &mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      nc_put_att_double(ncid, var_id, "grid_boundary_bottom_projected_y",
			NC_DOUBLE, 1, &lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      nc_put_att_double(ncid, var_id, "grid_boundary_right_projected_x",
			NC_DOUBLE, 1, &lfValue);
      nc_put_att_double(ncid, var_id, "grid_boundary_left_projected_x",
			NC_DOUBLE, 1, &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);
      nc_put_att_text(ncid, var_id, "spatial_ref", strlen(spatial_ref),
		      spatial_ref);
      sprintf(str, "+proj=utm +zone=%d", mp->param.utm.zone);
      if (meta->general->center_latitude < 0)
	strcat(str, " +south");
      nc_put_att_text(ncid, var_id, "proj4text", strlen(str), str);
      nc_put_att_int(ncid, var_id, "zone", NC_INT, 1, &mp->param.utm.zone);
      nc_put_att_double(ncid, var_id, "semimajor_radius", NC_DOUBLE, 1,
			&mp->re_major);
      nc_put_att_double(ncid, var_id, "semiminor_radius", NC_DOUBLE, 1,
			&mp->re_minor);
      sprintf(str, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      nc_put_att_text(ncid, var_id, "GeoTransform", strlen(str), str);
    }
    else if (mp->type == POLAR_STEREOGRAPHIC) {

      strcpy(str, "polar_stereographic");
      nc_put_att_text(ncid, var_id, "grid_mapping_name", strlen(str), str);
      lfValue = 90.0;
      nc_put_att_double(ncid, var_id, "straight_vertical_longitude_from_pole",
			NC_DOUBLE, 1, &mp->param.ps.slon);
      nc_put_att_double(ncid, var_id, "longitude_of_central_meridian",
			NC_DOUBLE, 1, &lfValue);
      nc_put_att_double(ncid, var_id, "standard_parallel",
			NC_DOUBLE, 1, &mp->param.ps.slat);
      nc_put_att_double(ncid, var_id, "false_easting", NC_DOUBLE, 1,
			&mp->param.ps.false_easting);
      nc_put_att_double(ncid, var_id, "false_northing", NC_DOUBLE, 1,
			&mp->param.ps.false_northing);
      strcpy(str, "xgrid");
      nc_put_att_text(ncid, var_id, "projection_x_coordinate", strlen(str),
		      str);
      strcpy(str, "ygrid");
      nc_put_att_text(ncid, var_id, "projection_y_coordinate", strlen(str),
		      str);
      strcpy(str, "m");
      nc_put_att_text(ncid, var_id, "units", strlen(str), str);
      nc_put_att_double(ncid, var_id, "grid_boundary_top_projected_y",
			NC_DOUBLE, 1, &mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      nc_put_att_double(ncid, var_id, "grid_boundary_bottom_projected_y",
			NC_DOUBLE, 1, &lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      nc_put_att_double(ncid, var_id, "grid_boundary_right_projected_x",
			NC_DOUBLE, 1, &lfValue);
      nc_put_att_double(ncid, var_id, "grid_boundary_left_projected_x",
			NC_DOUBLE, 1, &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[\"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\",%.3lf],PARAMETER[\"false_northing\",%.3lf],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);
      nc_put_att_text(ncid, var_id, "spatial_ref", strlen(spatial_ref),
		      spatial_ref);
      if (mp->param.ps.is_north_pole)
	sprintf(str, "+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, "+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);
      nc_put_att_text(ncid, var_id, "proj4text", strlen(str), str);
      nc_put_att_double(ncid, var_id, "latitude_of_true_scale", NC_DOUBLE, 1,
			&mp->param.ps.slat);
      nc_put_att_double(ncid, var_id, "longitude_of_projection_origin",
			NC_DOUBLE, 1, &mp->param.ps.slon);
      nc_put_att_double(ncid, var_id, "semimajor_radius", NC_DOUBLE, 1,
			&mp->re_major);
      nc_put_att_double(ncid, var_id, "semiminor_radius", NC_DOUBLE, 1,
			&mp->re_minor);
      sprintf(str, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      nc_put_att_text(ncid, var_id, "GeoTransform", strlen(str), str);
    }
    else if (mp->type == ALBERS_EQUAL_AREA) {

      strcpy(str, "albers_conical_equal_area");
      nc_put_att_text(ncid, var_id, "grid_mapping_name", strlen(str), str);
      lfValue = 90.0;
      nc_put_att_double(ncid, var_id, "standard_parallel_1",
			NC_DOUBLE, 1, &mp->param.albers.std_parallel1);
      nc_put_att_double(ncid, var_id, "standard_parallel_2",
			NC_DOUBLE, 1, &mp->param.albers.std_parallel2);
      nc_put_att_double(ncid, var_id, "longitude_of_central_meridian",
			NC_DOUBLE, 1, &mp->param.albers.center_meridian);
      nc_put_att_double(ncid, var_id, "latitude_of_projection_origin",
			NC_DOUBLE, 1, &mp->param.albers.orig_latitude);
      nc_put_att_double(ncid, var_id, "false_easting", NC_DOUBLE, 1,
			&mp->param.albers.false_easting);
      nc_put_att_double(ncid, var_id, "false_northing", NC_DOUBLE, 1,
			&mp->param.albers.false_northing);
      strcpy(str, "xgrid");
      nc_put_att_text(ncid, var_id, "projection_x_coordinate", strlen(str),
		      str);
      strcpy(str, "ygrid");
      nc_put_att_text(ncid, var_id, "projection_y_coordinate", strlen(str),
		      str);
      strcpy(str, "m");
      nc_put_att_text(ncid, var_id, "units", strlen(str), str);
      nc_put_att_double(ncid, var_id, "grid_boundary_top_projected_y",
			NC_DOUBLE, 1, &mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      nc_put_att_double(ncid, var_id, "grid_boundary_bottom_projected_y",
			NC_DOUBLE, 1, &lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      nc_put_att_double(ncid, var_id, "grid_boundary_right_projected_x",
			NC_DOUBLE, 1, &lfValue);
      nc_put_att_double(ncid, var_id, "grid_boundary_left_projected_x",
			NC_DOUBLE, 1, &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);
      nc_put_att_text(ncid, var_id, "spatial_ref", strlen(spatial_ref),
		      spatial_ref);
      sprintf(str, "+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);
      nc_put_att_text(ncid, var_id, "proj4text", strlen(str), str);
      nc_put_att_double(ncid, var_id, "latitude_of_true_scale", NC_DOUBLE, 1,
			&mp->param.ps.slat);
      nc_put_att_double(ncid, var_id, "longitude_of_projection_origin",
			NC_DOUBLE, 1, &mp->param.ps.slon);
      nc_put_att_double(ncid, var_id, "semimajor_radius", NC_DOUBLE, 1,
			&mp->re_major);
      nc_put_att_double(ncid, var_id, "semiminor_radius", NC_DOUBLE, 1,
			&mp->re_minor);
      sprintf(str, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      nc_put_att_text(ncid, var_id, "GeoTransform", strlen(str), str);
    }
    else if (mp->type == LAMBERT_CONFORMAL_CONIC) {

      strcpy(str, "lambert_conformal_conic");
      nc_put_att_text(ncid, var_id, "grid_mapping_name", strlen(str), str);
      lfValue = 90.0;
      nc_put_att_double(ncid, var_id, "standard_parallel_1",
			NC_DOUBLE, 1, &mp->param.lamcc.plat1);
      nc_put_att_double(ncid, var_id, "standard_parallel_2",
			NC_DOUBLE, 1, &mp->param.lamcc.plat2);
      nc_put_att_double(ncid, var_id, "longitude_of_central_meridian",
			NC_DOUBLE, 1, &mp->param.lamcc.lon0);
      nc_put_att_double(ncid, var_id, "latitude_of_projection_origin",
			NC_DOUBLE, 1, &mp->param.lamcc.lat0);
      nc_put_att_double(ncid, var_id, "false_easting", NC_DOUBLE, 1,
			&mp->param.lamcc.false_easting);
      nc_put_att_double(ncid, var_id, "false_northing", NC_DOUBLE, 1,
			&mp->param.lamcc.false_northing);
      strcpy(str, "xgrid");
      nc_put_att_text(ncid, var_id, "projection_x_coordinate", strlen(str),
		      str);
      strcpy(str, "ygrid");
      nc_put_att_text(ncid, var_id, "projection_y_coordinate", strlen(str),
		      str);
      strcpy(str, "m");
      nc_put_att_text(ncid, var_id, "units", strlen(str), str);
      nc_put_att_double(ncid, var_id, "grid_boundary_top_projected_y",
			NC_DOUBLE, 1, &mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      nc_put_att_double(ncid, var_id, "grid_boundary_bottom_projected_y",
			NC_DOUBLE, 1, &lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      nc_put_att_double(ncid, var_id, "grid_boundary_right_projected_x",
			NC_DOUBLE, 1, &lfValue);
      nc_put_att_double(ncid, var_id, "grid_boundary_left_projected_x",
			NC_DOUBLE, 1, &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);
      nc_put_att_text(ncid, var_id, "spatial_ref", strlen(spatial_ref),
		      spatial_ref);
      sprintf(str, "+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);
      nc_put_att_text(ncid, var_id, "proj4text", strlen(str), str);
      nc_put_att_double(ncid, var_id, "semimajor_radius", NC_DOUBLE, 1,
			&mp->re_major);
      nc_put_att_double(ncid, var_id, "semiminor_radius", NC_DOUBLE, 1,
			&mp->re_minor);
      sprintf(str, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      nc_put_att_text(ncid, var_id, "GeoTransform", strlen(str), str);
    }
    else if (mp->type == LAMBERT_AZIMUTHAL_EQUAL_AREA) {

      strcpy(str, "lambert_azimuthal_equal_area");
      nc_put_att_text(ncid, var_id, "grid_mapping_name", strlen(str), str);
      nc_put_att_double(ncid, var_id, "longitude_of_projection_origin",
			NC_DOUBLE, 1, &mp->param.lamaz.center_lon);
      nc_put_att_double(ncid, var_id, "latitude_of_projection_origin",
			NC_DOUBLE, 1, &mp->param.lamaz.center_lat);
      nc_put_att_double(ncid, var_id, "false_easting", NC_DOUBLE, 1,
			&mp->param.lamaz.false_easting);
      nc_put_att_double(ncid, var_id, "false_northing", NC_DOUBLE, 1,
			&mp->param.lamaz.false_northing);
      strcpy(str, "xgrid");
      nc_put_att_text(ncid, var_id, "projection_x_coordinate", strlen(str),
		      str);
      strcpy(str, "ygrid");
      nc_put_att_text(ncid, var_id, "projection_y_coordinate", strlen(str),
		      str);
      strcpy(str, "m");
      nc_put_att_text(ncid, var_id, "units", strlen(str), str);
      nc_put_att_double(ncid, var_id, "grid_boundary_top_projected_y",
			NC_DOUBLE, 1, &mp->startY);
      lfValue = mp->startY + mg->line_count * mp->perY;
      nc_put_att_double(ncid, var_id, "grid_boundary_bottom_projected_y",
			NC_DOUBLE, 1, &lfValue);
      lfValue = mp->startX + mg->sample_count * mp->perX;
      nc_put_att_double(ncid, var_id, "grid_boundary_right_projected_x",
			NC_DOUBLE, 1, &lfValue);
      nc_put_att_double(ncid, var_id, "grid_boundary_left_projected_x",
			NC_DOUBLE, 1, &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);
      nc_put_att_text(ncid, var_id, "spatial_ref", strlen(spatial_ref),
		      spatial_ref);
      sprintf(str, "+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);
      nc_put_att_text(ncid, var_id, "proj4text", strlen(str), str);
      nc_put_att_double(ncid, var_id, "semimajor_radius", NC_DOUBLE, 1,
			&mp->re_major);
      nc_put_att_double(ncid, var_id, "semiminor_radius", NC_DOUBLE, 1,
			&mp->re_minor);
      sprintf(str, "%.6lf %.6lf 0 %.6lf 0 %.6lf", mp->startX, mp->perX,
	      mp->startY, mp->perY);
      nc_put_att_text(ncid, var_id, "GeoTransform", strlen(str), str);
    }
  }

  // Define variables and data attributes
  char **band_name = extract_band_names(meta->general->bands, band_count);
  int dims_bands[3];
  dims_bands[0] = dim_time_id;
  if (projected) {
    dims_bands[1] = dim_ygrid_id;
    dims_bands[2] = dim_xgrid_id;
  }
  else {
    dims_bands[1] = dim_lon_id;
    dims_bands[2] = dim_lat_id;
  }

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

    sprintf(str, "%s_AMPLITUDE_IMAGE", band_name[ii]);
    nc_def_var(ncid, str, datatype, 3, dims_bands, &var_id);
    netcdf->var_id[ii] = var_id;
    nc_def_var_deflate(ncid, var_id, 0, 1, 6);
    lfValue = -999.0;
    nc_put_att_double(ncid, var_id, "FillValue", NC_DOUBLE, 1, &lfValue);
    sprintf(str, "%s", mg->sensor);
    if (mg->image_data_type < 9)
      strcat(str, " radar backscatter");
    if (mg->radiometry >= r_SIGMA_DB && mg->radiometry <= r_GAMMA_DB)
      strcat(str, " in dB");
    nc_put_att_text(ncid, var_id, "long_name", strlen(str), str);
    strcpy(str, "area: backcatter value");
    nc_put_att_text(ncid, var_id, "cell_methods", strlen(str), str);
    strcpy(str, "1");
    nc_put_att_text(ncid, var_id, "units", strlen(str), str);
    strcpy(str, "unitless normalized radar cross-section");
    if (mg->radiometry >= r_SIGMA && mg->radiometry <= r_GAMMA)
      strcat(str, " stored as powerscale");
    else if (mg->radiometry >= r_SIGMA_DB && mg->radiometry <= r_GAMMA_DB)
      strcat(str, " stored as dB=10*log10(*)");
    nc_put_att_text(ncid, var_id, "units_description", strlen(str), str);
    strcpy(str, "longitude latitude");
    nc_put_att_text(ncid, var_id, "coordinates", strlen(str), str);
    if (projected) {
      strcpy(str, "projection");
      nc_put_att_text(ncid, var_id, "grid_mapping", strlen(str), str);
    }
  }

  // Define other attributes
  ymd_date ymd;
  hms_time hms;
  parse_date(mg->acquisition_date, &ymd, &hms);

  // Time
  ii = band_count;
  int dims_time[1] = { dim_time_id };
  nc_def_var(ncid, "time", NC_FLOAT, 1, dims_time, &var_id);
  netcdf->var_id[ii] = var_id;
  strcpy(str, "seconds since 1900-01-01T00:00:00Z");
  nc_put_att_text(ncid, var_id, "units", strlen(str), str);
  strcpy(str, "scene center time");
  nc_put_att_text(ncid, var_id, "references", strlen(str), str);
  strcpy(str, "time");
  nc_put_att_text(ncid, var_id, "standard_name", strlen(str), str);
  strcpy(str, "T");
  nc_put_att_text(ncid, var_id, "axis", strlen(str), str);
  strcpy(str, "serial date");
  nc_put_att_text(ncid, var_id, "long_name", strlen(str), str);

  // Longitude
  ii++;
  if (projected) {
    int dims_lon[2] = { dim_ygrid_id, dim_xgrid_id };
    nc_def_var(ncid, "longitude", NC_FLOAT, 2, dims_lon, &var_id);
  }
  else {
    int dims_lon[2] = { dim_lon_id, dim_lat_id };
    nc_def_var(ncid, "longitude", NC_FLOAT, 2, dims_lon, &var_id);
  }
  netcdf->var_id[ii] = var_id;
  nc_def_var_deflate(ncid, var_id, 0, 1, 6);
  strcpy(str, "longitude");
  nc_put_att_text(ncid, var_id, "standard_name", strlen(str), str);
  strcpy(str, "longitude");
  nc_put_att_text(ncid, var_id, "long_name", strlen(str), str);
  strcpy(str, "degrees_east");
  nc_put_att_text(ncid, var_id, "units", strlen(str), str);
  double *valid_range = (double *) MALLOC(sizeof(double)*2);
  valid_range[0] = -180.0;
  valid_range[1] = 180.0;
  nc_put_att_double(ncid, var_id, "valid_range", NC_DOUBLE, 2, valid_range);
  FREE(valid_range);
  lfValue = -999.0;
  nc_put_att_double(ncid, var_id, "FillValue", NC_DOUBLE, 1, &lfValue);

  // Latitude
  ii++;
  if (projected) {
    int dims_lat[2] = { dim_ygrid_id, dim_xgrid_id };
    nc_def_var(ncid, "latitude", NC_FLOAT, 2, dims_lat, &var_id);
  }
  else {
    int dims_lat[2] = { dim_lat_id, dim_lon_id };
    nc_def_var(ncid, "latitude", NC_FLOAT, 2, dims_lat, &var_id);
  }
  netcdf->var_id[ii] = var_id;
  nc_def_var_deflate(ncid, var_id, 0, 1, 6);
  strcpy(str, "latitude");
  nc_put_att_text(ncid, var_id, "standard_name", strlen(str), str);
  strcpy(str, "latitude");
  nc_put_att_text(ncid, var_id, "long_name", strlen(str), str);
  strcpy(str, "degrees_north");
  nc_put_att_text(ncid, var_id, "units", strlen(str), str);
  valid_range = (double *) MALLOC(sizeof(double)*2);
  valid_range[0] = -90.0;
  valid_range[1] = 90.0;
  nc_put_att_double(ncid, var_id, "valid_range", NC_DOUBLE, 2, valid_range);
  FREE(valid_range);
  lfValue = -999.0;
  nc_put_att_double(ncid, var_id, "FillValue", NC_DOUBLE, 1, &lfValue);

  if (projected) {

    // ygrid
    ii++;
    int dims_ygrid[1] = { dim_ygrid_id };
    nc_def_var(ncid, "ygrid", NC_FLOAT, 1, dims_ygrid, &var_id);
    netcdf->var_id[ii] = var_id;
    nc_def_var_deflate(ncid, var_id, 0, 1, 6);
    strcpy(str, "projection_y_coordinates");
    nc_put_att_text(ncid, var_id, "standard_name", strlen(str), str);
    strcpy(str, "projection_grid_y_centers");
    nc_put_att_text(ncid, var_id, "long_name", strlen(str), str);
    strcpy(str, "meters");
    nc_put_att_text(ncid, var_id, "units", strlen(str), str);
    strcpy(str, "Y");
    nc_put_att_text(ncid, var_id, "axis", strlen(str), str);

    // xgrid
    ii++;
    int dims_xgrid[1] = { dim_xgrid_id };
    nc_def_var(ncid, "xgrid", NC_FLOAT, 1, dims_xgrid, &var_id);
    netcdf->var_id[ii] = var_id;
    nc_def_var_deflate(ncid, var_id, 0, 1, 6);
    strcpy(str, "projection_x_coordinates");
    nc_put_att_text(ncid, var_id, "standard_name", strlen(str), str);
    strcpy(str, "projection_grid_x_centers");
    nc_put_att_text(ncid, var_id, "long_name", strlen(str), str);
    strcpy(str, "meters");
    nc_put_att_text(ncid, var_id, "units", strlen(str), str);
    strcpy(str, "X");
    nc_put_att_text(ncid, var_id, "axis", strlen(str), str);
  }

  // Define global attributes
  strcpy(str, "CF-1.4");
  nc_put_att_text(ncid, NC_GLOBAL, "Conventions", strlen(str), str);
  strcpy(str, "Alaska Satellite Facility");
  nc_put_att_text(ncid, NC_GLOBAL, "institution", strlen(str), str);
  sprintf(str, "%s %s %s image", mg->sensor, mg->sensor_name, mg->mode);
  nc_put_att_text(ncid, NC_GLOBAL, "title", strlen(str), str);
  if (mg->image_data_type == AMPLITUDE_IMAGE)
    strcpy(str, "SAR backcatter image");
  nc_put_att_text(ncid, NC_GLOBAL, "source", strlen(str), str);
  sprintf(str, "%s", mg->basename);
  nc_put_att_text(ncid, NC_GLOBAL, "original_file", strlen(str), str);
  if (strcmp_case(mg->sensor, "RSAT-1") == 0)
    sprintf(str, "Copyright Canadian Space Agency, %d", ymd.year);
  else if (strncmp_case(mg->sensor, "ERS", 3) == 0)
    sprintf(str, "Copyright European Space Agency, %d", ymd.year);
  else if (strcmp_case(mg->sensor, "JERS-1") == 0 ||
	   strcmp_case(mg->sensor, "ALOS") == 0)
    sprintf(str, "Copyright Japan Aerospace Exploration Agency , %d",
	    ymd.year);
  nc_put_att_text(ncid, NC_GLOBAL, "comment", strlen(str), str);
  strcpy(str, "Documentation available at: www.asf.alaska.edu");
  nc_put_att_text(ncid, NC_GLOBAL, "references", strlen(str), str);
  time_t t;
  struct tm *timeinfo;
  time(&t);
  timeinfo = gmtime(&t);
  sprintf(str, "%s", asctime(timeinfo));
  chomp(str);
  strcat(str, ", UTC: netCDF File created.");
  nc_put_att_text(ncid, NC_GLOBAL, "history", strlen(str), str);

  // Metadata
  int meta_id;
  nc_def_grp(ncid, "metadata", &meta_id);

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

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

  char tmp[50];
  if (mo) {
    // Metadata - state vector block
    nc_meta_int(meta_id, "orbit_year", "year of image start", NULL, &mo->year);
    nc_meta_int(meta_id, "orbit_day_of_year", "day of the year at image start",
		NULL, &mo->julDay);
    nc_meta_double(meta_id, "orbit_second_of_day",
		   "second of the day at image start", "seconds", &mo->second);
    int vector_count = mo->vector_count;
    nc_meta_int(meta_id, "orbit_vector_count", "number of state vectors", NULL,
		&vector_count);
    for (ii=0; ii<vector_count; ii++) {
      sprintf(tmp, "orbit_vector[%d]_time", ii+1);
      nc_meta_double(meta_id, tmp, "time relative to image start", "s",
		     &mo->vecs[ii].time);
      sprintf(tmp, "orbit_vector[%d]_position_x", ii+1);
      nc_meta_double(meta_id, tmp, "x coordinate, earth-fixed", "m",
		     &mo->vecs[ii].vec.pos.x);
      sprintf(tmp, "orbit_vector[%d]_position_y", ii+1);
      nc_meta_double(meta_id, tmp, "y coordinate, earth-fixed", "m",
		     &mo->vecs[ii].vec.pos.y);
      sprintf(tmp, "orbit_vector[%d]_position_z", ii+1);
      nc_meta_double(meta_id, tmp, "z coordinate, earth-fixed", "m",
		     &mo->vecs[ii].vec.pos.z);
      sprintf(tmp, "orbit_vector[%d]_velocity_x", ii+1);
      nc_meta_double(meta_id, tmp, "x velocity, earth-fixed", "m/s",
		     &mo->vecs[ii].vec.vel.x);
      sprintf(tmp, "orbit_vector[%d]_velocity_y", ii+1);
      nc_meta_double(meta_id, tmp, "y velocity, earth-fixed", "m/s",
		     &mo->vecs[ii].vec.vel.y);
      sprintf(tmp, "orbit_vector[%d]_velocity_z", ii+1);
      nc_meta_double(meta_id, tmp, "z velocity, earth-fixed", "m/s",
		     &mo->vecs[ii].vec.vel.z);
    }
  }

  // Finish off definition block
  nc_enddef(ncid);

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

  // Clean up
  FREE(str);
  if (spatial_ref)
    FREE(spatial_ref);

  return netcdf;
}

void finalize_netcdf_file(netcdf_t *netcdf, meta_parameters *md)
{
  int ncid = netcdf->ncid;
  int n = md->general->band_count;
  int nl = md->general->line_count;
  int ns = md->general->sample_count;
  long pixel_count = md->general->line_count * md->general->sample_count;
  int projected = FALSE;
  if (md->projection && md->projection->type != SCANSAR_PROJECTION)
    projected = TRUE;

  // Extra bands - Time
  float time = (float) seconds_from_str(md->general->acquisition_date);
  asfPrintStatus("Storing band 'time' ...\n");
  nc_put_var_float(ncid, netcdf->var_id[n], &time);

  // Extra bands - longitude
  n++;
  int ii, kk;
  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");
  nc_put_var_float(ncid, netcdf->var_id[n], &lons[0]);
  FREE(lons);

  // Extra bands - Latitude
  n++;
  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 (md->general->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");
  nc_put_var_float(ncid, netcdf->var_id[n], &lats[0]);
  FREE(lats);

  if (projected) {
    // Extra bands - ygrid
    n++;
    float *ygrids = (float *) MALLOC(sizeof(float)*pixel_count);
    for (ii=0; ii<nl; ii++) {
      for (kk=0; kk<ns; kk++)
	ygrids[ii*ns+kk] =
	  md->projection->startY + kk*md->projection->perY;
      asfLineMeter(ii, nl);
    }
    asfPrintStatus("Storing band 'ygrid' ...\n");
    nc_put_var_float(ncid, netcdf->var_id[n], &ygrids[0]);
    FREE(ygrids);

    // Extra bands - xgrid
    n++;
    float *xgrids = (float *) MALLOC(sizeof(float)*pixel_count);
    for (ii=0; ii<nl; ii++) {
      for (kk=0; kk<ns; kk++)
	xgrids[ii*ns+kk] =
	  md->projection->startX + kk*md->projection->perX;
      asfLineMeter(ii, nl);
    }
    asfPrintStatus("Storing band 'xgrid' ...\n");
    nc_put_var_float(ncid, netcdf->var_id[n], &xgrids[0]);
    FREE(xgrids);
  }

  // Close file and clean up
  int status = nc_close(ncid);
  if (status != NC_NOERR)
    asfPrintError("Could not close netCDF file (%s).\n", nc_strerror(status));
  FREE(netcdf->var_id);
  FREE(netcdf);
}


/* $Id$  */
/*--------------------------------------------------------------------*/
/*;  Copyright (C) 2013                                               */
/*;  Associated Universities, Inc. Washington DC, USA.                */
/*;                                                                   */
/*;  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., 675 Massachusetts Ave, Cambridge,     */
/*;  MA 02139, USA.                                                   */
/*;                                                                   */
/*;Correspondence about this software should be addressed as follows: */
/*;         Internet email: bcotton@nrao.edu.                         */
/*;         Postal address: William Cotton                            */
/*;                         National Radio Astronomy Observatory      */
/*;                         520 Edgemont Road                         */
/*;                         Charlottesville, VA 22903-2475 USA        */
/*--------------------------------------------------------------------*/

#include <time.h>
#include <gsl/gsl_randist.h>
#include "ObitThread.h"
#include "ObitOTFUtil.h"

/*----------------Obit: Merx mollis mortibus nuper ------------------*/
/**
 * \file ObitVEGASUtil.c
 * ObitOTF class utility function definitions.
 */

/*---------------Private structures----------------*/

/*---------------Private function prototypes----------------*/
/*----------------------Public functions---------------------------*/
/**
 * Average the frequencies in a GBT/VEGAS OTF
 * \param inVEGAS  Input VEGAS, any calibration and flagging should be applied.
 * \param outVEGAS Output VEGAS, must already be defined
 * \param chAvg  Number of channels to average, -1 => all
 * \param err    Error stack
 */
void ObitVEGASUtilAverage(ObitOTF *inOTF, ObitOTF *outOTF, olong chAvg,
			  ObitErr *err)
{
  const ObitClassInfo *ParentClass;
  ObitIOCode retCode;
  gboolean doCalSelect, done;
  olong firstRec, navg, nchanIn, nchanOut;
  olong ichan, ochan, ochn, ifeed, nfeed, istok, nstok, indx, ondx;
  ObitInfoType type;
  ObitIOAccess access;
  gint32 dim[MAXINFOELEMDIM] = {1,1,1,1,1};
  ofloat sum, sumWt, *iData, *oData;
  olong NPIO, iran, irec;
  ObitOTFDesc *iDesc, *oDesc;
  ObitHistory *inHist=NULL, *outHist=NULL;
  /* Don't copy Cal and Soln or data or flag tables */
  gchar *exclude[]={"OTFSoln", "OTFCal", "OTFScanData", "OTFFlag", NULL};
  gchar *routine = "ObitVEGASUtilAverage";

    /* error checks */
  g_assert (ObitErrIsA(err));
  if (err->error) return;
  g_assert (ObitOTFIsA(inOTF));
  g_assert (ObitOTFIsA(outOTF));
  /* Input and output must be different */
  Obit_return_if_fail ((!ObitOTFSame (inOTF, outOTF, err)), err,
		       "%s: Output cannot be the same as the input", routine);

  /* deep copy any base class members */
  ParentClass = ((ObitClassInfo*)inOTF->ClassInfo)->ParentClass;
  g_assert ((ParentClass!=NULL) && (ParentClass->ObitCopy!=NULL));
  ParentClass->ObitCopy (inOTF, outOTF, err);
  outOTF->mySel     = newObitOTFSel (outOTF->name);
  outOTF->tableList = newObitTableList(outOTF->name);
  outOTF->geom      = newObitOTFArrayGeom(outOTF->name);

  /* Calibration wanted? */
  doCalSelect = FALSE;
  ObitInfoListGetTest(inOTF->info, "doCalSelect", &type, (gint32*)dim,
		      &doCalSelect);
  if (doCalSelect) access = OBIT_IO_ReadCal;
  else access = OBIT_IO_ReadWrite;

  /* Make sure NPIO the same */
  NPIO = 1000;
  dim[0] = 1;
  ObitInfoListGetTest (inOTF->info, "nRecPIO", &type, dim,  &NPIO);
  ObitInfoListAlwaysPut (inOTF->info, "nRecPIO", OBIT_long, dim,  &NPIO);
  ObitInfoListAlwaysPut (outOTF->info, "nRecPIO", OBIT_long, dim,  &NPIO);

  /* Open Input Data */
  retCode = ObitOTFOpen (inOTF, access, err);
  if ((retCode != OBIT_IO_OK) || (err->error>0)) goto cleanup;

  /* How many channels to average? Defaults to all. */
  iDesc = inOTF->myDesc;
  nchanIn = iDesc->inaxes[iDesc->jlocf];
  if (chAvg>0) navg = chAvg;
  else         navg = nchanIn;
  navg     = MAX (1, MIN (navg, nchanIn));
  nchanOut = MAX (1, (olong)(((ofloat)(nchanIn)/navg)+0.9999));

  /* Number of feeds */
  nfeed = iDesc->inaxes[iDesc->jlocfeed];
  /* NUmber of Stokes */
  nstok = iDesc->inaxes[iDesc->jlocs];

  /* Copy descriptor */
  outOTF->myDesc = (gpointer)ObitOTFDescCopy(iDesc, outOTF->myDesc, err);
  outOTF->myDesc->nrecord = 0;     /* may not copy all */
  oDesc = outOTF->myDesc;

  /* Averaging in frequency */
  oDesc->inaxes[oDesc->jlocf]      = nchanOut;
  oDesc->colRepeat[oDesc->ncol-1] /= navg;
  oDesc->cdelt[oDesc->jlocf]      *= navg;
  oDesc->crpix[oDesc->jlocf]      /= navg;
  ObitOTFDescIndex (oDesc);

  /* copy/average Array Geometry - this time with full information
     NO, VEGAS doesn't have one "detector" per frequency
  outOTF->geom = ObitOTFArrayGeomAver(inOTF->geom, iDesc, outOTF->geom, oDesc, err);
  if (err->error) goto cleanup; */

  /* Open Output Data */
  retCode = ObitOTFOpen (outOTF, OBIT_IO_WriteOnly, err) ;
  if ((retCode != OBIT_IO_OK) || (err->error>0)) goto cleanup;

  /* Copy any history */
  inHist  = newObitHistoryValue("in history", inOTF->info, err);
  outHist = newObitHistoryValue("out history", outOTF->info, err);
  outHist = ObitHistoryCopy (inHist, outHist, err);
  if (err->error)  goto cleanup;
  inHist  = ObitHistoryUnref(inHist);
  outHist = ObitHistoryUnref(outHist);

   /* Copy tables before data  */
  retCode = ObitOTFCopyTables (inOTF, outOTF, exclude, NULL, err);
  if (err->error) goto cleanup;

  /* Close and reopen input to init calibration which will have
     been disturbed by the table copy */
  retCode = ObitOTFClose (inOTF, err);
  if (err->error) goto cleanup;

  retCode = ObitOTFOpen (inOTF, access, err);
  if ((retCode != OBIT_IO_OK) || (err->error>0)) goto cleanup;

  /* Loop over data */
  done = (retCode != OBIT_IO_OK);
  while (!done) {

    /* read buffer */#
    retCode = ObitOTFRead (inOTF, NULL, err);
    if (err->error) goto cleanup;
    done = (retCode == OBIT_IO_EOF); /* done? */
    if (done) break;
    firstRec = inOTF->myDesc->firstRec;

    iData = inOTF->buffer;   /* Input data pointer */
    oData = outOTF->buffer;  /* Output data pointer */

    /* How many? */
    outOTF->myDesc->numRecBuff = inOTF->myDesc->numRecBuff;

    /* Loop over buffer */
    for (irec=0; irec<iDesc->numRecBuff; irec++) {

      /* Copy random (descriptive parameters */
      for (iran=0; iran<iDesc->numDesc; iran++) oData[iran] = iData[iran];

      /* Average input to output */
      /* Loop over Stokes */
      for (istok=0; istok<nstok; istok++) {
	/* Loop over Feed */
	for (ifeed=0; ifeed<nfeed; ifeed++) {
	  /* Outer loop over channel */
	  ochn = 0;  /* output channel 0-rel index */
	  for (ochan=0; ochan<nchanIn; ochan+=navg) {
	    /* Inner frequency loop summing */
	    sum = sumWt = 0.0;
	    indx = iDesc->ilocdata + istok*iDesc->incs + ifeed*iDesc->incfeed;
	    ondx = oDesc->ilocdata + istok*oDesc->incs + ifeed*oDesc->incfeed;
	    for (ichan=ochan; ichan<ochan+navg; ichan++) {
	      sum   += iData[indx+ichan*iDesc->incf] * iData[indx+ichan*iDesc->incf+1];
	      sumWt += iData[indx+ichan*iDesc->incf+1];
	    } /* end inner loop */
	    /* Save average to outout */
	    if (sumWt>0.0) {
	      oData[ondx+ochn*oDesc->incf]   = sum/sumWt;
	      oData[ondx+ochn*oDesc->incf+1] = sumWt;
	    } else { /* bad */
	      oData[ondx+ochn*oDesc->incf]   = 0.0;
	      oData[ondx+ochn*oDesc->incf+1] = 0.0;
	    }
	    ochn++;
	  } /* end outer channel loop */
	} /* end feed loop */
      } /* end Stokes loop */
      iData += iDesc->lrec;  /* Update buffer pointers */
      oData += oDesc->lrec;
    } /* end loop over buffer */

    /* Write buffer */
    if (outOTF->myDesc->numRecBuff>0) retCode = ObitOTFWrite (outOTF, NULL, err);
    if (err->error) goto cleanup;
  } /* end loop over file */

 cleanup:
  /* unset output buffer  */
  outOTF->buffer = NULL;
  outOTF->bufferSize = 0;
  retCode = ObitOTFClose (outOTF, err); /* Close output */

  /* Close input */
  retCode = ObitOTFClose (inOTF, err);

  /* cleanup */
  if (err->error) Obit_traceback_msg (err, routine, inOTF->name);

} /* end ObitOTFUtilAverage */


/* histogram/file2d.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 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 3 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.
 */

#include <config.h>
#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_block.h>
#include <gsl/gsl_histogram2d.h>

int
gsl_histogram2d_fread (FILE * stream, gsl_histogram2d * h)
{
  int status = gsl_block_raw_fread (stream, h->xrange, h->nx + 1, 1);

  if (status)
    return status;

  status = gsl_block_raw_fread (stream, h->yrange, h->ny + 1, 1);

  if (status)
    return status;

  status = gsl_block_raw_fread (stream, h->bin, h->nx * h->ny, 1);

  return status;
}

int
gsl_histogram2d_fwrite (FILE * stream, const gsl_histogram2d * h)
{
  int status = gsl_block_raw_fwrite (stream, h->xrange, h->nx + 1, 1);

  if (status)
    return status;

  status = gsl_block_raw_fwrite (stream, h->yrange, h->ny + 1, 1);

  if (status)
    return status;

  status = gsl_block_raw_fwrite (stream, h->bin, h->nx * h->ny, 1);
  return status;
}

int
gsl_histogram2d_fprintf (FILE * stream, const gsl_histogram2d * h,
                         const char *range_format, const char *bin_format)
{
  size_t i, j;
  const size_t nx = h->nx;
  const size_t ny = h->ny;
  int status;

  for (i = 0; i < nx; i++)
    {
      for (j = 0; j < ny; j++)
        {
          status = fprintf (stream, range_format, h->xrange[i]);

          if (status < 0)
            {
              GSL_ERROR ("fprintf failed", GSL_EFAILED);
            }

          status = putc (' ', stream);

          if (status == EOF)
            {
              GSL_ERROR ("putc failed", GSL_EFAILED);
            }

          status = fprintf (stream, range_format, h->xrange[i + 1]);

          if (status < 0)
            {
              GSL_ERROR ("fprintf failed", GSL_EFAILED);
            }

          status = putc (' ', stream);

          if (status == EOF)
            {
              GSL_ERROR ("putc failed", GSL_EFAILED);
            }

          status = fprintf (stream, range_format, h->yrange[j]);

          if (status < 0)
            {
              GSL_ERROR ("fprintf failed", GSL_EFAILED);
            }

          status = putc (' ', stream);

          if (status == EOF)
            {
              GSL_ERROR ("putc failed", GSL_EFAILED);
            }

          status = fprintf (stream, range_format, h->yrange[j + 1]);

          if (status < 0)
            {
              GSL_ERROR ("fprintf failed", GSL_EFAILED);
            }

          status = putc (' ', stream);

          if (status == EOF)
            {
              GSL_ERROR ("putc failed", GSL_EFAILED);
            }

          status = fprintf (stream, bin_format, h->bin[i * ny + j]);

          if (status < 0)
            {
              GSL_ERROR ("fprintf failed", GSL_EFAILED);
            }

          status = putc ('\n', stream);

          if (status == EOF)
            {
              GSL_ERROR ("putc failed", GSL_EFAILED);
            }
        }
      status = putc ('\n', stream);

      if (status == EOF)
        {
          GSL_ERROR ("putc failed", GSL_EFAILED);
        }
    }

  return GSL_SUCCESS;
}

int
gsl_histogram2d_fscanf (FILE * stream, gsl_histogram2d * h)
{
  size_t i, j;
  const size_t nx = h->nx;
  const size_t ny = h->ny;
  double xupper, yupper;

  for (i = 0; i < nx; i++)
    {
      for (j = 0; j < ny; j++)
        {
          int status = fscanf (stream,
                               "%lg %lg %lg %lg %lg",
                               h->xrange + i, &xupper,
                               h->yrange + j, &yupper,
                               h->bin + i * ny + j);

          if (status != 5)
            {
              GSL_ERROR ("fscanf failed", GSL_EFAILED);
            }
        }
      h->yrange[ny] = yupper;
    }

  h->xrange[nx] = xupper;

  return GSL_SUCCESS;
}