@stdlib/math-iter-sequences-continued-fraction 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +481 -0
- package/NOTICE +1 -0
- package/README.md +384 -0
- package/docs/img/equation_continued_fraction.svg +82 -0
- package/docs/img/equation_simple_continued_fraction.svg +69 -0
- package/docs/img/equation_simple_continued_fraction_convergents.svg +100 -0
- package/docs/repl.txt +77 -0
- package/docs/types/index.d.ts +87 -0
- package/docs/types/test.ts +98 -0
- package/lib/index.js +54 -0
- package/lib/main.js +349 -0
- package/lib/validate.js +89 -0
- package/package.json +95 -0
package/docs/repl.txt
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
{{alias}}( x[, options] )
|
|
3
|
+
Returns an iterator which generates a list of all continued fraction terms
|
|
4
|
+
which can be obtained given the precision of `x`.
|
|
5
|
+
|
|
6
|
+
If an environment supports Symbol.iterator, the returned iterator is
|
|
7
|
+
iterable.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
x: number
|
|
12
|
+
Input value.
|
|
13
|
+
|
|
14
|
+
options: Object (optional)
|
|
15
|
+
Function options.
|
|
16
|
+
|
|
17
|
+
options.iter: integer (optional)
|
|
18
|
+
Maximum number of iterations. Default: 1e308.
|
|
19
|
+
|
|
20
|
+
options.tol: number (optional)
|
|
21
|
+
Tolerance at which to terminate further evaluation of the continued
|
|
22
|
+
fraction. Default: floating-point epsilon.
|
|
23
|
+
|
|
24
|
+
options.returns: string (optional)
|
|
25
|
+
Specifies the type of result to return. Must be one of
|
|
26
|
+
|
|
27
|
+
- terms: return continued fraction terms
|
|
28
|
+
- convergents: return continued fraction convergents
|
|
29
|
+
- *: return both continued fraction terms and their associated
|
|
30
|
+
convergents as a two-element array.
|
|
31
|
+
|
|
32
|
+
Default: 'terms'.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
iterator: Object
|
|
37
|
+
Iterator.
|
|
38
|
+
|
|
39
|
+
iterator.next(): Function
|
|
40
|
+
Returns an iterator protocol-compliant object containing the next
|
|
41
|
+
iterated value (if one exists) and a boolean flag indicating whether the
|
|
42
|
+
iterator is finished.
|
|
43
|
+
|
|
44
|
+
iterator.return( [value] ): Function
|
|
45
|
+
Finishes an iterator and returns a provided value.
|
|
46
|
+
|
|
47
|
+
Examples
|
|
48
|
+
--------
|
|
49
|
+
// Return continued fraction terms...
|
|
50
|
+
> var it = {{alias}}( 3.245 );
|
|
51
|
+
> var v = it.next().value
|
|
52
|
+
3
|
|
53
|
+
> v = it.next().value
|
|
54
|
+
4
|
|
55
|
+
> v = it.next().value
|
|
56
|
+
12
|
|
57
|
+
> v = it.next().value
|
|
58
|
+
4
|
|
59
|
+
> var bool = it.next().done
|
|
60
|
+
true
|
|
61
|
+
|
|
62
|
+
// Return continued fraction convergents...
|
|
63
|
+
> it = {{alias}}( 3.245, { 'returns': 'convergents' } );
|
|
64
|
+
> v = it.next().value
|
|
65
|
+
3.0
|
|
66
|
+
> v = it.next().value
|
|
67
|
+
3.25
|
|
68
|
+
> v = it.next().value
|
|
69
|
+
~3.2449
|
|
70
|
+
> v = it.next().value
|
|
71
|
+
3.245
|
|
72
|
+
> bool = it.next().done
|
|
73
|
+
true
|
|
74
|
+
|
|
75
|
+
See Also
|
|
76
|
+
--------
|
|
77
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @license Apache-2.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2022 The Stdlib Authors.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// TypeScript Version: 2.0
|
|
20
|
+
|
|
21
|
+
/// <reference types="@stdlib/types"/>
|
|
22
|
+
|
|
23
|
+
import { Iterator as Iter, IterableIterator } from '@stdlib/types/iter';
|
|
24
|
+
|
|
25
|
+
// Define a union type representing both iterable and non-iterable iterators:
|
|
26
|
+
type Iterator = Iter | IterableIterator;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Interface describing function options.
|
|
30
|
+
*/
|
|
31
|
+
interface Options {
|
|
32
|
+
/**
|
|
33
|
+
* Maximum number of iterations.
|
|
34
|
+
*/
|
|
35
|
+
iter?: number;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Tolerance at which to terminate further evaluation of the continued fraction.
|
|
39
|
+
*/
|
|
40
|
+
tol?: number;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Specifies the type of result to return.
|
|
44
|
+
*/
|
|
45
|
+
returns?: 'terms' | 'convergents' | '*';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns an iterator which generates a list of all continued fraction terms which can be obtained given the precision of `x`.
|
|
50
|
+
*
|
|
51
|
+
* ## Notes
|
|
52
|
+
*
|
|
53
|
+
* - If an environment supports `Symbol.iterator`, the returned iterator is iterable.
|
|
54
|
+
*
|
|
55
|
+
* @param x - input value
|
|
56
|
+
* @param options - function options
|
|
57
|
+
* @param options.iter - maximum number of iterations (default: 1e308)
|
|
58
|
+
* @param options.tol - tolerance at which to terminate further evaluation of the continued fraction (default: floating-point epsilon)
|
|
59
|
+
* @param options.returns - specifies the type of result to return (default: 'terms')
|
|
60
|
+
* @throws `iter` option must be a nonnegative integer
|
|
61
|
+
* @throws `tol` option must be a positive finite number
|
|
62
|
+
* @returns iterator
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* var iter = iterContinuedFractionSeq( 3.245 );
|
|
66
|
+
*
|
|
67
|
+
* var v = iter.next().value;
|
|
68
|
+
* // returns 3
|
|
69
|
+
*
|
|
70
|
+
* v = iter.next().value;
|
|
71
|
+
* // returns 4
|
|
72
|
+
*
|
|
73
|
+
* v = iter.next().value;
|
|
74
|
+
* // returns 12
|
|
75
|
+
*
|
|
76
|
+
* v = iter.next().value;
|
|
77
|
+
* // returns 4
|
|
78
|
+
*
|
|
79
|
+
* var bool = iter.next().done;
|
|
80
|
+
* // returns true
|
|
81
|
+
*/
|
|
82
|
+
declare function iterContinuedFractionSeq( x: number, options?: Options ): Iterator; // tslint:disable-line:max-line-length
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
// EXPORTS //
|
|
86
|
+
|
|
87
|
+
export = iterContinuedFractionSeq;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @license Apache-2.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2022 The Stdlib Authors.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import iterContinuedFractionSeq = require( './index' );
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// TESTS //
|
|
23
|
+
|
|
24
|
+
// The function returns an iterator...
|
|
25
|
+
{
|
|
26
|
+
iterContinuedFractionSeq( 3.14 ); // $ExpectType Iterator
|
|
27
|
+
iterContinuedFractionSeq( 3.14, {} ); // $ExpectType Iterator
|
|
28
|
+
iterContinuedFractionSeq( 3.14, { 'iter': 10 } ); // $ExpectType Iterator
|
|
29
|
+
iterContinuedFractionSeq( 3.14, { 'tol': 1.0e-10 } ); // $ExpectType Iterator
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The compiler throws an error if the function is provided a first argument which is not a number...
|
|
33
|
+
{
|
|
34
|
+
iterContinuedFractionSeq( '5' ); // $ExpectError
|
|
35
|
+
iterContinuedFractionSeq( true ); // $ExpectError
|
|
36
|
+
iterContinuedFractionSeq( false ); // $ExpectError
|
|
37
|
+
iterContinuedFractionSeq( null ); // $ExpectError
|
|
38
|
+
iterContinuedFractionSeq( [] ); // $ExpectError
|
|
39
|
+
iterContinuedFractionSeq( {} ); // $ExpectError
|
|
40
|
+
iterContinuedFractionSeq( ( x: number ): number => x ); // $ExpectError
|
|
41
|
+
|
|
42
|
+
iterContinuedFractionSeq( '5', {} ); // $ExpectError
|
|
43
|
+
iterContinuedFractionSeq( true, {} ); // $ExpectError
|
|
44
|
+
iterContinuedFractionSeq( false, {} ); // $ExpectError
|
|
45
|
+
iterContinuedFractionSeq( null, {} ); // $ExpectError
|
|
46
|
+
iterContinuedFractionSeq( [], {} ); // $ExpectError
|
|
47
|
+
iterContinuedFractionSeq( {}, {} ); // $ExpectError
|
|
48
|
+
iterContinuedFractionSeq( ( x: number ): number => x, {} ); // $ExpectError
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// The compiler throws an error if the function is provided a second argument which is not an options object...
|
|
52
|
+
{
|
|
53
|
+
iterContinuedFractionSeq( 3.14, '5' ); // $ExpectError
|
|
54
|
+
iterContinuedFractionSeq( 3.14, 5 ); // $ExpectError
|
|
55
|
+
iterContinuedFractionSeq( 3.14, true ); // $ExpectError
|
|
56
|
+
iterContinuedFractionSeq( 3.14, false ); // $ExpectError
|
|
57
|
+
iterContinuedFractionSeq( 3.14, null ); // $ExpectError
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The compiler throws an error if the function is provided an `iter` option which is not a number...
|
|
61
|
+
{
|
|
62
|
+
iterContinuedFractionSeq( 3.14, { 'iter': '5' } ); // $ExpectError
|
|
63
|
+
iterContinuedFractionSeq( 3.14, { 'iter': true } ); // $ExpectError
|
|
64
|
+
iterContinuedFractionSeq( 3.14, { 'iter': false } ); // $ExpectError
|
|
65
|
+
iterContinuedFractionSeq( 3.14, { 'iter': null } ); // $ExpectError
|
|
66
|
+
iterContinuedFractionSeq( 3.14, { 'iter': [] } ); // $ExpectError
|
|
67
|
+
iterContinuedFractionSeq( 3.14, { 'iter': {} } ); // $ExpectError
|
|
68
|
+
iterContinuedFractionSeq( 3.14, { 'iter': ( x: number ): number => x } ); // $ExpectError
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The compiler throws an error if the function is provided a `tol` option which is not a number...
|
|
72
|
+
{
|
|
73
|
+
iterContinuedFractionSeq( 3.14, { 'tol': '5' } ); // $ExpectError
|
|
74
|
+
iterContinuedFractionSeq( 3.14, { 'tol': true } ); // $ExpectError
|
|
75
|
+
iterContinuedFractionSeq( 3.14, { 'tol': false } ); // $ExpectError
|
|
76
|
+
iterContinuedFractionSeq( 3.14, { 'tol': null } ); // $ExpectError
|
|
77
|
+
iterContinuedFractionSeq( 3.14, { 'tol': [] } ); // $ExpectError
|
|
78
|
+
iterContinuedFractionSeq( 3.14, { 'tol': {} } ); // $ExpectError
|
|
79
|
+
iterContinuedFractionSeq( 3.14, { 'tol': ( x: number ): number => x } ); // $ExpectError
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// The compiler throws an error if the function is provided a `returns` option which is not a supported value...
|
|
83
|
+
{
|
|
84
|
+
iterContinuedFractionSeq( 3.14, { 'returns': '5' } ); // $ExpectError
|
|
85
|
+
iterContinuedFractionSeq( 3.14, { 'returns': 5 } ); // $ExpectError
|
|
86
|
+
iterContinuedFractionSeq( 3.14, { 'returns': true } ); // $ExpectError
|
|
87
|
+
iterContinuedFractionSeq( 3.14, { 'returns': false } ); // $ExpectError
|
|
88
|
+
iterContinuedFractionSeq( 3.14, { 'returns': null } ); // $ExpectError
|
|
89
|
+
iterContinuedFractionSeq( 3.14, { 'returns': [] } ); // $ExpectError
|
|
90
|
+
iterContinuedFractionSeq( 3.14, { 'returns': {} } ); // $ExpectError
|
|
91
|
+
iterContinuedFractionSeq( 3.14, { 'returns': ( x: number ): number => x } ); // $ExpectError
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// The compiler throws an error if the function is provided an unsupported number of arguments...
|
|
95
|
+
{
|
|
96
|
+
iterContinuedFractionSeq(); // $ExpectError
|
|
97
|
+
iterContinuedFractionSeq( 3.14, {}, {} ); // $ExpectError
|
|
98
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Apache-2.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2022 The Stdlib Authors.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create an iterator which generates a list of all continued fraction terms which can be obtained given the precision of `x`.
|
|
23
|
+
*
|
|
24
|
+
* @module @stdlib/math-iter-sequences-continued-fraction
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* var iterContinuedFractionSeq = require( '@stdlib/math-iter-sequences-continued-fraction' );
|
|
28
|
+
*
|
|
29
|
+
* var iter = iterContinuedFractionSeq( 3.245 );
|
|
30
|
+
*
|
|
31
|
+
* var v = iter.next().value;
|
|
32
|
+
* // returns 3
|
|
33
|
+
*
|
|
34
|
+
* v = iter.next().value;
|
|
35
|
+
* // returns 4
|
|
36
|
+
*
|
|
37
|
+
* v = iter.next().value;
|
|
38
|
+
* // returns 12
|
|
39
|
+
*
|
|
40
|
+
* v = iter.next().value;
|
|
41
|
+
* // returns 4
|
|
42
|
+
*
|
|
43
|
+
* var bool = iter.next().done;
|
|
44
|
+
* // returns true
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
// MODULES //
|
|
48
|
+
|
|
49
|
+
var iterator = require( './main.js' );
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// EXPORTS //
|
|
53
|
+
|
|
54
|
+
module.exports = iterator;
|
package/lib/main.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Apache-2.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2022 The Stdlib Authors.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
// MODULES //
|
|
22
|
+
|
|
23
|
+
var setReadOnly = require( '@stdlib/utils-define-nonenumerable-read-only-property' );
|
|
24
|
+
var isFinite = require( '@stdlib/assert-is-finite' ).isPrimitive; // eslint-disable-line stdlib/no-redeclare
|
|
25
|
+
var iteratorSymbol = require( '@stdlib/symbol-iterator' );
|
|
26
|
+
var floor = require( '@stdlib/math-base-special-floor' );
|
|
27
|
+
var abs = require( '@stdlib/math-base-special-abs' );
|
|
28
|
+
var EPS = require( '@stdlib/constants-float64-eps' );
|
|
29
|
+
var FIFO = require( '@stdlib/utils-fifo' );
|
|
30
|
+
var validate = require( './validate.js' );
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
// VARIABLES //
|
|
34
|
+
|
|
35
|
+
var TINY = 1.0e-50;
|
|
36
|
+
var RESULT_FUNCTION = {
|
|
37
|
+
'terms': terms,
|
|
38
|
+
'convergents': convergents,
|
|
39
|
+
'*': both
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
// FUNCTIONS //
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns a continued fraction term result.
|
|
47
|
+
*
|
|
48
|
+
* @private
|
|
49
|
+
* @param {number} term - continued fraction term
|
|
50
|
+
* @param {number} convergent - continued fraction convergent
|
|
51
|
+
* @param {boolean} negative - boolean indicating whether the input value is negative
|
|
52
|
+
* @returns {number} term
|
|
53
|
+
*/
|
|
54
|
+
function terms( term, convergent, negative ) {
|
|
55
|
+
return ( negative && term > 0 ) ? -term : term;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns a continued fraction convergent result.
|
|
60
|
+
*
|
|
61
|
+
* @private
|
|
62
|
+
* @param {number} term - continued fraction term
|
|
63
|
+
* @param {number} convergent - continued fraction convergent
|
|
64
|
+
* @param {boolean} negative - boolean indicating whether the input value is negative
|
|
65
|
+
* @returns {number} convergent
|
|
66
|
+
*/
|
|
67
|
+
function convergents( term, convergent, negative ) {
|
|
68
|
+
return ( negative && term > 0 ) ? -convergent : convergent;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns a result as two-element array consisting of a continued fraction term and its associated convergent.
|
|
73
|
+
*
|
|
74
|
+
* @private
|
|
75
|
+
* @param {number} term - continued fraction term
|
|
76
|
+
* @param {number} convergent - continued fraction convergent
|
|
77
|
+
* @param {boolean} negative - boolean indicating whether the input value is negative
|
|
78
|
+
* @returns {Array<number>} result
|
|
79
|
+
*/
|
|
80
|
+
function both( term, convergent, negative ) {
|
|
81
|
+
return ( negative && term > 0 ) ? [ -term, -convergent ] : [ term, convergent ]; // eslint-disable-line max-len
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
// MAIN //
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Returns an iterator which generates a list of all continued fraction terms which can be obtained given the precision of `x`.
|
|
89
|
+
*
|
|
90
|
+
* ## Notes
|
|
91
|
+
*
|
|
92
|
+
* - If an environment supports `Symbol.iterator`, the returned iterator is iterable.
|
|
93
|
+
*
|
|
94
|
+
* @param {number} x - input value
|
|
95
|
+
* @param {Options} [options] - function options
|
|
96
|
+
* @param {NonNegativeInteger} [options.iter=1e308] - maximum number of iterations
|
|
97
|
+
* @param {PositiveNumber} [options.tol] - tolerance at which to terminate further evaluation of the continued fraction
|
|
98
|
+
* @param {string} [options.returns='terms'] - specifies the type of result to return (must be one of `'terms'`, `'convergents'`, or `'*'`)
|
|
99
|
+
* @throws {TypeError} first argument must be a finite number
|
|
100
|
+
* @throws {TypeError} options argument must be an object
|
|
101
|
+
* @throws {TypeError} must provide valid options
|
|
102
|
+
* @returns {Iterator} iterator
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* var iter = iterContinuedFractionSeq( 3.245 );
|
|
106
|
+
*
|
|
107
|
+
* var v = iter.next().value;
|
|
108
|
+
* // returns 3
|
|
109
|
+
*
|
|
110
|
+
* v = iter.next().value;
|
|
111
|
+
* // returns 4
|
|
112
|
+
*
|
|
113
|
+
* v = iter.next().value;
|
|
114
|
+
* // returns 12
|
|
115
|
+
*
|
|
116
|
+
* v = iter.next().value;
|
|
117
|
+
* // returns 4
|
|
118
|
+
*
|
|
119
|
+
* var bool = iter.next().done;
|
|
120
|
+
* // returns true
|
|
121
|
+
*/
|
|
122
|
+
function iterContinuedFractionSeq( x, options ) {
|
|
123
|
+
var result;
|
|
124
|
+
var delta;
|
|
125
|
+
var orig;
|
|
126
|
+
var opts;
|
|
127
|
+
var iter;
|
|
128
|
+
var FLG;
|
|
129
|
+
var err;
|
|
130
|
+
var neg;
|
|
131
|
+
var bi;
|
|
132
|
+
var fi;
|
|
133
|
+
var Ci;
|
|
134
|
+
var Di;
|
|
135
|
+
var q;
|
|
136
|
+
var v;
|
|
137
|
+
var r;
|
|
138
|
+
var i;
|
|
139
|
+
|
|
140
|
+
if ( !isFinite( x ) ) {
|
|
141
|
+
throw new TypeError( 'invalid argument. First argument must be a finite number. Value: `' + x + '`.' );
|
|
142
|
+
}
|
|
143
|
+
opts = {
|
|
144
|
+
'iter': 1e308,
|
|
145
|
+
'tol': EPS,
|
|
146
|
+
'returns': 'terms'
|
|
147
|
+
};
|
|
148
|
+
if ( arguments.length > 1 ) {
|
|
149
|
+
err = validate( opts, options );
|
|
150
|
+
if ( err ) {
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
result = RESULT_FUNCTION[ opts.returns ];
|
|
155
|
+
FLG = 0;
|
|
156
|
+
orig = x;
|
|
157
|
+
|
|
158
|
+
// If `x < 0`, we choose to negate the terms (i.e., [b0; b1, b2, ..., bn] => [-b0; -b1, -b2, ..., -bn]), thus allowing the evaluation of those terms to return the input value. This differs from an alternative approach of having only a negative `b0` and the rest `{b1, ..., bn}` be positive. When doing the latter, floating-point rounding error can prevent recovering the original value...
|
|
159
|
+
if ( x < 0 ) {
|
|
160
|
+
neg = true;
|
|
161
|
+
x = -x;
|
|
162
|
+
} else {
|
|
163
|
+
neg = false;
|
|
164
|
+
}
|
|
165
|
+
i = 0;
|
|
166
|
+
|
|
167
|
+
// Create an iterator protocol-compliant object:
|
|
168
|
+
iter = {};
|
|
169
|
+
setReadOnly( iter, 'next', next );
|
|
170
|
+
setReadOnly( iter, 'return', end );
|
|
171
|
+
|
|
172
|
+
// If an environment supports `Symbol.iterator`, make the iterator iterable:
|
|
173
|
+
if ( iteratorSymbol ) {
|
|
174
|
+
setReadOnly( iter, iteratorSymbol, factory );
|
|
175
|
+
}
|
|
176
|
+
// Continued fractions may not be unique (e.g., [b0; b1, ..., bn, 1] = [b0; b1, ..., bn+1]). Accordingly, in order to ensure we return a canonical representation (i.e., the shorter representation), returned values must lag by two terms, as we'll need to peek ahead to determine if we need to canonicalize before returning the last term. In which case, let's create a FIFO queue for storing terms and their associated convergents...
|
|
177
|
+
q = new FIFO();
|
|
178
|
+
|
|
179
|
+
// Extract the integer part of the provided value:
|
|
180
|
+
bi = floor( x );
|
|
181
|
+
q.push( [ bi, bi ] ); // b0 is both the first term and the first convergent
|
|
182
|
+
if ( bi === x ) {
|
|
183
|
+
// We've been provided an integer value, so no continued fractions to compute...
|
|
184
|
+
FLG = 1;
|
|
185
|
+
return iter;
|
|
186
|
+
}
|
|
187
|
+
// For `0 < x < 1`, leverage the fact that continued fraction representations of a positive rational number and its reciprocal are identical, except for shifting one place to the right (i.e., [b0; b1, b1, ..., bn] and [0; b0, b1, b2, ..., bn] are reciprocals)...
|
|
188
|
+
if ( bi === 0 ) {
|
|
189
|
+
x = 1.0 / x;
|
|
190
|
+
bi = floor( x );
|
|
191
|
+
q.push( [ bi, 1.0/bi ] );
|
|
192
|
+
if ( bi === x ) {
|
|
193
|
+
// No more continued fractions to compute...
|
|
194
|
+
FLG = 1;
|
|
195
|
+
return iter;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Compute the next term of the continued fraction...
|
|
199
|
+
r = x - bi;
|
|
200
|
+
v = 1.0 / r;
|
|
201
|
+
|
|
202
|
+
// Initialize parameters of the modified Lentz's algorithm...
|
|
203
|
+
fi = bi;
|
|
204
|
+
Ci = fi;
|
|
205
|
+
Di = 0.0;
|
|
206
|
+
|
|
207
|
+
// Compute the next two terms:
|
|
208
|
+
q.push( nextTerm() );
|
|
209
|
+
q.push( nextTerm() );
|
|
210
|
+
|
|
211
|
+
return iter;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Computes the next term of the continued fraction.
|
|
215
|
+
*
|
|
216
|
+
* @private
|
|
217
|
+
* @returns {number} next term
|
|
218
|
+
*/
|
|
219
|
+
function nextTerm() {
|
|
220
|
+
var fj = fi;
|
|
221
|
+
|
|
222
|
+
// Compute the next term of the continued fraction...
|
|
223
|
+
bi = floor( v );
|
|
224
|
+
r = v - bi;
|
|
225
|
+
v = 1.0 / r;
|
|
226
|
+
|
|
227
|
+
// Use the modified Lentz's algorithm to find the next convergent...
|
|
228
|
+
Di += bi; // b_j + a_j*D_{j-1}, where a_j = 1.0 for all j
|
|
229
|
+
if ( Di === 0.0 ) {
|
|
230
|
+
Di = TINY;
|
|
231
|
+
}
|
|
232
|
+
Ci = bi + ( 1.0/Ci ); // b_j + a_j/C_{j-1}, where a_j = 1.0 for all j
|
|
233
|
+
if ( Ci === 0.0 ) {
|
|
234
|
+
Ci = TINY;
|
|
235
|
+
}
|
|
236
|
+
Di = 1.0 / Di;
|
|
237
|
+
delta = Ci * Di;
|
|
238
|
+
fi *= delta;
|
|
239
|
+
|
|
240
|
+
// Check whether we can terminate computation...
|
|
241
|
+
if ( abs( delta - 1.0 ) <= opts.tol ) {
|
|
242
|
+
FLG = 1;
|
|
243
|
+
if ( fj === fi ) {
|
|
244
|
+
// Return a sentinel value to indicate that the last term did not improve the approximation:
|
|
245
|
+
return [ -1, fi ];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return [ bi, fi ];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Returns an iterator protocol-compliant object containing the next iterated value.
|
|
253
|
+
*
|
|
254
|
+
* @private
|
|
255
|
+
* @returns {Object} iterator protocol-compliant object
|
|
256
|
+
*/
|
|
257
|
+
function next() {
|
|
258
|
+
var item;
|
|
259
|
+
var term;
|
|
260
|
+
var f;
|
|
261
|
+
|
|
262
|
+
i += 1;
|
|
263
|
+
if ( FLG > 0 ) {
|
|
264
|
+
if ( FLG === 1 && q.length > 0 ) {
|
|
265
|
+
item = q.pop();
|
|
266
|
+
term = item[ 0 ];
|
|
267
|
+
f = item[ 1 ];
|
|
268
|
+
if ( term === -1 ) {
|
|
269
|
+
return {
|
|
270
|
+
'done': true
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// Check whether we need to adjust the current term to ensure canonical representation...
|
|
274
|
+
item = q.first();
|
|
275
|
+
if ( q.length === 2 && item[ 0 ] === 1 ) {
|
|
276
|
+
term += 1;
|
|
277
|
+
f = item[ 1 ];
|
|
278
|
+
q.clear();
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
'value': result( term, f, neg ),
|
|
282
|
+
'done': false
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
FLG = 2;
|
|
286
|
+
return {
|
|
287
|
+
'done': true
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if ( i === opts.iter ) {
|
|
291
|
+
item = q.pop();
|
|
292
|
+
term = item[ 0 ];
|
|
293
|
+
f = item[ 1 ];
|
|
294
|
+
|
|
295
|
+
// Check whether we need to adjust the final term to ensure canonical representation...
|
|
296
|
+
item = q.first();
|
|
297
|
+
if ( item[ 0 ] === 1 ) {
|
|
298
|
+
term += 1;
|
|
299
|
+
f = item[ 1 ];
|
|
300
|
+
}
|
|
301
|
+
q.clear();
|
|
302
|
+
FLG = 2;
|
|
303
|
+
return {
|
|
304
|
+
'value': result( term, f, neg ),
|
|
305
|
+
'done': false
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
item = q.push( nextTerm() ).pop();
|
|
309
|
+
return {
|
|
310
|
+
'value': result( item[ 0 ], item[ 1 ], neg ),
|
|
311
|
+
'done': false
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Finishes an iterator.
|
|
317
|
+
*
|
|
318
|
+
* @private
|
|
319
|
+
* @param {*} [value] - value to return
|
|
320
|
+
* @returns {Object} iterator protocol-compliant object
|
|
321
|
+
*/
|
|
322
|
+
function end( value ) {
|
|
323
|
+
FLG = 2;
|
|
324
|
+
if ( arguments.length ) {
|
|
325
|
+
return {
|
|
326
|
+
'value': value,
|
|
327
|
+
'done': true
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
'done': true
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Returns a new iterator.
|
|
337
|
+
*
|
|
338
|
+
* @private
|
|
339
|
+
* @returns {Iterator} iterator
|
|
340
|
+
*/
|
|
341
|
+
function factory() {
|
|
342
|
+
return iterContinuedFractionSeq( orig, opts );
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
// EXPORTS //
|
|
348
|
+
|
|
349
|
+
module.exports = iterContinuedFractionSeq;
|