1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7 **
8 **
9 ** This file may be distributed and/or modified under the terms of the
10 ** GNU General Public License version 2 as published by the Free Software
11 ** Foundation and appearing in the file gpl-2.0.txt included in the
12 ** packaging of this file.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 **
23 ** This copyright notice MUST APPEAR in all copies of the script!
24 **
25 **********************************************************************/
26 #include <boost/numeric/ublas/matrix.hpp>
27 #include <boost/numeric/ublas/io.hpp>
28 #include <boost/numeric/ublas/lu.hpp>
29 #include <boost/math/special_functions/ellint_2.hpp>
30
31 #include <cmath>
32 #include <muParser.h>
33 #include <QString>
34 #include <QDebug>
35
36 #include "rs_math.h"
37 #include "rs_vector.h"
38 #include "rs_debug.h"
39
40 #ifdef EMU_C99
41 #include "emu_c99.h"
42 #endif
43
44
45 namespace {
46 constexpr double m_piX2 = M_PI*2; //2*PI
47 }
48
49 /**
50 * Rounds the given double to the closest int.
51 */
round(double v)52 int RS_Math::round(double v) {
53 return (int) lrint(v);
54 }
55
56 /**
57 * Save pow function
58 */
pow(double x,double y)59 double RS_Math::pow(double x, double y) {
60 errno = 0;
61 double ret = ::pow(x, y);
62 if (errno==EDOM) {
63 RS_DEBUG->print(RS_Debug::D_ERROR,
64 "RS_Math::pow: EDOM in pow");
65 ret = 0.0;
66 }
67 else if (errno==ERANGE) {
68 RS_DEBUG->print(RS_Debug::D_WARNING,
69 "RS_Math::pow: ERANGE in pow");
70 ret = 0.0;
71 }
72 return ret;
73 }
74
75 /* pow of vector components */
pow(RS_Vector vp,double y)76 RS_Vector RS_Math::pow(RS_Vector vp, double y) {
77 return RS_Vector(pow(vp.x,y),pow(vp.y,y));
78 }
79
80 /**
81 * Save equal function for real types
82 */
equal(const double d1,const double d2)83 bool RS_Math::equal(const double d1, const double d2)
84 {
85 return fabs(d1 - d2) < RS_TOLERANCE;
86 }
87
88 /**
89 * Converts radians to degrees.
90 */
rad2deg(double a)91 double RS_Math::rad2deg(double a) {
92 return 180./M_PI*a;
93 }
94
95 /**
96 * Converts degrees to radians.
97 */
deg2rad(double a)98 double RS_Math::deg2rad(double a) {
99 return M_PI/180.0*a;
100 }
101
102 /**
103 * Converts radians to gradians.
104 */
rad2gra(double a)105 double RS_Math::rad2gra(double a) {
106 return 200./M_PI*a;
107 }
108
gra2rad(double a)109 double RS_Math::gra2rad(double a) {
110 return M_PI/200.*a;
111 }
112
113
114 /**
115 * Finds greatest common divider using Euclid's algorithm.
116 */
findGCD(unsigned a,unsigned b)117 unsigned RS_Math::findGCD(unsigned a, unsigned b) {
118
119 while (b) {
120 unsigned rem = a % b;
121 a = b;
122 b = rem;
123 }
124
125 return a;
126 }
127
128
129
130 /**
131 * Tests if angle a is between a1 and a2. a, a1 and a2 must be in the
132 * range between 0 and 2*PI.
133 * All angles in rad.
134 *
135 * @param reversed true for clockwise testing. false for ccw testing.
136 * @return true if the angle a is between a1 and a2.
137 */
isAngleBetween(double a,double a1,double a2,bool reversed)138 bool RS_Math::isAngleBetween(double a,
139 double a1, double a2,
140 bool reversed) {
141
142 if (reversed) std::swap(a1,a2);
143 if(getAngleDifferenceU(a2, a1 ) < RS_TOLERANCE_ANGLE) return true;
144 const double tol=0.5*RS_TOLERANCE_ANGLE;
145 const double diff0=correctAngle(a2 -a1) + tol;
146
147 return diff0 >= correctAngle(a - a1) || diff0 >= correctAngle(a2 - a);
148 }
149
150 /**
151 * Corrects the given angle to the range of 0 to +PI*2.0.
152 */
correctAngle(double a)153 double RS_Math::correctAngle(double a) {
154 return fmod(M_PI + remainder(a - M_PI, m_piX2), m_piX2);
155 }
156
157 /**
158 * Corrects the given angle to the range of -PI to +PI.
159 */
correctAngle2(double a)160 double RS_Math::correctAngle2(double a) {
161 return remainder(a, m_piX2);
162 }
163
164 /**
165 * Returns the given angle as an Unsigned Angle in the range of 0 to +PI.
166 */
correctAngleU(double a)167 double RS_Math::correctAngleU(double a) {
168 return fabs(remainder(a, m_piX2));
169 }
170
171
172 /**
173 * @return The angle that needs to be added to a1 to reach a2.
174 * Always positive and less than 2*pi.
175 */
getAngleDifference(double a1,double a2,bool reversed)176 double RS_Math::getAngleDifference(double a1, double a2, bool reversed) {
177 if(reversed) std::swap(a1, a2);
178 return correctAngle(a2 - a1);
179 }
180
getAngleDifferenceU(double a1,double a2)181 double RS_Math::getAngleDifferenceU(double a1, double a2)
182 {
183 return correctAngleU(a1 - a2);
184 }
185
186
187 /**
188 * Makes a text constructed with the given angle readable. Used
189 * for dimension texts and for mirroring texts.
190 *
191 * @param readable true: make angle readable, false: unreadable
192 * @param corrected Will point to true if the given angle was
193 * corrected, false otherwise.
194 *
195 * @return The given angle or the given angle+PI, depending which on
196 * is readable from the bottom or right.
197 */
makeAngleReadable(double angle,bool readable,bool * corrected)198 double RS_Math::makeAngleReadable(double angle, bool readable,
199 bool* corrected) {
200
201 double ret=correctAngle(angle);
202
203 bool cor = isAngleReadable(ret) ^ readable;
204
205 // quadrant 1 & 4
206 if (cor) {
207 // ret = angle;
208 // }
209 // quadrant 2 & 3
210 // else {
211 ret = correctAngle(angle+M_PI);
212 }
213
214 if (corrected) {
215 *corrected = cor;
216 }
217
218 return ret;
219 }
220
221
222 /**
223 * @return true: if the given angle is in a range that is readable
224 * for texts created with that angle.
225 */
isAngleReadable(double angle)226 bool RS_Math::isAngleReadable(double angle) {
227 const double tolerance=0.001;
228 if (angle>M_PI_2)
229 return fabs(remainder(angle, m_piX2)) < (M_PI_2 - tolerance);
230 else
231 return fabs(remainder(angle, m_piX2)) < (M_PI_2 + tolerance);
232 }
233
234 /**
235 * @param tol Tolerance in rad.
236 * @retval true The two angles point in the same direction.
237 */
isSameDirection(double dir1,double dir2,double tol)238 bool RS_Math::isSameDirection(double dir1, double dir2, double tol) {
239 return getAngleDifferenceU(dir1, dir2) < tol;
240 }
241
242 /**
243 * Evaluates a mathematical expression and returns the result.
244 * If an error occurred, the given default value 'def' will be returned.
245 */
eval(const QString & expr,double def)246 double RS_Math::eval(const QString& expr, double def) {
247
248 bool ok;
249 double res = RS_Math::eval(expr, &ok);
250
251 if (!ok) {
252 //std::cerr << "RS_Math::evaluate: Parse error at col "
253 //<< ret << ": " << fp.ErrorMsg() << "\n";
254 return def;
255 }
256
257 return res;
258 }
259
260
261 /**
262 * Evaluates a mathematical expression and returns the result.
263 * If an error occurred, ok will be set to false (if ok isn't NULL).
264 */
eval(const QString & expr,bool * ok)265 double RS_Math::eval(const QString& expr, bool* ok) {
266 bool okTmp(false);
267 if(!ok) ok=&okTmp;
268 if (expr.isEmpty()) {
269 *ok = false;
270 return 0.0;
271 }
272 double ret(0.);
273 try{
274 mu::Parser p;
275 p.DefineConst(_T("pi"),M_PI);
276 #ifdef _UNICODE
277 p.SetExpr(expr.toStdWString());
278 #else
279 p.SetExpr(expr.toStdString());
280 #endif
281 ret=p.Eval();
282 *ok=true;
283 }
284 catch (mu::Parser::exception_type &e)
285 {
286 mu::console() << e.GetMsg() << std::endl;
287 *ok=false;
288 }
289 return ret;
290 }
291
292
293 /**
294 * Converts a double into a string which is as short as possible
295 *
296 * @param value The double value
297 * @param prec Precision e.g. a precision of 1 would mean that a
298 * value of 2.12030 will be converted to "2.1". 2.000 is always just "2").
299 */
doubleToString(double value,double prec)300 QString RS_Math::doubleToString(double value, double prec) {
301 if (prec< RS_TOLERANCE ) {
302 RS_DEBUG->print(RS_Debug::D_ERROR,
303 "RS_Math::doubleToString: invalid precision");
304 return QString().setNum(value, prec);
305 }
306
307 double const num = RS_Math::round(value / prec)*prec;
308
309 QString exaStr = RS_Math::doubleToString(1./prec, 10);
310 int const dotPos = exaStr.indexOf('.');
311
312 if (dotPos==-1) {
313 //big numbers for the precision
314 return QString().setNum(RS_Math::round(num));
315 } else {
316 //number of digits after the point
317 int digits = dotPos - 1;
318 return RS_Math::doubleToString(num, digits);
319 }
320 }
321
322 /**
323 * Converts a double into a string which is as short as possible.
324 *
325 * @param value The double value
326 * @param prec Precision
327 */
doubleToString(double value,int prec)328 QString RS_Math::doubleToString(double value, int prec) {
329 QString valStr;
330
331 valStr.setNum(value, 'f', prec);
332
333 if(valStr.contains('.')) {
334 // Remove tailing point and zeros:
335 // valStr.replace(QRegExp("0*$"), "");
336 // valStr.replace(QRegExp(R"(\.$)"), "");
337 // while (valStr.at(valStr.length()-1)=='0') {
338 // valStr.truncate(valStr.length()-1);
339 // }
340
341 if(valStr.at(valStr.length()-1)=='.') {
342 valStr.truncate(valStr.length()-1);
343 }
344
345 }
346
347 return valStr;
348 }
349
350
351
352 /**
353 * Performs some testing for the math class.
354 */
test()355 void RS_Math::test() {
356 {
357 std::cout<<"testing quadratic solver"<<std::endl;
358 //equations x^2 + v[0] x + v[1] = 0
359 std::vector<std::vector<double>> const eqns{
360 {-1., -1.},
361 {-101., -1.},
362 {-1., -100.},
363 {2., 1.},
364 {-2., 1.}
365 };
366 //expected roots
367 std::vector<std::vector<double>> roots{
368 {-0.6180339887498948, 1.6180339887498948},
369 {-0.0099000196991084878, 101.009900019699108},
370 {-9.5124921972503929, 10.5124921972503929},
371 {-1.},
372 {1.}
373 };
374
375 for(size_t i=0; i < eqns.size(); i++) {
376 std::cout<<"Test quadratic solver, test case: x^2 + ("
377 <<eqns[i].front()<<") x + ("
378 <<eqns[i].back()<<") = 0"<<std::endl;
379 auto sol = quadraticSolver(eqns[i]);
380 assert(sol.size()==roots[i].size());
381 if (sol.front() > sol.back())
382 std::swap(sol[0], sol[1]);
383 auto expected=roots[i];
384 if (expected.front() > expected.back())
385 std::swap(expected[0], expected[1]);
386 for (size_t j=0; j < sol.size(); j++) {
387 double x0 = sol[j];
388 double x1 = expected[j];
389 double const prec = (x0 - x1)/(fabs(x0 + x1) + RS_TOLERANCE2);
390 std::cout<<"root "<<j<<" : precision level = "<<prec<<std::endl;
391 std::cout<<std::setprecision(17)<<"found: "<<x0<<"\texpected: "<<x1<<std::endl;
392 assert(prec < RS_TOLERANCE);
393 }
394 std::cout<<std::endl;
395 }
396 return;
397 }
398 QString s;
399 double v;
400
401 std::cout << "RS_Math::test: doubleToString:\n";
402
403 v = 0.1;
404 s = RS_Math::doubleToString(v, 0.1);
405 assert(s=="0.1");
406 s = RS_Math::doubleToString(v, 0.01);
407 assert(s=="0.10");
408
409 v = 0.01;
410 s = RS_Math::doubleToString(v, 0.1);
411 assert(s=="0.0");
412 s = RS_Math::doubleToString(v, 0.01);
413 assert(s=="0.01");
414 s = RS_Math::doubleToString(v, 0.001);
415 assert(s=="0.010");
416
417 v = 0.001;
418 s = RS_Math::doubleToString(v, 0.1);
419 assert(s=="0.0");
420 s = RS_Math::doubleToString(v, 0.01);
421 assert(s=="0.00");
422 s = RS_Math::doubleToString(v, 0.001);
423 assert(s=="0.001");
424
425 std::cout << "RS_Math::test: complete"<<std::endl;
426 }
427
428
429
430 //Equation solvers
431
432 // quadratic, cubic, and quartic equation solver
433 // @ ce[] contains coefficient of the cubic equation:
434 // @ returns a vector contains real roots
435 //
436 // solvers assume arguments are valid, and there's no attempt to verify validity of the argument pointers
437 //
438 // @author Dongxu Li <dongxuli2011@gmail.com>
quadraticSolver(const std::vector<double> & ce)439 std::vector<double> RS_Math::quadraticSolver(const std::vector<double>& ce)
440 //quadratic solver for
441 // x^2 + ce[0] x + ce[1] =0
442 {
443 std::vector<double> ans(0,0.);
444 if (ce.size() != 2) return ans;
445 using LDouble = long double;
446 LDouble const b = -0.5L * ce[0];
447 LDouble const c = ce[1];
448 // x^2 -2 b x + c=0
449 // (x - b)^2 = b^2 - c
450 // b^2 >= fabs(c)
451 // x = b \pm b sqrt(1. - c/(b^2))
452 LDouble const b2= b * b;
453 LDouble const discriminant= b2 - c;
454 LDouble const fc = std::abs(c);
455
456 //TODO, fine tune to tolerance level
457 LDouble const TOL = 1e-24L;
458
459 if (discriminant < 0.L)
460 //negative discriminant, no real root
461 return ans;
462
463 //find the radical
464 LDouble r;
465
466 // given |p| >= |q|
467 // sqrt(p^2 \pm q^2) = p sqrt(1 \pm q^2/p^2)
468 if (b2 >= fc)
469 r = std::abs(b) * std::sqrt(1.L - c/b2);
470 else
471 // c is negative, because b2 - c is non-negative
472 r = std::sqrt(fc) * std::sqrt(1.L + b2/fc);
473
474 if (r >= TOL*std::abs(b)) {
475 //two roots
476 if (b >= 0.L)
477 //since both (b,r)>=0, avoid (b - r) loss of significance
478 ans.push_back(b + r);
479 else
480 //since b<0, r>=0, avoid (b + r) loss of significance
481 ans.push_back(b - r);
482
483 //Vieta's formulas for the second root
484 ans.push_back(c/ans.front());
485 } else
486 //multiple roots
487 ans.push_back(b);
488 return ans;
489 }
490
491
cubicSolver(const std::vector<double> & ce)492 std::vector<double> RS_Math::cubicSolver(const std::vector<double>& ce)
493 //cubic equation solver
494 // x^3 + ce[0] x^2 + ce[1] x + ce[2] = 0
495 {
496 // std::cout<<"x^3 + ("<<ce[0]<<")*x^2+("<<ce[1]<<")*x+("<<ce[2]<<")==0"<<std::endl;
497 std::vector<double> ans(0,0.);
498 if (ce.size() != 3) return ans;
499 // depressed cubic, Tschirnhaus transformation, x= t - b/(3a)
500 // t^3 + p t +q =0
501 double shift=(1./3)*ce[0];
502 double p=ce[1] -shift*ce[0];
503 double q=ce[0]*( (2./27)*ce[0]*ce[0]-(1./3)*ce[1])+ce[2];
504 //Cardano's method,
505 // t=u+v
506 // u^3 + v^3 + ( 3 uv + p ) (u+v) + q =0
507 // select 3uv + p =0, then,
508 // u^3 + v^3 = -q
509 // u^3 v^3 = - p^3/27
510 // so, u^3 and v^3 are roots of equation,
511 // z^2 + q z - p^3/27 = 0
512 // and u^3,v^3 are,
513 // -q/2 \pm sqrt(q^2/4 + p^3/27)
514 // discriminant= q^2/4 + p^3/27
515 //std::cout<<"p="<<p<<"\tq="<<q<<std::endl;
516 double discriminant= (1./27)*p*p*p+(1./4)*q*q;
517 if ( fabs(p)< 1.0e-75) {
518 ans.push_back((q>0)?-pow(q,(1./3)):pow(-q,(1./3)));
519 ans[0] -= shift;
520 // DEBUG_HEADER
521 // std::cout<<"cubic: one root: "<<ans[0]<<std::endl;
522 return ans;
523 }
524 //std::cout<<"discriminant="<<discriminant<<std::endl;
525 if(discriminant>0) {
526 std::vector<double> ce2(2,0.);
527 ce2[0]=q;
528 ce2[1]=-1./27*p*p*p;
529 auto r=quadraticSolver(ce2);
530 if ( r.size()==0 ) { //should not happen
531 std::cerr<<__FILE__<<" : "<<__func__<<" : line"<<__LINE__<<" :cubicSolver()::Error cubicSolver("<<ce[0]<<' '<<ce[1]<<' '<<ce[2]<<")\n";
532 }
533 double u,v;
534 u= (q<=0) ? pow(r[0], 1./3): -pow(-r[1],1./3);
535 //u=(q<=0)?pow(-0.5*q+sqrt(discriminant),1./3):-pow(0.5*q+sqrt(discriminant),1./3);
536 v=(-1./3)*p/u;
537 //std::cout<<"u="<<u<<"\tv="<<v<<std::endl;
538 //std::cout<<"u^3="<<u*u*u<<"\tv^3="<<v*v*v<<std::endl;
539 ans.push_back(u+v - shift);
540
541 // DEBUG_HEADER
542 // std::cout<<"cubic: one root: "<<ans[0]<<std::endl;
543 }else{
544 std::complex<double> u(q,0),rt[3];
545 u=std::pow(-0.5*u-sqrt(0.25*u*u+p*p*p/27),1./3);
546 rt[0]=u-p/(3.*u)-shift;
547 std::complex<double> w(-0.5,sqrt(3.)/2);
548 rt[1]=u*w-p/(3.*u*w)-shift;
549 rt[2]=u/w-p*w/(3.*u)-shift;
550 // DEBUG_HEADER
551 // std::cout<<"Roots:\n";
552 // std::cout<<rt[0]<<std::endl;
553 // std::cout<<rt[1]<<std::endl;
554 // std::cout<<rt[2]<<std::endl;
555 ans.push_back(rt[0].real());
556 ans.push_back(rt[1].real());
557 ans.push_back(rt[2].real());
558 }
559 // newton-raphson
560 for(double& x0: ans){
561 double dx=0.;
562 for(size_t i=0; i<20; ++i){
563 double f=( (x0 + ce[0])*x0 + ce[1])*x0 +ce[2];
564 double df=(3.*x0+2.*ce[0])*x0 +ce[1];
565 if(fabs(df)>fabs(f)+RS_TOLERANCE){
566 dx=f/df;
567 x0 -= dx;
568 }else
569 break;
570 }
571 }
572
573 return ans;
574 }
575
576 /** quartic solver
577 * x^4 + ce[0] x^3 + ce[1] x^2 + ce[2] x + ce[3] = 0
578 @ce, a vector of size 4 contains the coefficient in order
579 @return, a vector contains real roots
580 **/
quarticSolver(const std::vector<double> & ce)581 std::vector<double> RS_Math::quarticSolver(const std::vector<double>& ce)
582 {
583 std::vector<double> ans(0,0.);
584 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
585 DEBUG_HEADER
586 std::cout<<"expected array size=4, got "<<ce.size()<<std::endl;
587 }
588 if(ce.size() != 4) return ans;
589 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
590 std::cout<<"x^4+("<<ce[0]<<")*x^3+("<<ce[1]<<")*x^2+("<<ce[2]<<")*x+("<<ce[3]<<")==0"<<std::endl;
591 }
592
593 // x^4 + a x^3 + b x^2 +c x + d = 0
594 // depressed quartic, x= t - a/4
595 // t^4 + ( b - 3/8 a^2 ) t^2 + (c - a b/2 + a^3/8) t + d - a c /4 + a^2 b/16 - 3 a^4/256 =0
596 // t^4 + p t^2 + q t + r =0
597 // p= b - (3./8)*a*a;
598 // q= c - 0.5*a*b+(1./8)*a*a*a;
599 // r= d - 0.25*a*c+(1./16)*a*a*b-(3./256)*a^4
600 double shift=0.25*ce[0];
601 double shift2=shift*shift;
602 double a2=ce[0]*ce[0];
603 double p= ce[1] - (3./8)*a2;
604 double q= ce[2] + ce[0]*((1./8)*a2 - 0.5*ce[1]);
605 double r= ce[3] - shift*ce[2] + (ce[1] - 3.*shift2)*shift2;
606 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
607 DEBUG_HEADER
608 std::cout<<"x^4+("<<p<<")*x^2+("<<q<<")*x+("<<r<<")==0"<<std::endl;
609 }
610 if (q*q <= 1.e-4*RS_TOLERANCE*fabs(p*r)) {// Biquadratic equations
611 double discriminant= 0.25*p*p -r;
612 if (discriminant < -1.e3*RS_TOLERANCE) {
613
614 // DEBUG_HEADER
615 // std::cout<<"discriminant="<<discriminant<<"\tno root"<<std::endl;
616 return ans;
617 }
618 double t2[2];
619 t2[0]=-0.5*p-sqrt(fabs(discriminant));
620 t2[1]= -p - t2[0];
621 // std::cout<<"t2[0]="<<t2[0]<<std::endl;
622 // std::cout<<"t2[1]="<<t2[1]<<std::endl;
623 if ( t2[1] >= 0.) { // two real roots
624 ans.push_back(sqrt(t2[1])-shift);
625 ans.push_back(-sqrt(t2[1])-shift);
626 }
627 if ( t2[0] >= 0. ) {// four real roots
628 ans.push_back(sqrt(t2[0])-shift);
629 ans.push_back(-sqrt(t2[0])-shift);
630 }
631 // DEBUG_HEADER
632 // for(int i=0;i<ans.size();i++){
633 // std::cout<<"root x: "<<ans[i]<<std::endl;
634 // }
635 return ans;
636 }
637 if ( fabs(r)< 1.0e-75 ) {
638 std::vector<double> cubic(3,0.);
639 cubic[1]=p;
640 cubic[2]=q;
641 ans.push_back(0.);
642 auto r=cubicSolver(cubic);
643 std::copy(r.begin(),r.end(), std::back_inserter(ans));
644 for(size_t i=0; i<ans.size(); i++) ans[i] -= shift;
645 return ans;
646 }
647 // depressed quartic to two quadratic equations
648 // t^4 + p t^2 + q t + r = ( t^2 + u t + v) ( t^2 - u t + w)
649 // so,
650 // p + u^2= w+v
651 // q/u= w-v
652 // r= wv
653 // so,
654 // (p+u^2)^2 - (q/u)^2 = 4 r
655 // y=u^2,
656 // y^3 + 2 p y^2 + ( p^2 - 4 r) y - q^2 =0
657 //
658 std::vector<double> cubic(3,0.);
659 cubic[0]=2.*p;
660 cubic[1]=p*p-4.*r;
661 cubic[2]=-q*q;
662 auto r3= cubicSolver(cubic);
663 //std::cout<<"quartic_solver:: real roots from cubic: "<<ret<<std::endl;
664 //for(unsigned int i=0; i<ret; i++)
665 // std::cout<<"cubic["<<i<<"]="<<cubic[i]<<" x= "<<croots[i]<<std::endl;
666 //newton-raphson
667 if (r3.size()==1) { //one real root from cubic
668 if (r3[0]< 0.) {//this should not happen
669 DEBUG_HEADER
670 qDebug()<<"Quartic Error:: Found one real root for cubic, but negative\n";
671 return ans;
672 }
673 double sqrtz0=sqrt(r3[0]);
674 std::vector<double> ce2(2,0.);
675 ce2[0]= -sqrtz0;
676 ce2[1]=0.5*(p+r3[0])+0.5*q/sqrtz0;
677 auto r1=quadraticSolver(ce2);
678 if (r1.size()==0 ) {
679 ce2[0]= sqrtz0;
680 ce2[1]=0.5*(p+r3[0])-0.5*q/sqrtz0;
681 r1=quadraticSolver(ce2);
682 }
683 for(auto& x: r1){
684 x -= shift;
685 }
686 return r1;
687 }
688 if ( r3[0]> 0. && r3[1] > 0. ) {
689 double sqrtz0=sqrt(r3[0]);
690 std::vector<double> ce2(2,0.);
691 ce2[0]= -sqrtz0;
692 ce2[1]=0.5*(p+r3[0])+0.5*q/sqrtz0;
693 ans=quadraticSolver(ce2);
694 ce2[0]= sqrtz0;
695 ce2[1]=0.5*(p+r3[0])-0.5*q/sqrtz0;
696 auto r1=quadraticSolver(ce2);
697 std::copy(r1.begin(),r1.end(),std::back_inserter(ans));
698 for(auto& x: ans){
699 x -= shift;
700 }
701 }
702 // newton-raphson
703 for(double& x0: ans){
704 double dx=0.;
705 for(size_t i=0; i<20; ++i){
706 double f=(( (x0 + ce[0])*x0 + ce[1])*x0 +ce[2])*x0 + ce[3] ;
707 double df=((4.*x0+3.*ce[0])*x0 +2.*ce[1])*x0+ce[2];
708 // DEBUG_HEADER
709 // qDebug()<<"i="<<i<<"\tx0="<<x0<<"\tf="<<f<<"\tdf="<<df;
710 if(fabs(df)>RS_TOLERANCE2){
711 dx=f/df;
712 x0 -= dx;
713 }else
714 break;
715 }
716 }
717
718 return ans;
719 }
720
721 /** quartic solver
722 * ce[4] x^4 + ce[3] x^3 + ce[2] x^2 + ce[1] x + ce[0] = 0
723 @ce, a vector of size 5 contains the coefficient in order
724 @return, a vector contains real roots
725 *ToDo, need a robust algorithm to locate zero terms, better handling of tolerances
726 **/
quarticSolverFull(const std::vector<double> & ce)727 std::vector<double> RS_Math::quarticSolverFull(const std::vector<double>& ce)
728 {
729 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
730 DEBUG_HEADER
731 std::cout<<ce[4]<<"*y^4+("<<ce[3]<<")*y^3+("<<ce[2]<<"*y^2+("<<ce[1]<<")*y+("<<ce[0]<<")==0"<<std::endl;
732 }
733
734 std::vector<double> roots(0,0.);
735 if(ce.size()!=5) return roots;
736 std::vector<double> ce2(4,0.);
737
738 if ( fabs(ce[4]) < 1.0e-14) { // this should not happen
739 if ( fabs(ce[3]) < 1.0e-14) { // this should not happen
740 if ( fabs(ce[2]) < 1.0e-14) { // this should not happen
741 if( fabs(ce[1]) > 1.0e-14) {
742 roots.push_back(-ce[0]/ce[1]);
743 } else { // can not determine y. this means overlapped, but overlap should have been detected before, therefore return empty set
744 return roots;
745 }
746 } else {
747 ce2.resize(2);
748 ce2[0]=ce[1]/ce[2];
749 ce2[1]=ce[0]/ce[2];
750 //std::cout<<"ce2[2]={ "<<ce2[0]<<' '<<ce2[1]<<" }\n";
751 roots=RS_Math::quadraticSolver(ce2);
752 }
753 } else {
754 ce2.resize(3);
755 ce2[0]=ce[2]/ce[3];
756 ce2[1]=ce[1]/ce[3];
757 ce2[2]=ce[0]/ce[3];
758 //std::cout<<"ce2[3]={ "<<ce2[0]<<' '<<ce2[1]<<' '<<ce2[2]<<" }\n";
759 roots=RS_Math::cubicSolver(ce2);
760 }
761 } else {
762 ce2[0]=ce[3]/ce[4];
763 ce2[1]=ce[2]/ce[4];
764 ce2[2]=ce[1]/ce[4];
765 ce2[3]=ce[0]/ce[4];
766 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
767 DEBUG_HEADER
768 std::cout<<"ce2[4]={ "<<ce2[0]<<' '<<ce2[1]<<' '<<ce2[2]<<' '<<ce2[3]<<" }\n";
769 }
770 if(fabs(ce2[3])<= RS_TOLERANCE15) {
771 //constant term is zero, factor 0 out, solve a cubic equation
772 ce2.resize(3);
773 roots=RS_Math::cubicSolver(ce2);
774 roots.push_back(0.);
775 }else
776 roots=RS_Math::quarticSolver(ce2);
777 }
778 return roots;
779 }
780
781 //linear Equation solver by Gauss-Jordan
782 /**
783 * Solve linear equation set
784 *@ mt holds the augmented matrix
785 *@ sn holds the solution
786 *@ return true, if the equation set has a unique solution, return false otherwise
787 *
788 *@Author: Dongxu Li
789 */
790
linearSolver(const std::vector<std::vector<double>> & mt,std::vector<double> & sn)791 bool RS_Math::linearSolver(const std::vector<std::vector<double> >& mt, std::vector<double>& sn){
792 //verify the matrix size
793 size_t mSize(mt.size()); //rows
794 size_t aSize(mSize+1); //columns of augmented matrix
795 if(std::any_of(mt.begin(), mt.end(), [&aSize](const std::vector<double>& v)->bool{
796 return v.size() != aSize;
797 }))
798 return false;
799 sn.resize(mSize);//to hold the solution
800 #if false
801 boost::numeric::ublas::matrix<double> bm (mSize, mSize);
802 boost::numeric::ublas::vector<double> bs(mSize);
803
804 for(int i=0;i<mSize;i++) {
805 for(int j=0;j<mSize;j++) {
806 bm(i,j)=mt[i][j];
807 }
808 bs(i)=mt[i][mSize];
809 }
810 //solve the linear equation set by LU decomposition in boost ublas
811
812 if ( boost::numeric::ublas::lu_factorize<boost::numeric::ublas::matrix<double> >(bm) ) {
813 std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
814 std::cout<<" linear solver failed"<<std::endl;
815 // RS_DEBUG->print(RS_Debug::D_WARNING, "linear solver failed");
816 return false;
817 }
818
819 boost::numeric::ublas:: triangular_matrix<double, boost::numeric::ublas::unit_lower>
820 lm = boost::numeric::ublas::triangular_adaptor< boost::numeric::ublas::matrix<double>, boost::numeric::ublas::unit_lower>(bm);
821 boost::numeric::ublas:: triangular_matrix<double, boost::numeric::ublas::upper>
822 um = boost::numeric::ublas::triangular_adaptor< boost::numeric::ublas::matrix<double>, boost::numeric::ublas::upper>(bm);
823 ;
824 boost::numeric::ublas::inplace_solve(lm,bs, boost::numeric::ublas::lower_tag());
825 boost::numeric::ublas::inplace_solve(um,bs, boost::numeric::ublas::upper_tag());
826 for(int i=0;i<mSize;i++){
827 sn[i]=bs(i);
828 }
829 // std::cout<<"dn="<<dn<<std::endl;
830 // data.center.set(-0.5*dn(1)/dn(0),-0.5*dn(3)/dn(2)); // center
831 // double d(1.+0.25*(dn(1)*dn(1)/dn(0)+dn(3)*dn(3)/dn(2)));
832 // if(fabs(dn(0))<RS_TOLERANCE2
833 // ||fabs(dn(2))<RS_TOLERANCE2
834 // ||d/dn(0)<RS_TOLERANCE2
835 // ||d/dn(2)<RS_TOLERANCE2
836 // ) {
837 // //ellipse not defined
838 // return false;
839 // }
840 // d=sqrt(d/dn(0));
841 // data.majorP.set(d,0.);
842 // data.ratio=sqrt(dn(0)/dn(2));
843 #else
844 // solve the linear equation by Gauss-Jordan elimination
845 std::vector<std::vector<double> > mt0(mt); //copy the matrix;
846 for(size_t i=0;i<mSize;++i){
847 size_t imax(i);
848 double cmax(fabs(mt0[i][i]));
849 for(size_t j=i+1;j<mSize;++j) {
850 if(fabs(mt0[j][i]) > cmax ) {
851 imax=j;
852 cmax=fabs(mt0[j][i]);
853 }
854 }
855 if(cmax<RS_TOLERANCE2) return false; //singular matrix
856 if(imax != i) {//move the line with largest absolute value at column i to row i, to avoid division by zero
857 std::swap(mt0[i],mt0[imax]);
858 }
859 for(size_t k=i+1;k<=mSize;++k) { //normalize the i-th row
860 mt0[i][k] /= mt0[i][i];
861 }
862 mt0[i][i]=1.;
863 for(size_t j=0;j<mSize;++j) {//Gauss-Jordan
864 if(j != i ) {
865 double& a = mt0[j][i];
866 for(size_t k=i+1;k<=mSize;++k) {
867 mt0[j][k] -= mt0[i][k]*a;
868 }
869 a=0.;
870 }
871 }
872 //output gauss-jordan results for debugging
873 // std::cout<<"========"<<i<<"==========\n";
874 // for(auto v0: mt0){
875 // for(auto v1:v0)
876 // std::cout<<v1<<'\t';
877 // std::cout<<std::endl;
878 // }
879 }
880 for(size_t i=0;i<mSize;++i) {
881 sn[i]=mt0[i][mSize];
882 }
883 #endif
884
885 return true;
886 }
887
888 /**
889 * wrapper of elliptic integral of the second type, Legendre form
890 * @param k the elliptic modulus or eccentricity
891 * @param phi elliptic angle, must be within range of [0, M_PI]
892 *
893 * @author: Dongxu Li
894 */
ellipticIntegral_2(const double & k,const double & phi)895 double RS_Math::ellipticIntegral_2(const double& k, const double& phi)
896 {
897 double a= remainder(phi-M_PI_2,M_PI);
898 if(a>0.) {
899 return boost::math::ellint_2<double,double>(k,a);
900 } else {
901 return - boost::math::ellint_2<double,double>(k,fabs(a));
902 }
903 }
904
905 /** solver quadratic simultaneous equations of set two **/
906 /* solve the following quadratic simultaneous equations,
907 * ma000 x^2 + ma011 y^2 - 1 =0
908 * ma100 x^2 + 2 ma101 xy + ma111 y^2 + mb10 x + mb11 y +mc1 =0
909 *
910 *@m, a vector of size 8 contains coefficients in the strict order of:
911 ma000 ma011 ma100 ma101 ma111 mb10 mb11 mc1
912 * m[0] m[1] must be positive
913 *@return a vector contains real roots
914 */
simultaneousQuadraticSolver(const std::vector<double> & m)915 RS_VectorSolutions RS_Math::simultaneousQuadraticSolver(const std::vector<double>& m)
916 {
917 RS_VectorSolutions ret(0);
918 if(m.size() != 8 ) return ret; // valid m should contain exact 8 elements
919 std::vector< double> c1(0,0.);
920 std::vector< std::vector<double> > m1(0,c1);
921 c1.resize(6);
922 c1[0]=m[0];
923 c1[1]=0.;
924 c1[2]=m[1];
925 c1[3]=0.;
926 c1[4]=0.;
927 c1[5]=-1.;
928 m1.push_back(c1);
929 c1[0]=m[2];
930 c1[1]=2.*m[3];
931 c1[2]=m[4];
932 c1[3]=m[5];
933 c1[4]=m[6];
934 c1[5]=m[7];
935 m1.push_back(c1);
936
937 return simultaneousQuadraticSolverFull(m1);
938 }
939
940 /** solver quadratic simultaneous equations of a set of two **/
941 /* solve the following quadratic simultaneous equations,
942 * ma000 x^2 + ma001 xy + ma011 y^2 + mb00 x + mb01 y + mc0 =0
943 * ma100 x^2 + ma101 xy + ma111 y^2 + mb10 x + mb11 y + mc1 =0
944 *
945 *@m, a vector of size 2 each contains a vector of size 6 coefficients in the strict order of:
946 ma000 ma001 ma011 mb00 mb01 mc0
947 ma100 ma101 ma111 mb10 mb11 mc1
948 *@return a RS_VectorSolutions contains real roots (x,y)
949 */
simultaneousQuadraticSolverFull(const std::vector<std::vector<double>> & m)950 RS_VectorSolutions RS_Math::simultaneousQuadraticSolverFull(const std::vector<std::vector<double> >& m)
951 {
952 RS_VectorSolutions ret;
953 if(m.size()!=2) return ret;
954 if( m[0].size() ==3 || m[1].size()==3 ){
955 return simultaneousQuadraticSolverMixed(m);
956 }
957 if(m[0].size()!=6 || m[1].size()!=6) return ret;
958 /** eliminate x, quartic equation of y **/
959 auto& a=m[0][0];
960 auto& b=m[0][1];
961 auto& c=m[0][2];
962 auto& d=m[0][3];
963 auto& e=m[0][4];
964 auto& f=m[0][5];
965
966 auto& g=m[1][0];
967 auto& h=m[1][1];
968 auto& i=m[1][2];
969 auto& j=m[1][3];
970 auto& k=m[1][4];
971 auto& l=m[1][5];
972 /**
973 Collect[Eliminate[{ a*x^2 + b*x*y+c*y^2+d*x+e*y+f==0,g*x^2+h*x*y+i*y^2+j*x+k*y+l==0},x],y]
974 **/
975 /*
976 f^2 g^2 - d f g j + a f j^2 - 2 a f g l + (2 e f g^2 - d f g h - b f g j + 2 a f h j - 2 a f g k) y + (2 c f g^2 - b f g h + a f h^2 - 2 a f g i) y^2
977 ==
978 -(d^2 g l) + a d j l - a^2 l^2
979 +
980 (d e g j - a e j^2 - d^2 g k + a d j k - 2 b d g l + 2 a e g l + a d h l + a b j l - 2 a^2 k l) y
981 +
982 (-(e^2 g^2) + d e g h - d^2 g i + c d g j + b e g j - 2 a e h j + a d i j - a c j^2 - 2 b d g k + 2 a e g k + a d h k + a b j k - a^2 k^2 - b^2 g l + 2 a c g l + a b h l - 2 a^2 i l) y^2
983 +
984 (-2 c e g^2 + c d g h + b e g h - a e h^2 - 2 b d g i + 2 a e g i + a d h i + b c g j - 2 a c h j + a b i j - b^2 g k + 2 a c g k + a b h k - 2 a^2 i k) y^3
985 +
986 (-(c^2 g^2) + b c g h - a c h^2 - b^2 g i + 2 a c g i + a b h i - a^2 i^2) y^4
987
988
989 */
990 double a2=a*a;
991 double b2=b*b;
992 double c2=c*c;
993 double d2=d*d;
994 double e2=e*e;
995 double f2=f*f;
996
997 double g2=g*g;
998 double h2=h*h;
999 double i2=i*i;
1000 double j2=j*j;
1001 double k2=k*k;
1002 double l2=l*l;
1003 std::vector<double> qy(5,0.);
1004 //y^4
1005 qy[4]=-c2*g2 + b*c*g*h - a*c*h2 - b2*g*i + 2.*a*c*g*i + a*b*h*i - a2*i2;
1006 //y^3
1007 qy[3]=-2.*c*e*g2 + c*d*g*h + b*e*g*h - a*e*h2 - 2.*b*d*g*i + 2.*a*e*g*i + a*d*h*i +
1008 b*c*g*j - 2.*a*c*h*j + a*b*i*j - b2*g*k + 2.*a*c*g*k + a*b*h*k - 2.*a2*i*k;
1009 //y^2
1010 qy[2]=(-e2*g2 + d*e*g*h - d2*g*i + c*d*g*j + b*e*g*j - 2.*a*e*h*j + a*d*i*j - a*c*j2 -
1011 2.*b*d*g*k + 2.*a*e*g*k + a*d*h*k + a*b*j*k - a2*k2 - b2*g*l + 2.*a*c*g*l + a*b*h*l - 2.*a2*i*l)
1012 - (2.*c*f*g2 - b*f*g*h + a*f*h2 - 2.*a*f*g*i);
1013 //y
1014 qy[1]=(d*e*g*j - a*e*j2 - d2*g*k + a*d*j*k - 2.*b*d*g*l + 2.*a*e*g*l + a*d*h*l + a*b*j*l - 2.*a2*k*l)
1015 -(2.*e*f*g2 - d*f*g*h - b*f*g*j + 2.*a*f*h*j - 2.*a*f*g*k);
1016 //y^0
1017 qy[0]=-d2*g*l + a*d*j*l - a2*l2
1018 - ( f2*g2 - d*f*g*j + a*f*j2 - 2.*a*f*g*l);
1019 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
1020 DEBUG_HEADER
1021 std::cout<<qy[4]<<"*y^4 +("<<qy[3]<<")*y^3+("<<qy[2]<<")*y^2+("<<qy[1]<<")*y+("<<qy[0]<<")==0"<<std::endl;
1022 }
1023 //quarticSolver
1024 auto roots=quarticSolverFull(qy);
1025 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
1026 std::cout<<"roots.size()= "<<roots.size()<<std::endl;
1027 }
1028
1029 if (roots.size()==0 ) { // no intersection found
1030 return ret;
1031 }
1032 std::vector<double> ce(0,0.);
1033
1034 for(size_t i0=0;i0<roots.size();i0++){
1035 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
1036 DEBUG_HEADER
1037 std::cout<<"y="<<roots[i0]<<std::endl;
1038 }
1039 /*
1040 Collect[Eliminate[{ a*x^2 + b*x*y+c*y^2+d*x+e*y+f==0,g*x^2+h*x*y+i*y^2+j*x+k*y+l==0},x],y]
1041 */
1042 ce.resize(3);
1043 ce[0]=a;
1044 ce[1]=b*roots[i0]+d;
1045 ce[2]=c*roots[i0]*roots[i0]+e*roots[i0]+f;
1046 // DEBUG_HEADER
1047 // std::cout<<"("<<ce[0]<<")*x^2 + ("<<ce[1]<<")*x + ("<<ce[2]<<") == 0"<<std::endl;
1048 if(fabs(ce[0])<1e-75 && fabs(ce[1])<1e-75) {
1049 ce[0]=g;
1050 ce[1]=h*roots[i0]+j;
1051 ce[2]=i*roots[i0]*roots[i0]+k*roots[i0]+f;
1052 // DEBUG_HEADER
1053 // std::cout<<"("<<ce[0]<<")*x^2 + ("<<ce[1]<<")*x + ("<<ce[2]<<") == 0"<<std::endl;
1054
1055 }
1056 if(fabs(ce[0])<1e-75 && fabs(ce[1])<1e-75) continue;
1057
1058 if(fabs(a)>1e-75){
1059 std::vector<double> ce2(2,0.);
1060 ce2[0]=ce[1]/ce[0];
1061 ce2[1]=ce[2]/ce[0];
1062 // DEBUG_HEADER
1063 // std::cout<<"x^2 +("<<ce2[0]<<")*x+("<<ce2[1]<<")==0"<<std::endl;
1064 auto xRoots=quadraticSolver(ce2);
1065 for(size_t j0=0;j0<xRoots.size();j0++){
1066 // DEBUG_HEADER
1067 // std::cout<<"x="<<xRoots[j0]<<std::endl;
1068 RS_Vector vp(xRoots[j0],roots[i0]);
1069 if(simultaneousQuadraticVerify(m,vp)) ret.push_back(vp);
1070 }
1071 continue;
1072 }
1073 RS_Vector vp(-ce[2]/ce[1],roots[i0]);
1074 if(simultaneousQuadraticVerify(m,vp)) ret.push_back(vp);
1075 }
1076 if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
1077 DEBUG_HEADER
1078 std::cout<<"ret="<<ret<<std::endl;
1079 }
1080 return ret;
1081 }
1082
simultaneousQuadraticSolverMixed(const std::vector<std::vector<double>> & m)1083 RS_VectorSolutions RS_Math::simultaneousQuadraticSolverMixed(const std::vector<std::vector<double> >& m)
1084 {
1085 RS_VectorSolutions ret;
1086 auto p0=& (m[0]);
1087 auto p1=& (m[1]);
1088 if(p1->size()==3){
1089 std::swap(p0,p1);
1090 }
1091 if(p1->size()==3) {
1092 //linear
1093 std::vector<double> sn(2,0.);
1094 std::vector<std::vector<double> > ce;
1095 ce.push_back(m[0]);
1096 ce.push_back(m[1]);
1097 ce[0][2]=-ce[0][2];
1098 ce[1][2]=-ce[1][2];
1099 if( RS_Math::linearSolver(ce,sn)) ret.push_back(RS_Vector(sn[0],sn[1]));
1100 return ret;
1101 }
1102 // DEBUG_HEADER
1103 // std::cout<<"p0: size="<<p0->size()<<"\n Solve[{("<< p0->at(0)<<")*x + ("<<p0->at(1)<<")*y + ("<<p0->at(2)<<")==0,";
1104 // std::cout<<"("<< p1->at(0)<<")*x^2 + ("<<p1->at(1)<<")*x*y + ("<<p1->at(2)<<")*y^2 + ("<<p1->at(3)<<")*x +("<<p1->at(4)<<")*y+("
1105 // <<p1->at(5)<<")==0},{x,y}]"<<std::endl;
1106 const double& a=p0->at(0);
1107 const double& b=p0->at(1);
1108 const double& c=p0->at(2);
1109 const double& d=p1->at(0);
1110 const double& e=p1->at(1);
1111 const double& f=p1->at(2);
1112 const double& g=p1->at(3);
1113 const double& h=p1->at(4);
1114 const double& i=p1->at(5);
1115 /**
1116 y (2 b c d-a c e)-a c g+c^2 d = y^2 (a^2 (-f)+a b e-b^2 d)+y (a b g-a^2 h)+a^2 (-i)
1117 */
1118 std::vector<double> ce(3,0.);
1119 const double& a2=a*a;
1120 const double& b2=b*b;
1121 const double& c2=c*c;
1122 ce[0]= -f*a2+a*b*e-b2*d;
1123 ce[1]=a*b*g-a2*h- (2*b*c*d-a*c*e);
1124 ce[2]=a*c*g-c2*d-a2*i;
1125 // DEBUG_HEADER
1126 // std::cout<<"("<<ce[0]<<") y^2 + ("<<ce[1]<<") y + ("<<ce[2]<<")==0"<<std::endl;
1127 std::vector<double> roots(0,0.);
1128 if( fabs(ce[1])>RS_TOLERANCE15 && fabs(ce[0]/ce[1])<RS_TOLERANCE15){
1129 roots.push_back( - ce[2]/ce[1]);
1130 }else{
1131 std::vector<double> ce2(2,0.);
1132 ce2[0]=ce[1]/ce[0];
1133 ce2[1]=ce[2]/ce[0];
1134 roots=quadraticSolver(ce2);
1135 }
1136 // for(size_t i=0;i<roots.size();i++){
1137 // std::cout<<"x="<<roots.at(i)<<std::endl;
1138 // }
1139
1140
1141 if(roots.size()==0) {
1142 return RS_VectorSolutions();
1143 }
1144 for(size_t i=0;i<roots.size();i++){
1145 ret.push_back(RS_Vector(-(b*roots.at(i)+c)/a,roots.at(i)));
1146 // std::cout<<ret.at(ret.size()-1).x<<", "<<ret.at(ret.size()-1).y<<std::endl;
1147 }
1148
1149 return ret;
1150
1151 }
1152
1153 /** verify a solution for simultaneousQuadratic
1154 *@m the coefficient matrix
1155 *@v, a candidate to verify
1156 *@return true, for a valid solution
1157 **/
simultaneousQuadraticVerify(const std::vector<std::vector<double>> & m,RS_Vector & v)1158 bool RS_Math::simultaneousQuadraticVerify(const std::vector<std::vector<double> >& m, RS_Vector& v)
1159 {
1160 RS_Vector v0=v;
1161 auto& a=m[0][0];
1162 auto& b=m[0][1];
1163 auto& c=m[0][2];
1164 auto& d=m[0][3];
1165 auto& e=m[0][4];
1166 auto& f=m[0][5];
1167
1168 auto& g=m[1][0];
1169 auto& h=m[1][1];
1170 auto& i=m[1][2];
1171 auto& j=m[1][3];
1172 auto& k=m[1][4];
1173 auto& l=m[1][5];
1174 /**
1175 * tolerance test for bug#3606099
1176 * verifying the equations to floating point tolerance by terms
1177 */
1178 double sum0=0., sum1=0.;
1179 double f00=0.,f01=0.;
1180 double amax0, amax1;
1181 for(size_t i0=0; i0<20; ++i0){
1182 double& x=v.x;
1183 double& y=v.y;
1184 double x2=x*x;
1185 double y2=y*y;
1186 double const terms0[12]={ a*x2, b*x*y, c*y2, d*x, e*y, f, g*x2, h*x*y, i*y2, j*x, k*y, l};
1187 amax0=fabs(terms0[0]), amax1=fabs(terms0[6]);
1188 double px=2.*a*x+b*y+d;
1189 double py=b*x+2.*c*y+e;
1190 sum0=0.;
1191 for(int i=0; i<6; i++) {
1192 if(amax0<fabs(terms0[i])) amax0=fabs(terms0[i]);
1193 sum0 += terms0[i];
1194 }
1195 std::vector<std::vector<double>> nrCe;
1196 nrCe.push_back(std::vector<double>{px, py, sum0});
1197 px=2.*g*x+h*y+j;
1198 py=h*x+2.*i*y+k;
1199 sum1=0.;
1200 for(int i=6; i<12; i++) {
1201 if(amax1<fabs(terms0[i])) amax1=fabs(terms0[i]);
1202 sum1 += terms0[i];
1203 }
1204 nrCe.push_back(std::vector<double>{px, py, sum1});
1205 std::vector<double> dn;
1206 bool ret=linearSolver(nrCe, dn);
1207 // DEBUG_HEADER
1208 // qDebug()<<"i0="<<i0<<"\tf=("<<sum0<<','<<sum1<<")\tdn=("<<dn[0]<<","<<dn[1]<<")";
1209 if(!i0){
1210 f00=sum0;
1211 f01=sum1;
1212 }
1213 if(!ret) break;
1214 v -= RS_Vector(dn[0], dn[1]);
1215 }
1216 if( fabs(sum0)> fabs(f00) && fabs(sum1)>fabs(f01)){
1217 v=v0;
1218 sum0=f00;
1219 sum1=f01;
1220 }
1221
1222 // DEBUG_HEADER
1223 // std::cout<<"verifying: x="<<x<<"\ty="<<y<<std::endl;
1224 // std::cout<<"0: maxterm: "<<amax0<<std::endl;
1225 // std::cout<<"verifying: fabs(a*x2 + b*x*y+c*y2+d*x+e*y+f)/maxterm="<<fabs(sum0)/amax0<<" required to be smaller than "<<sqrt(6.)*sqrt(DBL_EPSILON)<<std::endl;
1226 // std::cout<<"1: maxterm: "<<amax1<<std::endl;
1227 // std::cout<<"verifying: fabs(g*x2+h*x*y+i*y2+j*x+k*y+l)/maxterm="<< fabs(sum1)/amax1<<std::endl;
1228 const double tols=2.*sqrt(6.)*sqrt(DBL_EPSILON); //experimental tolerances to verify simultaneous quadratic
1229
1230 return (amax0<=tols || fabs(sum0)/amax0<tols) && (amax1<=tols || fabs(sum1)/amax1<tols);
1231 }
1232 //EOF
1233