text:
/**
 * Author : Pierre Schnizer
 * Date: January 2003
 */
#ifndef PyGSL_FUNCTION_HELPERS_H
#define  PyGSL_FUNCTION_HELPERS_H 1
/*   -------------------------------------------------------------------------
     See gsl_functions_reference.txt for a compilation of the different
     callbacks found in GSL.

     Todo:
           Perhaps split the file in general helpers and
	   special helpers ????
	   Make all helpers reporting error via
           PyGSL_set_error_string_for_callback

	   List all functions in these file in a header
   ------------------------------------------------------------------------- */
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_multiroots.h>
#include <gsl/gsl_multimin.h>
#include <gsl/gsl_multifit_nlin.h>
#include <gsl/gsl_monte.h>
#include <pygsl/utils.h>
#include <pygsl/intern.h>
#include <pygsl/error_helpers.h>
#include <pygsl/general_helpers.h>
#include <pygsl/block_helpers.h>
#include <pygsl/function_helpers.h>
#include <math.h>
#include <setjmp.h>

/* -------------------------------------------------------------------------
   Helper Structs

   *_func_name : a descriptive message for the internal function used
                 when reporting an error to the user.
   buffer_is_set : It depends on the user that she/he uses the
                   *BUFFER in the function interfaces. So this variable is
                  set to zero when the struct is generated. I hope this
		  will stop the wrapper trying to jump to NIRVANA.
   ------------------------------------------------------------------------- */
/*
 *  11 December 2003
 *  I return the flag that GSL returned using the flag argument to longjmp.
 *  This flag must be different from zero to be useful. GSL uses
 *  0 (== GSL_SUCCESS) to indicate it. I check for that here to see if it is
 *  always like that.
 */
#if GSL_SUCCESS != 0
#error "The function helpers use longjmp. GSL_SUCCESS must be zero. Pygsl Design error"
#endif
typedef struct {
     PyObject *function;
     PyObject *arguments;
     const char * c_func_name;
     jmp_buf buffer;
     int buffer_is_set;
} callback_function_params;

typedef struct {
     PyObject *f;
     PyObject *df;
     PyObject *fdf;
     PyObject *arguments;
     const char * c_f_func_name;
     const char * c_df_func_name;
     const char * c_fdf_func_name;
     jmp_buf buffer;
     int buffer_is_set;
} callback_function_params_fdf;

/* -------------------------------------------------------------------------
   Copy PyArray to gsl vector, gslarray and vice versa
   Are these functions ever needed by pure vector or matrix conversion?
   If so these functions should go into gsl_block_helpers.i
   ------------------------------------------------------------------------- */

/* 1. A_n O -> A_p  */
int
PyGSL_function_wrap_Op_On(const gsl_vector * x, gsl_vector *f, PyObject *callback,
			  PyObject * arguments, int n, int p, const char *c_func_name);
/* 2. A_n O -> A_n_p */
int
PyGSL_function_wrap_Op_Opn(const gsl_vector * x, gsl_matrix *f, PyObject *callback,
			   PyObject *arguments, int n, int p, const char * c_func_name);

/* 3   dO -> d      gsl_function     */
/* 3.1 dO -> d d    gsl_function_fdf */
PyGSL_API_EXTERN int
PyGSL_function_wrap_helper(double x, double * result, double *result2,
			   PyObject *callback, PyObject *arguments,
			   const char *c_func_name);

/*
 * Pass a NULL pointer for result 2, if not needed.
 */
/* 4. A_n O   ->  d (A_n) */
 int
PyGSL_function_wrap_On_O(const gsl_vector * x, PyObject *callback,
			PyObject *arguments, double *result1,
			 gsl_vector *result2, int n, const char * c_func_name);

/* 5. A_n O -> A_n A_n_p */
 int
PyGSL_function_wrap_Op_On_Opn(const gsl_vector * x, gsl_vector *f1,
			      gsl_matrix *f2, PyObject *callback,
			      PyObject *arguments, int n, int p,
			      const char * c_func_name);

/* -------------------------------------------------------------------------
      Register Python Call backs

      Generic Helper Functions
   ------------------------------------------------------------------------ */
/* Callbacks using one function */
callback_function_params *
PyGSL_convert_to_generic_function(PyObject *object, int *size, int *size2, const char * c_func_name);
/* Callbacks using 3  functions */
callback_function_params_fdf *
PyGSL_convert_to_generic_function_fdf(PyObject *object, int *size, int *size2,
				      const char * c_f_func_name, const char * c_df_func_name, const char * c_fdf_func_name);
void
PyGSL_params_free(callback_function_params *p);
void
PyGSL_params_free_fdf(callback_function_params_fdf *p);

double
PyGSL_function_wrap(double x, void * params);
double
PyGSL_function_wrap_f(double x, void * params);
double
PyGSL_function_wrap_df(double x, void * params);
void
PyGSL_function_wrap_fdf(double x,  void * params, double *f, double * fdf);
gsl_function *
PyGSL_convert_to_gsl_function(PyObject * object);
gsl_function_fdf *
PyGSL_convert_to_gsl_function_fdf(PyObject * object);

/* Specialised functions ... should they go to callbacks? */

