Canola  0.8.D001
lib/polyfit.cc
Go to the documentation of this file.
00001 //
00002 // canola - canon canola 1614p emulator
00003 // Copyright (C) 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License, version 3, as
00007 // published by the Free Software Foundation.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program. If not, see <http://www.gnu.org/licenses/>.
00016 //
00017 
00018 #include <lib/ac/assert.h>
00019 #include <lib/ac/math.h>
00020 #include <lib/ac/stdio.h>
00021 
00022 #include <lib/polyfit.h>
00023 
00024 
00025 polyfit::data_t polyfit::epsilon = 1e-8;
00026 
00027 
00028 polyfit::~polyfit()
00029 {
00030     delete [] coefficients;
00031     coefficients = 0;
00032     mat_free(matrix);
00033     matrix = 0;
00034 }
00035 
00036 
00037 polyfit::polyfit(int order) :
00038     rows(order + 1),
00039     cols(order + 2),
00040     coefficients(0),
00041     matrix(0)
00042 {
00043     assert(order >= 1);
00044     matrix = mat_alloc();
00045 }
00046 
00047 
00048 void
00049 polyfit::enter(double xord, double yord)
00050 {
00051     assert(matrix);
00052     data_t product = 1.0;
00053     for (unsigned row = 0; row < rows; row++)
00054     {
00055         matrix[row][cols - 1] += product * yord;
00056         data_t product2 = product;
00057         for (unsigned col = 0; col < rows; col++)
00058         {
00059             matrix[row][col] += product2;
00060             product2 *= xord;
00061         }
00062         product *= xord;
00063     }
00064 }
00065 
00066 
00067 polyfit::solve_t
00068 polyfit::solve(void)
00069 {
00070     assert(matrix);
00071     if (!enough_points())
00072         return solve_insufficient;
00073 
00074     //
00075     // create and initialise the working area
00076     //
00077     data_t epsilon_magnitude = 0;
00078     data_t **working = mat_alloc();
00079     for (unsigned row = 0; row < rows; row++)
00080     {
00081         for (unsigned col = 0; col < cols; col++)
00082         {
00083             data_t n = matrix[row][col];
00084             data_t nn = fabsl(n);
00085             if (epsilon_magnitude < nn)
00086                 epsilon_magnitude = nn;
00087             working[row][col] = n;
00088         }
00089     }
00090     // double Mantissa is 48 bits
00091     // long double Mantissa is 64 bits
00092     // throw 8 away
00093     epsilon = ldexpl(epsilon_magnitude, -56);
00094     if (epsilon > 1e-5)
00095         epsilon = 1e-5;
00096 
00097 #if 0
00098     printf("epsilon = %g\n", (double)epsilon);
00099     mat_dump(working);
00100 #endif
00101 
00102     //
00103     // reduce the matrix
00104     //
00105     for (unsigned row = 0; row < rows; row++)
00106     {
00107         //
00108         // find largest magnitude to pivot on
00109         //
00110         unsigned pivot_row = row;
00111         data_t pivot_mag = fabsl(working[row][row]);
00112         for (unsigned row2 = row + 1; row2 < rows; ++row2)
00113         {
00114             data_t mag = fabsl(working[row2][row]);
00115             if (mag > pivot_mag)
00116             {
00117                 pivot_row = row2;
00118                 pivot_mag = mag;
00119             }
00120         }
00121         if (pivot_row != row)
00122         {
00123             //
00124             // exchange rows
00125             //
00126             for (unsigned col = row; col < cols; col++)
00127             {
00128                 data_t temp = working[row][col];
00129                 working[row][col] = working[pivot_row][col];
00130                 working[pivot_row][col] = temp;
00131             }
00132         }
00133 
00134         if (fabsl(working[row][row]) < epsilon)
00135         {
00136             //
00137             // singular matrix
00138             //
00139             mat_free(working);
00140             return solve_singular;
00141         }
00142 
00143         //
00144         // get a 1 in the pivot position
00145         //
00146         data_t factor = 1. / working[row][row];
00147         for (unsigned col = row; col < cols; col++)
00148         {
00149             data_t n = working[row][col] * factor;
00150             // if close to an integer, go there
00151             data_t nn = floorl(n + 0.5);
00152             if (fabsl(n - nn) < epsilon)
00153                 n = nn;
00154             working[row][col] = n;
00155         }
00156 
00157         //
00158         // subtract multiple of pivot row from
00159         // all others to get zeros in rest of
00160         // pivot col.
00161         //
00162         for (unsigned row2 = 0; row2 < rows; row2++)
00163         {
00164             // except pivot row
00165             if (row2 != row)
00166             {
00167                 data_t factor2 = working[row2][row];
00168                 for (unsigned col = row; col < cols; col++)
00169                     working[row2][col] -= factor2 * working[row][col];
00170             }
00171         }
00172 #if 0
00173         mat_dump(working);
00174 #endif
00175     }
00176 
00177     //
00178     // create solution space
00179     //
00180     if (!coefficients)
00181         coefficients = new data_t [rows];
00182 
00183     //
00184     // copy solution
00185     //
00186     for (unsigned row = 0; row < rows; row++)
00187         coefficients[row] = working[row][rows];
00188     mat_free(working);
00189     return solve_ok;
00190 }
00191 
00192 
00193 double
00194 polyfit::coefficient(int n)
00195 {
00196     if (!coefficients || n < 0 || (unsigned)n >= rows)
00197         return 0;
00198     return coefficients[n];
00199 }
00200 
00201 
00202 double
00203 polyfit::evaluate(double x)
00204 {
00205     if (!coefficients)
00206         return 0;
00207     double sum = 0;
00208     for (int j = rows - 1; j >= 0; j--)
00209         sum = sum * x + coefficients[j];
00210     double rsum = floorl(sum + 0.5);
00211     if (fabsl(sum - rsum) < epsilon)
00212         sum = rsum;
00213     return sum;
00214 }
00215 
00216 
00217 polyfit::data_t **
00218 polyfit::mat_alloc(void)
00219 {
00220     data_t **result = new data_t * [rows];
00221     result[0] = new data_t [rows * cols];
00222     for (unsigned row = 1; row < rows; ++row)
00223         result[row] = result[row - 1] + cols;
00224     for (unsigned row = 0; row < rows; ++row)
00225         for (unsigned col = 0; col < cols; ++col)
00226             result[row][col] = 0;
00227     return result;
00228 }
00229 
00230 
00231 void
00232 polyfit::mat_dump(data_t **mat)
00233 {
00234     for (unsigned row = 0; row < rows; row++)
00235     {
00236         for (unsigned col = 0; col < cols; col++)
00237             printf("%10.5f", (double)mat[row][col]);
00238         putchar('\n');
00239     }
00240     putchar('\n');
00241 }
00242 
00243 
00244 void
00245 polyfit::mat_free(data_t **mat)
00246 {
00247     if (mat)
00248     {
00249         assert(mat[0]);
00250         delete [] mat[0];
00251         delete [] mat;
00252     }
00253 }
00254 
00255 
00256 void
00257 polyfit::print(void)
00258     const
00259 {
00260     printf("y = ");
00261     for (unsigned j = 0; j < rows; ++j)
00262     {
00263         if (j)
00264             printf(" + ");
00265         printf("%.12g", (double)coefficients[j]);
00266         switch (j)
00267         {
00268         case 0:
00269             break;
00270 
00271         case 1:
00272             printf(" * x");
00273             break;
00274 
00275         default:
00276             printf(" * x**%d", j);
00277             break;
00278         }
00279     }
00280     printf("\n");
00281 }