1 // This code has been mechanically translated from the original FORTRAN 2 // code at http://netlib.org/minpack. 3 4 /** Authors: Lars Tandle Kyllingstad 5 Copyright: Copyright (c) 2009, Lars T. Kyllingstad. All rights reserved. 6 License: Boost Licence 1.0 7 */ 8 module scid.ports.minpack.r1updt; 9 10 11 private import std.math: abs, sqrt; 12 13 14 15 16 /** Given an m by n lower trapezoidal matrix S, an m-vector u, 17 and an n-vector v, the problem is to determine an 18 orthogonal matrix Q such that 19 --- 20 t 21 (S + u v ) Q 22 --- 23 is again lower trapezoidal. 24 25 This subroutine determines Q as the product of 2*(n - 1) 26 transformations 27 --- 28 gv(n-1)*...*gv(1)*gw(1)*...*gw(n-1) 29 --- 30 where gv(i), gw(i) are Givens rotations in the (i,n) plane 31 which eliminate elements in the i-th and n-th planes, 32 respectively. Q itself is not accumulated, rather the 33 information to recover the gv, gw rotations is returned. 34 35 Params: 36 37 m = a positive integer input variable set to the number 38 of rows of S. 39 40 n = a positive integer input variable set to the number 41 of columns of S. n must not exceed m. 42 43 s = an array of length ls. on input s must contain the lower 44 trapezoidal matrix S stored by columns. on output s contains 45 the lower trapezoidal matrix produced as described above. 46 47 ls = a positive integer input variable not less than 48 (n*(2*m-n+1))/2. 49 50 u = an input array of length m which must contain the 51 vector u. 52 53 v = an array of length n. on input v must contain the vector 54 v. on output v(i) contains the information necessary to 55 recover the Givens rotation gv(i) described above. 56 57 w = an output array of length m. w(i) contains information 58 necessary to recover the Givens rotation gw(i) described 59 above. 60 61 sing = a logical output variable. sing is set true if any 62 of the diagonal elements of the output s are zero. otherwise 63 sing is set false. 64 */ 65 void r1updt(Real)(size_t m, size_t n, Real* s, size_t ls, 66 Real* u, Real* v, Real* w, out bool sing) 67 { 68 size_t i, j, jj, l, nmj, nm1; 69 Real cos, cotan, sin, tan, tau, temp; 70 71 enum : Real 72 { 73 one = 1.0, 74 p5 = 0.5, 75 p25 = 0.25, 76 zero = 0.0, 77 78 // giant is the largest magnitude 79 giant = Real.max 80 } 81 82 83 // Initialize the diagonal element pointer. 84 jj = (n*(2*m - n + 1))/2 - (m - n) - 1; 85 86 87 // Move the nontrivial part of the last column of s into w. 88 nm1 = n - 1; 89 for (i=nm1, l=jj; i<m; i++, l++) 90 w[i] = s[l]; 91 92 93 // Rotate the vector v into a multiple of the n-th unit vector 94 // in such a way that a spike is introduced into w. 95 if (n > 1) 96 { 97 for (nmj=2; nmj<=n; nmj++) 98 { 99 j = n - nmj; 100 jj = jj - (m - j); 101 w[j] = zero; 102 103 if (v[j] != zero) 104 { 105 // Determine a Givens rotation which eliminates the 106 // j-th element of v. 107 if (abs(v[nm1]) < abs(v[j])) 108 { 109 cotan = v[nm1]/v[j]; 110 sin = p5/sqrt(p25+p25*cotan*cotan); 111 cos = sin*cotan; 112 tau = one; 113 if (abs(cos)*giant > one) tau = one/cos; 114 } 115 else 116 { 117 tan = v[j]/v[nm1]; 118 cos = p5/sqrt(p25+p25*tan*tan); 119 sin = cos*tan; 120 tau = sin; 121 } 122 123 124 // Apply the transformation to v and store the information 125 // necessary to recover the Givens rotation. 126 v[nm1] = sin*v[j] + cos*v[nm1]; 127 v[j] = tau; 128 129 130 // Apply the transformation to S and extend the spike in w. 131 for (i=j, l=jj; i<m; i++, l++) 132 { 133 temp = cos*s[l] - sin*w[i]; 134 w[i] = sin*s[l] + cos*w[i]; 135 s[l] = temp; 136 } 137 } 138 } 139 } 140 141 142 // Add the spike from the rank 1 update to w. 143 for (i=0; i<m; i++) 144 w[i] = w[i] + v[nm1]*u[i]; 145 146 147 // Eliminate the spike. 148 sing = false; 149 if (n > 1) 150 { 151 for (j=0; j<nm1; j++) 152 { 153 if (w[j] != zero) 154 { 155 // Determine a Givens rotation which eliminates the 156 // j-th element of the spike. 157 if (abs(s[jj]) < abs(w[j])) 158 { 159 cotan = s[jj]/w[j]; 160 sin = p5/sqrt(p25+p25*cotan*cotan); 161 cos = sin*cotan; 162 tau = one; 163 if (abs(cos)*giant > one) tau = one/cos; 164 } 165 else 166 { 167 tan = w[j]/s[jj]; 168 cos = p5/sqrt(p25+p25*tan*tan); 169 sin = cos*tan; 170 tau = sin; 171 } 172 173 174 // Apply the transformation to S and reduce the spike in w. 175 for (i=j, l=jj; i<m; i++, l++) 176 { 177 temp = cos*s[l] + sin*w[i]; 178 w[i] = -sin*s[l] + cos*w[i]; 179 s[l] = temp; 180 } 181 182 183 // Store the information necessary to recover the 184 // Givens rotation. 185 w[j] = tau; 186 } 187 188 189 // Test for zero diagonal elements in the output S. 190 if (s[jj] == zero) sing = true; 191 jj += m - j; 192 } 193 } 194 195 196 // Move w back into the last column of the output s. 197 for (i=nm1, l=jj; i<m; i++, l++) 198 s[l] = w[i]; 199 if (s[jj] == zero) sing = true; 200 } 201 202 203 unittest { alias r1updt!(real) rr1updt; }