int
PyGSL_multiroot_function_wrap(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multiroot_function_wrap_f(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multiroot_function_wrap_df(const gsl_vector *x, void *params, gsl_matrix *J);
int
PyGSL_multiroot_function_wrap_fdf(const gsl_vector *x, void *params, gsl_vector *f, gsl_matrix *J);
gsl_multiroot_function *
PyGSL_convert_to_gsl_multiroot_function(PyObject * object);
gsl_multiroot_function_fdf *
PyGSL_convert_to_gsl_multiroot_function_fdf(PyObject * object);

double
PyGSL_multimin_function_wrap(const gsl_vector *x, void *params);
double
PyGSL_multimin_function_wrap_f(const gsl_vector *x, void *params);
void
PyGSL_multimin_function_wrap_df(const gsl_vector *x, void *params, gsl_vector *g);
void
PyGSL_multimin_function_wrap_fdf(const gsl_vector *x, void *params, double *f, gsl_vector *g);
gsl_multimin_function *
PyGSL_convert_to_gsl_multimin_function(PyObject * object);
gsl_multimin_function_fdf *
PyGSL_convert_to_gsl_multimin_function_fdf(PyObject * object);

int
PyGSL_multifit_function_wrap(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multifit_function_wrap_f(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multifit_function_wrap_df(const gsl_vector *x, void *params, gsl_matrix *df);
int
PyGSL_multifit_function_wrap_fdf(const gsl_vector *x, void *params, gsl_vector *f, gsl_matrix *df);
gsl_multifit_function *
PyGSL_convert_to_gsl_multifit_function(PyObject * object);
gsl_multifit_function_fdf *
PyGSL_convert_to_gsl_multifit_function_fdf(PyObject * object);
gsl_monte_function *
PyGSL_convert_to_gsl_monte_function(PyObject * object);

/* gsl_function */
extern char * pygsl_gsl_function;
extern char * pygsl_gsl_f_function;
extern char * pygsl_gsl_df_function;
extern char * pygsl_gsl_fdf_function;
/* gsl_multifit */
extern char * pygsl_multifit_function;
extern char * pygsl_multifit_f_function;
extern char * pygsl_multifit_df_function;
extern char * pygsl_multifit_fdf_function;

extern char * pygsl_multimin_function;
extern char * pygsl_multimin_f_function;
extern char * pygsl_multimin_df_function;
extern char * pygsl_multimin_fdf_function;
/* gsl_multiroot */
extern char * pygsl_multiroot_function;
extern char * pygsl_multiroot_f_function;
extern char * pygsl_multiroot_df_function;
extern char * pygsl_multiroot_fdf_function;

/* monte */
extern char * pygsl_monte_function;
#ifndef _PyGSL_API_MODULE
#define PyGSL_function_wrap_helper \
(*(int (*) (double, double *, double *, PyObject *, PyObject *, const char *)) PyGSL_API[PyGSL_function_wrap_helper_NUM])
#endif  /* _PyGSL_API_MODULE */
#endif  /* PyGSL_FUNCTION_HELPERS_H */

max_stars_repo_name: juhnowski/FishingRod

max_issues_repo_name: juhnowski/FishingRod

max_forks_repo_name: juhnowski/FishingRod


text:
#include "ccv.h"
#include "ccv_internal.h"
#include "nnc/ccv_nnc.h"
#include "nnc/ccv_nnc_easy.h"
#include "nnc/ccv_nnc_internal.h"
#include "../_ccv_nnc_gemm_cpu_opt.h"
#if HAVE_ACCELERATE_FRAMEWORK
#include <Accelerate/Accelerate.h>
#elif HAVE_CBLAS
#include <cblas.h>
#endif

int _ccv_nnc_gemm_forw_cpu_sys(const int transpose_a[2], const int transpose_b[2], const ccv_nnc_tensor_view_t* const a, const ccv_nnc_tensor_view_t* const w, const ccv_nnc_tensor_view_t* const bias, ccv_nnc_tensor_view_t* const b)
{
#if (defined HAVE_CBLAS || defined HAVE_ACCELERATE_FRAMEWORK)
	assert(!bias || (bias->info.dim[1] == 0 || bias->info.dim[2] == 0 || bias->info.dim[3] == 0)); // It is a 1-d array
	int a_batch_size, a_rows, a_cols, a_batch_inc, a_rows_inc, a_cols_inc;
	int w_batch_size, w_rows, w_cols, w_batch_inc, w_rows_inc, w_cols_inc;
	int b_batch_size, b_rows, b_cols, b_batch_inc, b_rows_inc, b_cols_inc;
	const static int no_transpose[2] = {};
	ccv_nnc_tensor_get_matrix_params(a->info, CCV_IS_TENSOR_VIEW(a) ? a->inc : a->info.dim, transpose_a, &a_batch_size, &a_rows, &a_cols, &a_batch_inc, &a_rows_inc, &a_cols_inc);
	ccv_nnc_tensor_get_matrix_params(w->info, CCV_IS_TENSOR_VIEW(w) ? w->inc : w->info.dim, transpose_b, &w_batch_size, &w_rows, &w_cols, &w_batch_inc, &w_rows_inc, &w_cols_inc);
	ccv_nnc_tensor_get_matrix_params(b->info, CCV_IS_TENSOR_VIEW(b) ? b->inc : b->info.dim, no_transpose, &b_batch_size, &b_rows, &b_cols, &b_batch_inc, &b_rows_inc, &b_cols_inc);
	assert(a_batch_size == b_batch_size);
	assert(a_batch_size == b_batch_size || a_batch_size == 1);
	if (a_batch_size == 1 && b_batch_size > 1)
		a_batch_inc = 0;
	assert(w_batch_size == a_batch_size || w_batch_size == 1);
	if (w_batch_size == 1 && b_batch_size > 1)
		w_batch_inc = 0;
	assert(a_rows == b_rows);
	assert(a_cols == w_rows);
	assert(w_cols == b_cols);
	const int is_transpose_a = ccv_nnc_is_matrix_transpose(a->info, transpose_a);
	const int is_transpose_w = ccv_nnc_is_matrix_transpose(w->info, transpose_b);
	if (bias)
	{
		float* const ones = (float*)ccmalloc(sizeof(float) * b_rows);
		int i;
		for (i = 0; i < b_rows; i++)
			ones[i] = 1;
		int bias_batch_size, bias_rows, bias_cols, bias_batch_inc, bias_rows_inc, bias_cols_inc;
		ccv_nnc_tensor_get_matrix_params(bias->info, CCV_IS_TENSOR_VIEW(bias) ? bias->inc : bias->info.dim, no_transpose, &bias_batch_size, &bias_rows, &bias_cols, &bias_batch_inc, &bias_rows_inc, &bias_cols_inc);
		assert(bias_batch_size == b_batch_size || bias_batch_size == 1);
		if (bias_batch_size == 1 && b_batch_size > 1)
			bias_batch_inc = 0;
		assert(bias_cols == b_cols);
		for (i = 0; i < b_batch_size; i++)
			cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, b_cols, b_rows, 1, 1.0, bias->data.f32 + i * bias_batch_inc, bias_rows_inc, ones, 1, 0.0, b->data.f32 + i * b_batch_inc, b_rows_inc);
		ccfree(ones);
		const int transa = is_transpose_w ? CblasTrans : CblasNoTrans;
		const int transb = is_transpose_a ? CblasTrans : CblasNoTrans;
		const int lda_inc = is_transpose_w ? w_cols_inc : w_rows_inc;
		const int ldb_inc = is_transpose_a ? a_cols_inc : a_rows_inc;
		for (i = 0; i < b_batch_size; i++)
			cblas_sgemm(CblasColMajor, transa, transb, b_cols, b_rows, a_cols, 1.0, w->data.f32 + i * w_batch_inc, lda_inc, a->data.f32 + i * a_batch_inc, ldb_inc, 1.0, b->data.f32 + i * b_batch_inc, b_rows_inc);
	} else {
		const int transa = is_transpose_w ? CblasTrans : CblasNoTrans;
		const int transb = is_transpose_a ? CblasTrans : CblasNoTrans;
		const int lda_inc = is_transpose_w ? w_cols_inc : w_rows_inc;
		const int ldb_inc = is_transpose_a ? a_cols_inc : a_rows_inc;
		int i;
		for (i = 0; i < b_batch_size; i++)
			cblas_sgemm(CblasColMajor, transa, transb, b_cols, b_rows, a_cols, 1.0, w->data.f32 + i * w_batch_inc, lda_inc, a->data.f32 + i * a_batch_inc, ldb_inc, 0.0, b->data.f32 + i * b_batch_inc, b_rows_inc);
	}
	return CCV_NNC_EXEC_SUCCESS;
#else
	return CCV_NNC_EXEC_INVALID;
#endif
}

int _ccv_nnc_gemm_back_cpu_sys(const int transpose_a[2], const int transpose_b[2], const ccv_nnc_tensor_view_t* const g, const ccv_nnc_tensor_view_t* const a, const ccv_nnc_tensor_view_t* const w, ccv_nnc_tensor_view_t* const dw, ccv_nnc_tensor_view_t* const bias, ccv_nnc_tensor_view_t* const h, const int flags)
{
#if (defined HAVE_CBLAS || defined HAVE_ACCELERATE_FRAMEWORK)
	// inputs: gradient, forw prop input, [w]
	// outputs: [output gradient], weight updates, bias updates
	assert(!bias || (bias->info.dim[1] == 0 || bias->info.dim[2] == 0 || bias->info.dim[3] == 0)); // It is a 2-d or 3-d array.
	int g_batch_size, g_rows, g_cols, g_batch_inc, g_rows_inc, g_cols_inc;
	const static int no_transpose[2] = {};
	ccv_nnc_tensor_get_matrix_params(g->info, CCV_IS_TENSOR_VIEW(g) ? g->inc : g->info.dim, no_transpose, &g_batch_size, &g_rows, &g_cols, &g_batch_inc, &g_rows_inc, &g_cols_inc);
	int i;
	if (bias)
	{
		int bias_batch_size, bias_rows, bias_cols, bias_batch_inc, bias_rows_inc, bias_cols_inc;
		ccv_nnc_tensor_get_matrix_params(bias->info, CCV_IS_TENSOR_VIEW(bias) ? bias->inc : bias->info.dim, no_transpose, &bias_batch_size, &bias_rows, &bias_cols, &bias_batch_inc, &bias_rows_inc, &bias_cols_inc);
		assert(bias_cols == g_cols);
		assert(bias_batch_size == 1 || bias_batch_size == g_batch_size);
		if (bias_batch_size == 1 && g_batch_size > 1)
			bias_batch_inc = 0;
		float* const ones = (float*)ccmalloc(sizeof(float) * g_rows);
		for (i = 0; i < g_rows; i++)
			ones[i] = 1;
		if (g_batch_size > 1 && bias_batch_size == g_batch_size)
		{
			for (i = 0; i < g_batch_size; i++)
				cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, bias_cols, bias_rows, g_rows, 1.0, g->data.f32 + i * g_batch_inc, g_rows_inc, ones, g_rows, 0.0, bias->data.f32 + i * bias_batch_inc, bias_rows_inc);
		} else {
			cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, bias_cols, bias_rows, g_rows, 1.0, g->data.f32, g_rows_inc, ones, g_rows, 0.0, bias->data.f32, bias_rows_inc);
			// We cannot use strided batched alternative because on write, the data could race to the same position
			for (i = 1; i < g_batch_size; i++)
				cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, bias_cols, bias_rows, g_rows, 1.0, g->data.f32 + i * g_batch_inc, g_rows_inc, ones, g_rows, 1.0, bias->data.f32, bias_rows_inc);
		}
		ccfree(ones);
	}
	if (dw)
	{
		const int is_transpose_a = ccv_nnc_is_matrix_transpose(a->info, transpose_a);
		const int is_transpose_w = ccv_nnc_is_matrix_transpose(dw->info, transpose_b);
		int a_batch_size, a_rows, a_cols, a_batch_inc, a_rows_inc, a_cols_inc;
		int dw_batch_size, dw_rows, dw_cols, dw_batch_inc, dw_rows_inc, dw_cols_inc;
		ccv_nnc_tensor_get_matrix_params(a->info, CCV_IS_TENSOR_VIEW(a) ? a->inc : a->info.dim, transpose_a, &a_batch_size, &a_rows, &a_cols, &a_batch_inc, &a_rows_inc, &a_cols_inc);
		ccv_nnc_tensor_get_matrix_params(dw->info, CCV_IS_TENSOR_VIEW(dw) ? dw->inc : dw->info.dim, transpose_b, &dw_batch_size, &dw_rows, &dw_cols, &dw_batch_inc, &dw_rows_inc, &dw_cols_inc);
		assert(a_rows == g_rows);
		assert(a_cols == dw_rows);
		assert(dw_cols == g_cols);
		assert(a_batch_size == g_batch_size || a_batch_size == 1);
		if (a_batch_size == 1 && g_batch_size > 1)
			a_batch_inc = 0;
		assert(dw_batch_size == g_batch_size || dw_batch_size == 1);
		if (dw_batch_size == 1 && g_batch_size > 1)
			dw_batch_inc = 0;
		if (g_batch_size > 1 && g_batch_size == dw_batch_size)
		{
			if (is_transpose_w)
			{
				const int transa = is_transpose_a ? CblasTrans : CblasNoTrans;
				const int lda_inc = is_transpose_a ? a_cols_inc : a_rows_inc;
				for (i = 0; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, transa, CblasTrans, dw_rows, dw_cols, a_rows, 1.0, a->data.f32 + i * a_batch_inc, lda_inc, g->data.f32 + i * g_batch_inc, g_rows_inc, 0.0, dw->data.f32 + i * dw_batch_inc, dw_cols_inc);
			} else {
				const int transb = is_transpose_a ? CblasNoTrans : CblasTrans;
				const int ldb_inc = is_transpose_a ? a_cols_inc : a_rows_inc;
				for (i = 0; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, CblasNoTrans, transb, dw_cols, dw_rows, a_rows, 1.0, g->data.f32 + i * g_batch_inc, g_rows_inc, a->data.f32 + i * a_batch_inc, ldb_inc, 0.0, dw->data.f32 + i * dw_batch_inc, dw_rows_inc);
			}
		} else {
			if (is_transpose_w)
			{
				const int transa = is_transpose_a ? CblasTrans : CblasNoTrans;
				const int lda_inc = is_transpose_a ? a_cols_inc : a_rows_inc;
				cblas_sgemm(CblasColMajor, transa, CblasTrans, dw_rows, dw_cols, a_rows, 1.0, a->data.f32, lda_inc, g->data.f32, g_rows_inc, 0.0, dw->data.f32, dw_cols_inc);
				for (i = 1; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, transa, CblasTrans, dw_rows, dw_cols, a_rows, 1.0, a->data.f32 + i * a_batch_inc, lda_inc, g->data.f32 + i * g_batch_inc, g_rows_inc, 1.0, dw->data.f32, dw_cols_inc);
			} else {
				const int transb = is_transpose_a ? CblasNoTrans : CblasTrans;
				const int ldb_inc = is_transpose_a ? a_cols_inc : a_rows_inc;
				cblas_sgemm(CblasColMajor, CblasNoTrans, transb, dw_cols, dw_rows, a_rows, 1.0, g->data.f32, g_rows_inc, a->data.f32, ldb_inc, 0.0, dw->data.f32, dw_rows_inc);
				for (i = 1; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, CblasNoTrans, transb, dw_cols, dw_rows, a_rows, 1.0, g->data.f32 + i * g_batch_inc, g_rows_inc, a->data.f32 + i * a_batch_inc, ldb_inc, 1.0, dw->data.f32, dw_rows_inc);
			}
		}
	}
	if (h)
	{
		const int is_transpose_h = ccv_nnc_is_matrix_transpose(h->info, transpose_a);
		const int is_transpose_w = ccv_nnc_is_matrix_transpose(w->info, transpose_b);
		int h_batch_size, h_rows, h_cols, h_batch_inc, h_rows_inc, h_cols_inc;
		int w_batch_size, w_rows, w_cols, w_batch_inc, w_rows_inc, w_cols_inc;
		ccv_nnc_tensor_get_matrix_params(h->info, CCV_IS_TENSOR_VIEW(h) ? h->inc : h->info.dim, transpose_a, &h_batch_size, &h_rows, &h_cols, &h_batch_inc, &h_rows_inc, &h_cols_inc);
		ccv_nnc_tensor_get_matrix_params(w->info, CCV_IS_TENSOR_VIEW(w) ? w->inc : w->info.dim, transpose_b, &w_batch_size, &w_rows, &w_cols, &w_batch_inc, &w_rows_inc, &w_cols_inc);
		assert(h_rows == g_rows);
		assert(h_cols == w_rows);
		assert(w_cols == g_cols);
		assert(h_batch_size == g_batch_size || h_batch_size == 1);
		if (h_batch_size == 1 && g_batch_size > 1)
			h_batch_inc = 0;
		assert(w_batch_size == g_batch_size || w_batch_size == 1);
		if (w_batch_size == 1 && g_batch_size > 1)
			w_batch_inc = 0;
		if (g_batch_size > 1 && g_batch_size == h_batch_size)
		{
			if (is_transpose_h)
			{
				const int transb = is_transpose_w ? CblasTrans : CblasNoTrans;
				const int ldb_inc = is_transpose_w ? w_cols_inc : w_rows_inc;
				for (i = 0; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, CblasTrans, transb, h_rows, h_cols, g_cols, 1.0, g->data.f32 + i * g_batch_inc, g_rows_inc, w->data.f32 + i * w_batch_inc, ldb_inc, 0.0, h->data.f32 + i * h_batch_inc, h_cols_inc);
			} else {
				const int transa = is_transpose_w ? CblasNoTrans : CblasTrans;
				const int lda_inc = is_transpose_w ? w_cols_inc : w_rows_inc;
				for (i = 0; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, transa, CblasNoTrans, h_cols, h_rows, g_cols, 1.0, w->data.f32 + i * w_batch_inc, lda_inc, g->data.f32 + i * g_batch_inc, g_rows_inc, 0.0, h->data.f32 + i * h_batch_inc, h_rows_inc);
			}
		} else {
			if (is_transpose_h)
			{
				const int transb = is_transpose_w ? CblasTrans : CblasNoTrans;
				const int ldb_inc = is_transpose_w ? w_cols_inc : w_rows_inc;
				cblas_sgemm(CblasColMajor, CblasTrans, transb, h_rows, h_cols, g_cols, 1.0, g->data.f32, g_rows_inc, w->data.f32, ldb_inc, 0.0, h->data.f32, h_cols_inc);
				for (i = 1; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, CblasTrans, transb, h_rows, h_cols, g_cols, 1.0, g->data.f32 + i * g_batch_inc, g_rows_inc, w->data.f32 + i * w_batch_inc, ldb_inc, 1.0, h->data.f32, h_cols_inc);
			} else {
				const int transa = is_transpose_w ? CblasNoTrans : CblasTrans;
				const int lda_inc = is_transpose_w ? w_cols_inc : w_rows_inc;
				cblas_sgemm(CblasColMajor, transa, CblasNoTrans, h_cols, h_rows, g_cols, 1.0, w->data.f32, lda_inc, g->data.f32, g_rows_inc, 0.0, h->data.f32, h_rows_inc);
				for (i = 1; i < g_batch_size; i++)
					cblas_sgemm(CblasColMajor, transa, CblasNoTrans, h_cols, h_rows, g_cols, 1.0, w->data.f32 + i * w_batch_inc, lda_inc, g->data.f32 + i * g_batch_inc, g_rows_inc, 1.0, h->data.f32, h_rows_inc);
			}
		}
	}
	return CCV_NNC_EXEC_SUCCESS;
#else
	return CCV_NNC_EXEC_INVALID;
#endif
}

