#include "include/rb_gsl.h"
#ifdef GSL_1_9_LATER

static VALUE cWorkspace;

static VALUE rb_gsl_sf_mathieu_alloc(VALUE klass, VALUE n, VALUE q)
{
	gsl_sf_mathieu_workspace *w;
	w = gsl_sf_mathieu_alloc((size_t) FIX2INT(n), NUM2DBL(q));
	return Data_Wrap_Struct(klass, 0, gsl_sf_mathieu_free, w);
}
 
static VALUE sf_mathieu_eval(VALUE order, VALUE qq,
	int (*f)(int, double, gsl_sf_result*))
{
	gsl_sf_result r;
	(*f)(FIX2INT(order), NUM2DBL(qq), &r);
	return rb_float_new(r.val);
}

static VALUE sf_mathieu_eval2(VALUE n1, VALUE n2, VALUE q, VALUE x,
	int (*f)(int, int, double, double, gsl_sf_result*))
{
	gsl_sf_result r;
	(*f)(FIX2INT(n1),FIX2INT(n2),  NUM2DBL(q), NUM2DBL(x), &r);
	return rb_float_new(r.val);
}

static VALUE sf_mathieu_array_eval(int argc, VALUE *argv,
	int (*f)(int, int, double, gsl_sf_mathieu_workspace*, double[]))
{
	gsl_sf_mathieu_workspace *w;
	gsl_vector *v;
	int n1, n2;
	double q;
	switch (argc) {
	case 4:
		if (!rb_obj_is_kind_of(argv[3], cWorkspace)) {
			rb_raise(rb_eTypeError, "Wrong argument type 3 (%s detected, %s expected)",
				rb_class2name(CLASS_OF(argv[3])), rb_class2name(cWorkspace));
		}
		n1 = FIX2INT(argv[0]);
		n2 = FIX2INT(argv[1]);
		q = NUM2DBL(argv[2]);
		Data_Get_Struct(argv[3], gsl_sf_mathieu_workspace, w);
		break;
	default:
		rb_raise(rb_eArgError, "Wrong number of arguments. (%d for 4)", argc);
	}
	v = gsl_vector_alloc(n2 - n1 + 1);
	(*f)(n1, n2, q, w, v->data);
	return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, v);
}

static VALUE sf_mathieu_array_eval2(int argc, VALUE *argv,
	int (*f)(int, int,  double, double, gsl_sf_mathieu_workspace*, double[]))
{
	gsl_sf_mathieu_workspace *w;
	gsl_vector *v;
	int n1, n2;
	double q, x;
	switch (argc) {
	case 5:
		if (!rb_obj_is_kind_of(argv[4], cWorkspace)) {
			rb_raise(rb_eTypeError, "Wrong argument type 4 (%s detected, %s expected)",
				rb_class2name(CLASS_OF(argv[4])), rb_class2name(cWorkspace));
		}
		n1 = FIX2INT(argv[0]);
		n2 = FIX2INT(argv[1]);
		q = NUM2DBL(argv[2]);
		x = NUM2DBL(argv[3]);		
		Data_Get_Struct(argv[4], gsl_sf_mathieu_workspace, w);
		break;
	default:
		rb_raise(rb_eArgError, "Wrong number of arguments. (%d for 5)", argc);
	}
	v = gsl_vector_alloc(n2 - n1 + 1);
	(*f)(n1, n2, q, x, w, v->data);
	return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, v);
}
static VALUE sf_mathieu_array_eval3(int argc, VALUE *argv,
	int (*f)(int, int, int, double, double, gsl_sf_mathieu_workspace*, double[]))
{
	gsl_sf_mathieu_workspace *w;
	gsl_vector *v;
	int n1, n2, n3;
	double q, x;
	switch (argc) {
	case 6:
		if (!rb_obj_is_kind_of(argv[5], cWorkspace)) {
			rb_raise(rb_eTypeError, "Wrong argument type 5 (%s detected, %s expected)",
				rb_class2name(CLASS_OF(argv[5])), rb_class2name(cWorkspace));
		}
		n1 = FIX2INT(argv[0]);
		n2 = FIX2INT(argv[1]);
		n3 = FIX2INT(argv[2]);		
		q = NUM2DBL(argv[3]);
		x = NUM2DBL(argv[4]);		
		Data_Get_Struct(argv[5], gsl_sf_mathieu_workspace, w);
		break;
	default:
		rb_raise(rb_eArgError, "Wrong number of arguments. (%d for 6)", argc);
	}
	v = gsl_vector_alloc(n3 - n2 + 1);
	(*f)(n1, n2, n3, q, x, w, v->data);
	return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, v);
}
static VALUE sf_mathieu_eval_int_double2(VALUE order, VALUE qq, VALUE zz,
	int (*f)(int, double, double, gsl_sf_result*))
{
	gsl_sf_result r;
	(*f)(FIX2INT(order), NUM2DBL(qq), NUM2DBL(zz), &r);
	return rb_float_new(r.val);
}
static VALUE sf_mathieu_eval_e_int_double2(VALUE order, VALUE qq, VALUE zz,
	int (*f)(int, double, double, gsl_sf_result*))
{
	gsl_sf_result *r;
	VALUE val;
	val = Data_Make_Struct(cgsl_sf_result, gsl_sf_result, 0, free, r);	
	(*f)(FIX2INT(order), NUM2DBL(qq), NUM2DBL(zz), r);
  return val;
}

