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; }