ext: c

max_stars_repo_name: neonkingfr/ccv

max_issues_repo_name: neonkingfr/ccv

max_forks_repo_name: neonkingfr/ccv


text:
/* Copyright (c) 2011-2012, Jérémy Fix. All rights reserved. */

/* Redistribution and use in source and binary forms, with or without */
/* modification, are permitted provided that the following conditions are met: */

/* * Redistributions of source code must retain the above copyright notice, */
/* this list of conditions and the following disclaimer. */
/* * Redistributions in binary form must reproduce the above copyright notice, */
/* this list of conditions and the following disclaimer in the documentation */
/* and/or other materials provided with the distribution. */
/* * None of the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. */

/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND */
/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE */
/* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL */
/* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR */
/* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER */
/* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */
/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#ifndef UKF_MATH_H
#define UKF_MATH_H

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>

#include <iostream>
#include <cassert>
#include <cmath>

namespace ukf
{
    namespace math
    {

        inline double min(double a, double b)
        {
            return a < b ? a : b;
        }

        inline double max(double a, double b)
        {
            return a < b ? b : a;
        }

        inline double signof(double x)
        {
            return x <= 0.0 ? -1.0 : 1.0;
        }

        inline bool cmp_equal(double x, double y)
        {
            return fabs(x - y) <= 1e-10;
        }
        inline bool cmp_diff(double x, double y)
        {
            return fabs(x - y) >= 1e-10;
        }

