| 1 | //$$ solution.cpp // solve routines |
|---|
| 2 | |
|---|
| 3 | // Copyright (C) 1994: R B Davies |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | #define WANT_STREAM // include.h will get stream fns |
|---|
| 7 | #define WANT_MATH // include.h will get math fns |
|---|
| 8 | |
|---|
| 9 | #include "include.h" |
|---|
| 10 | #include "boolean.h" |
|---|
| 11 | #include "myexcept.h" |
|---|
| 12 | |
|---|
| 13 | #include "solution.h" |
|---|
| 14 | |
|---|
| 15 | #ifdef use_namespace |
|---|
| 16 | namespace RBD_COMMON { |
|---|
| 17 | #endif |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | void R1_R1::Set(Real X) |
|---|
| 21 | { |
|---|
| 22 | if ((!minXinf && X <= minX) || (!maxXinf && X >= maxX)) |
|---|
| 23 | Throw(SolutionException("X value out of range")); |
|---|
| 24 | x = X; xSet = true; |
|---|
| 25 | } |
|---|
| 26 | |
|---|
| 27 | R1_R1::operator Real() |
|---|
| 28 | { |
|---|
| 29 | if (!xSet) Throw(SolutionException("Value of X not set")); |
|---|
| 30 | Real y = operator()(); |
|---|
| 31 | return y; |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | unsigned long SolutionException::Select; |
|---|
| 35 | |
|---|
| 36 | SolutionException::SolutionException(const char* a_what) : Exception() |
|---|
| 37 | { |
|---|
| 38 | Select = Exception::Select; |
|---|
| 39 | AddMessage("Error detected by solution package\n"); |
|---|
| 40 | AddMessage(a_what); AddMessage("\n"); |
|---|
| 41 | if (a_what) Tracer::AddTrace(); |
|---|
| 42 | }; |
|---|
| 43 | |
|---|
| 44 | inline Real square(Real x) { return x*x; } |
|---|
| 45 | |
|---|
| 46 | void OneDimSolve::LookAt(int V) |
|---|
| 47 | { |
|---|
| 48 | lim--; |
|---|
| 49 | if (!lim) Throw(SolutionException("Does not converge")); |
|---|
| 50 | Last = V; |
|---|
| 51 | Real yy = function(x[V]) - YY; |
|---|
| 52 | Finish = (fabs(yy) <= accY) || (Captured && fabs(x[L]-x[U]) <= accX ); |
|---|
| 53 | y[V] = vpol*yy; |
|---|
| 54 | } |
|---|
| 55 | |
|---|
| 56 | void OneDimSolve::HFlip() { hpol=-hpol; State(U,C,L); } |
|---|
| 57 | |
|---|
| 58 | void OneDimSolve::VFlip() |
|---|
| 59 | { vpol = -vpol; y[0] = -y[0]; y[1] = -y[1]; y[2] = -y[2]; } |
|---|
| 60 | |
|---|
| 61 | void OneDimSolve::Flip() |
|---|
| 62 | { |
|---|
| 63 | hpol=-hpol; vpol=-vpol; State(U,C,L); |
|---|
| 64 | y[0] = -y[0]; y[1] = -y[1]; y[2] = -y[2]; |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | void OneDimSolve::State(int I, int J, int K) { L=I; C=J; U=K; } |
|---|
| 68 | |
|---|
| 69 | void OneDimSolve::Linear(int I, int J, int K) |
|---|
| 70 | { |
|---|
| 71 | x[J] = (x[I]*y[K] - x[K]*y[I])/(y[K] - y[I]); |
|---|
| 72 | // cout << "Linear\n"; |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | void OneDimSolve::Quadratic(int I, int J, int K) |
|---|
| 76 | { |
|---|
| 77 | // result to overwrite I |
|---|
| 78 | Real YJK, YIK, YIJ, XKI, XKJ; |
|---|
| 79 | YJK = y[J] - y[K]; YIK = y[I] - y[K]; YIJ = y[I] - y[J]; |
|---|
| 80 | XKI = (x[K] - x[I]); |
|---|
| 81 | XKJ = (x[K]*y[J] - x[J]*y[K])/YJK; |
|---|
| 82 | if ( square(YJK/YIK)>(x[K] - x[J])/XKI || |
|---|
| 83 | square(YIJ/YIK)>(x[J] - x[I])/XKI ) |
|---|
| 84 | { |
|---|
| 85 | x[I] = XKJ; |
|---|
| 86 | // cout << "Quadratic - exceptional\n"; |
|---|
| 87 | } |
|---|
| 88 | else |
|---|
| 89 | { |
|---|
| 90 | XKI = (x[K]*y[I] - x[I]*y[K])/YIK; |
|---|
| 91 | x[I] = (XKJ*y[I] - XKI*y[J])/YIJ; |
|---|
| 92 | // cout << "Quadratic - normal\n"; |
|---|
| 93 | } |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | Real OneDimSolve::Solve(Real Y, Real X, Real Dev, int Lim) |
|---|
| 97 | { |
|---|
| 98 | enum Loop { start, captured1, captured2, binary, finish }; |
|---|
| 99 | Tracer et("OneDimSolve::Solve"); |
|---|
| 100 | lim=Lim; Captured = false; |
|---|
| 101 | if (Dev==0.0) Throw(SolutionException("Dev is zero")); |
|---|
| 102 | L=0; C=1; U=2; vpol=1; hpol=1; y[C]=0.0; y[U]=0.0; |
|---|
| 103 | if (Dev<0.0) { hpol=-1; Dev = -Dev; } |
|---|
| 104 | YY=Y; // target value |
|---|
| 105 | x[L] = X; // initial trial value |
|---|
| 106 | if (!function.IsValid(X)) |
|---|
| 107 | Throw(SolutionException("Starting value is invalid")); |
|---|
| 108 | Loop TheLoop = start; |
|---|
| 109 | for (;;) |
|---|
| 110 | { |
|---|
| 111 | switch (TheLoop) |
|---|
| 112 | { |
|---|
| 113 | case start: |
|---|
| 114 | LookAt(L); if (Finish) { TheLoop = finish; break; } |
|---|
| 115 | if (y[L]>0.0) VFlip(); // so Y[L] < 0 |
|---|
| 116 | |
|---|
| 117 | x[U] = X + Dev * hpol; |
|---|
| 118 | if (!function.maxXinf && x[U] > function.maxX) |
|---|
| 119 | x[U] = (function.maxX + X) / 2.0; |
|---|
| 120 | if (!function.minXinf && x[U] < function.minX) |
|---|
| 121 | x[U] = (function.minX + X) / 2.0; |
|---|
| 122 | |
|---|
| 123 | LookAt(U); if (Finish) { TheLoop = finish; break; } |
|---|
| 124 | if (y[U] > 0.0) { TheLoop = captured1; Captured = true; break; } |
|---|
| 125 | if (y[U] == y[L]) |
|---|
| 126 | Throw(SolutionException("Function is flat")); |
|---|
| 127 | if (y[U] < y[L]) HFlip(); // Change direction |
|---|
| 128 | State(L,U,C); |
|---|
| 129 | for (i=0; i<20; i++) |
|---|
| 130 | { |
|---|
| 131 | // cout << "Searching for crossing point\n"; |
|---|
| 132 | // Have L C then crossing point, Y[L]<Y[C]<0 |
|---|
| 133 | x[U] = x[C] + Dev * hpol; |
|---|
| 134 | if (!function.maxXinf && x[U] > function.maxX) |
|---|
| 135 | x[U] = (function.maxX + x[C]) / 2.0; |
|---|
| 136 | if (!function.minXinf && x[U] < function.minX) |
|---|
| 137 | x[U] = (function.minX + x[C]) / 2.0; |
|---|
| 138 | |
|---|
| 139 | LookAt(U); if (Finish) { TheLoop = finish; break; } |
|---|
| 140 | if (y[U] > 0) { TheLoop = captured2; Captured = true; break; } |
|---|
| 141 | if (y[U] < y[C]) |
|---|
| 142 | Throw(SolutionException("Function is not monotone")); |
|---|
| 143 | Dev *= 2.0; |
|---|
| 144 | State(C,U,L); |
|---|
| 145 | } |
|---|
| 146 | if (TheLoop != start ) break; |
|---|
| 147 | Throw(SolutionException("Cannot locate a crossing point")); |
|---|
| 148 | |
|---|
| 149 | case captured1: |
|---|
| 150 | // cout << "Captured - 1\n"; |
|---|
| 151 | // We have 2 points L and U with crossing between them |
|---|
| 152 | Linear(L,C,U); // linear interpolation |
|---|
| 153 | // - result to C |
|---|
| 154 | LookAt(C); if (Finish) { TheLoop = finish; break; } |
|---|
| 155 | if (y[C] > 0.0) Flip(); // Want y[C] < 0 |
|---|
| 156 | if (y[C] < 0.5*y[L]) { State(C,L,U); TheLoop = binary; break; } |
|---|
| 157 | |
|---|
| 158 | case captured2: |
|---|
| 159 | // cout << "Captured - 2\n"; |
|---|
| 160 | // We have L,C before crossing, U after crossing |
|---|
| 161 | Quadratic(L,C,U); // quad interpolation |
|---|
| 162 | // - result to L |
|---|
| 163 | State(C,L,U); |
|---|
| 164 | if ((x[C] - x[L])*hpol <= 0.0 || (x[C] - x[U])*hpol >= 0.0) |
|---|
| 165 | { TheLoop = captured1; break; } |
|---|
| 166 | LookAt(C); if (Finish) { TheLoop = finish; break; } |
|---|
| 167 | // cout << "Through first stage\n"; |
|---|
| 168 | if (y[C] > 0.0) Flip(); |
|---|
| 169 | if (y[C] > 0.5*y[L]) { TheLoop = captured2; break; } |
|---|
| 170 | else { State(C,L,U); TheLoop = captured1; break; } |
|---|
| 171 | |
|---|
| 172 | case binary: |
|---|
| 173 | // We have L, U around crossing - do binary search |
|---|
| 174 | // cout << "Binary\n"; |
|---|
| 175 | for (i=3; i; i--) |
|---|
| 176 | { |
|---|
| 177 | x[C] = 0.5*(x[L]+x[U]); |
|---|
| 178 | LookAt(C); if (Finish) { TheLoop = finish; break; } |
|---|
| 179 | if (y[C]>0.0) State(L,U,C); else State(C,L,U); |
|---|
| 180 | } |
|---|
| 181 | if (TheLoop != binary) break; |
|---|
| 182 | TheLoop = captured1; break; |
|---|
| 183 | |
|---|
| 184 | case finish: |
|---|
| 185 | return x[Last]; |
|---|
| 186 | |
|---|
| 187 | } |
|---|
| 188 | } |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | bool R1_R1::IsValid(Real X) |
|---|
| 192 | { |
|---|
| 193 | Set(X); |
|---|
| 194 | return (minXinf || x > minX) && (maxXinf || x < maxX); |
|---|
| 195 | } |
|---|
| 196 | |
|---|
| 197 | #ifdef use_namespace |
|---|
| 198 | } |
|---|
| 199 | #endif |
|---|
| 200 | |
|---|