summaryrefslogtreecommitdiffstats
path: root/kig/misc/kignumerics.cpp
blob: 4711d0583a6b88a3e947a6639320663b2df61a1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/**
 This file is part of Kig, a KDE program for Interactive Geometry...
 Copyright (C) 2002  Maurizio Paolini <paolini@dmf.unicatt.it>

 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 2 of the License, 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
**/

#include "kignumerics.h"
#include "common.h"

/*
 * compute one of the roots of a cubic polynomial
 * if xmin << 0 or xmax >> 0 then autocompute a bound for all the
 * roots
 */

double calcCubicRoot ( double xmin, double xmax, double a,
      double b, double c, double d, int root, bool& valid, int& numroots )
{
  // renormalize: positive a and infinity norm = 1

  double infnorm = fabs(a);
  if ( infnorm < fabs(b) ) infnorm = fabs(b);
  if ( infnorm < fabs(c) ) infnorm = fabs(c);
  if ( infnorm < fabs(d) ) infnorm = fabs(d);
  if ( a < 0 ) infnorm = -infnorm;
  a /= infnorm;
  b /= infnorm;
  c /= infnorm;
  d /= infnorm;

  const double small = 1e-7;
  valid = false;
  if ( fabs(a) < small )
  {
    if ( fabs(b) < small )
    {
      if ( fabs(c) < small )
      { // degree = 0;
	numroots = 0;
	return 0.0;
      }
      // degree = 1
      double rootval = -d/c;
      numroots = 1;
      if ( rootval < xmin || xmax < rootval ) numroots--;
      if ( root > numroots ) return 0.0;
      valid = true;
      return rootval;
    }
    // degree = 2
    if ( b < 0 ) { b = -b; c = -c; d = -d; }
    double discrim = c*c - 4*b*d;
    numroots = 2;
    if ( discrim < 0 )
    {
      numroots = 0;
      return 0.0;
    }
    discrim = sqrt(discrim)/(2*fabs(b));
    double rootmiddle = -c/(2*b);
    if ( rootmiddle - discrim < xmin ) numroots--;
    if ( rootmiddle + discrim > xmax ) numroots--;
    if ( rootmiddle + discrim < xmin ) numroots--;
    if ( rootmiddle - discrim > xmax ) numroots--;
    if ( root > numroots ) return 0.0;
    valid = true;
    if ( root == 2 || rootmiddle - discrim < xmin ) return rootmiddle + discrim;
    return rootmiddle - discrim;
  }

  if ( xmin < -1e8 || xmax > 1e8 )
  {

    // compute a bound for all the real roots:

    xmax = fabs(d/a);
    if ( fabs(c/a) + 1 > xmax ) xmax = fabs(c/a) + 1;
    if ( fabs(b/a) + 1 > xmax ) xmax = fabs(b/a) + 1;
    xmin = -xmax;
  }

  // computing the coefficients of the Sturm sequence
  double p1a = 2*b*b - 6*a*c;
  double p1b = b*c - 9*a*d;
  double p0a = c*p1a*p1a + p1b*(3*a*p1b - 2*b*p1a);

  int varbottom = calcCubicVariations (xmin, a, b, c, d, p1a, p1b, p0a);
  int vartop = calcCubicVariations (xmax, a, b, c, d, p1a, p1b, p0a);
  numroots = vartop - varbottom;
  valid = false;
  if (root <= varbottom || root > vartop ) return 0.0;

  valid = true;

  // now use bisection to separate the required root
  double dx = (xmax - xmin)/2;
  while ( vartop - varbottom > 1 )
  {
    if ( fabs( dx ) < 1e-8 ) return (xmin + xmax)/2;
    double xmiddle = xmin + dx;
    int varmiddle = calcCubicVariations (xmiddle, a, b, c, d, p1a, p1b, p0a);
    if ( varmiddle < root )   // I am below
    {
      xmin = xmiddle;
      varbottom = varmiddle;
    } else {
      xmax = xmiddle;
      vartop = varmiddle;
    }
    dx /= 2;
  }

  /*
   * now [xmin, xmax] enclose a single root, try using Newton
   */
  if ( vartop - varbottom == 1 )
  {
    double fval1 = a;     // double check...
    double fval2 = a;
    fval1 = b + xmin*fval1;
    fval2 = b + xmax*fval2;
    fval1 = c + xmin*fval1;
    fval2 = c + xmax*fval2;
    fval1 = d + xmin*fval1;
    fval2 = d + xmax*fval2;
    assert ( fval1 * fval2 <= 0 );
    return calcCubicRootwithNewton ( xmin, xmax, a, b, c, d, 1e-8 );
  }
  else   // probably a double root here!
    return ( xmin + xmax )/2;
}

