Skip to content

Commit 867f950

Browse files
committed
Refactor by moving derivative table calculation to separate fcn
1 parent cb79ebe commit 867f950

File tree

1 file changed

+54
-43
lines changed
  • lib/node_modules/@stdlib/math/base/special/polygamma/lib

1 file changed

+54
-43
lines changed

lib/node_modules/@stdlib/math/base/special/polygamma/lib/polycotpi.js

+54-43
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,44 @@ function zeros( len ) {
100100
return out;
101101
} // end FUNCTION zeros()
102102

103+
/**
104+
* Updates the derivatives table.
105+
*
106+
* @private
107+
* @param {PositiveInteger} n - derivative
108+
*/
109+
function calculateDerivatives( n ) {
110+
var noffset; // offset for next row
111+
var offset; // 1 if the first cos power is 0; otherwise 0
112+
var ncols; // how many entries there are in the current row
113+
var mcols; // how many entries there will be in the next row
114+
var mo; // largest order of the polynomial of cos terms
115+
var so; // order of the sin term
116+
var co; // order of the cosine term in entry "j"
117+
var i;
118+
var j;
119+
var k;
120+
121+
for ( i = table.length-1; i < n-1; i++ ) {
122+
offset = ( i&1 )|0;
123+
so = ( i+2 )|0;
124+
mo = ( so-1 )|0;
125+
ncols = ( (mo-offset)/2 )|0;
126+
noffset = offset ? 0 : 1;
127+
mcols = ( (mo+1-noffset)/2 )|0;
128+
table.push( zeros( mcols+1 ) );
129+
for ( j = 0; j <= ncols; j++ ) {
130+
co = ( (2*j)+offset )|0;
131+
k = ( (co+1)/2 )|0;
132+
table[ i+1 ][ k ] += ((co-so)*table[i][j]) / (so-1);
133+
if ( co ) {
134+
k = ( (co-1)/2 )|0;
135+
table[ i+1 ][ k ] += (-co*table[i][j]) / (so-1);
136+
}
137+
}
138+
}
139+
}
140+
103141

104142
// MAIN //
105143

