e0469073ab
* 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
914 lines
17 KiB
C
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
|
|
};
|