/*
 * computation of the number of sign changes in the sturm sequence for
 * a third degree polynomial at x.  This number counts the number of
 * roots of the polynomial on the left of point x.
 *
 * a, b, c, d: coefficients of the third degree polynomial (a*x^3 + ...)
 *
 * the second degree polynomial in the sturm sequence is just minus the
 * derivative, so we don't need to compute it.
 *
 * p1a*x + p1b: is the third (first degree) polynomial in the sturm sequence.
 *
 * p0a: is the (constant) fourth polynomial of the sturm sequence.
 */

int calcCubicVariations (double x, double a, double b, double c,
      double d, double p1a, double p1b, double p0a)
{
  double fval, fpval;
  fval = fpval = a;
  fval = b + x*fval;
  fpval = fval + x*fpval;
  fval = c + x*fval;
  fpval = fval + x*fpval;
  fval = d + x*fval;

  double f1val = p1a*x + p1b;

  bool f3pos = fval >= 0;
  bool f2pos = fpval <= 0;
  bool f1pos = f1val >= 0;
  bool f0pos = p0a >= 0;

  int variations = 0;
  if ( f3pos != f2pos ) variations++;
  if ( f2pos != f1pos ) variations++;
  if ( f1pos != f0pos ) variations++;
  return variations;
}

/*
 * use newton to solve a third degree equation with already isolated
 * root
 */

inline void calcCubicDerivatives ( double x, double a, double b, double c,
      double d, double& fval, double& fpval, double& fppval )
{
  fval = fpval = fppval = a;
  fval = b + x*fval;
  fpval = fval + x*fpval;
  fppval = fpval + x*fppval;   // this is really half the second derivative
  fval = c + x*fval;
  fpval = fval + x*fpval;
  fval = d + x*fval;
}

double calcCubicRootwithNewton ( double xmin, double xmax, double a,
      double b, double c, double d, double tol )
{
  double fval, fpval, fppval;

  double fval1, fval2, fpval1, fpval2, fppval1, fppval2;
  calcCubicDerivatives ( xmin, a, b, c, d, fval1, fpval1, fppval1 );
  calcCubicDerivatives ( xmax, a, b, c, d, fval2, fpval2, fppval2 );
  assert ( fval1 * fval2 <= 0 );

  assert ( xmax > xmin );
  while ( xmax - xmin > tol )
  {
    // compute the values of function, derivative and second derivative:
    assert ( fval1 * fval2 <= 0 );
    if ( fppval1 * fppval2 < 0 || fpval1 * fpval2 < 0 )
    {
      double xmiddle = (xmin + xmax)/2;
      calcCubicDerivatives ( xmiddle, a, b, c, d, fval, fpval, fppval );
      if ( fval1*fval <= 0 )
      {
        xmax = xmiddle;
	fval2 = fval;
	fpval2 = fpval;
	fppval2 = fppval;
      } else {
        xmin = xmiddle;
	fval1 = fval;
	fpval1 = fpval;
	fppval1 = fppval;
      }
    } else
    {
      // now we have first and second derivative of constant sign, we
      // can start with Newton from the Fourier point.
      double x = xmin;
      if ( fval2*fppval2 > 0 ) x = xmax;
      double p = 1.0;
      int iterations = 0;
      while ( fabs(p) > tol && iterations++ < 100 )
      {
        calcCubicDerivatives ( x, a, b, c, d, fval, fpval, fppval );
        p = fval/fpval;
        x -= p;
      }
      if( iterations >= 100 )
      {
        // Newton scheme did not converge..
        // we should end up with an invalid Coordinate
        return double_inf;
      };
      return x;
    }
  }

  // we cannot apply Newton, (perhaps we are at an inflection point)

  return (xmin + xmax)/2;
}