static VALUE sf_mathieu_eval_e_int2_double2(VALUE n1, VALUE n2, VALUE qq, VALUE zz,
	int (*f)(int, int, double, double, gsl_sf_result*))
{
	gsl_sf_result *r;
	VALUE val;
	val = Data_Make_Struct(cgsl_sf_result, gsl_sf_result, 0, free, r);	
	(*f)(FIX2INT(n1), FIX2INT(n2), NUM2DBL(qq), NUM2DBL(zz), r);
  return val;
}
/**********/
static VALUE rb_gsl_sf_mathieu_a_e(VALUE module, VALUE order, VALUE qq)
{
	return rb_gsl_sf_eval_e_int_double(gsl_sf_mathieu_a, order, qq);
}
static VALUE rb_gsl_sf_mathieu_a(VALUE module, VALUE order, VALUE qq)
{
	return sf_mathieu_eval(order, qq, gsl_sf_mathieu_a);
}
static VALUE rb_gsl_sf_mathieu_a_array(VALUE module, int argc, VALUE *argv)
{
	return sf_mathieu_array_eval(argc, argv, gsl_sf_mathieu_a_array);
}
static VALUE rb_gsl_sf_mathieu_b_e(VALUE module, VALUE order, VALUE qq)
{
	return rb_gsl_sf_eval_e_int_double(gsl_sf_mathieu_b, order, qq);
}
static VALUE rb_gsl_sf_mathieu_b(VALUE module, VALUE order, VALUE qq)
{
	return sf_mathieu_eval(order, qq, gsl_sf_mathieu_b);
}
static VALUE rb_gsl_sf_mathieu_b_array(VALUE module, int argc, VALUE *argv)
{
	return sf_mathieu_array_eval(argc, argv, gsl_sf_mathieu_b_array);
}
static VALUE rb_gsl_sf_mathieu_ce_e(VALUE module, VALUE order, VALUE qq, VALUE zz)
{
	return sf_mathieu_eval_e_int_double2(order, qq, zz, gsl_sf_mathieu_ce);
}
static VALUE rb_gsl_sf_mathieu_ce(VALUE module, VALUE order, VALUE qq, VALUE zz)
{
	return sf_mathieu_eval_int_double2(order, qq, zz, gsl_sf_mathieu_ce);
}
static VALUE rb_gsl_sf_mathieu_ce_array(VALUE module, int argc, VALUE *argv)
{
	return sf_mathieu_array_eval2(argc, argv, gsl_sf_mathieu_ce_array);
}	
static VALUE rb_gsl_sf_mathieu_se_e(VALUE module, VALUE order, VALUE qq, VALUE zz)
{
	return sf_mathieu_eval_e_int_double2(order, qq, zz, gsl_sf_mathieu_se);
}
static VALUE rb_gsl_sf_mathieu_se(VALUE module, VALUE order, VALUE qq, VALUE zz)
{
	return sf_mathieu_eval_int_double2(order, qq, zz, gsl_sf_mathieu_se);
}
static VALUE rb_gsl_sf_mathieu_se_array(VALUE module, int argc, VALUE *argv)
{
	return sf_mathieu_array_eval2(argc, argv, gsl_sf_mathieu_se_array);
}	