@@ -125,7 +163,7 @@ function zeros( len ) {
125163
*
126164
* - Note that there are many different ways of representing this derivative thanks to the many trigonometric identities available. In particular, the sum of powers of cosines could be replaced by a sum of cosine multiple angles, and, indeed, if you plug the derivative into Mathematica, this is the form it will give. The two forms are related via the Chebeshev polynomials of the first kind and \\( T_n(\cos(x)) = \cos(n x) \\). The polynomial form has the great advantage that all the cosine terms are zero at half integer arguments - right where this function has it's minimum - thus avoiding cancellation error in this region.
127165
*
128-
* - And finally, since every other term in the polynomials is zero, we can save space by only storing the non-zero terms. This greatly complexifies subscripting the tables in the calculation, but halves the storage space (and complexity for that matter).
166+
* - And finally, since every other term in the polynomials is zero, we can save space by only storing the non-zero terms. This greatly increases complexity when subscripting the tables in the calculation, but halves the storage space (and complexity for that matter).
129167
*
130168
*
131169
* @private
@@ -135,21 +173,11 @@ function zeros( len ) {
135173
* @returns {number} n'th derivative
136174
*/
137175
function polycotpi( n, x, xc ) {
138-
var nextMaxColumns;
139-
var maxCosOrder;
140-
var maxColumns;
141-
var nextOffset;
142176
var powTerms;
143-
var cosOrder;
144-
var sinOrder;
145-
var column;
146-
var offset;
147-
var index;
148177
var idx;
149178
var out;
150179
var sum;
151180
var c;
152-
var i;
153181
var s;
154182

155183
s = ( abs( x ) < abs( xc ) ) ? sinpi( x ) : sinpi( xc );
@@ -180,34 +208,18 @@ function polycotpi( n, x, xc ) {
180208
case 12:
181209
return PI12 * c * polyval12( c*c ) / pow( s, 13.0 );
182210
}
183-
// We'll have to compute the coefficients up to n, complexity is O(n^2) which we don't worry about as the values are computed once and then cached. However, if the final evaluation would have too many terms just bail out right away:
184-
if ( n / 2 > MAX_SERIES_ITERATIONS ) {
185-
debug( 'The value of n is so large that we\'re unable to compute the result in reasonable time.' );
211+
// We'll have to compute the coefficients up to `n`, complexity is O(n^2) which we don't worry about as the values are computed once and then cached. However, if the final evaluation would have too many terms just bail out right away:
212+
if ( n/2 > MAX_SERIES_ITERATIONS ) {
213+
debug( 'The value of `n` is so large that we\'re unable to compute the result in reasonable time.' );
186214
return NaN;
187215
}
188-
index = n - 1;
189-
if ( index >= table.length ) {
190-
for ( i = table.length - 1; i < index; ++i ) {
191-
offset = ( i & 1 ) | 0; // 1 if the first cos power is 0, otherwise 0.
192-
sinOrder = ( i + 2 ) | 0; // Order of the sin term
193-
maxCosOrder = ( sinOrder - 1 ) | 0; // Largest order of the polynomial of cos terms
194-
maxColumns = ( ( maxCosOrder - offset ) / 2 ) | 0; // How many entries there are in the current row.
195-
nextOffset = offset ? 0 : 1;
196-
nextMaxColumns = ( ( maxCosOrder + 1 - nextOffset ) / 2 ) | 0; // How many entries there will be in the next row
197-
table.push( zeros( nextMaxColumns + 1 ) );
198-
for ( column = 0; column <= maxColumns; ++column ) {
199-
cosOrder = ( ( 2*column ) + offset ) | 0; // Order of the cosine term in entry "column"
200-
idx = ( ( cosOrder+1 ) / 2 ) | 0;
201-
table[i + 1][ idx ] += ((cosOrder - sinOrder) * table[i][column]) / (sinOrder - 1); // eslint-disable-line max-len
202-
if ( cosOrder ) {
203-
idx = ( ( cosOrder-1 ) / 2 ) | 0; //
204-
table[i + 1][ idx ] += (-cosOrder * table[i][column]) / (sinOrder - 1); // eslint-disable-line max-len
205-
}
206-
}
207-
}
216+
idx = n - 1;
217+
if ( idx >= table.length ) {
218+
// Lazily calculate derivatives:
219+
calculateDerivatives( n );
208220
}
209-
sum = evalpoly( table[ index ], c*c );
210-
if ( index & 1 ) {
221+
sum = evalpoly( table[ idx ], c*c );
222+
if ( idx & 1 ) {
211223
sum *= c; // First coefficient is order 1, and really an odd polynomial.
212224
}
213225
if ( sum === 0.0 ) {
@@ -216,17 +228,16 @@ function polycotpi( n, x, xc ) {
216228
// The remaining terms are computed using logs since the powers and factorials get real large real quick:
217229
powTerms = n * LN_PI;
218230
if ( s === 0.0 ) {
219-
return ( sum >= 0 ) ? PINF : NINF;
231+
return ( sum >= 0.0 ) ? PINF : NINF;
220232
}
221-
powTerms -= ln( abs( s ) ) * ( n + 1 );
222-
powTerms += gammaln( n );
223-
powTerms += ln( abs(sum) );
233+
powTerms -= ln( abs( s ) ) * ( n+1 );
234+
powTerms += gammaln( n ) + ln( abs(sum) );
224235

225-
if (powTerms > MAX_LN ) {
226-
return ( sum >= 0 ) ? PINF : NINF;
236+
if ( powTerms > MAX_LN ) {
237+
return ( sum >= 0.0 ) ? PINF : NINF;
227238
}
228239
out = exp( powTerms ) * signum( sum );
229-
if ( s < 0 && ( (n + 1) & 1 ) ) {
240+
if ( s < 0.0 && ( (n+1)&1 ) ) {
230241
out *= -1;
231242
}
232243
return out;

0 commit comments

Comments
 (0)