/*
 * This function computes the LU factorization of a mxn matrix, with
 * m typically less then n.  This is done with complete pivoting; the
 * exchanges in columns are recorded in the integer vector "exchange"
 */
bool GaussianElimination( double *matrix[], int numrows,
                          int numcols, int exchange[] )
{
  // start gaussian elimination
  for ( int k = 0; k < numrows; ++k )
  {
    // ricerca elemento di modulo massimo
    double maxval = -double_inf;
    int imax = k;
    int jmax = k;
    for( int i = k; i < numrows; ++i )
    {
      for( int j = k; j < numcols; ++j )
      {
        if (fabs(matrix[i][j]) > maxval)
        {
          maxval = fabs(matrix[i][j]);
          imax = i;
          jmax = j;
        }
      }
    }

    // row exchange
    if ( imax != k )
      for( int j = k; j < numcols; ++j )
      {
        double t = matrix[k][j];
        matrix[k][j] = matrix[imax][j];
        matrix[imax][j] = t;
      }

    // column exchange
    if ( jmax != k )
      for( int i = 0; i < numrows; ++i )
      {
        double t = matrix[i][k];
        matrix[i][k] = matrix[i][jmax];
        matrix[i][jmax] = t;
      }

    // remember this column exchange at step k
    exchange[k] = jmax;

    // we can't usefully eliminate a singular matrix..
    if ( maxval == 0. ) return false;

    // ciclo sulle righe
    for( int i = k+1; i < numrows; ++i)
    {
      double mik = matrix[i][k]/matrix[k][k];
      matrix[i][k] = mik;    //ricorda il moltiplicatore... (not necessary)
      // ciclo sulle colonne
      for( int j = k+1; j < numcols; ++j )
      {
        matrix[i][j] -= mik*matrix[k][j];
      }
    }
  }
  return true;
}

/*
 * solve an undetermined homogeneous triangular system. the matrix is nonzero
 * on its diagonal. The last unknown(s) are chosen to be 1. The
 * vector "exchange" contains exchanges to be performed on the
 * final solution components.
 */

void BackwardSubstitution( double *matrix[], int numrows, int numcols,
        int exchange[], double solution[] )
{
  // the system is homogeneous and underdetermined, the last unknown(s)
  // are chosen = 1
  for ( int j = numrows; j < numcols; ++j )
  {
    solution[j] = 1.0;          // other choices are possible here
  };

  for( int k = numrows - 1; k >= 0; --k )
  {
    // backward substitution
    solution[k] = 0.0;
    for ( int j = k+1; j < numcols; ++j)
    {
      solution[k] -= matrix[k][j]*solution[j];
    }
    solution[k] /= matrix[k][k];
  }

  // ultima fase: riordinamento incognite

  for( int k = numrows - 1; k >= 0; --k )
  {
    int jmax = exchange[k];
    double t = solution[k];
    solution[k] = solution[jmax];
    solution[jmax] = t;
  }
}

bool Invert3by3matrix ( const double m[3][3], double inv[3][3] )
{
  double det = m[0][0]*(m[1][1]*m[2][2] - m[1][2]*m[2][1]) -
               m[0][1]*(m[1][0]*m[2][2] - m[1][2]*m[2][0]) +
               m[0][2]*(m[1][0]*m[2][1] - m[1][1]*m[2][0]);
  if (det == 0) return false;

  for (int i=0; i < 3; i++)
  {
    for (int j=0; j < 3; j++)
    {
      int i1 = (i+1)%3;
      int i2 = (i+2)%3;
      int j1 = (j+1)%3;
      int j2 = (j+2)%3;
      inv[j][i] = (m[i1][j1]*m[i2][j2] - m[i1][j2]*m[i2][j1])/det;
    }
  }
  return true;
}