        /**
         * @brief This function performs a cholesky update according to Strange et al.(2007)
         * @author Mathieu.Geist@Supelec.fr
         *
         */
        void choleskyUpdate(gsl_matrix * sigmaTheta, double alpha, gsl_vector *x) {

            /*
                This function performs a cholesky update of the
                cholesky factorization sigmaTheta, that is it replaces
                the Cholesky factorization sigmaTheta by the cholesky factorization of

                            sigmaTheta*sigmaTheta^T + alpha * x * x^T

                The algorithm is an adaptation of a LU factorization rank one update.

                Reference is :

                Peter Strange, Andreas Griewank and Matthias Bollhöfer.
                On the Efficient Update of Rectangular LU Factorizations subject to Low Rank Modifications.
                Electronic Transactions on Numerical Analysis, 26:161-177, 2007.
                alg. is given in left part of fig.2.1.

                Perhaps a more efficient algorithm exists, however it should do the work for now. And the code is probably not optimal...
            */

            if (sigmaTheta->size1 != sigmaTheta->size2)
                std::cout<<"ERROR ukf::math::choleskyUpdate Cannot use CholeskyUpdate on non-squared matrices"<<std::endl ;

            int i,j,n= sigmaTheta->size1 ;
            double tmp ;
            gsl_matrix *U = gsl_matrix_alloc(n,n) ;
            gsl_vector *D = gsl_vector_alloc(n) ;
            gsl_vector *y = gsl_vector_alloc(n) ;

            // A first thing is to set SS' (chol factor) in a LU form, L being unitriangular
            // Compute U = L^T and D = diag(L)
            gsl_matrix_set_zero(U) ;
            for (i=0; i<n; i++){
                gsl_vector_set(D,i,gsl_matrix_get(sigmaTheta,i,i));
                for (j=0; j<=i; j++){
                    gsl_matrix_set(U,j,i,gsl_matrix_get(sigmaTheta,i,j));
                }
            }
            // Replace L by L*D^{-1} and U by D*U
            for (i=0; i<n; i++){
                for (j=0; j<=i; j++){
                    tmp = gsl_matrix_get(sigmaTheta,i,j);
                    tmp /= gsl_vector_get(D,j);
                    gsl_matrix_set(sigmaTheta,i,j,tmp);
                    tmp = gsl_matrix_get(U,j,i);
                    tmp *= gsl_vector_get(D,j);
                    gsl_matrix_set(U,j,i,tmp);
                }
            }

            // compute the y = alpha x vector
            gsl_vector_memcpy(y,x) ;
            gsl_vector_scale(y,alpha) ;

            // perform the rank 1 LU modification
            for (i=0; i<n; i++){

                // diagonal update
                tmp = gsl_matrix_get(U,i,i) +
                      gsl_vector_get(x,i)*gsl_vector_get(y,i);
                gsl_matrix_set(U,i,i,tmp);
                tmp = gsl_vector_get(y,i);
                tmp /= gsl_matrix_get(U,i,i);
                gsl_vector_set(y,i,tmp);

                for (j=i+1; j<n; j++){
                    // L (that is sigmaTheta) update
                    tmp = gsl_vector_get(x,j) -
                          gsl_vector_get(x,i)*gsl_matrix_get(sigmaTheta,j,i);
                    gsl_vector_set(x,j,tmp) ;
                    tmp = gsl_matrix_get(sigmaTheta,j,i) + gsl_vector_get(y,i) * gsl_vector_get(x,j);
                    gsl_matrix_set(sigmaTheta,j,i,tmp);
                }

                for (j=i+1; j<n; j++) {
                    // U update
                    tmp = gsl_matrix_get(U,i,j) +
                          gsl_vector_get(x,i)*gsl_vector_get(y,j);
                    gsl_matrix_set(U,i,j,tmp) ;
                    tmp = gsl_vector_get(y,j) -
                          gsl_vector_get(y,i) * gsl_matrix_get(U,i,j);
                    gsl_vector_set(y,j,tmp) ;
                }
            }

            // Now we want the chol decomposition
            // first D = sqrt(diag(U)) ;
            for (i=0; i<n; i++){
                tmp =  gsl_matrix_get(U,i,i) ;

                if (tmp<=0){std::cout<< "WARNING ukf::math::choleskyUpdate::matrix not positive definite  : " << tmp << std::endl;}
                gsl_vector_set(D,i,sqrt(tmp)) ;
            }
            // then L = L*D ;
            for (i=0; i<n; i++) {
                for (j=0; j<n; j++) {
                    tmp = gsl_matrix_get(sigmaTheta,i,j) * gsl_vector_get(D,j);
                    gsl_matrix_set(sigmaTheta,i,j,tmp);
                }
            }
            // that's all folks !

            //free memory
            gsl_matrix_free(U);
            gsl_vector_free(y);
            gsl_vector_free(D);
        }
    }  // math
}  // ukf

#endif  // UKF_MATH_H

ext: h


text:
/*	$Id$ */
/*
 * Copyright (c) 2014, 2015 Kristaps Dzonsons <kristaps@kcons.eu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#ifdef MAC_INTEGRATION
#include <gtkosxapplication.h>
#endif
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_multifit.h>
#include <gsl/gsl_histogram.h>
#include <kplot.h>

#include "extern.h"

static	const char *const inputs[INPUT__MAX] = {
	"uniform",
	"variable",
	"mapped",
};

static	const char *const views[VIEW__MAX] = {
	"times-cdf", /* VIEW_TIMEESCDF */
	"times-pdf", /* VIEW_TIMEESPDF */
	"raw-mean-stddev", /* VIEW_DEV */
	"extinct-incumbent", /* VIEW_EXTI */
	"extinct-incumbent-min-cdf", /* VIEW_EXTIMINCDF */
	"extinct-incumbent-min-pdf", /* VIEW_EXTIMINPDF */
	"extinct-incumbent-min-mean", /* VIEW_EXTIMINS */
	"extinct-mutant", /* VIEW_EXTM */
	"extinct-mutant-max-cdf", /* VIEW_EXTMMAXCDF */
	"extinct-mutant-max-pdf", /* VIEW_EXTMMAXPDF */
	"extinct-mutant-max-mean", /* VIEW_EXTMMAXS */
	"island-mean", /* VIEW_ISLANDMEAN */
	"islander-mean", /* VIEW_ISLANDERMEAN */
	"raw-mean", /* VIEW_MEAN */
	"raw-mean-min-cdf", /* VIEW_MEANMINCDF */
	"raw-mean-min-pdf", /* VIEW_MEANMINPDF */
	"raw-mean-min-history", /* VIEW_MEANMINQ */
	"raw-mean-min-mean", /* VIEW_MEANMINS */
	"fitted-mean", /* VIEW_POLY */
	"fitted-mean-min-cdf", /* VIEW_POLYMINCDF */
	"fitted-mean-min-pdf", /* VIEW_POLYMINPDF */
	"fitted-mean-min-history", /* VIEW_POLYMINQ */
	"fitted-mean-min-mean", /* VIEW_POLYMINS */
	"extinct-mutant-smooth", /* VIEW_SEXTM */
	"extinct-incunmbent-smooth", /* VIEW_SEXTI */
	"raw-mean-smooth", /* VIEW_SMEAN */
};

static void
hwin_init(struct hwin *c, GtkBuilder *b)
{
	GObject		*w;
	gchar		 buf[1024];
	gchar		*bufp;
	GTimeVal	 gt;
	size_t		 nprocs;

	c->maptop[MAPTOP_RECORD] = win_init_toggle(b, "radiobutton10");
	c->maptop[MAPTOP_RAND] = win_init_toggle(b, "radiobutton11");
	c->maptop[MAPTOP_TORUS] = win_init_toggle(b, "radiobutton13");
	c->rangeminlambda = win_init_label(b, "label55");
	c->rangemaxlambda = win_init_label(b, "label52");
	c->rangemeanlambda = win_init_label(b, "label58");
	c->rangeerrorbox = win_init_box(b, "box39");
	c->rangeerror = win_init_label(b, "label74");
	c->rangemin = win_init_label(b, "label43");
	c->rangemax = win_init_label(b, "label41");
	c->rangemean = win_init_label(b, "label45");
	c->rangestatus = win_init_label(b, "label47");
	c->rangefunc = win_init_label(b, "label50");
	c->rangeparms = win_init_label(b, "label72");
	c->buttonrange = win_init_button(b, "button4");
	c->mapindices[MAPINDEX_STRIPED] = win_init_toggle(b, "radiobutton15");
	c->mapindices[MAPINDEX_FIXED] = win_init_toggle(b, "radiobutton16");
	c->mapindexfix = win_init_adjustment(b, "adjustment11");
	c->namefill[NAMEFILL_DATE] = win_init_toggle(b, "radiobutton3");
	c->namefill[NAMEFILL_M] = win_init_toggle(b, "radiobutton4");
	c->namefill[NAMEFILL_T] = win_init_toggle(b, "radiobutton7");
	c->namefill[NAMEFILL_MUTANTS] = win_init_toggle(b, "radiobutton8");
	c->namefill[NAMEFILL_NONE] = win_init_toggle(b, "radiobutton9");
	c->mapbox = win_init_box(b, "box31");
	c->config = win_init_window(b, "window1");
	c->rangefind = win_init_window(b, "window2");
	c->status = win_init_status(b, "statusbar1");
	c->menu = win_init_menubar(b, "menubar1");
	c->mutants[MUTANTS_DISCRETE] = win_init_radio(b, "radiobutton1");
	c->mutants[MUTANTS_GAUSSIAN] = win_init_radio(b, "radiobutton2");
	c->weighted = win_init_toggle(b, "checkbutton1");
	c->menuquit = win_init_menuitem(b, "menuitem5");
	c->input = win_init_label(b, "label19");
	c->mutantsigma = win_init_entry(b, "entry17");
	c->name = win_init_entry(b, "entry16");
	c->stop = win_init_entry(b, "entry9");
	c->xmin = win_init_entry(b, "entry8");
	c->xmax = win_init_entry(b, "entry10");
	c->ymin = win_init_entry(b, "entry18");
	c->ymax = win_init_entry(b, "entry19");
	c->inputs = win_init_notebook(b, "notebook1");
	c->error = win_init_label(b, "label8");
	c->func = win_init_entry(b, "entry2");
	c->smoothing = win_init_adjustment(b, "adjustment10");
	c->nthreads = win_init_adjustment(b, "adjustment3");
	c->fitpoly = win_init_adjustment(b, "adjustment4");
	c->pop = win_init_adjustment(b, "adjustment1");
	c->totalpop = win_init_label(b, "label68");
	c->islands = win_init_adjustment(b, "adjustment2");
	c->ideathmean = win_init_adjustment(b, "adjustment12");
	c->ideathcoef = win_init_entry(b, "entry3");
	c->resprocs = win_init_label(b, "label3");
	c->onprocs = win_init_label(b, "label36");
	c->alpha = win_init_entry(b, "entry13");
	c->delta = win_init_entry(b, "entry14");
	c->migrate[INPUT_UNIFORM] = win_init_entry(b, "entry1");
	c->migrate[INPUT_VARIABLE] = win_init_entry(b, "entry20");
	c->migrate[INPUT_MAPPED] = win_init_entry(b, "entry4");
	c->incumbents = win_init_entry(b, "entry15");
	c->mapfile = win_init_filechoose(b, "filechooserbutton1");
	c->mapmigrants[MAPMIGRANT_UNIFORM] = win_init_toggle(b, "radiobutton5");
	c->mapmigrants[MAPMIGRANT_DISTANCE] = win_init_toggle(b, "radiobutton6");
	c->mapmigrants[MAPMIGRANT_NEAREST] = win_init_toggle(b, "radiobutton12");
	c->mapmigrants[MAPMIGRANT_TWONEAREST] = win_init_toggle(b, "radiobutton14");
	c->maprandislands = win_init_adjustment(b, "adjustment6");
	c->maprandislanders = win_init_adjustment(b, "adjustment7");
	c->maptorusislands = win_init_adjustment(b, "adjustment8");
	c->maptorusislanders = win_init_adjustment(b, "adjustment9");

	gtk_widget_show_all(GTK_WIDGET(c->config));

	/* Hide our error message. */
	gtk_widget_hide(GTK_WIDGET(c->error));

	/* Set the initially-selected notebooks. */
	gtk_label_set_text(c->input, inputs
		[gtk_notebook_get_current_page(c->inputs)]);

	/* XXX: builder doesn't do this. */
	w = gtk_builder_get_object(b, "comboboxtext1");
	gtk_combo_box_set_active(GTK_COMBO_BOX(w), 0);

#if GLIB_CHECK_VERSION(2, 36, 0)
	nprocs = g_get_num_processors();
#else
	nprocs = sysconf(_SC_NPROCESSORS_ONLN);
#endif

	/* Maximum number of processors. */
	gtk_adjustment_set_upper(c->nthreads, nprocs);
	w = gtk_builder_get_object(b, "label12");
	(void)g_snprintf(buf, sizeof(buf), "%zu", nprocs);
	gtk_label_set_text(GTK_LABEL(w), buf);

	/* Compute initial total population. */
	(void)g_snprintf(buf, sizeof(buf),
		"%g", gtk_adjustment_get_value(c->pop) *
		gtk_adjustment_get_value(c->islands));
	gtk_label_set_text(c->totalpop, buf);

	g_get_current_time(&gt);
	bufp = g_time_val_to_iso8601(&gt);
	gtk_entry_set_text(c->name, bufp);
	g_free(bufp);

	/* Hide the rangefinder when we start up. */
	gtk_widget_set_visible(GTK_WIDGET(c->rangefind), FALSE);

#ifdef MAC_INTEGRATION
	gtk_widget_hide(GTK_WIDGET(c->menu));
	gtk_widget_hide(GTK_WIDGET(c->menuquit));
	gtkosx_application_set_menu_bar
		(gtkosx_application_get(),
		 GTK_MENU_SHELL(c->menu));
	gtkosx_application_sync_menubar
		(gtkosx_application_get());
#endif
}