/*****/
static VALUE rb_gsl_sf_mathieu_Mc_e(VALUE module, VALUE n1, VALUE n2, VALUE q, VALUE x)
{
	return sf_mathieu_eval_e_int2_double2(n1, n2, q, x, gsl_sf_mathieu_Mc);
}
static VALUE rb_gsl_sf_mathieu_Mc(VALUE module, VALUE n1, VALUE n2, VALUE q, VALUE x)
{
	return sf_mathieu_eval2(n1, n2, q, x, gsl_sf_mathieu_Mc);
}
static VALUE rb_gsl_sf_mathieu_Mc_array(VALUE module, int argc, VALUE *argv)
{
	return sf_mathieu_array_eval3(argc, argv, gsl_sf_mathieu_Mc_array);
}	
static VALUE rb_gsl_sf_mathieu_Ms_e(VALUE module, VALUE n1, VALUE n2, VALUE q, VALUE x)
{
	return sf_mathieu_eval_e_int2_double2(n1, n2, q, x, gsl_sf_mathieu_Ms);
}
static VALUE rb_gsl_sf_mathieu_Ms(VALUE module, VALUE n1, VALUE n2, VALUE q, VALUE x)
{
	return sf_mathieu_eval2(n1, n2, q, x, gsl_sf_mathieu_Ms);
}
static VALUE rb_gsl_sf_mathieu_Ms_array(VALUE module, int argc, VALUE *argv)
{
	return sf_mathieu_array_eval3(argc, argv, gsl_sf_mathieu_Ms_array);
}	
/*****/
void Init_sf_mathieu(VALUE module)
{
	VALUE mMathieu;
	
	mMathieu = rb_define_module_under(module, "Mathieu");
	cWorkspace = rb_define_class_under(mMathieu, "Workspace", cGSL_Object);
	rb_define_singleton_method(cWorkspace, "alloc", rb_gsl_sf_mathieu_alloc, 2);
	
	rb_define_module_function(module, "mathieu_a", rb_gsl_sf_mathieu_a, 2);	
	rb_define_module_function(module, "mathieu_a_e", rb_gsl_sf_mathieu_a_e, 2);
	rb_define_module_function(module, "mathieu_a_array", rb_gsl_sf_mathieu_a_array, -1);	
	rb_define_module_function(module, "mathieu_b", rb_gsl_sf_mathieu_b, 2);	
	rb_define_module_function(module, "mathieu_b_e", rb_gsl_sf_mathieu_b_e, 2);	
	rb_define_module_function(module, "mathieu_b_array", rb_gsl_sf_mathieu_b_array, -1);		
	rb_define_module_function(module, "mathieu_ce", rb_gsl_sf_mathieu_ce, 3);			
	rb_define_module_function(module, "mathieu_ce_e", rb_gsl_sf_mathieu_ce_e, 3);
	rb_define_module_function(module, "mathieu_ce_array", rb_gsl_sf_mathieu_ce_array, -1);
	rb_define_module_function(module, "mathieu_se", rb_gsl_sf_mathieu_se, 3);	
	rb_define_module_function(module, "mathieu_se_e", rb_gsl_sf_mathieu_se_e, 3);
	rb_define_module_function(module, "mathieu_se_array", rb_gsl_sf_mathieu_se_array, -1);
	rb_define_module_function(module, "mathieu_Mc", rb_gsl_sf_mathieu_Mc, 4);	
	rb_define_module_function(module, "mathieu_Mc_e", rb_gsl_sf_mathieu_Mc_e, 4);
	rb_define_module_function(module, "mathieu_Mc_array", rb_gsl_sf_mathieu_Mc_array, -1);
	rb_define_module_function(module, "mathieu_Ms", rb_gsl_sf_mathieu_Ms, 4);	
	rb_define_module_function(module, "mathieu_Ms_e", rb_gsl_sf_mathieu_Ms_e, 4);
	rb_define_module_function(module, "mathieu_Ms_array", rb_gsl_sf_mathieu_Ms_array, -1);	
}

#endif
