gambas-source-code/gb.gsl/src/c_polynomial.c
Benoît Minisini e0469073ab [GB.GSL]
* BUG: Polynomial: Create a new void polymonial does lead to a crash 
  anymore.


git-svn-id: svn://localhost/gambas/trunk@6055 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2014-01-02 21:18:08 +00:00

914 lines
17 KiB
C

/***************************************************************************
c_polynomial.c
gb.gsl component
(c) 2012 Randall Morgan <rmorgan62@gmail.com>
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 1, 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.
***************************************************************************/
#define __C_POLYNOMIAL_C
#include "c_complex.h"
#include "c_vector.h"
#include "c_polynomial.h"
#define THIS ((CPOLYNOMIAL *)_object)
#define DATA(_p) ((double *)(_p)->data)
#define CDATA(_p) ((gsl_complex *)(_p)->data)
#define COUNT(_p) ((_p)->size)
#define COMPLEX(_p) ((_p)->complex)
//---- Utility methods ------------------------------------------------------
static CPOLYNOMIAL *POLYNOMIAL_create(int size, bool complex)
{
CPOLYNOMIAL *p = (CPOLYNOMIAL *)GB.Create(CLASS_Polynomial, NULL, NULL);
GB.NewArray(POINTER(&p->data), complex ? sizeof(gsl_complex) : sizeof(double), size);
p->size = size;
p->complex = complex;
return p;
}
static CPOLYNOMIAL *POLYNOMIAL_copy(CPOLYNOMIAL *_object)
{
CPOLYNOMIAL *p = POLYNOMIAL_create(COUNT(THIS), COMPLEX(THIS));
memcpy(p->data, THIS->data, THIS->size * (COMPLEX(THIS) ? sizeof(gsl_complex) : sizeof(double)));
return p;
}
#define POLYNOMIAL_make(_a) (((_a)->ob.ref <= 1) ? (_a) : POLYNOMIAL_copy(_a))
static int get_degree(CPOLYNOMIAL *_object)
{
int i;
if (COMPLEX(THIS))
{
gsl_complex *d = CDATA(THIS);
for (i = COUNT(THIS) - 1; i >= 0; i--)
{
if (GSL_REAL(d[i]) != 0.0 || GSL_IMAG(d[i]) != 0.0)
return i;
}
}
else
{
double *d = DATA(THIS);
for (i = COUNT(THIS) - 1; i >= 0; i--)
{
if (d[i] != 0.0)
return i;
}
}
return 0;
}
static void ensure_size(CPOLYNOMIAL *_object, int size)
{
if (size > COUNT(THIS))
{
GB.Insert(POINTER(&THIS->data), -1, size - COUNT(THIS));
THIS->size = size;
}
}
static CPOLYNOMIAL *POLYNOMIAL_make_size(CPOLYNOMIAL *a, int min_size)
{
if (a->size >= min_size)
return POLYNOMIAL_make(a);
a = POLYNOMIAL_copy(a);
ensure_size(a, min_size);
return a;
}
static void ensure_complex(CPOLYNOMIAL *_object)
{
gsl_complex *d;
int size, i;
if (COMPLEX(THIS))
return;
if (THIS->data)
{
size = COUNT(THIS);
GB.NewArray(POINTER(&d), sizeof(gsl_complex), size);
for (i = 0; i < size; i++)
d[i].dat[0] = DATA(THIS)[i];
GB.FreeArray(POINTER(&THIS->data));
THIS->data = d;
}
THIS->complex = TRUE;
}
static bool ensure_not_complex(CPOLYNOMIAL *_object)
{
gsl_complex *cd;
double *d;
int size, i;
if (!COMPLEX(THIS))
return FALSE;
if (THIS->data)
{
size = COUNT(THIS);
cd = CDATA(THIS);
for (i = 0; i < size; i++)
{
if (GSL_IMAG(cd[i]) != 0.0)
return TRUE;
}
GB.NewArray(POINTER(&d), sizeof(double), size);
for (i = 0; i < size; i++)
d[i] = GSL_REAL(cd[i]);
GB.FreeArray(POINTER(&THIS->data));
THIS->data = d;
}
THIS->complex = FALSE;
return FALSE;
}
static void poly_negative(CPOLYNOMIAL *_object)
{
int i;
if (COMPLEX(THIS))
{
for (i = 0; i < COUNT(THIS); i++)
DATA(THIS)[i] = (- DATA(THIS)[i]);
}
else
{
for (i = 0; i < COUNT(THIS); i++)
CDATA(THIS)[i] = gsl_complex_negative(CDATA(THIS)[i]);
}
}
//---- Arithmetic operators -------------------------------------------------
static CPOLYNOMIAL *_addf(CPOLYNOMIAL *a, double f, bool invert)
{
CPOLYNOMIAL *p = POLYNOMIAL_make(a);
DATA(p)[0] += f;
return p;
}
static CPOLYNOMIAL *_add(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert)
{
int da = get_degree(a);
int db = get_degree(b);
int d = Max(da, db);
int dm = Min(d, db);
int i;
CPOLYNOMIAL *p = POLYNOMIAL_make_size(a, d + 1);
if (COMPLEX(a) || COMPLEX(b))
{
ensure_complex(p);
ensure_complex(b);
for (i = 0; i <= dm; i++)
CDATA(p)[i] = gsl_complex_add(CDATA(p)[i], CDATA(b)[i]);
}
else
{
for (i = 0; i <= dm; i++)
DATA(p)[i] += DATA(b)[i];
}
return p;
}
static CPOLYNOMIAL *op_array(CPOLYNOMIAL *a, void *b, bool invert, CPOLYNOMIAL *(*func)(CPOLYNOMIAL *, CPOLYNOMIAL *, bool))
{
GB_VALUE conv;
bool err;
CPOLYNOMIAL *p;
conv._object.type = (GB_TYPE)GB.GetClass(b);
conv._object.value = b;
GB.Ref(b);
err = GB.Conv(&conv, CLASS_Polynomial);
if (!err)
{
if (invert)
{
GB.Ref(conv._object.value);
p = (*func)(conv._object.value, a, FALSE);
GB.Unref(&conv._object.value);
}
else
{
p = (*func)(a, conv._object.value, FALSE);
}
GB.Unref(&conv._object.value);
return p;
}
else
return NULL;
}
static CPOLYNOMIAL *_addo(CPOLYNOMIAL *a, void *b, bool invert)
{
CPOLYNOMIAL *p;
if (GB.Is(b, CLASS_Complex))
{
p = POLYNOMIAL_make(a);
ensure_complex(p);
CDATA(p)[0] = gsl_complex_add(CDATA(p)[0], ((CCOMPLEX *)b)->number);
return p;
}
else if (GB.Is(b, CLASS_Array))
{
return op_array(a, b, invert, _add);
}
return NULL;
}
static CPOLYNOMIAL *_subf(CPOLYNOMIAL *a, double f, bool invert)
{
CPOLYNOMIAL *p = POLYNOMIAL_make(a);
if (invert)
poly_negative(p);
else
f = -f;
DATA(p)[0] += f;
return p;
}
static CPOLYNOMIAL *_sub(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert)
{
int da = get_degree(a);
int db = get_degree(b);
int d = Max(da, db);
int dm = Min(d, db);
int i;
CPOLYNOMIAL *p = POLYNOMIAL_make_size(a, d + 1);
if (COMPLEX(a) || COMPLEX(b))
{
ensure_complex(p);
ensure_complex(b);
for (i = 0; i <= dm; i++)
CDATA(p)[i] = gsl_complex_sub(CDATA(p)[i], CDATA(b)[i]);
}
else
{
for (i = 0; i <= dm; i++)
DATA(p)[i] -= DATA(b)[i];
}
return p;
}
static CPOLYNOMIAL *_subo(CPOLYNOMIAL *a, void *b, bool invert)
{
CPOLYNOMIAL *p;
if (GB.Is(b, CLASS_Complex))
{
p = POLYNOMIAL_make(a);
if (invert)
{
poly_negative(p);
ensure_complex(p);
CDATA(p)[0] = gsl_complex_add(CDATA(p)[0], ((CCOMPLEX *)b)->number);
}
else
{
ensure_complex(p);
CDATA(p)[0] = gsl_complex_sub(CDATA(p)[0], ((CCOMPLEX *)b)->number);
}
return p;
}
else if (GB.Is(b, CLASS_Array))
{
return op_array(a, b, invert, _sub);
}
return NULL;
}
static CPOLYNOMIAL *_neg(CPOLYNOMIAL *a)
{
CPOLYNOMIAL *p = POLYNOMIAL_make(a);
poly_negative(p);
return p;
}
static int _equal(CPOLYNOMIAL *a, CPOLYNOMIAL *b, bool invert)
{
int da = get_degree(a);
int db = get_degree(b);
int i;
if (da != db)
return FALSE;
if (COMPLEX(a) || COMPLEX(b))
{
ensure_complex(a);
ensure_complex(b);
for (i = 0; i <= da; i++)
if (GSL_REAL(CDATA(a)[i]) != GSL_REAL(CDATA(b)[i]) || GSL_IMAG(CDATA(a)[i]) != GSL_IMAG(CDATA(b)[i]))
return FALSE;
}
else
{
for (i = 0; i <= da; i++)
if (DATA(a)[i] != DATA(b)[i])
return FALSE;
}
return TRUE;
}
static GB_OPERATOR_DESC _operator =
{
add: (void *)_add,
addf: (void *)_addf,
addo: (void *)_addo,
sub: (void *)_sub,
subf: (void *)_subf,
subo: (void *)_subo,
/*mul: (void *)_mul,
mulf: (void *)_mulf,
div: (void *)_div,
divf: (void *)_divf,
idivf: (void *)_idivf,*/
equal: (void *)_equal,
/*equalf: (void *)_equalf,
abs: (void *)_abs,*/
neg: (void *)_neg
};
//---- Conversions ----------------------------------------------------------
char *POLYNOMIAL_to_string(CPOLYNOMIAL *p, bool local)
{
int i;
int size = COUNT(p);
char *result = NULL;
char *str;
int len;
char buffer[16];
double re, im;
bool add = FALSE;
bool complex = COMPLEX(p);
gsl_complex c;
bool par;
i = size;
while (i > 0)
{
i--;
if (complex)
{
c = CDATA(p)[i];
re = GSL_REAL(c);
im = GSL_IMAG(c);
}
else
{
re = DATA(p)[i];
im = 0.0;
}
if (re == 0.0 && im == 0.0)
continue;
par = i > 0 && re != 0.0 && im != 0.0;
if (!add)
add = TRUE;
else if (re > 0.0 || (re == 0.0 && im > 0.0) || par)
result = GB.AddChar(result, '+');
//l = GB.StringLength(result);
if (par)
result = GB.AddChar(result, '(');
if (i == 0 || re != 1.0 || im != 0.0)
{
if (re != 0.0)
{
if (re == -1.0 && i > 0)
result = GB.AddChar(result, '-');
else
{
GB.NumberToString(local, re, NULL, &str, &len);
result = GB.AddString(result, str, len);
}
}
if (im != 0.0)
{
if (re != 0.0 && im > 0.0)
result = GB.AddChar(result, '+');
if (local && im == -1.0)
result = GB.AddChar(result, '-');
else if (!local || im != 1.0)
{
GB.NumberToString(local, im, NULL, &str, &len);
result = GB.AddString(result, str, len);
}
result = GB.AddChar(result, 'i');
}
}
if (par)
result = GB.AddChar(result, ')');
if (i > 0)
{
if (!local && ((im == 0 && re != 0 && re != 1 && re != -1) || (im != 0)))
result = GB.AddChar(result, '*');
result = GB.AddChar(result, 'x');
if (i > 1)
{
result = GB.AddChar(result, '^');
len = sprintf(buffer, "%d", i);
result = GB.AddString(result, buffer, len);
}
}
}
if (!result)
result = GB.NewString("0", 1);
return result;
}
bool POLYNOMIAL_convert(CPOLYNOMIAL *a, GB_TYPE type, GB_VALUE *conv)
{
if (a)
{
switch (type)
{
case GB_T_STRING:
case GB_T_CSTRING:
conv->_string.value.addr = POLYNOMIAL_to_string(a, type == GB_T_CSTRING);
conv->_string.value.start = 0;
conv->_string.value.len = GB.StringLength(conv->_string.value.addr);
return FALSE;
default:
return TRUE;
}
}
else
{
double coef;
switch(type)
{
case GB_T_FLOAT: coef = conv->_float.value; break;
case GB_T_SINGLE: coef = conv->_single.value; break;
case GB_T_INTEGER: case GB_T_SHORT: case GB_T_BYTE: coef = conv->_integer.value;
return FALSE;
default:
if (type >= GB_T_OBJECT)
{
if (GB.Is(conv->_object.value, CLASS_Array))
{
CPOLYNOMIAL *p;
CCOMPLEX *c;
GB_ARRAY array = (GB_ARRAY)conv->_object.value;
int size = GB.Array.Count(array);
int i;
GB_VALUE temp;
void *data;
GB_TYPE atype = GB.Array.Type(array);
if (atype > GB_T_BOOLEAN && atype <= GB_T_FLOAT)
{
p = POLYNOMIAL_create(size, FALSE);
for (i = 0; i < size; i++)
{
data = GB.Array.Get(array, i);
GB.ReadValue(&temp, data, atype);
GB.Conv(&temp, GB_T_FLOAT);
DATA(p)[i] = temp._float.value;
}
conv->_object.value = p;
return FALSE;
}
else if (atype == GB_T_VARIANT)
{
p = POLYNOMIAL_create(size, TRUE);
for (i = 0; i < size; i++)
{
GB.ReadValue(&temp, GB.Array.Get(array, i), atype);
GB.BorrowValue(&temp);
GB.Conv(&temp, CLASS_Complex);
c = temp._object.value;
CDATA(p)[i] = c->number;
GB.ReleaseValue(&temp);
}
conv->_object.value = p;
return FALSE;
}
else if (atype == CLASS_Complex)
{
p = POLYNOMIAL_create(size, TRUE);
for (i = 0; i < size; i++)
{
c = *(CCOMPLEX **)GB.Array.Get(array, i);
if (c)
CDATA(p)[i] = c->number;
else
CDATA(p)[i] = COMPLEX_zero;
}
conv->_object.value = p;
return FALSE;
}
}
}
return TRUE;
}
a = POLYNOMIAL_create(1, FALSE);
*DATA(a) = coef;
conv->_object.value = a;
return FALSE;
}
}
//---------------------------------------------------------------------------
BEGIN_METHOD(Polynomial_new, GB_INTEGER size; GB_BOOLEAN complex)
bool complex = VARGOPT(complex, FALSE);
int size = VARGOPT(size, 0);
GB.NewArray(POINTER(&THIS->data), complex ? sizeof(gsl_complex) : sizeof(double), size);
THIS->size = size;
THIS->complex = complex;
END_METHOD
BEGIN_METHOD_VOID(Polynomial_free)
GB.FreeArray(POINTER(&THIS->data));
END_METHOD
BEGIN_PROPERTY(Polynomial_Count)
GB.ReturnInteger(COUNT(THIS));
END_PROPERTY
BEGIN_PROPERTY(Polynomial_Degree)
GB.ReturnInteger(get_degree(THIS));
END_PROPERTY
BEGIN_METHOD(Polynomial_ToString, GB_BOOLEAN local)
GB.ReturnString(GB.FreeStringLater(POLYNOMIAL_to_string(THIS, VARGOPT(local, FALSE))));
END_METHOD
BEGIN_METHOD(Polynomial_get, GB_INTEGER index)
int index = VARG(index);
if (index < 0 || index >= COUNT(THIS))
{
if (COMPLEX(THIS))
GB.ReturnObject(COMPLEX_create(COMPLEX_zero));
else
GB.ReturnFloat(0.0);
}
else
{
if (COMPLEX(THIS))
GB.ReturnObject(COMPLEX_create(CDATA(THIS)[index]));
else
GB.ReturnFloat(DATA(THIS)[index]);
}
GB.ReturnConvVariant();
END_METHOD
BEGIN_METHOD(Polynomial_put, GB_VARIANT value; GB_INTEGER index)
int index = VARG(index);
GB_VALUE *value = (GB_VALUE *)ARG(value);
int type;
COMPLEX_VALUE cv;
if (index < 0 || index > 65535)
{
GB.Error(GB_ERR_ARG);
return;
}
type = COMPLEX_get_value(value, &cv);
if (type == CGV_ERR)
return;
ensure_size(THIS, index + 1);
if (type == CGV_COMPLEX)
{
ensure_complex(THIS);
CDATA(THIS)[index] = cv.z;
}
else
{
if (COMPLEX(THIS))
CDATA(THIS)[index] = cv.z;
else
DATA(THIS)[index] = cv.x;
}
END_METHOD
BEGIN_METHOD(Polynomial_Eval, GB_VARIANT value)
GB_VALUE *value = (GB_VALUE *)ARG(value);
int type;
COMPLEX_VALUE cv;
type = COMPLEX_get_value(value, &cv);
if (type == CGV_ERR)
return;
if (COMPLEX(THIS))
{
GB.ReturnObject(COMPLEX_create(gsl_complex_poly_complex_eval(CDATA(THIS), COUNT(THIS), cv.z)));
}
else
{
if (type == CGV_COMPLEX)
GB.ReturnObject(COMPLEX_create(gsl_poly_complex_eval(DATA(THIS), COUNT(THIS), cv.z)));
else
GB.ReturnFloat(gsl_poly_eval(DATA(THIS), COUNT(THIS), cv.x));
}
END_METHOD
BEGIN_METHOD(Polynomial_Solve, GB_BOOLEAN want_croot)
bool want_croot = VARGOPT(want_croot, FALSE);
bool complex = COMPLEX(THIS);
double *data = DATA(THIS);
gsl_complex *cdata = CDATA(THIS);
int nr = 0, i, dg, ret;
GB_ARRAY result;
double r[3];
gsl_complex cr[3];
double *z = NULL;
gsl_poly_complex_workspace *work;
double *root;
CCOMPLEX **croot;
dg = get_degree(THIS) + 1;
if (dg > 2)
{
if (complex && ensure_not_complex(THIS))
{
GB.Error("Cannot solve polynomial with complex coefficients");
return;
}
data = DATA(THIS);
}
switch(dg)
{
case 1:
GB.ReturnNull();
return;
case 2:
nr = 1;
if (complex)
{
cr[0] = gsl_complex_div(gsl_complex_negative(cdata[0]), cdata[1]);
if (!want_croot)
{
if (GSL_IMAG(cr[0]) == 0.0)
r[0] = GSL_REAL(cr[0]);
else
nr = 0;
}
}
else
{
r[0] = -data[0] / data[1];
if (want_croot)
cr[0] = gsl_complex_rect(r[0], 0);
}
break;
case 3:
if (want_croot)
nr = gsl_poly_complex_solve_quadratic(data[2], data[1], data[0], &cr[0], &cr[1]);
else
nr = gsl_poly_solve_quadratic(data[2], data[1], data[0], &r[0], &r[1]);
break;
case 4:
if (data[3] == 1.0)
{
if (want_croot)
nr = gsl_poly_complex_solve_cubic(data[2], data[1], data[0], &cr[0], &cr[1], &cr[2]);
else
nr = gsl_poly_solve_cubic(data[2], data[1], data[0], &r[0], &r[1], &r[2]);
break;
}
default:
work = gsl_poly_complex_workspace_alloc(dg);
GB.Alloc(POINTER(&z), sizeof(double) * (dg - 1) * 2);
ret = gsl_poly_complex_solve(data, dg, work, z);
gsl_poly_complex_workspace_free(work);
if (ret != GSL_SUCCESS)
{
GB.Free(POINTER(&z));
return;
}
if (!want_croot)
{
nr = 0;
for (i = 0; i < (dg - 1); i++)
{
if (z[i * 2 + 1] == 0.0)
nr++;
}
}
else
nr = dg - 1;
}
if (!want_croot)
{
GB.Array.New(&result, GB_T_FLOAT, nr);
if (nr > 0)
root = (double *)GB.Array.Get(result, 0);
if (!z)
{
if (nr >= 1) root[0] = r[0];
if (nr >= 2) root[1] = r[1];
if (nr >= 3) root[2] = r[2];
}
else
{
if (nr > 0)
{
nr = 0;
for (i = 0; i < (dg - 1); i++)
{
if (z[i * 2 + 1] == 0.0)
root[nr++] = z[i * 2];
}
}
GB.Free(POINTER(&z));
}
}
else
{
GB.Array.New(&result, CLASS_Complex, nr);
if (nr > 0)
croot = (CCOMPLEX **)GB.Array.Get(result, 0);
else
croot = NULL;
if (!z)
{
if (nr >= 1) croot[0] = COMPLEX_create(cr[0]);
if (nr >= 2) croot[1] = COMPLEX_create(cr[1]);
if (nr >= 3) croot[2] = COMPLEX_create(cr[2]);
}
else
{
for (i = 0; i < nr; i++)
croot[i] = COMPLEX_create(gsl_complex_rect(z[i * 2], z[i * 2 + 1]));
GB.Free(POINTER(&z));
}
for (i = 0; i < nr; i++)
GB.Ref(croot[i]);
}
GB.ReturnObject(result);
END_METHOD
//---------------------------------------------------------------------------
GB_DESC PolynomialDesc[] =
{
GB_DECLARE("Polynomial", sizeof(CPOLYNOMIAL)),
GB_METHOD("_new", NULL, Polynomial_new, "[(Size)i(Complex)b]"),
GB_METHOD("_free", NULL, Polynomial_free, NULL),
GB_METHOD("ToString", "s", Polynomial_ToString, "[(Local)b]"),
GB_PROPERTY_READ("Degree", "i", Polynomial_Degree),
GB_PROPERTY_READ("Count", "i", Polynomial_Count),
GB_METHOD("_get", "v", Polynomial_get, "(Index)i"),
GB_METHOD("_put", NULL, Polynomial_put, "(Value)v(Index)i"),
GB_METHOD("_call", "v", Polynomial_Eval, "(X)v"),
GB_METHOD("Eval", "v", Polynomial_Eval, "(X)v"),
GB_METHOD("Solve", "Array", Polynomial_Solve, "[(Complex)b]"),
GB_INTERFACE("_operator", &_operator),
GB_INTERFACE("_convert", &POLYNOMIAL_convert),
GB_END_DECLARE
};