/*
 * Free a given simulation, possibly waiting for the simulation threads
 * to exit.
 * The simulation is presumed to have been set as terminating before
 * running this.
 * Yes, we can wait if the simulation takes a long time between updates.
 */
static void
sim_free(gpointer arg)
{
	struct sim	*p = arg;
	size_t		 i;

	if (NULL == p)
		return;

	g_debug("%p: Freeing simulation", p);

	/*
	 * Join all of our running threads.
	 * They were stopped in the sim_stop function, which was called
	 * before this one.
	 */
	for (i = 0; i < p->nprocs; i++)
		if (NULL != p->threads[i].thread) {
			g_debug("%p: Freeing joining thread "
				"(simulation %p)",
				p->threads[i].thread, p);
			g_thread_join(p->threads[i].thread);
		}
	p->nprocs = 0;

	simbuf_free(p->bufs.means);
	simbuf_free(p->bufs.stddevs);
	simbuf_free(p->bufs.imeans);
	simbuf_free(p->bufs.times);
	simbuf_free(p->bufs.istddevs);
	simbuf_free(p->bufs.islandmeans);
	simbuf_free(p->bufs.islandstddevs);
	simbuf_free(p->bufs.mextinct);
	simbuf_free(p->bufs.iextinct);
	kdata_destroy(p->bufs.fractions);
	kdata_destroy(p->bufs.ifractions);
	kdata_destroy(p->bufs.mutants);
	kdata_destroy(p->bufs.incumbents);
	kdata_destroy(p->bufs.islands);
	kdata_destroy(p->bufs.meanmins);
	kdata_destroy(p->bufs.mextinctmaxs);
	kdata_destroy(p->bufs.iextinctmins);
	kdata_destroy(p->bufs.fitpoly);
	kdata_destroy(p->bufs.fitpolybuf);
	kdata_destroy(p->bufs.fitpolymins);
	kdata_destroy(p->bufs.meanminqbuf);
	kdata_destroy(p->bufs.fitminqbuf);

	hnode_free(p->exp);
	g_mutex_clear(&p->hot.mux);
	g_cond_clear(&p->hot.cond);
	g_free(p->name);
	g_free(p->func);
	if (NULL != p->ms)
		for (i = 0; i < p->islands; i++)
			g_free(p->ms[i]);
	g_free(p->ms);
	g_free(p->pops);
	kml_free(p->kml);
	if (p->fitpoly) {
		g_free(p->work.coeffs);
		gsl_matrix_free(p->work.X);
		gsl_vector_free(p->work.y);
		gsl_vector_free(p->work.w);
		gsl_vector_free(p->work.c);
		gsl_matrix_free(p->work.cov);
		gsl_multifit_linear_free(p->work.work);
		p->fitpoly = 0;
	}
	g_free(p->threads);
	g_free(p);
	g_debug("%p: Simulation freed", p);
}

/*
 * Set a given simulation to stop running.
 * This must be invoked before sim_free() or simulation threads will
 * still be running.
 */
void
sim_stop(gpointer arg, gpointer unused)
{
	struct sim	*p = arg;
	int		 pause;

	if (p->terminate)
		return;
	g_debug("%p: Simulation stopping", p);
	p->terminate = 1;
	g_mutex_lock(&p->hot.mux);
	if (0 != (pause = p->hot.pause)) {
		p->hot.pause = 0;
		g_cond_broadcast(&p->hot.cond);
	}
	g_mutex_unlock(&p->hot.mux);
	if (pause)
		g_debug("%p: Simulation unpausing to stop", p);
}

/*
 * Free up all memory.
 * This can be reentrant (due to gtk-osx's funny handling of
 * termination), so be careful to nullify things so that if recalled it
 * doesn't puke.
 */
static void
bmigrate_free(struct bmigrate *p)
{

	g_debug("%p: Freeing main", p);
	g_list_foreach(p->sims, sim_stop, NULL);
	g_list_free_full(p->sims, sim_free);
	p->sims = NULL;
	hnode_free(p->range.exp);
	p->range.exp = NULL;
	if (NULL != p->status_elapsed)
		g_timer_destroy(p->status_elapsed);
	p->status_elapsed = NULL;
	p->clrsz = 0;
	free(p->clrs);
	p->clrs = NULL;
}

/*
 * Pause or unpause (unset pause and broadcast to condition) a given
 * simulation depending on its current pause status.
 */
static void
on_sim_pause(struct sim *sim, int dopause)
{
	int		 pause = -1;

	g_mutex_lock(&sim->hot.mux);
	if (0 == dopause && sim->hot.pause) {
		sim->hot.pause = pause = 0;
		g_cond_broadcast(&sim->hot.cond);
	} else if (dopause && 0 == sim->hot.pause)
		sim->hot.pause = pause = 1;
	g_mutex_unlock(&sim->hot.mux);

	if (0 == pause && 0 == dopause)
		g_debug("Unpausing simulation %p", sim);
	else if (pause && dopause)
		g_debug("Pausing simulation %p", sim);
}

static void
cqueue_fill(size_t pos, struct kpair *kp, void *arg)
{
	struct cqueue	*q = arg;

	kp->x = -(double)(CQUEUESZ - pos);
	kp->y = q->vals[(q->pos + pos + 1) % CQUEUESZ];
}

static void
cqueue_push(struct cqueue *q, double val)
{

	q->vals[q->pos] = val;
	if (val > q->vals[q->maxpos])
		q->maxpos = q->pos;
	q->pos = (q->pos + 1) % CQUEUESZ;
}

/*
 * This copies data from the threads into local ("cold") storage.
 * It does so by checking whether the threads have copied out of "hot"
 * storage (locking their mutex in the process) and, if so, indicates
 * that we're about to read it (and thus to do so again), then after
 * reading, reset that the data is stale.
 */
