I am an apprentice of C language, trying to learn some stuff here.
I had confronted with a problem, and I wish I could have solved it on my own.
But, I am a short wrapper and quite prone to procrastination, so I need your good support.
As the title explicitly says, I do not know why my topMatches() function is spitting out some random float values, printing out at the beginning and the end of an array of scores[].
Like this,
Output #1
(-0.00000, Lisa Rose),(0.99124, Gene Seymour),(0.92447, Michael Phillips),(0.89341, Claudia Puig),(0.66285, Mick LaSalle),(0.38125, Jack Matthews),(-1.00000, Toby)
Output #2
(107185664961793568883398204719104.00000, Lisa Rose),(0.99124, Gene Seymour),(0.92447, Michael Phillips),(0.89341, Claudia Puig),(0.66285, Mick LaSalle),(0.38125, Jack Matthews),(-1.00000, Toby)
Output #3
(0.99124, Lisa Rose),(0.92447, Gene Seymour),(0.89341, Michael Phillips),(0.66285, Claudia Puig),(0.38125, Mick LaSalle),(-118195603315995709432961818167345152.00000, Jack Matthews),(-1.00000, Toby)
...
The value should be in the range of -1 and 1.
I would really appreciate to see your feedback.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
//data types
struct mInfo
{
char mName[20];
float rating;
};
struct cInfo
{
char name[20];
struct mInfo movi[7];
};
//prototype fxns
typedef double (*sim_fp)(struct cInfo *, const char *, const char *);
double sim_D(struct cInfo *prefs, const char *person1, const char *person2);
void topMatches(sim_fp fp, struct cInfo *prefs, const char *person1, int num);
int cmpFxn (const void * a, const void * b);
void reverseFxn(float arr[], int num);
int main()
{
int num = 7;
struct cInfo critics[num];
critics[0] = (struct cInfo) {"Lisa Rose", {"Lady in the Water", 2.5, "Snakes on a Plane", 3.5, "Just My Luck", 3, "Superman Returns", 3.5, "The Night Listener", 3, "You, Me and Dupree", 2.5}};
critics[1] = (struct cInfo) {"Gene Seymour",{"Lady in the Water", 3, "Snakes on a Plane", 3.5, "Just My Luck", 1.5, "Superman Returns", 5, "The Night Listener", 3, "You, Me and Dupree", 3.5}};
critics[2] = (struct cInfo) {"Michael Phillips",{"Lady in the Water", 2.5, "Snakes on a Plane", 3, "Superman Returns", 3.5, "The Night Listener", 4}};
critics[3] = (struct cInfo) {"Claudia Puig",{"Snakes on a Plane", 3.5, "Just My Luck", 3, "Superman Returns", 4, "The Night Listener", 4.5, "You, Me and Dupree", 2.5}};
critics[4] = (struct cInfo) {"Mick LaSalle",{"Lady in the Water", 3, "Snakes on a Plane", 4, "Just My Luck", 2, "Superman Returns", 3, "The Night Listener", 3, "You, Me and Dupree", 2}};
critics[5] = (struct cInfo) {"Jack Matthews",{"Lady in the Water", 3, "Snakes on a Plane", 4, "Superman Returns", 5, "You, Me and Dupree", 3.5}};
critics[6] = (struct cInfo) {"Toby",{"Snakes on a Plane", 4.5, "Superman Returns", 4, "You, Me and Dupree",1}};
topMatches(sim_D, critics, "Toby", 7);
return 0;
}
double sim_D(struct cInfo *prefs, const char *person1, const char *person2)
{
int i=0;
int x=0;
int next=0;
int p1;
int p2;
float X = 0;
float Y = 0;
float sumSq1 = 0;
float sumSq2 = 0;
float pSum = 0;
float num = 0;
float den = 0;
float Pscore = 0;
int nElements =0;
for (i=0;i<7;i++) {
if(strcmp(prefs[i].name, person1) ==0)
{
p1 = i;
}
else if(strcmp(prefs[i].name, person2) ==0)
{
p2 = i;
}
}
for (x=0;x<7;x++) {
for (next=0;next<7;next++)
{
if (!prefs[p1].movi[x].rating && !prefs[p2].movi[next].rating);
else if (strcmp(prefs[p1].movi[x].mName, prefs[p2].movi[next].mName) == 0)
{
X += prefs[p1].movi[x].rating;
Y += prefs[p2].movi[next].rating;
sumSq1 += pow(prefs[p1].movi[x].rating,2);
sumSq2 += pow(prefs[p2].movi[next].rating,2);
pSum += (prefs[p1].movi[x].rating*prefs[p2].movi[next].rating);
nElements++;
}
}
next = 0;
}
num = pSum-(X*Y/nElements);
den=sqrt((sumSq1-pow(X,2)/nElements)*(sumSq2-pow(Y,2)/nElements));
if(den ==0) return -1;
Pscore = num/den;
return Pscore;
}
void topMatches(sim_fp fp, struct cInfo *prefs, const char *person1, int num)
{
float scores[8];
char *buf;
int i=0;
for (i=0;i<num;i++)
{
if(strcmp(person1, prefs[i].name)==0)
{
continue;
}
scores[i] = fp(prefs, person1, prefs[i].name);
}
qsort(scores, num, sizeof(float), (*cmpFxn));
reverseFxn(scores, num);
printf("\n\n");
for(i=0;i<num;i++)
{
if (i == num-1)
{
printf("(%.5f, %s)", scores[num-1], prefs[num-1].name);
}
else
{
printf("(%.5f, %s),", scores[i], prefs[i].name);
}
}
}
void reverseFxn(float arr[], int num)
{
float scoresTmp[num];
int j;
for(j=0;j<num;j++)
{
scoresTmp[num-1-j] = arr[j];
}
for(j=0;j<num;j++)
{
arr[j]=scoresTmp[j];
}
}
int cmpFxn (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
The value of each elements of the scores[] array needs to be within the range of -1 and 1.
At least these issues:
Wrong compare function
qsort() is called with an array of float and cmpFxn, yet cmpFxn() compares int. *1
Need to cast to float. *2
// return (*(int*)a - *(int*)b )
return (*(float*)a > *(float*)b ) - (*(float*)a < *(float*)b );
No not use return (*(float*)a - *(float*)b ) as the float difference may overflow int range or truncate to 0.
Code risks sqrt(some_negative)
Watch out for slight floating point artifacts that result in a negative that mathematically should never be less than 0.
// den=sqrt((sumSq1-pow(X,2)/nElements)*(sumSq2-pow(Y,2)/nElements));
t = (sumSq1-pow(X,2)/nElements)*(sumSq2-pow(Y,2)/nElements);
den = t >= 0.0 ? sqrt(t) : 0;
float vs. double
Code is casual about mixing float and double objects and functions. Suggest double only.
For debug purposes, easy enough to use #define float double to temporarily do this.
*1
cmpFxn() is also a weak compare function for int too as it risks int overflow.
// return ( *(int*)a - *(int*)b );
return (*(int*)a > *(int*)b ) - (*(int*)a < *(int*)b );
*2
If not-a-numbers are involved, the compare is more complicated. Let us assume, for now, that is not the case.
Unexpected variable output is a hint that uninitialized data is being used. In struct cInfo you have a member struct mInfo movi[7]; but assign between 3 and 6 of these in main(). By switching from assignment to initialization those records that are not explicit set will be zero initialized. This seems to be sufficient to get consistent output.
int main() {
struct cInfo critics[] = {
{"Lisa Rose", {
{"Lady in the Water", 2.5},
{"Snakes on a Plane", 3.5},
{"Just My Luck", 3},
{"Superman Returns", 3.5},
{"The Night Listener", 3},
{"You, Me and Dupree", 2.5}
}},
{"Gene Seymour", {
{"Lady in the Water", 3},
{"Snakes on a Plane", 3.5},
{"Just My Luck", 1.5},
{"Superman Returns", 5},
{"The Night Listener", 3},
{"You, Me and Dupree", 3.5}
}},
{"Michael Phillips", {
{"Lady in the Water", 2.5},
{"Snakes on a Plane", 3},
{"Superman Returns", 3.5},
{"The Night Listener", 4}
}},
{"Claudia Puig", {
{"Snakes on a Plane", 3.5},
{"Just My Luck", 3},
{"Superman Returns", 4},
{"The Night Listener", 4.5},
{"You, Me and Dupree", 2.5}
}},
{"Mick LaSalle", {
{"Lady in the Water", 3},
{"Snakes on a Plane", 4},
{"Just My Luck", 2},
{"Superman Returns", 3},
{"The Night Listener", 3},
{"You, Me and Dupree", 2}
}},
{"Jack Matthews", {
{"Lady in the Water", 3},
{"Snakes on a Plane", 4},
{"Superman Returns", 5},
{"You, Me and Dupree", 3.5}
}},
{"Toby", {
{"Snakes on a Plane", 4.5},
{"Superman Returns", 4},
{"You, Me and Dupree",1}
}}
};
topMatches(sim_D, critics, "Toby", sizeof critics / sizeof *critics);
}
and here is example output:
(0.99124, Lisa Rose),(0.92447, Gene Seymour),(0.89341, Michael Phillips),(0.66285, Claudia Puig),(0.38125, Mick LaSalle),(0.00000, Jack Matthews),(-1.00000, Toby)
(not fixed) Consider rewriting the unusual if expression with an empty body:
if (!prefs[p1].movi[x].rating && !prefs[p2].movi[next].rating);
else if (strcmp(prefs[p1].movi[x].mName, prefs[p2].movi[next].mName) == 0) {
to:
if (
(prefs[p1].movi[x].rating || prefs[p2].movi[next].rating) &&
!strcmp(prefs[p1].movi[x].mName, prefs[p2].movi[next].mName)
) {
If you introduce a couple of vanity variables it could be written a more compactly.
Replace float with double and fix the sort function:
int cmpFxn (const void * a, const void * b) {
double a2 = *(double *) a;
double b2 = *(double *) b;
if(a2 < b2) return -1;
if(a2 > b2) return 1;
return 0;
}
valgrind reports use of uninitialized data in topMatches specially double scores[8];. You only iterate over num which is 7, and you don't set scores[i] for the entry that corresponds to person1 so I suggest you do:
double scores[num];
memset(scores, 0, sizeof(scores));
(not fixed) Audit your code for magic values, like 7 and 20. Use constants, pass in an argument or sizeof a / sizeof *a if you need the size of an array (be careful arrays degrade to pointers when passed as arguments).
Related
I'm trying to write a function that takes in position data, an array and a counter that stores number of elements in that array and populates that array with new position data and increments the counter. And since I need this array outside of the function, I'm dynamically allocating memory using realloc.
Code
I have a struct to store co-ordinate data
typedef struct CoOrdinates
{
int x;
int y;
} CoOrdinates;
and a function that draws a box at any given position
void drawBox(WINDOW* inWindow,
int aAtPositionX,
int aAtPositionY,
int* aOccupiedCoordinateCount,
CoOrdinates** aOccupiedCoordinates)
{
int newCount = (*aOccupiedCoordinateCount) + 1;
size_t sizeResized = sizeof(**aOccupiedCoordinates) * newCount;
*aOccupiedCoordinates = realloc(*aOccupiedCoordinates, sizeResized);
if (*aOccupiedCoordinates == NULL)
{
fprintf(stderr, "Failed to allocate CoOrdinate array at %d in %s.", __LINE__, __FILE__);
exit(-1);
}
else
{
(*aOccupiedCoordinates)[newCount].x = aAtPositionX;
(*aOccupiedCoordinates)[newCount].y = aAtPositionY;
*aOccupiedCoordinateCount = newCount;
mvwprintw(inWindow, aAtPositionY, aAtPositionX, "██");
}
}
where mvwprintw and WINDOW* are defined in ncurses.
Another function is drawTwoVerticleBoxes defined like this
void drawTwoVerticleBoxes(WINDOW* inWindow,
int aAtPositionX,
int aAtPositionY,
int* aOccupiedCoordinateCount,
CoOrdinates** aOccupiedCoordinates)
{
drawBox(inWindow, aAtPositionX, aAtPositionY, aOccupiedCoordinateCount, aOccupiedCoordinates);
drawBox(inWindow, aAtPositionX, aAtPositionY + 1, aOccupiedCoordinateCount, aOccupiedCoordinates);
}
and a function called drawWithColor
void drawWithColor(WINDOW* inWindow,
int aAtPositionX,
int aAtPositionY,
int* aOccupiedCoordinateCount,
CoOrdinates** aOccupiedCoordinates)
{
init_pair(1, COLOR_GREEN, COLOR_BLACK);
wattron(inWindow, COLOR_PAIR(1));
drawTwoVerticleBoxes(
inWindow, aAtPositionX, aAtPositionY, aOccupiedCoordinateCount, aOccupiedCoordinates);
wattroff(inWindow, COLOR_PAIR(1));
Finally all of those are called in renderRoutine
void renderRoutine()
{
...
...
int occupiedCoordinateCount = 0;
CoOrdinates* occupiedCoordinates = NULL;
drawWithColor(windowPointer, 4, 4, &occupiedCoordinateCount, &occupiedCoordinates);
wgetch(windowPointer);
endwin();
for (int i = 0; i < occupiedCoordinateCount; ++i)
{
printf("X: %d, Y: %d \n", occupiedCoordinates[i].x, occupiedCoordinates[i].y);
free(occupiedCoordinates);
}
}
Summery
The idea is, every time a box is drawn, occupiedCoordinates will store CoOrdinate of that position. occupiedCoordinateCount will store how many items occupiedCoordinates have.
Errors
Expected results
X: 4, Y: 4
X: 4, Y: 5
Actual results
X: 0, Y: 0
X: 504864784, Y: 22033
free(): double free detected in tcache 2
Tweaks
If I define a function say drawFiveHorizontalBoxes
void drawThreeHorizontalBoxes(WINDOW* inWindow,
int aAtPositionX,
int aAtPositionY,
int* aOccupiedCoordinateCount,
CoOrdinates** aOccupiedCoordinates)
{
drawBox(inWindow, aAtPositionX, aAtPositionY, aOccupiedCoordinateCount, aOccupiedCoordinates);
drawBox(inWindow, aAtPositionX + 1, aAtPositionY, aOccupiedCoordinateCount, aOccupiedCoordinates);
drawBox(inWindow, aAtPositionX + 2, aAtPositionY, aOccupiedCoordinateCount, aOccupiedCoordinates);
drawBox(inWindow, aAtPositionX + 3, aAtPositionY, aOccupiedCoordinateCount, aOccupiedCoordinates);
drawBox(inWindow, aAtPositionX + 4, aAtPositionY, aOccupiedCoordinateCount, aOccupiedCoordinates);
}
Expected results
X: 4, Y: 4
X: 5, Y: 4
X: 6, Y: 4
X: 7, Y: 4
X: 8, Y: 4
Actual results
realloc(): Invalid next size
core dumped
Edit
(*aOccupiedCoordinates)[*aOccupiedCoordinateCount].x = aAtPositionX;
(*aOccupiedCoordinates)[*aOccupiedCoordinateCount].y = aAtPositionY;
or
(*aOccupiedCoordinates)[newCount - 1].x = aAtPositionX;
(*aOccupiedCoordinates)[newCount - 1].y = aAtPositionY;
shows
X: 4, Y: 4
X: -1300275184, Y: 21961
free(): double free detected in tcache 2
in drawThreeHorizontalBoxes.
One problem is this:
(*aOccupiedCoordinates)[newCount].x = aAtPositionX;
(*aOccupiedCoordinates)[newCount].y = aAtPositionY;
Here newCount is the size of the "array", which will be one out of bounds of the allocated memory.
Use the old counter *aOccupiedCoordinateCount instead (or subtract one from newCount in the index).
A second problem is that you free the memory inside the loop where you print the values:
for (int i = 0; i < occupiedCoordinateCount; ++i)
{
printf("X: %d, Y: %d \n", occupiedCoordinates[i].x, occupiedCoordinates[i].y);
free(occupiedCoordinates);
}
The first iteration will free the memory, so each other iteration will use memory that isn't owned by you anymore, and try to free it again.
The free call should be outside of the loop.
I have to initialize an structure using a point to it, in a way that upper_left is the point (10,25) and lower_right(20,15)
struct point {int x, y;};
struct rectangle {struct point upper_left, lower_right;};
struct rectangle *p;
p = malloc(sizeof(*p));
p->upper_left.x = 10;
p->upper_left.y = 25;
p->lower_right.x = 20;
p->lower_right.y = 15;
Is there any way to do it more "compact" and not one by one? I've tried this but the errors from the compiler are the same "Expected expression before '{' token"
p->upper_left = {10, 25};
p->lower_right = {20,15};
/////////////
p->upper_left = {.x = 10, .y = 25};
p->lower_right = {.x = 20, .y = 15};
//////////////////////////////
*p = {.upper_left = {10, 25}, .lower_right = {20, 15}};
Yes compound literal
p->upper_left = (struct point){10, 25};
*p = (struct rectangle){.upper_left = {10, 25}, .lower_right = {20, 15}};
you can experiment yourself here: https://godbolt.org/z/yw_JC0
(Disclaimer: I am terrible with math and am coming from JavaScript, so I apologize for any inaccuracies and will do my best to correct them.)
The example on Rosetta Code shows how to calculate coefficients using gsl. Here is the code:
polifitgsl.h:
#ifndef _POLIFITGSL_H
#define _POLIFITGSL_H
#include <gsl/gsl_multifit.h>
#include <stdbool.h>
#include <math.h>
bool polynomialfit(int obs, int degree,
double *dx, double *dy, double *store); /* n, p */
#endif
polifitgsl.cpp:
#include "polifitgsl.h"
bool polynomialfit(int obs, int degree,
double *dx, double *dy, double *store) /* n, p */
{
gsl_multifit_linear_workspace *ws;
gsl_matrix *cov, *X;
gsl_vector *y, *c;
double chisq;
int i, j;
X = gsl_matrix_alloc(obs, degree);
y = gsl_vector_alloc(obs);
c = gsl_vector_alloc(degree);
cov = gsl_matrix_alloc(degree, degree);
for(i=0; i < obs; i++) {
for(j=0; j < degree; j++) {
gsl_matrix_set(X, i, j, pow(dx[i], j));
}
gsl_vector_set(y, i, dy[i]);
}
ws = gsl_multifit_linear_alloc(obs, degree);
gsl_multifit_linear(X, y, c, cov, &chisq, ws);
/* store result ... */
for(i=0; i < degree; i++)
{
store[i] = gsl_vector_get(c, i);
}
gsl_multifit_linear_free(ws);
gsl_matrix_free(X);
gsl_matrix_free(cov);
gsl_vector_free(y);
gsl_vector_free(c);
return true; /* we do not "analyse" the result (cov matrix mainly)
to know if the fit is "good" */
}
main.cpp (note I've replaced the sample numbers for x any y with my own):
#include <stdio.h>
#include "polifitgsl.h"
#define NP 11
double x[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
double y[] = {98.02, 98.01, 98.01, 98.02, 97.98, 97.97, 97.96, 97.94, 97.96, 97.96, 97.97, 97.97, 97.94, 97.94, 97.94, 97.92, 97.96, 97.9, 97.85, 97.9};
#define DEGREE 3
double coeff[DEGREE];
int main()
{
int i;
polynomialfit(NP, DEGREE, x, y, coeff);
for(i=0; i < DEGREE; i++) {
printf("%lf\n", coeff[i]);
}
return 0;
}
And here is the output:
98.030909
-0.016182
0.000909
So that gives me the coefficients. But what I really want is the actual fitted points. In JavaScript, I've used the regression package to calculate the points:
var regression = require('regression');
var calculateRegression = function(values, degree) {
var data = [];
var regressionOutput;
var valuesCount = values.length;
var i = 0;
// Format the data in a way the regression library expects.
for (i = 0; i < valuesCount; i++) {
data[i] = [i, values[i]];
}
// Use the library to calculate the regression.
regressionOutput = regression('polynomial', data, degree);
return regressionOutput;
};
var y = [98.02, 98.01, 98.01, 98.02, 97.98, 97.97, 97.96, 97.94, 97.96, 97.96, 97.97, 97.97, 97.94, 97.94, 97.94, 97.92, 97.96, 97.9, 97.85, 97.9];
console.log(calculateRegression(y, 3));
Which produces:
{ equation:
[ 98.02987916431594,
-0.017378390369880512,
0.0015748071645344357,
-0.00005721503635571101 ],
points:
[ [ 0, 98.02987916431594 ],
[ 1, 98.01401836607424 ],
[ 2, 98.00096389194348 ],
[ 3, 97.9903724517055 ],
[ 4, 97.98190075514219 ],
[ 5, 97.97520551203543 ],
[ 6, 97.96994343216707 ],
[ 7, 97.96577122531896 ],
[ 8, 97.96234560127297 ],
[ 9, 97.959323269811 ],
[ 10, 97.95636094071487 ],
[ 11, 97.95311532376647 ],
[ 12, 97.94924312874768 ],
[ 13, 97.94440106544033 ],
[ 14, 97.93824584362629 ],
[ 15, 97.93043417308745 ],
[ 16, 97.92062276360569 ],
[ 17, 97.90846832496283 ],
[ 18, 97.89362756694074 ],
[ 19, 97.87575719932133 ] ],
string: 'y = 0x^3 + 0x^2 + -0.02x + 98.03' }
(Note there are floating point issues in JavaScript, so the numbers aren't perfectly exact.)
points here is what I'm wanting to generate using gsl. Is there a way I can do this?
Chad, if I understand what you are needing, you simply need to compute the values based on the polynomial equation using the coefficients you found with your polynomialfit function. After you compute the coefficients, you can find the value y for any x (for DEGREE = 3) with the equation:
y = x^2*(coeff2) + x*(coeff1) + coeff0
or in C-syntax
y = x*x*coeff[2] + x*coeff[1] + coeff[0];
You can modify your main.cpp as follows (you should rename both main.cpp to main.c and polyfitgsl.cpp to polyfitgsl.c -- as they are both C-source files, not C++)
#include <stdio.h>
#include "polifitgsl.h"
#define NP 20
double x[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
double y[] = { 98.02, 98.01, 98.01, 98.02, 97.98,
97.97, 97.96, 97.94, 97.96, 97.96,
97.97, 97.97, 97.94, 97.94, 97.94,
97.92, 97.96, 97.9, 97.85, 97.9 };
#define DEGREE 3
double coeff[DEGREE];
int main (void)
{
int i;
polynomialfit (NP, DEGREE, x, y, coeff);
printf ("\n polynomial coefficients:\n\n");
for (i = 0; i < DEGREE; i++) {
printf (" coeff[%d] : %11.7lf\n", i, coeff[i]);
}
putchar ('\n');
printf (" computed values:\n\n x y\n");
for (i = 0; i < (int)(sizeof x/sizeof *x); i++)
printf (" %2d, %11.7lf\n", i,
i*i*coeff[2] + i*coeff[1] + coeff[0]);
putchar ('\n');
return 0;
}
Which provides the following output:
Example Use/Output
$ ./bin/main
polynomial coefficients:
coeff[0] : 98.0132468
coeff[1] : -0.0053003
coeff[2] : -0.0000558
computed values:
x y
0, 98.0132468
1, 98.0078906
2, 98.0024229
3, 97.9968435
4, 97.9911524
5, 97.9853497
6, 97.9794354
7, 97.9734094
8, 97.9672718
9, 97.9610226
10, 97.9546617
11, 97.9481891
12, 97.9416049
13, 97.9349091
14, 97.9281016
15, 97.9211825
16, 97.9141517
17, 97.9070093
18, 97.8997553
19, 97.8923896
That seems like what you are asking for. Look it over, compare your values and let me know if you need additional help.
Cleaning Up A Bit Further
Just to clean the code up a bit further, since there is no need for global declaration of x, y, or coeff, and using an enum to define the constants DEGREE and NP, a tidier version would be:
#include <stdio.h>
#include "polifitgsl.h"
enum { DEGREE = 3, NP = 20 };
int main (void)
{
int i;
double x[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
double y[] = { 98.02, 98.01, 98.01, 98.02, 97.98,
97.97, 97.96, 97.94, 97.96, 97.96,
97.97, 97.97, 97.94, 97.94, 97.94,
97.92, 97.96, 97.9, 97.85, 97.9 };
double coeff[DEGREE] = {0};
polynomialfit (NP, DEGREE, x, y, coeff);
printf ("\n polynomial coefficients:\n\n");
for (i = 0; i < DEGREE; i++)
printf (" coeff[%d] : %11.7lf\n", i, coeff[i]);
putchar ('\n');
printf (" computed values:\n\n x y\n");
for (i = 0; i < (int)(sizeof x/sizeof *x); i++)
printf (" %2d, %11.7lf\n", i,
i*i*coeff[2] + i*coeff[1] + coeff[0]);
putchar ('\n');
return 0;
}
I had the same problem and have taken the answers above and added a solution for direct calculation (without gsl) taken from http://www.cplusplus.com/forum/general/181580/
Below, you find a standalone test program with a gsl-based and a direct calculation solution.
I have done some profiling runs and the performance of direct calculation is an impressive 65 times higher on my system, 5.22s vs. 0.08s for 1000 calls to the respective functions.
As a side note, the direct calculation uses Cramer's rule, so you should be careful with ill conditioned data. Normally I would avoid Cramer's but using a full linear system solver for a 3x3 system seems overkill to me.
/*
* =====================================================================================
*
* Filename: polyfit.cpp
*
* Description: Least squares fit of second order polynomials
* Test program using gsl and direct calculation
* Version: 1.0
* Created: 2017-07-17 09:32:55
* Compiler: gcc
*
* Author: Bernhard Brunner, brb_blog#epr.ch
* References: This code was merged, adapted and optimized from these two sources:
* http://www.cplusplus.com/forum/general/181580/
* https://stackoverflow.com/questions/36522882/how-can-i-use-gsl-to-calculate-polynomial-regression-data-points
* http://brb.epr.ch/blog/blog:least_squares_regression_of_parabola
* Build: compile and link using
* g++ -c -o polifitgsl.o polifitgsl.cpp
* gcc polifitgsl.o -lgsl -lm -lblas -o polifitgsl
*
* Profiling:
* valgrind --tool=callgrind ./polifitgsl
* kcachegrind
*
* polynomialfit takes 5.22s for 1000 calls
* findQuadCoefficients takes 0.08s for 1000 calls
* 65x faster
* =====================================================================================
*/
#include <stdio.h>
#include <gsl/gsl_multifit.h>
#include <stdbool.h>
#include <math.h>
bool polynomialfit(int obs, int degree,
double *dx, double *dy, double *store); /* n, p */
double x[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
double y[] = { 98.02, 98.01, 98.01, 98.02, 97.98,
97.97, 97.96, 97.94, 97.96, 97.96,
97.97, 97.97, 97.94, 97.94, 97.94,
97.92, 97.96, 97.9, 97.85, 97.9 };
#define NP (sizeof(x)/sizeof(double)) // 20
#define DEGREE 3
double coeff[DEGREE];
bool findQuadCoefficients(double timeArray[], double valueArray[], double *coef, double &critPoint, int PointsNum){
const double S00=PointsNum;//points number
double S40=0, S10=0, S20=0, S30=0, S01=0, S11=0, S21 = 0;
// const double MINvalue = valueArray[0];
// const double MINtime = timeArray[0];
for (int i=0; i<PointsNum; i++ ){
double value = valueArray[i]; // - MINvalue); //normalizing
// cout << "i=" << i << " index=" << index << " value=" << value << endl;
int index = timeArray[i]; // - MINtime;
int index2 = index * index;
int index3 = index2 * index;
int index4 = index3 * index;
S40+= index4;
S30+= index3;
S20+= index2;
S10+= index;
S01 += value;
S11 += value*index;
S21 += value*index2;
}
double S20squared = S20*S20;
//minors M_ij=M_ji
double M11 = S20*S00 - S10*S10;
double M21 = S30*S00 - S20*S10;
double M22 = S40*S00 - S20squared;
double M31 = S30*S10 - S20squared;
double M32 = S40*S10 - S20*S30;
// double M33 = S40*S20 - pow(S30,2);
double discriminant = S40*M11 - S30*M21 + S20*M31;
// printf("discriminant :%lf\n", discriminant);
if (abs(discriminant) < .00000000001) return false;
double Da = S21*M11
-S11*M21
+S01*M31;
coef[2] = Da/discriminant;
// cout << "discriminant=" << discriminant;
// cout << " Da=" << Da;
double Db = -S21*M21
+S11*M22
-S01*M32;
coef[1] = Db/discriminant;
// cout << " Db=" << Db << endl;
double Dc = S40*(S20*S01 - S10*S11)
- S30*(S30*S01 - S10*S21)
+ S20*(S30*S11 - S20*S21);
coef[0] = Dc/discriminant;
// printf("c=%lf\n", c);
critPoint = -Db/(2*Da); // + MINtime; //-b/(2*a)= -Db/discriminant / (2*(Da/discriminant)) = -Db/(2*Da);
return true;
}
bool polynomialfit(int obs, int degree,
double *dx, double *dy, double *store) /* n, p */
{
gsl_multifit_linear_workspace *ws;
gsl_matrix *cov, *X;
gsl_vector *y, *c;
double chisq;
int i, j;
X = gsl_matrix_alloc(obs, degree);
y = gsl_vector_alloc(obs);
c = gsl_vector_alloc(degree);
cov = gsl_matrix_alloc(degree, degree);
for(i=0; i < obs; i++) {
for(j=0; j < degree; j++) {
gsl_matrix_set(X, i, j, pow(dx[i], j));
}
gsl_vector_set(y, i, dy[i]);
}
ws = gsl_multifit_linear_alloc(obs, degree);
gsl_multifit_linear(X, y, c, cov, &chisq, ws);
/* store result ... */
for(i=0; i < degree; i++) {
store[i] = gsl_vector_get(c, i);
}
gsl_multifit_linear_free(ws);
gsl_matrix_free(X);
gsl_matrix_free(cov);
gsl_vector_free(y);
gsl_vector_free(c);
return true; /* we do not "analyse" the result (cov matrix mainly)
to know if the fit is "good" */
}
void testcoeff(double *coeff)
{
printf ("\n polynomial coefficients\n");
for (int i = 0; i < DEGREE; i++) {
printf (" coeff[%d] : %11.7lf\n", i, coeff[i]);
}
putchar ('\n');
printf (" computed values:\n\n x, yi, yip\n");
for (unsigned i = 0; i < NP; i++) {
printf ("%2u,%.7lf,%.7lf\n", i,
y[i],
i*i*coeff[2] + i*coeff[1] + coeff[0]);
}
putchar ('\n');
}
int main (void)
{
#define ITER 1000
for (int i=0; i< ITER; i++) {
polynomialfit (NP, DEGREE, x, y, coeff);
}
testcoeff(coeff);
double sx;
for (int i=0; i< ITER; i++) {
findQuadCoefficients(x, y, coeff, sx, NP);
}
printf("critical point %lf\n", sx);
testcoeff(coeff);
return 0;
}
References:
http://www.cplusplus.com/forum/general/181580/
http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html
I have the part of the following C code that uses data from a file names WMM.COF and uses the data stored in the file to compute the magnetic field of the earth. The program works perfectly except I can't have the program access the external file; I want to have all of the data already stored in the program. I tried using a structure array to replicate the data and then put the array into a string but this causes an error in the program and doesn't produce the correct results. Here is the code of the program that I'm trying to modify.
static void E0000(int IENTRY, int *maxdeg, double alt, double glat, double glon, double time, double *dec, double *dip, double *ti, double *gv)
{
static int maxord,i,icomp,n,m,j,D1,D2,D3,D4;
static double c[13][13],cd[13][13],tc[13][13],dp[13][13],snorm[169],
sp[13],cp[13],fn[13],fm[13],pp[13],k[13][13],pi,dtr,a,b,re,
a2,b2,c2,a4,b4,c4,epoch,gnm,hnm,dgnm,dhnm,flnmj,otime,oalt,
olat,olon,dt,rlon,rlat,srlon,srlat,crlon,crlat,srlat2,
crlat2,q,q1,q2,ct,st,r2,r,d,ca,sa,aor,ar,br,bt,bp,bpp,
par,temp1,temp2,parp,bx,by,bz,bh;
static char model[20], c_str[81], c_new[5];
static double *p = snorm;
char answer;
FILE *wmmdat;
wmmdat = fopen("WMM.COF","r");
/* INITIALIZE CONSTANTS */
maxord = *maxdeg;
sp[0] = 0.0;
cp[0] = *p = pp[0] = 1.0;
dp[0][0] = 0.0;
a = 6378.137;
b = 6356.7523142;
re = 6371.2;
a2 = a*a;
b2 = b*b;
c2 = a2-b2;
a4 = a2*a2;
b4 = b2*b2;
c4 = a4 - b4;
/* READ WORLD MAGNETIC MODEL SPHERICAL HARMONIC COEFFICIENTS */
c[0][0] = 0.0;
cd[0][0] = 0.0;
fgets(c_str, 80, wmmdat);
S3:
if (fgets(c_str, 80, wmmdat) == NULL) goto S4;
/* CHECK FOR LAST LINE IN FILE */
for (i=0; i<4 && (c_str[i] != '\0'); i++)
{
c_new[i] = c_str[i];
c_new[i+1] = '\0';
}
icomp = strcmp("9999", c_new);
if (icomp == 0) goto S4;
/* END OF FILE NOT ENCOUNTERED, GET VALUES */
sscanf(c_str,"%d%d%lf%lf%lf%lf",&n,&m,&gnm,&hnm,&dgnm,&dhnm);
if (n > maxord) goto S4;
if (m > n || m < 0.0)
{
fprintf(stderr, "Corrupt record in model file WMM.COF\n");
exit(1);
}
if (m <= n)
{
c[m][n] = gnm;
cd[m][n] = dgnm;
if (m != 0)
{
c[n][m-1] = hnm;
cd[n][m-1] = dhnm;
}
}
goto S3;
/* CONVERT SCHMIDT NORMALIZED GAUSS COEFFICIENTS TO UNNORMALIZED */
S4:
*snorm = 1.0;
fm[0] = 0.0;
for (n=1; n<=maxord; n++)
{
*(snorm+n) = *(snorm+n-1)*(double)(2*n-1)/(double)n;
j = 2;
for (m=0,D1=1,D2=(n-m+D1)/D1; D2>0; D2--,m+=D1)
{
k[m][n] = (double)(((n-1)*(n-1))-(m*m))/(double)((2*n-1)*(2*n-3));
if (m > 0)
{
flnmj = (double)((n-m+1)*j)/(double)(n+m);
*(snorm+n+m*13) = *(snorm+n+(m-1)*13)*sqrt(flnmj);
j = 1;
c[n][m-1] = *(snorm+n+m*13)*c[n][m-1];
cd[n][m-1] = *(snorm+n+m*13)*cd[n][m-1];
}
c[m][n] = *(snorm+n+m*13)*c[m][n];
cd[m][n] = *(snorm+n+m*13)*cd[m][n];
}
fn[n] = (double)(n+1);
fm[n] = (double)n;
}
k[1][1] = 0.0;
otime = oalt = olat = olon = -1000.0;
fclose(wmmdat);
return;
The code that I came up with to include the data in the program is as follows:
struct wmm
{
int alpha;
int beta;
float gamma;
float delta;
float epsilon;
float zeta;
}book[90]= {{1, 0, -29496.6, 0.0, 11.6, 0.0},
{1, 1, -1586.3, 4944.4, 16.5, -25.9},
{2, 0, -2396.6, 0.0, -12.1, 0.0},
{2, 1, 3026.1, -2707.7, -4.4, -22.5},
{2, 2, 1668.6, -576.1, 1.9, -11.8},
{3, 0, 1340.1, 0.0, 0.4, 0.0},
/* 50+ similar lines of code */
{12, 8, -0.4, 0.1, 0.0, 0.0},
{12, 9, -0.4, 0.3, 0.0, 0.0},
{12, 10, 0.2, -0.9, 0.0, 0.0},
{12, 11, -0.8, -0.2, -0.1, 0.0},
{12, 12, 0.0, 0.9, 0.1, 0.0}};
for (i = 0; i < 90 && offset < buf_size; i++)
{
offset += snprintf(c_str + offset,buf_size - offset, "%d %d %7.1lf %7.1lf %7.1lf %7.1lf \n", book[i].alpha, book[i].beta , book[i].gamma , book[i].delta, book[i].epsilon, book[i].zeta);
}
sscanf(c_str,"%d%d%lf%lf%lf%lf",&n,&m,&gnm,&hnm,&dgnm,&dhnm);
The problem is the snprintf causes the program to freeze and terminate every time it is placed in the program. When the code that I wrote is run on it's own it seems to create c_str properly except when I try to view the variables n,m,gnm,hnm,dgnm, and dhnm only a single value for each is displayed.
I need to continue in an answer due to a lack of space/formatting in a comment.
First of all, you do have 90 entries but you can let the compiler figure out how many entries the book array needs:
struct wmm {
int alpha;
int beta;
float gamma;
float delta;
float epsilon;
float zeta;
} book[] = {
{1, 0, -29496.6, 0.0, 11.6, 0.0},
{1, 1, -1586.3, 4944.4, 16.5, -25.9},
/* ... */
{12, 12, 0.0, 0.9, 0.1, 0.0}
};
And, more importantly, you don't need to put them in a string and pull them back out when you already have them on hand:
for(i = 0; i < sizeof(book)/sizeof(book[0]); ++i) {
n = book[i].alpha;
m = book[i].beta;
gnm = book[i].gamma;
hnm = book[i].delta;
dgnm = book[i].epsilon;
dhnm = book[i].zeta;
/* Do whatever you need to do with the above variables. */
}
This will neatly side step whatever buffer overflow you were causing with your snprintf.
Your c_str is only a char[81] and you're going through your loop 90 times and incrementing your offset into c_str each time; so, you'll run off the end of c_str before long and then you'll tell snprintf to scribble all over unallocated memory. Hence your segfault.
How can I initialize this nested struct in C?
typedef struct _s0 {
int size;
double * elems;
}StructInner ;
typedef struct _s1 {
StructInner a, b, c, d, e;
long f;
char[16] s;
}StructOuter; StructOuter myvar = {/* what ? */ };
To initialize everything to 0 (of the right kind)
StructOuter myvar = {0};
To initialize the members to a specific value
StructOuter myvar = {{0, NULL}, {0, NULL}, {0, NULL},
{0, NULL}, {0, NULL}, 42.0, "foo"};
/* that's {a, b, c, d, e, f, s} */
/* where each of a, b, c, d, e is {size, elems} */
Edit
If you have a C99 compiler, you can also use "designated initializers", as in:
StructOuter myvar = {.c = {1000, NULL}, .f = 42.0, .s = "foo"};
/* c, f, and s initialized to specific values */
/* a, b, d, and e will be initialized to 0 (of the right kind) */
To highlight struct labels in particular:
StructInner a = {
.size: 1,
.elems: { 1.0, 2.0 }, /* optional comma */
};
StructOuter b = {
.a = a, /* struct labels start with a dot */
.b = a,
a, /* they are optional and you can mix-and-match */
a,
.e = { /* nested struct initialization */
.size: 1,
.elems: a.elems
},
.f = 1.0,
.s = "Hello", /* optional comma */
};
double a[] = { 1.0, 2.0 };
double b[] = { 1.0, 2.0, 3.0 };
StructOuter myvar = { { 2, a }, { 3, b }, { 2, a }, { 3, b }, { 2, a }, 1, "a" };
It seems a and b cannot be initialized in-place in plain C
Following also works with GCC, C99. GCC doesnt complain about it. I am not sure if this is standard.
double arr[] = { 1.0, 2.0 }; // should be static or global
StructOuter myvar =
{
.f = 42,
.s = "foo",
.a.size = 2,
.a.elems = &arr,
.b.size = 0, // you can explicitly show that it is zero
// missing members will actually be initialized to zero
};