static gboolean
on_sim_copyout(gpointer dat)
{
	struct bmigrate	*b = dat;
	GList		*list, *w, *sims;
	ssize_t		 pos;
	struct kpair	 kp;
	struct sim	*sim;
	struct curwin	*cur;
	int		 copy, rc;

	for (list = b->sims; NULL != list; list = g_list_next(list)) {
		sim = list->data;
		if (0 == sim->nprocs)
			continue;
		/*
		 * Instruct the simulation to copy out its data into warm
		 * storage.
		 * If it hasn't already done so, then don't copy into
		 * cold storage, as nothing has changed.
		 */
		g_mutex_lock(&sim->hot.mux);
		copy = 0 == sim->hot.copyout;
		g_mutex_unlock(&sim->hot.mux);

		if ( ! copy)
			continue;

		/*
		 * Don't copy stale data.
		 * Trigger the simulation to copy-out when it gets a
		 * chance.
		 */
		if (sim->cold.truns == sim->warm.truns) {
			assert(sim->cold.tgens == sim->warm.tgens);
			g_mutex_lock(&sim->hot.mux);
			g_assert(0 == sim->hot.copyout);
			sim->hot.copyout = 1;
			g_mutex_unlock(&sim->hot.mux);
			continue;
		}

		/*
		 * Since we're updating this particular simulation, make
		 * sure that all windows tied to this simulation are
		 * also going to be redrawn when the redrawer is called.
		 */
		for (w = b->windows ; w != NULL; w = w->next) {
			cur = w->data;
			sims = cur->sims;
			g_assert(NULL != sims);
			for ( ; NULL != sims; sims = sims->next)
				if (sim == sims->data) {
					cur->redraw = 1;
					break;
				}
		}

		/* Most strutures we simply copy over. */
		simbuf_copy_cold(sim->bufs.times);
		simbuf_copy_cold(sim->bufs.imeans);
		simbuf_copy_cold(sim->bufs.istddevs);
		simbuf_copy_cold(sim->bufs.islandmeans);
		simbuf_copy_cold(sim->bufs.islandstddevs);
		simbuf_copy_cold(sim->bufs.means);
		simbuf_copy_cold(sim->bufs.stddevs);
		simbuf_copy_cold(sim->bufs.mextinct);
		simbuf_copy_cold(sim->bufs.iextinct);
		rc = kdata_buffer_copy
			(sim->bufs.fitpolybuf,
			 sim->bufs.fitpoly);
		g_assert(0 != rc);

		sim->cold.truns = sim->warm.truns;
		sim->cold.tgens = sim->warm.tgens;

		pos = kdata_ymin(sim->bufs.means->cold, &kp);
		g_assert(pos >= 0);
		kdata_array_add(sim->bufs.meanmins, pos, 1.0);
		cqueue_push(&sim->bufs.meanminq, kp.x);
		kdata_array_fill(sim->bufs.meanminqbuf,
			&sim->bufs.meanminq, cqueue_fill);

		kdata_array_add(sim->bufs.mextinctmaxs,
			kdata_ymax(sim->bufs.mextinct->cold, NULL), 1.0);

		kdata_array_add(sim->bufs.iextinctmins,
			kdata_ymin(sim->bufs.iextinct->cold, NULL), 1.0);

		pos = kdata_ymin(sim->bufs.fitpolybuf, &kp);
		g_assert(pos >= 0);
		kdata_array_add(sim->bufs.fitpolymins, pos, 1.0);
		cqueue_push(&sim->bufs.fitminq, kp.x);
		kdata_array_fill(sim->bufs.fitminqbuf,
			&sim->bufs.fitminq, cqueue_fill);

		/* Copy-out when convenient. */
		g_mutex_lock(&sim->hot.mux);
		g_assert(0 == sim->hot.copyout);
		sim->hot.copyout = 1;
		g_mutex_unlock(&sim->hot.mux);
	}

	return(TRUE);
}

static gboolean
on_sim_autosave(gpointer dat)
{
	struct bmigrate	*b = dat;
	struct curwin	*cur;
	GList		*l;
	GtkWidget	*dialog;
	enum view	 sv, view;
	gchar		*file;
	int		 rc;

	for (l = b->windows; l != NULL; l = l->next) {
		cur = l->data;
		if (NULL == cur->autosave)
			continue;
		sv = cur->view;
		for (view = 0; view < VIEW__MAX; view++) {
			file = g_strdup_printf
				("%s" G_DIR_SEPARATOR_S "%s.pdf",
				 cur->autosave, views[view]);
			cur->view = view;
			rc = save(file, cur);
			g_free(file);
			if (0 == rc)
				break;
		}
		cur->view = sv;
		if (view != VIEW__MAX)
			break;
		file = g_strdup_printf("%s"
			G_DIR_SEPARATOR_S "README.txt",
			cur->autosave);
		rc = saveconfig(file, cur);
		g_free(file);
		if (0 == rc)
			break;
	}
	if (NULL == l)
		return(TRUE);

	dialog = gtk_message_dialog_new
		(GTK_WINDOW(cur->wins.window),
		 GTK_DIALOG_DESTROY_WITH_PARENT,
		 GTK_MESSAGE_ERROR,
		 GTK_BUTTONS_CLOSE,
		 "Error auto-saving: %s",
		 strerror(errno));
	gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
	g_free(cur->autosave);
	cur->autosave = NULL;
	gtk_widget_hide(GTK_WIDGET
		(cur->wins.menuunautoexport));
	gtk_widget_show(GTK_WIDGET
		(cur->wins.menuautoexport));
	return(TRUE);
}

static void
on_win_redraw(struct curwin *cur)
{
	GList		*l;
	struct sim	*sim;
	size_t		 i;
	int		 rc;

	for (i = 0, l = cur->sims; NULL != l; l = g_list_next(l), i++) {
		sim = l->data;
		rc = kdata_vector_set(cur->winmean, i, i,
			kdata_pmfmean(sim->bufs.meanmins));
		g_assert(0 != rc);
		rc = kdata_vector_set(cur->winstddev, i, i,
			kdata_pmfstddev(sim->bufs.meanmins));
		g_assert(0 != rc);
		rc = kdata_vector_set(cur->winfitmean, i, i,
			kdata_pmfmean(sim->bufs.fitpolymins));
		g_assert(0 != rc);
		rc = kdata_vector_set(cur->winfitstddev, i, i,
			kdata_pmfstddev(sim->bufs.fitpolymins));
		g_assert(0 != rc);
		rc = kdata_vector_set(cur->winmextinctmean, i, i,
			kdata_pmfmean(sim->bufs.mextinctmaxs));
		g_assert(0 != rc);
		rc = kdata_vector_set(cur->winmextinctstddev, i, i,
			kdata_pmfstddev(sim->bufs.mextinctmaxs));
		g_assert(0 != rc);
		rc = kdata_vector_set(cur->winiextinctmean, i, i,
			kdata_pmfmean(sim->bufs.iextinctmins));
		g_assert(0 != rc);
		rc = kdata_vector_set(cur->winiextinctstddev, i, i,
			kdata_pmfstddev(sim->bufs.iextinctmins));
		g_assert(0 != rc);
	}

	gtk_widget_queue_draw(GTK_WIDGET(cur->wins.window));
}

/*
 * Run this fairly often to see if we need to join any worker threads.
 * Worker threads are joined when they have zero references are in the
 * terminating state.
 */
static gboolean
on_sim_timer(gpointer dat)
{
	struct bmigrate	*b = dat;
	struct sim	*sim;
	gchar		 buf[1024];
	GList		*list;
	uint64_t	 runs;
	size_t		 i, onprocs, resprocs;
	double		 elapsed;

	onprocs = resprocs = runs = 0;
	for (list = b->sims; NULL != list; list = g_list_next(list)) {
		sim = (struct sim *)list->data;
		runs += sim->cold.tgens;
		/*
		 * If "terminate" is set, then the thread is (or already
		 * did finish) exiting, so wait for it.
		 * If we wait, it should take only a very small while.
		 */
		if (sim->terminate && sim->nprocs > 0) {
			for (i = 0; i < sim->nprocs; i++)  {
				if (NULL == sim->threads[i].thread)
					continue;
				g_debug("%p: Timeout handler joining "
					"thread (simulation %p)",
					sim->threads[i].thread, sim);
				g_thread_join(sim->threads[i].thread);
				sim->threads[i].thread = NULL;
			}
			sim->nprocs = 0;
			assert(0 == sim->refs);
		} else if ( ! sim->terminate && ! sim->hot.pause) {
			onprocs += sim->nprocs;
			resprocs += sim->nprocs;
		} else if ( ! sim->terminate)
			resprocs += sim->nprocs;
	}

	/*
	 * Remind us of how many threads we're running.
	 * FIXME: this shows the number of allocated threads, not
	 * necessarily the number of running threads.
	 */
	(void)g_snprintf(buf, sizeof(buf), "%zu", resprocs);
	gtk_label_set_text(b->wins.resprocs, buf);

	(void)g_snprintf(buf, sizeof(buf), "%zu", onprocs);
	gtk_label_set_text(b->wins.onprocs, buf);

	/*
	 * Tell us how many generations have transpired (if no time has
	 * elapsed, then make sure we don't divide by zero).
	 * Then update the status bar.
	 * This will in turn redraw that window portion.
	 */
	elapsed = g_timer_elapsed(b->status_elapsed, NULL);
	if (0.0 == elapsed)
		elapsed = DBL_MIN;
	(void)g_snprintf(buf, sizeof(buf),
		"Running %.0f generations/second.",
		(runs - b->lastmatches) / elapsed);
	gtk_statusbar_pop(b->wins.status, 0);
	gtk_statusbar_push(b->wins.status, 0, buf);
	g_timer_start(b->status_elapsed);
	b->lastmatches = runs;

	/*
	 * Conditionally update our windows.
	 * We do this by iterating through all simulation windows and
	 * seeing if they have the "update" flag set to true.
	 */
	for (list = b->windows; list != NULL; list = list->next)
		if (((struct curwin *)list->data)->redraw)
			on_win_redraw(list->data);

	return(TRUE);
}

gboolean
onfocussim(GtkWidget *w, GdkEvent *event, gpointer dat)
{
#ifdef MAC_INTEGRATION
	struct curwin	  *c = dat;

	gtkosx_application_set_menu_bar
		(gtkosx_application_get(),
		 GTK_MENU_SHELL(c->wins.menu));
	gtkosx_application_sync_menubar
		(gtkosx_application_get());
#endif
	return(TRUE);
}

gboolean
onfocusmain(GtkWidget *w, GdkEvent *event, gpointer dat)
{
#ifdef MAC_INTEGRATION
	struct bmigrate	  *b = dat;

	gtkosx_application_set_menu_bar
		(gtkosx_application_get(),
		 GTK_MENU_SHELL(b->wins.menu));
	gtkosx_application_sync_menubar
		(gtkosx_application_get());
#endif
	return(TRUE);
}

gboolean
ondraw(GtkWidget *w, cairo_t *cr, gpointer dat)
{

	draw(w, cr, dat);
	return(TRUE);
}

void
onunautoexport(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*cur = dat;

	g_assert(NULL != cur->autosave);
	g_debug("Disabling auto-exporting: %s", cur->autosave);
	g_free(cur->autosave);
	cur->autosave = NULL;
	gtk_widget_show(GTK_WIDGET(cur->wins.menuautoexport));
	gtk_widget_hide(GTK_WIDGET(cur->wins.menuunautoexport));
}

void
onautoexport(GtkMenuItem *menuitem, gpointer dat)
{
	GtkWidget	*dialog;
	gint		 res;
	struct curwin	*cur = dat;
	GtkFileChooser	*chooser;

	g_assert(NULL == cur->autosave);
	dialog = gtk_file_chooser_dialog_new
		("Create Data Folder", cur->wins.window,
		 GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
		 "_Cancel", GTK_RESPONSE_CANCEL,
		 "_Create", GTK_RESPONSE_ACCEPT, NULL);
	chooser = GTK_FILE_CHOOSER(dialog);
	gtk_file_chooser_set_current_name(chooser, "bmigrate");
	res = gtk_dialog_run(GTK_DIALOG(dialog));
	if (res != GTK_RESPONSE_ACCEPT) {
		gtk_widget_destroy(dialog);
		return;
	}
	cur->autosave = gtk_file_chooser_get_filename(chooser);
	gtk_widget_destroy(dialog);
	gtk_widget_hide(GTK_WIDGET(cur->wins.menuautoexport));
	gtk_widget_show(GTK_WIDGET(cur->wins.menuunautoexport));
	g_debug("Auto-exporting: %s", cur->autosave);
	g_mkdir_with_parents(cur->autosave, 0755);
}

/*
 * Toggle a different view of the current window.
 */
void
onviewtoggle(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*cur = dat;

	/*
	 * First, set the "view" indicator to be the current view as
	 * found in the drop-down menu.
	 */
	for (cur->view = 0; cur->view < VIEW__MAX; cur->view++)
		if (gtk_check_menu_item_get_active
			(cur->wins.views[cur->view]))
			break;
	/*
	 * Next, set the window name to be the label associated with the
	 * respective menu check item.
	 */
	g_assert(cur->view < VIEW__MAX);
	gtk_window_set_title(GTK_WINDOW(cur->wins.window),
		gtk_menu_item_get_label
		(GTK_MENU_ITEM(cur->wins.views[cur->view])));

	/* Redraw the window. */
	gtk_widget_queue_draw(GTK_WIDGET(cur->wins.window));
}

/*
 * Pause all simulations connect to a view.
 */
void
onpause(GtkMenuItem *menuitem, gpointer dat)
{
	GList		*l;
	struct curwin	*cur = dat;

	for (l = cur->sims; NULL != l; l = l->next)
		on_sim_pause(l->data, 1);
}

/*
 * Unpause all simulations connect to a view.
 */
void
onunpause(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*cur = dat;
	GList		*l;

	for (l = cur->sims ; NULL != l; l = l->next)
		on_sim_pause(l->data, 0);
}

gboolean
onrangedelete(GtkWidget *widget, GdkEvent *event, gpointer dat)
{
	struct bmigrate	*b = dat;

	gtk_widget_set_visible
		(GTK_WIDGET(b->wins.rangefind), FALSE);

	if (b->rangeid > 0) {
		g_debug("Disabling rangefinder (user request)");
		g_source_remove(b->rangeid);
		b->rangeid = 0;
	} else
		g_debug("Rangefinder already disabled (user request)");

	return(TRUE);
}

void
onrangeclose(GtkButton *button, gpointer dat)
{
	struct bmigrate	*b = dat;

	gtk_widget_set_visible
		(GTK_WIDGET(b->wins.rangefind), FALSE);

	if (b->rangeid > 0) {
		g_debug("Disabling rangefinder (user request)");
		g_source_remove(b->rangeid);
		b->rangeid = 0;
	} else
		g_debug("Rangefinder already disabled (user request)");
}

/*
 * One of the preset continuum functions for our continuum game
 * possibility.
 */
void
onpreset(GtkComboBox *widget, gpointer dat)
{
	struct bmigrate	*b = dat;

	switch (gtk_combo_box_get_active(widget)) {
	case (1):
		/* Tullock */
		gtk_entry_set_text(b->wins.func,
			"x * (1 / X) - x");
		break;
	case (2):
		/* Cournot */
		gtk_entry_set_text(b->wins.func, "(1 - X) * x");
		break;
	case (3):
		/* Exponential Public Goods */
		gtk_entry_set_text(b->wins.func,
			"(1 - exp(-X)) - x");
		break;
	case (4):
		/* Quadratic Public Goods */
		gtk_entry_set_text(b->wins.func,
			"sqrt(1 / n * X) - 0.5 * x^2");
		break;
	default:
		gtk_entry_set_text(b->wins.func, "");
		break;
	}
}

static void
on_totalpop(struct bmigrate *b, gint pnum)
{
	GList		*l, *cl, *sv;
	gchar	 	 buf[32];
	double		 v = 0.0;
	enum maptop	 maptop;
	struct kml	*kml;
	struct kmlplace	*kmlp;
	gchar		*file;

	switch (pnum) {
	case (INPUT_UNIFORM):
		gtk_label_set_text(b->wins.input, "uniform");
		v = gtk_adjustment_get_value(b->wins.pop) *
			gtk_adjustment_get_value(b->wins.islands);
		break;
	case (INPUT_VARIABLE):
		gtk_label_set_text(b->wins.input, "variable");
		l = sv = gtk_container_get_children
			(GTK_CONTAINER(b->wins.mapbox));
		for (; NULL != l; l = g_list_next(l)) {
			cl = gtk_container_get_children
				(GTK_CONTAINER(l->data));
			v += gtk_spin_button_get_value_as_int
				(GTK_SPIN_BUTTON(g_list_next(cl)->data));
			g_list_free(cl);
		}
		g_list_free(sv);
		break;
	case (INPUT_MAPPED):
		for (maptop = 0; maptop < MAPTOP__MAX; maptop++)
			if (gtk_toggle_button_get_active
				 (b->wins.maptop[maptop]))
				break;
		switch (maptop) {
		case (MAPTOP_RECORD):
			gtk_label_set_text(b->wins.input,
				"KML islands");
			file = gtk_file_chooser_get_filename
				(b->wins.mapfile);
			if (NULL == file)
				break;
			kml = kml_parse(file, NULL);
			if (NULL == kml)
				break;
			l = kml->kmls;
			for ( ; NULL != l; l = g_list_next(l)) {
				kmlp = l->data;
				v += kmlp->pop;
			}
			kml_free(kml);
			break;
		case (MAPTOP_RAND):
			gtk_label_set_text(b->wins.input,
				"random islands");
			v = gtk_adjustment_get_value
				(b->wins.maprandislands) *
				gtk_adjustment_get_value
				(b->wins.maprandislanders);
			break;
		case (MAPTOP_TORUS):
			gtk_label_set_text(b->wins.input,
				"toroidal islands");
			v = gtk_adjustment_get_value
				(b->wins.maptorusislands) *
				gtk_adjustment_get_value
				(b->wins.maptorusislanders);
			break;
		default:
			abort();
		}
		break;
	default:
		abort();
	}

	g_snprintf(buf, sizeof(buf), "%g", v);
	gtk_label_set_text(b->wins.totalpop, buf);
}

void
on_change_input(GtkNotebook *notebook,
	GtkWidget *page, gint pnum, gpointer dat)
{

	on_totalpop(dat, pnum);
}

void
on_change_mapfile(GtkFileChooserButton *widget, gpointer dat)
{
	struct bmigrate	*b = dat;

	on_totalpop(b, gtk_notebook_get_current_page(b->wins.inputs));
}

void
on_change_maptype(GtkToggleButton *togglebutton, gpointer dat)
{
	struct bmigrate	*b = dat;

	on_totalpop(b, gtk_notebook_get_current_page(b->wins.inputs));
}

void
on_change_totalpop(GtkSpinButton *spinbutton, gpointer dat)
{
	struct bmigrate	*b = dat;

	on_totalpop(b, gtk_notebook_get_current_page(b->wins.inputs));
}

void
onsaveall(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*cur = dat;
	GtkWidget	*dialog;
	gint		 res;
	GtkFileChooser	*chooser;
	char 		*dir, *file;
	enum view	 view, sv;
	int		 rc;

	dialog = gtk_file_chooser_dialog_new
		("Create View Data Folder", cur->wins.window,
		 GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
		 "_Cancel", GTK_RESPONSE_CANCEL,
		 "_Create", GTK_RESPONSE_ACCEPT, NULL);

	chooser = GTK_FILE_CHOOSER(dialog);
	gtk_file_chooser_set_current_name(chooser, "bmigrate");
	res = gtk_dialog_run(GTK_DIALOG(dialog));
	if (res != GTK_RESPONSE_ACCEPT) {
		gtk_widget_destroy(dialog);
		return;
	}
	dir = gtk_file_chooser_get_filename(chooser);
	gtk_widget_destroy(dialog);
	g_assert(NULL != dir);
	g_assert('\0' != *dir);

	sv = cur->view;
	for (view = 0; view < VIEW__MAX; view++) {
		file = g_strdup_printf
			("%s" G_DIR_SEPARATOR_S "%s.pdf",
			 dir, views[view]);
		cur->view = view;
		rc = save(file, cur);
		g_free(file);
		if (0 == rc)
			break;
	}
	cur->view = sv;
	if (view == VIEW__MAX) {
		file = g_strdup_printf("%s"
			G_DIR_SEPARATOR_S "README.txt", dir);
		if (0 == (rc = saveconfig(file, cur)))
			view = 0;
		g_free(file);
	}
	g_free(dir);
	if (view == VIEW__MAX)
		return;
	dialog = gtk_message_dialog_new
		(GTK_WINDOW(cur->wins.window),
		 GTK_DIALOG_DESTROY_WITH_PARENT,
		 GTK_MESSAGE_ERROR,
		 GTK_BUTTONS_CLOSE,
		 "Error saving: %s",
		 strerror(errno));
	gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
}

void
onsave(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*cur = dat;
	GtkWidget	*dialog;
	gint		 res;
	GtkFileChooser	*chooser;
	int		 rc;
	char 		*file;

	dialog = gtk_file_chooser_dialog_new
		("Save View Data", cur->wins.window,
		 GTK_FILE_CHOOSER_ACTION_SAVE,
		 "_Cancel", GTK_RESPONSE_CANCEL,
		 "_Save", GTK_RESPONSE_ACCEPT, NULL);
	chooser = GTK_FILE_CHOOSER(dialog);
	gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE);
	gtk_file_chooser_set_current_name(chooser, "bmigrate.pdf");
	res = gtk_dialog_run(GTK_DIALOG(dialog));
	if (res != GTK_RESPONSE_ACCEPT) {
		gtk_widget_destroy(dialog);
		return;
	}
	file = gtk_file_chooser_get_filename(chooser);
	gtk_widget_destroy(dialog);
	g_assert(NULL != file);
	g_assert('\0' != *file);
	rc = save(file, cur);
	g_free(file);
	if (0 != rc)
		return;
	dialog = gtk_message_dialog_new
		(GTK_WINDOW(cur->wins.window),
		 GTK_DIALOG_DESTROY_WITH_PARENT,
		 GTK_MESSAGE_ERROR,
		 GTK_BUTTONS_CLOSE,
		 "Error saving: %s",
		 strerror(errno));
	gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
}

#ifdef MAC_INTEGRATION
gboolean
onterminate(GtkosxApplication *action, gpointer dat)
{

	bmigrate_free(dat);
	gtk_main_quit();
	return(FALSE);
}
#endif

/*
 * Run when we quit from a simulation window.
 */
void
onclose(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*cur = dat;

	g_debug("Simulation window closing");
	gtk_widget_destroy(GTK_WIDGET(cur->wins.window));
}

/*
 * Run when we quit from a simulation window.
 */
void
onquitsim(GtkMenuItem *menuitem, gpointer dat)
{
	struct curwin	*cur = dat;

	bmigrate_free(cur->b);
	gtk_main_quit();
}

/*
 * Run when we quit from a simulation window.
 */
void
onquitmain(GtkMenuItem *menuitem, gpointer dat)
{

	bmigrate_free(dat);
	gtk_main_quit();
}

/*
 * Run when we destroy the config screen.
 */
void
ondestroy(GtkWidget *object, gpointer dat)
{

	bmigrate_free(dat);
	gtk_main_quit();
}

/*
 * Run when we press "Quit" on the config screen.
 */
void
on_deactivate(GtkButton *button, gpointer dat)
{

	bmigrate_free(dat);
	gtk_main_quit();
}

/*
 * Add an island configuration.
 * For the time being, we only allow the population size of the island
 * to be specified.
 * (Inter-island migration probabilities may not yet be assigned.)
 */
static void
mapbox_add(struct bmigrate *b, size_t sz)
{
	GtkWidget	*box, *label, *btn;
	GtkAdjustment	*adj;
	gchar		 buf[64];

	box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);

	g_snprintf(buf, sizeof(buf), "Population %zu:", sz);
	label = gtk_label_new(buf);
	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	gtk_label_set_width_chars(GTK_LABEL(label), 20);
	gtk_container_add(GTK_CONTAINER(box), label);

	adj = gtk_adjustment_new(2.0, 2.0, 1000.0, 1.0, 10.0, 0.0);
	btn = gtk_spin_button_new(adj, 1.0, 0);
	g_signal_connect(btn, "value-changed",
		G_CALLBACK(on_change_totalpop), b);
	gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(btn), TRUE);
	gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(btn), TRUE);
	gtk_container_add(GTK_CONTAINER(box), btn);
	gtk_container_add(GTK_CONTAINER(b->wins.mapbox), box);
	gtk_widget_show_all(box);
}

/*
 * Remove the last island configuration.
 */
static void
mapbox_rem(struct bmigrate *b)
{
	GList		*list, *last;

	list = gtk_container_get_children(GTK_CONTAINER(b->wins.mapbox));
	last = g_list_last(list);
	gtk_widget_destroy(GTK_WIDGET(last->data));
	g_list_free(list);
}

/*
 * We've requested more or fewer islands for the mapped scenario.
 */
void
onislandspin(GtkSpinButton *spinbutton, gpointer dat)
{
	struct bmigrate	*b = dat;
	guint		 oldsz, newsz;
	GList		*list;

	list = gtk_container_get_children(GTK_CONTAINER(b->wins.mapbox));
	oldsz = g_list_length(list);
	g_list_free(list);
	newsz = (guint)gtk_spin_button_get_value(spinbutton);

	if (newsz > oldsz) {
		while (oldsz++ < newsz)
			mapbox_add(b, oldsz);
	} else if (oldsz > newsz) {
		while (oldsz-- > newsz)
			mapbox_rem(b);
	}

	on_totalpop(b, gtk_notebook_get_current_page(b->wins.inputs));
}

int
main(int argc, char *argv[])
{
	GtkBuilder	*builder;
	struct bmigrate	 b;
	int		 rc;

	memset(&b, 0, sizeof(struct bmigrate));
	gtk_init(&argc, &argv);

	rc = kplotcfg_default_palette(&b.clrs, &b.clrsz);
	g_assert(0 != rc);

	/*
	 * Sanity-check to make sure that the hnode expression evaluator
	 * is working properly.
	 * You'll need to actually look at the debugging output to see
	 * if that's the case, of course.
	 */
	hnode_test();

	builder = builder_get("bmigrate.glade");
	if (NULL == builder)
		return(EXIT_FAILURE);

	hwin_init(&b.wins, builder);
	gtk_builder_connect_signals(builder, &b);
	g_object_unref(G_OBJECT(builder));

	/*
	 * Have two running timers: once per second, forcing a refresh of
	 * the window system; then another at four times per second
	 * updating our cold statistics.
	 */
	b.status_elapsed = g_timer_new();
	g_timeout_add_seconds(1, (GSourceFunc)on_sim_timer, &b);
	g_timeout_add_seconds(60, (GSourceFunc)on_sim_autosave, &b);
	g_timeout_add(250, (GSourceFunc)on_sim_copyout, &b);
	gtk_statusbar_push(b.wins.status, 0, "No simulations.");

#ifdef MAC_INTEGRATION
	g_signal_connect(gtkosx_application_get(),
		"NSApplicationWillTerminate",
		G_CALLBACK(onterminate), &b);
	gtkosx_application_ready(gtkosx_application_get());
#endif

	gtk_main();
	bmigrate_free(&b);
	return(EXIT_SUCCESS);
}

max_stars_repo_name: kristapsdz/bmigrate

max_issues_repo_name: kristapsdz/bmigrate

max_forks_repo_name: kristapsdz/bmigrate


text:
#ifndef TTT_GFX_BUTTON_H
#define TTT_GFX_BUTTON_H

#include <SDL_render.h>
#include <SDL_mouse.h>
#include <SDL_mixer.h>

#include <gsl/pointers>

#include <optional>

namespace ttt::gfx
{
	// A basic clickable image button.
	class Button final
	{
	public:
		Button() = default;
		Button(SDL_Texture *texture, const SDL_Rect &normalSrc, const SDL_FRect &dst, Mix_Chunk *sound) noexcept;
		Button(SDL_Texture *texture, const SDL_Rect &normalSrc, const std::optional<SDL_Rect> &hoverSrc, const std::optional<SDL_Rect> &pressedSrc, const SDL_FRect &dst, Mix_Chunk *sound) noexcept;

		void setTexture(SDL_Texture *texture, const SDL_Rect &normalSrc) noexcept;
		void setTexture(SDL_Texture *texture, const SDL_Rect &normalSrc, const std::optional<SDL_Rect> &hoverSrc, const std::optional<SDL_Rect> &pressedSrc) noexcept;
		void setDestination(const SDL_FRect &dst) noexcept;
		void setSound(Mix_Chunk *sound) noexcept;

		void update() noexcept;
		void render(gsl::not_null<SDL_Renderer *> renderer) const noexcept;

		[[nodiscard]] bool isHoveredOver() const noexcept;
		[[nodiscard]] bool isPressed() const noexcept;
		[[nodiscard]] bool wasReleased() const noexcept;

	private:
		SDL_Texture *texture{nullptr};

		SDL_Rect normalSrc{0, 0, 0, 0};
		std::optional<SDL_Rect> hoverSrc;
		std::optional<SDL_Rect> pressedSrc;

		SDL_FRect dst{0.0f, 0.0f, 0.0f, 0.0f};
		Mix_Chunk *sound{nullptr};

		bool hoveredOver{false};
		bool pressed{false};
		bool released{false};
		std::optional<SDL_Point> click;
	};

	inline void Button::setSound(Mix_Chunk *sound) noexcept { this->sound = sound; }

	inline bool Button::isHoveredOver() const noexcept { return hoveredOver; }
	inline bool Button::isPressed() const noexcept { return pressed; }
	inline bool Button::wasReleased() const noexcept { return released; }
}

#endif

max_stars_repo_name: itsArtem/TicTacToe

max_issues_repo_name: itsArtem/TicTacToe

max_forks_repo_name: itsArtem/TicTacToe