continuedfraction.js 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Robert Eisele
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # ContinuedFraction.js
2
+
3
+ [![NPM Package](https://img.shields.io/npm/v/continuedfraction.js.svg?style=flat)](https://npmjs.org/package/continuedfraction.js "View this project on npm")
4
+ [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)
5
+
6
+
7
+ A lightweight JavaScript library sitting on the shoulders of [Fraction.js](https://github.com/rawify/Fraction.js) for generating and evaluating **standard** and **generalized** continued fractions. It is built with generator-based sequences and convergent recurrences for fast execution.
8
+
9
+ ## Features
10
+
11
+ - Generate the simple continued‑fraction expansion of √N
12
+ - Convert any real number or rational to its continued‑fraction
13
+ - Infinite generators for classic constants: φ (golden ratio), e, π, 4/π
14
+ - Evaluate (simple or generalized) continued fractions to a `Fraction`
15
+
16
+ ## Example
17
+
18
+ ```js
19
+ const frac = ContinuedFraction.eval(
20
+ ContinuedFraction.fromFraction(3021, 203),
21
+ 10 // Max Steps
22
+ );
23
+ console.log(frac.toString()); // → "3021/203"
24
+ ```
25
+
26
+ ## Installation
27
+
28
+ You can install `ContinuedFraction.js` via npm:
29
+
30
+ ```bash
31
+ npm install continuedfraction.js
32
+ ```
33
+
34
+ Or with yarn:
35
+
36
+ ```bash
37
+ yarn add continuedfraction.js
38
+ ```
39
+
40
+ Alternatively, download or clone the repository:
41
+
42
+ ```bash
43
+ git clone https://github.com/rawify/ContinuedFraction.js
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ Include the `continuedfraction.min.js` file in your project:
49
+
50
+ ```html
51
+ <script src="path/to/continuedfraction.min.js"></script>
52
+ <script>
53
+ var x = ContinuedFraction.sqrt(2);
54
+ </script>
55
+ ```
56
+
57
+ Or in a Node.js project:
58
+
59
+ ```javascript
60
+ const ContinuedFraction = require('continuedfraction.js');
61
+ ```
62
+
63
+ or
64
+
65
+ ```javascript
66
+ import ContinuedFraction from 'continuedfraction.js';
67
+ ```
68
+
69
+ ## ContinuedFraction API
70
+
71
+ Import the static utility class and use its generators and evaluator:
72
+
73
+ ```javascript
74
+ // 1) Continued‑fraction terms of √23
75
+ const sqrtGen = ContinuedFraction.sqrt(23);
76
+ console.log(sqrtGen.next().value); // 4
77
+ console.log(sqrtGen.next().value); // 1, 3, 1, 8, …
78
+
79
+ // 2) Continued‑fraction of a decimal
80
+ let cnt = 0;
81
+ for (let piCf of ContinuedFraction.fromNumber(Math.PI)) {
82
+ console.log(piCf);
83
+ if (cnt++ >= 10) break;
84
+ }
85
+
86
+ // 3) Golden ratio φ terms
87
+ const phiGen = ContinuedFraction.PHI();
88
+ console.log(phiGen.next().value); // 1, and forever 1…
89
+
90
+ // 4) Evaluate the first 10 terms of e’s CF to a Fraction
91
+ const approxE = ContinuedFraction.eval(ContinuedFraction.E, 10);
92
+ console.log(approxE.toString()); // e ≈ “193/71”
93
+
94
+ // 5) Generalized CF for π, 4/π
95
+ const genPi = ContinuedFraction.PI();
96
+ console.log(genPi.next().value); // { a: 3, b: 0 }
97
+ console.log(genPi.next().value); // { a: 6, b: 1 }
98
+
99
+ // 6) Continued‑fraction from two integers
100
+ const halfCf = ContinuedFraction.fromFraction(1, 2);
101
+ console.log([...halfCf]); // [0, 2]
102
+ ```
103
+
104
+ ### Methods
105
+
106
+ | Method | Signature | Description |
107
+ | ------------------------ | ----------------------------------------------------------------- | ----------------------------------------------------------------------- |
108
+ | `sqrt(N: number)` | `Generator<number>` | Simple CF terms of √N |
109
+ | `fromNumber(n)` | `Generator<number>` | CF terms of any real via Fraction.js |
110
+ | `fromFraction(a, b)` | `Generator<number>` | CF terms of the rational a/b |
111
+ | `PHI()` | `Generator<number>` | Infinite 1’s for the golden ratio |
112
+ | `FOUR_OVER_PI()` | `Generator<CFTerm>` | Generalized CF terms for 4/π |
113
+ | `PI()` | `Generator<CFTerm>` | Generalized CF terms for π |
114
+ | `E()` | `Generator<number>` | CF expansion of e |
115
+ | `eval(generator, steps)` | `(Generator|() => Generator, steps?: number) ⇒ Fraction` | Evaluate (generalized) continued fraction to a `Fraction` approximation |
116
+
117
+ ## Coding Style
118
+
119
+ As every library I publish, ContinuedFraction is also built to be as small as possible after compressing it with Google Closure Compiler in advanced mode. Thus the coding style orientates a little on maxing-out the compression rate. Please make sure you keep this style if you plan to extend the library.
120
+
121
+ ## Building the library
122
+
123
+ After cloning the Git repository run:
124
+
125
+ ```
126
+ npm install
127
+ npm run build
128
+ ```
129
+
130
+ ## Copyright and Licensing
131
+
132
+ Copyright (c) 2025, [Robert Eisele](https://raw.org/)
133
+ Licensed under the MIT license.
@@ -0,0 +1,80 @@
1
+
2
+ import type Fraction from 'fraction.js';
3
+
4
+ /**
5
+ * A term of a generalized continued fraction.
6
+ */
7
+ export interface CFTerm {
8
+ /** The aₙ coefficient */
9
+ a: number;
10
+ /** The bₙ coefficient */
11
+ b: number;
12
+ }
13
+
14
+ /**
15
+ * Utility class for generating continued-fraction expansions of various constants.
16
+ * All methods are static.
17
+ */
18
+ export default class ContinuedFraction {
19
+ /**
20
+ * Infinite generator for the continued‐fraction terms of √N.
21
+ * @param N Integer whose square‐root CF expansion is desired (N ≥ 0)
22
+ * @yields Next continued‐fraction term of √N
23
+ */
24
+ static sqrt(N: number): Generator<number, void, unknown>;
25
+
26
+ /**
27
+ * Generator for continued‐fraction terms of any real number via Fraction.js.
28
+ * @param n The real number to convert (as number, string, or bigint)
29
+ * @yields Next continued‐fraction term of n
30
+ */
31
+ static fromNumber(n: number | string | bigint): Generator<number, void, unknown>;
32
+
33
+ /**
34
+ * Generator for continued‐fraction terms of a rational a/b via Fraction.js.
35
+ * @param a Numerator (number, string, bigint, or Fraction)
36
+ * @param b Denominator (number, string, or bigint)
37
+ * @yields Next continued‐fraction term of a/b
38
+ */
39
+ static fromFraction(
40
+ a: number | string | bigint | Fraction,
41
+ b: number | string | bigint
42
+ ): Generator<number, void, unknown>;
43
+
44
+ /**
45
+ * Infinite generator of the golden ratio φ = [1; 1, 1, 1, …].
46
+ * @yields Always 1
47
+ */
48
+ static PHI(): Generator<number, void, unknown>;
49
+
50
+ /**
51
+ * Infinite generator for the generalized continued‐fraction terms of 4/π.
52
+ * @yields Term pairs { a, b } for the continued‐fraction
53
+ */
54
+ static FOUR_OVER_PI(): Generator<CFTerm, void, unknown>;
55
+
56
+ /**
57
+ * Infinite generator for the generalized continued‐fraction terms of π.
58
+ * @yields Term pairs { a, b } for the continued‐fraction
59
+ */
60
+ static PI(): Generator<CFTerm, void, unknown>;
61
+
62
+ /**
63
+ * Infinite generator of e = [2; 1, 2, 1, 1, 4, 1, …].
64
+ * @yields Next continued‐fraction term of e
65
+ */
66
+ static E(): Generator<number, void, unknown>;
67
+
68
+ /**
69
+ * Evaluate a (simple or generalized) continued fraction generator.
70
+ * @param generator A CF term generator or a function returning one
71
+ * @param steps Number of terms to include (default 10)
72
+ * @returns Rational approximation as a Fraction
73
+ */
74
+ static eval(
75
+ generator:
76
+ | Generator<number | CFTerm, any, any>
77
+ | (() => Generator<number | CFTerm, any, any>),
78
+ steps?: number
79
+ ): Fraction;
80
+ }
@@ -0,0 +1,179 @@
1
+ 'use strict';
2
+ const Fraction = require('fraction.js');
3
+
4
+
5
+
6
+ /**
7
+ * Utility class for generating continued-fraction expansions of various constants.
8
+ */
9
+ const ContinuedFraction = {
10
+
11
+ /**
12
+ * Infinite generator for the continued-fraction terms of sqrt(N).
13
+ * √N = [a0; (a1, a2, ..., a_p)], where the period ends when ak = 2*a0.
14
+ * For perfect squares, yields only a0 = floor(sqrt(N)) and then returns.
15
+ *
16
+ * @param {number} N - Integer whose square root CF expansion is desired (N ≥ 0).
17
+ * @yields {number} - Next continued-fraction term of √N.
18
+ */
19
+ "sqrt": function* (N) {
20
+
21
+ const a0 = Math.floor(Math.sqrt(N));
22
+ yield a0;
23
+ if (a0 * a0 === N) return; // perfect square: single-term CF
24
+
25
+ //while (true) {
26
+ let nk = 0;
27
+ let dk = 1;
28
+ let ak = a0;
29
+
30
+ do {
31
+ nk = ak * dk - nk;
32
+ dk = (N - nk * nk) / dk; // remains integer by construction
33
+ ak = Math.floor((a0 + nk) / dk);
34
+ yield ak;
35
+ } while (/* 2 * a0 !== ak */ true);
36
+ //}
37
+ },
38
+
39
+ /**
40
+ * Generator for continued-fraction terms of any real number via Fraction.js.
41
+ *
42
+ * @exports
43
+ * @param {number|string|BigInt} n - The real number to convert (as number or string).
44
+ * @yields {number} - Next continued-fraction term of n.
45
+ */
46
+ "fromNumber": function* (n) {
47
+
48
+ yield* Fraction(n)['toContinued']();
49
+ },
50
+
51
+ /**
52
+ * Generator for continued-fraction terms of a rational a/b via Fraction.js.
53
+ *
54
+ * @param {number|string|BigInt|Fraction} a - Numerator.
55
+ * @param {number|string|BigInt} b - Denominator.
56
+ * @yields {number} - Next continued-fraction term of a/b.
57
+ */
58
+ "fromFraction": function* (a, b) {
59
+
60
+ yield* Fraction(a, b)['toContinued']();
61
+ },
62
+
63
+ /**
64
+ * Infinite generator of the golden ratio φ = [1; 1, 1, 1, …].
65
+ *
66
+ * @yields {number} - Always 1.
67
+ */
68
+ "PHI": function* () {
69
+
70
+ while (true) {
71
+ yield 1;
72
+ }
73
+ },
74
+
75
+ /**
76
+ * Infinite generator for Continued‐fraction terms of 4/π:
77
+ * 4/π = 1 + 1²/(3 + 2²/(5 + 3²/(7 + …)))
78
+ * @yields {{a:number,b:number}} - Term pair (aₙ, bₙ) of the generalized continued fraction.
79
+ * - first: a₀ = 1
80
+ * - then: aₙ = 2n+1, bₙ = n² (n ≥ 1)
81
+ */
82
+ "FOUR_OVER_PI": function* () {
83
+
84
+ yield { "a": 1, "b": 0 }; // a₀ = 1
85
+
86
+ for (let n = 0; true; n++) {
87
+ yield { "a": 2, "b": (n * 2 + 1) ** 2 };
88
+ }
89
+ },
90
+
91
+ /**
92
+ * Infinite generator for Continued‐fraction terms of π:
93
+ * π = 3 + 1²/(6 + 3²/(6 + 5²/(6 + …)))
94
+ * @yields {{a:number,b:number}} - Term pair (aₙ, bₙ) of the generalized continued fraction.
95
+ * - first: a₀ = 3
96
+ * - then: aₙ = 6, bₙ = (2n-1)² (n ≥ 1)
97
+ */
98
+ "PI": function* () {
99
+
100
+ yield { "a": 3, "b": 0 }; // a₀ = 3
101
+
102
+ for (let n = 1; true; n++) {
103
+ yield { "a": 6, "b": (n * 2 - 1) ** 2 };
104
+ }
105
+ },
106
+
107
+ /**
108
+ * Infinite generator of e = [2; 1, 2, 1, 1, 4, 1, …].
109
+ * Terms follow the pattern [2; (1,2m,1) for m = 1,2,3,…].
110
+ *
111
+ * @yields {number} - Next continued-fraction term of e.
112
+ */
113
+ "E": function* () {
114
+
115
+ yield 2; // a₀ = 2
116
+
117
+ for (let m = 1; ; m++) {
118
+ yield 1;
119
+ yield 2 * m;
120
+ yield 1;
121
+ }
122
+
123
+ /* Alternative:
124
+ yield { a: 2, b: 0 }; // a₀ = 2
125
+ yield { a: 1, b: 1 }; // a₁ = 1
126
+
127
+ for (let m = 2; ; m++) {
128
+ yield { a: n, b: m - 1 };
129
+ }
130
+ */
131
+ },
132
+
133
+ /**
134
+ * Evaluate a (simple or generalized) continued fraction generator.
135
+ * For generalized: terms are objects { a, b }, else regular numbers.
136
+ *
137
+ * @param {Generator|Function} generator - Continued fraction term generator.
138
+ * @param {number} steps - Number of terms to evaluate.
139
+ * @returns {Fraction} - Rational approximation as Fraction.
140
+ */
141
+ "eval": function (generator, steps = 10) {
142
+
143
+ if (typeof generator === "function") {
144
+ generator = generator();
145
+ }
146
+
147
+ // pull off a₀ (with its b₀)
148
+ const first = generator['next']();
149
+ if (first['done']) return Fraction(0);
150
+
151
+ const a0 = Fraction(first['value']['a'] ?? first['value']);
152
+
153
+ // initialize convergents p₋₁/q₋₁ = 1/0, p₀/q₀ = a₀/1
154
+ let pCurr = a0, qCurr = Fraction(1);
155
+ let pPrev = Fraction(1), qPrev = Fraction(0);
156
+
157
+ // run through a₁… up to `steps-1` more
158
+ for (let k = 1; k < steps; k++) {
159
+ const { value, done } = generator['next']();
160
+ if (done) break;
161
+
162
+ const ak = Fraction(value['a'] ?? value),
163
+ bk = Fraction(value['b'] ?? 1);
164
+
165
+ // pₖ = aₖ·pₖ₋₁ + bₖ·pₖ₋₂
166
+ // qₖ = aₖ·qₖ₋₁ + bₖ·qₖ₋₂
167
+ [pPrev, pCurr] = [pCurr, ak['mul'](pCurr)['add'](bk['mul'](pPrev))];
168
+ [qPrev, qCurr] = [qCurr, ak['mul'](qCurr)['add'](bk['mul'](qPrev))];
169
+ }
170
+
171
+ // final convergent = pCurr/qCurr
172
+ return pCurr['div'](qCurr);
173
+ }
174
+ }
175
+
176
+ Object.defineProperty(ContinuedFraction, "__esModule", { 'value': true });
177
+ ContinuedFraction['default'] = ContinuedFraction;
178
+ ContinuedFraction['ContinuedFraction'] = ContinuedFraction;
179
+ module['exports'] = ContinuedFraction;
@@ -0,0 +1,10 @@
1
+ /*
2
+ ContinuedFraction.js v0.0.1 4/18/2025
3
+ https://github.com/rawify/ContinuedFraction.js
4
+
5
+ Copyright (c) 2025, Robert Eisele (https://raw.org/)
6
+ Licensed under the MIT license.
7
+ */
8
+ 'use strict';(function(n){const e={sqrt:function*(a){const c=Math.floor(Math.sqrt(a));yield c;if(c*c!==a){var b=0,d=1,f=c;do b=f*d-b,d=(a-b*b)/d,f=Math.floor((c+b)/d),yield f;while(1)}},fromNumber:function*(a){yield*Fraction(a).toContinued()},fromFraction:function*(a,c){yield*Fraction(a,c).toContinued()},PHI:function*(){for(;;)yield 1},FOUR_OVER_PI:function*(){yield{a:1,b:0};for(let a=0;;a++)yield{a:2,b:(2*a+1)**2}},PI:function*(){yield{a:3,b:0};for(let a=1;;a++)yield{a:6,b:(2*a-1)**2}},E:function*(){yield 2;
9
+ for(let a=1;;a++)yield 1,yield 2*a,yield 1},eval:function(a,c=10){"function"===typeof a&&(a=a());var b=a.next();if(b.done)return Fraction(0);b=Fraction(b.value.a??b.value);let d=Fraction(1),f=Fraction(1),h=Fraction(0);for(let k=1;k<c;k++){const {value:g,done:p}=a.next();if(p)break;const l=Fraction(g.a??g),m=Fraction(g.b??1);[f,b]=[b,l.mul(b).add(m.mul(f))];[h,d]=[d,l.mul(d).add(m.mul(h))]}return b.div(d)}};"function"===typeof define&&define.amd?define([],function(){return e}):"object"===typeof exports?
10
+ (Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=e,e.ContinuedFraction=e,module.exports=e):n.ContinuedFraction=e})(this);
@@ -0,0 +1,177 @@
1
+ 'use strict';
2
+ import Fraction from 'fraction.js';
3
+
4
+
5
+
6
+ /**
7
+ * Utility class for generating continued-fraction expansions of various constants.
8
+ */
9
+ const ContinuedFraction = {
10
+
11
+ /**
12
+ * Infinite generator for the continued-fraction terms of sqrt(N).
13
+ * √N = [a0; (a1, a2, ..., a_p)], where the period ends when ak = 2*a0.
14
+ * For perfect squares, yields only a0 = floor(sqrt(N)) and then returns.
15
+ *
16
+ * @param {number} N - Integer whose square root CF expansion is desired (N ≥ 0).
17
+ * @yields {number} - Next continued-fraction term of √N.
18
+ */
19
+ "sqrt": function* (N) {
20
+
21
+ const a0 = Math.floor(Math.sqrt(N));
22
+ yield a0;
23
+ if (a0 * a0 === N) return; // perfect square: single-term CF
24
+
25
+ //while (true) {
26
+ let nk = 0;
27
+ let dk = 1;
28
+ let ak = a0;
29
+
30
+ do {
31
+ nk = ak * dk - nk;
32
+ dk = (N - nk * nk) / dk; // remains integer by construction
33
+ ak = Math.floor((a0 + nk) / dk);
34
+ yield ak;
35
+ } while (/* 2 * a0 !== ak */ true);
36
+ //}
37
+ },
38
+
39
+ /**
40
+ * Generator for continued-fraction terms of any real number via Fraction.js.
41
+ *
42
+ * @exports
43
+ * @param {number|string|BigInt} n - The real number to convert (as number or string).
44
+ * @yields {number} - Next continued-fraction term of n.
45
+ */
46
+ "fromNumber": function* (n) {
47
+
48
+ yield* Fraction(n)['toContinued']();
49
+ },
50
+
51
+ /**
52
+ * Generator for continued-fraction terms of a rational a/b via Fraction.js.
53
+ *
54
+ * @param {number|string|BigInt|Fraction} a - Numerator.
55
+ * @param {number|string|BigInt} b - Denominator.
56
+ * @yields {number} - Next continued-fraction term of a/b.
57
+ */
58
+ "fromFraction": function* (a, b) {
59
+
60
+ yield* Fraction(a, b)['toContinued']();
61
+ },
62
+
63
+ /**
64
+ * Infinite generator of the golden ratio φ = [1; 1, 1, 1, …].
65
+ *
66
+ * @yields {number} - Always 1.
67
+ */
68
+ "PHI": function* () {
69
+
70
+ while (true) {
71
+ yield 1;
72
+ }
73
+ },
74
+
75
+ /**
76
+ * Infinite generator for Continued‐fraction terms of 4/π:
77
+ * 4/π = 1 + 1²/(3 + 2²/(5 + 3²/(7 + …)))
78
+ * @yields {{a:number,b:number}} - Term pair (aₙ, bₙ) of the generalized continued fraction.
79
+ * - first: a₀ = 1
80
+ * - then: aₙ = 2n+1, bₙ = n² (n ≥ 1)
81
+ */
82
+ "FOUR_OVER_PI": function* () {
83
+
84
+ yield { "a": 1, "b": 0 }; // a₀ = 1
85
+
86
+ for (let n = 0; true; n++) {
87
+ yield { "a": 2, "b": (n * 2 + 1) ** 2 };
88
+ }
89
+ },
90
+
91
+ /**
92
+ * Infinite generator for Continued‐fraction terms of π:
93
+ * π = 3 + 1²/(6 + 3²/(6 + 5²/(6 + …)))
94
+ * @yields {{a:number,b:number}} - Term pair (aₙ, bₙ) of the generalized continued fraction.
95
+ * - first: a₀ = 3
96
+ * - then: aₙ = 6, bₙ = (2n-1)² (n ≥ 1)
97
+ */
98
+ "PI": function* () {
99
+
100
+ yield { "a": 3, "b": 0 }; // a₀ = 3
101
+
102
+ for (let n = 1; true; n++) {
103
+ yield { "a": 6, "b": (n * 2 - 1) ** 2 };
104
+ }
105
+ },
106
+
107
+ /**
108
+ * Infinite generator of e = [2; 1, 2, 1, 1, 4, 1, …].
109
+ * Terms follow the pattern [2; (1,2m,1) for m = 1,2,3,…].
110
+ *
111
+ * @yields {number} - Next continued-fraction term of e.
112
+ */
113
+ "E": function* () {
114
+
115
+ yield 2; // a₀ = 2
116
+
117
+ for (let m = 1; ; m++) {
118
+ yield 1;
119
+ yield 2 * m;
120
+ yield 1;
121
+ }
122
+
123
+ /* Alternative:
124
+ yield { a: 2, b: 0 }; // a₀ = 2
125
+ yield { a: 1, b: 1 }; // a₁ = 1
126
+
127
+ for (let m = 2; ; m++) {
128
+ yield { a: n, b: m - 1 };
129
+ }
130
+ */
131
+ },
132
+
133
+ /**
134
+ * Evaluate a (simple or generalized) continued fraction generator.
135
+ * For generalized: terms are objects { a, b }, else regular numbers.
136
+ *
137
+ * @param {Generator|Function} generator - Continued fraction term generator.
138
+ * @param {number} steps - Number of terms to evaluate.
139
+ * @returns {Fraction} - Rational approximation as Fraction.
140
+ */
141
+ "eval": function (generator, steps = 10) {
142
+
143
+ if (typeof generator === "function") {
144
+ generator = generator();
145
+ }
146
+
147
+ // pull off a₀ (with its b₀)
148
+ const first = generator['next']();
149
+ if (first['done']) return Fraction(0);
150
+
151
+ const a0 = Fraction(first['value']['a'] ?? first['value']);
152
+
153
+ // initialize convergents p₋₁/q₋₁ = 1/0, p₀/q₀ = a₀/1
154
+ let pCurr = a0, qCurr = Fraction(1);
155
+ let pPrev = Fraction(1), qPrev = Fraction(0);
156
+
157
+ // run through a₁… up to `steps-1` more
158
+ for (let k = 1; k < steps; k++) {
159
+ const { value, done } = generator['next']();
160
+ if (done) break;
161
+
162
+ const ak = Fraction(value['a'] ?? value),
163
+ bk = Fraction(value['b'] ?? 1);
164
+
165
+ // pₖ = aₖ·pₖ₋₁ + bₖ·pₖ₋₂
166
+ // qₖ = aₖ·qₖ₋₁ + bₖ·qₖ₋₂
167
+ [pPrev, pCurr] = [pCurr, ak['mul'](pCurr)['add'](bk['mul'](pPrev))];
168
+ [qPrev, qCurr] = [qCurr, ak['mul'](qCurr)['add'](bk['mul'](qPrev))];
169
+ }
170
+
171
+ // final convergent = pCurr/qCurr
172
+ return pCurr['div'](qCurr);
173
+ }
174
+ }
175
+ export {
176
+ ContinuedFraction as default, ContinuedFraction
177
+ };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "continuedfraction.js",
3
+ "title": "ContinuedFraction.js",
4
+ "version": "0.0.1",
5
+ "homepage": "https://github.com/rawify/ContinuedFraction.js",
6
+ "bugs": "https://github.com/rawify/ContinuedFraction.js/issues",
7
+ "description": "The RAW continued fractions library",
8
+ "keywords": [
9
+ "math",
10
+ "continued fraction",
11
+ "fraction",
12
+ "bigint"
13
+ ],
14
+ "private": false,
15
+ "main": "./dist/continuedfraction.js",
16
+ "module": "./dist/continuedfraction.mjs",
17
+ "types": "./continuedfraction.d.ts",
18
+ "browser": "./dist/continuedfraction.min.js",
19
+ "unpkg": "./dist/continuedfraction.min.js",
20
+ "readmeFilename": "README.md",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./continuedfraction.d.ts",
24
+ "require": "./dist/continuedfraction.js",
25
+ "import": "./dist/continuedfraction.mjs"
26
+ }
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git@github.com:rawify/ContinuedFraction.js.git"
31
+ },
32
+ "funding": {
33
+ "type": "github",
34
+ "url": "https://github.com/sponsors/rawify"
35
+ },
36
+ "author": {
37
+ "name": "Robert Eisele",
38
+ "email": "robert@raw.org",
39
+ "url": "https://raw.org/"
40
+ },
41
+ "license": "MIT",
42
+ "engines": {
43
+ "node": "*"
44
+ },
45
+ "directories": {
46
+ "test": "tests"
47
+ },
48
+ "scripts": {
49
+ "build": "crude-build ContinuedFraction",
50
+ "test": "mocha tests/*.js"
51
+ },
52
+ "devDependencies": {
53
+ "crude-build": "^0.1.0",
54
+ "mocha": "^11.1.0"
55
+ },
56
+ "dependencies": {
57
+ "fraction.js": "^5.2.2"
58
+ }
59
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * @license ContinuedFraction.js v0.0.1 4/18/2025
3
+ * https://github.com/rawify/ContinuedFraction.js
4
+ *
5
+ * Copyright (c) 2025, Robert Eisele (https://raw.org/)
6
+ * Licensed under the MIT license.
7
+ **/
8
+
9
+ import Fraction from 'fraction.js';
10
+
11
+ /**
12
+ * Utility class for generating continued-fraction expansions of various constants.
13
+ */
14
+ const ContinuedFraction = {
15
+
16
+ /**
17
+ * Infinite generator for the continued-fraction terms of sqrt(N).
18
+ * √N = [a0; (a1, a2, ..., a_p)], where the period ends when ak = 2*a0.
19
+ * For perfect squares, yields only a0 = floor(sqrt(N)) and then returns.
20
+ *
21
+ * @param {number} N - Integer whose square root CF expansion is desired (N ≥ 0).
22
+ * @yields {number} - Next continued-fraction term of √N.
23
+ */
24
+ "sqrt": function* (N) {
25
+
26
+ const a0 = Math.floor(Math.sqrt(N));
27
+ yield a0;
28
+ if (a0 * a0 === N) return; // perfect square: single-term CF
29
+
30
+ //while (true) {
31
+ let nk = 0;
32
+ let dk = 1;
33
+ let ak = a0;
34
+
35
+ do {
36
+ nk = ak * dk - nk;
37
+ dk = (N - nk * nk) / dk; // remains integer by construction
38
+ ak = Math.floor((a0 + nk) / dk);
39
+ yield ak;
40
+ } while (/* 2 * a0 !== ak */ true);
41
+ //}
42
+ },
43
+
44
+ /**
45
+ * Generator for continued-fraction terms of any real number via Fraction.js.
46
+ *
47
+ * @exports
48
+ * @param {number|string|BigInt} n - The real number to convert (as number or string).
49
+ * @yields {number} - Next continued-fraction term of n.
50
+ */
51
+ "fromNumber": function* (n) {
52
+
53
+ yield* Fraction(n)['toContinued']();
54
+ },
55
+
56
+ /**
57
+ * Generator for continued-fraction terms of a rational a/b via Fraction.js.
58
+ *
59
+ * @param {number|string|BigInt|Fraction} a - Numerator.
60
+ * @param {number|string|BigInt} b - Denominator.
61
+ * @yields {number} - Next continued-fraction term of a/b.
62
+ */
63
+ "fromFraction": function* (a, b) {
64
+
65
+ yield* Fraction(a, b)['toContinued']();
66
+ },
67
+
68
+ /**
69
+ * Infinite generator of the golden ratio φ = [1; 1, 1, 1, …].
70
+ *
71
+ * @yields {number} - Always 1.
72
+ */
73
+ "PHI": function* () {
74
+
75
+ while (true) {
76
+ yield 1;
77
+ }
78
+ },
79
+
80
+ /**
81
+ * Infinite generator for Continued‐fraction terms of 4/π:
82
+ * 4/π = 1 + 1²/(3 + 2²/(5 + 3²/(7 + …)))
83
+ * @yields {{a:number,b:number}} - Term pair (aₙ, bₙ) of the generalized continued fraction.
84
+ * - first: a₀ = 1
85
+ * - then: aₙ = 2n+1, bₙ = n² (n ≥ 1)
86
+ */
87
+ "FOUR_OVER_PI": function* () {
88
+
89
+ yield { "a": 1, "b": 0 }; // a₀ = 1
90
+
91
+ for (let n = 0; true; n++) {
92
+ yield { "a": 2, "b": (n * 2 + 1) ** 2 };
93
+ }
94
+ },
95
+
96
+ /**
97
+ * Infinite generator for Continued‐fraction terms of π:
98
+ * π = 3 + 1²/(6 + 3²/(6 + 5²/(6 + …)))
99
+ * @yields {{a:number,b:number}} - Term pair (aₙ, bₙ) of the generalized continued fraction.
100
+ * - first: a₀ = 3
101
+ * - then: aₙ = 6, bₙ = (2n-1)² (n ≥ 1)
102
+ */
103
+ "PI": function* () {
104
+
105
+ yield { "a": 3, "b": 0 }; // a₀ = 3
106
+
107
+ for (let n = 1; true; n++) {
108
+ yield { "a": 6, "b": (n * 2 - 1) ** 2 };
109
+ }
110
+ },
111
+
112
+ /**
113
+ * Infinite generator of e = [2; 1, 2, 1, 1, 4, 1, …].
114
+ * Terms follow the pattern [2; (1,2m,1) for m = 1,2,3,…].
115
+ *
116
+ * @yields {number} - Next continued-fraction term of e.
117
+ */
118
+ "E": function* () {
119
+
120
+ yield 2; // a₀ = 2
121
+
122
+ for (let m = 1; ; m++) {
123
+ yield 1;
124
+ yield 2 * m;
125
+ yield 1;
126
+ }
127
+
128
+ /* Alternative:
129
+ yield { a: 2, b: 0 }; // a₀ = 2
130
+ yield { a: 1, b: 1 }; // a₁ = 1
131
+
132
+ for (let m = 2; ; m++) {
133
+ yield { a: n, b: m - 1 };
134
+ }
135
+ */
136
+ },
137
+
138
+ /**
139
+ * Evaluate a (simple or generalized) continued fraction generator.
140
+ * For generalized: terms are objects { a, b }, else regular numbers.
141
+ *
142
+ * @param {Generator|Function} generator - Continued fraction term generator.
143
+ * @param {number} steps - Number of terms to evaluate.
144
+ * @returns {Fraction} - Rational approximation as Fraction.
145
+ */
146
+ "eval": function (generator, steps = 10) {
147
+
148
+ if (typeof generator === "function") {
149
+ generator = generator();
150
+ }
151
+
152
+ // pull off a₀ (with its b₀)
153
+ const first = generator['next']();
154
+ if (first['done']) return Fraction(0);
155
+
156
+ const a0 = Fraction(first['value']['a'] ?? first['value']);
157
+
158
+ // initialize convergents p₋₁/q₋₁ = 1/0, p₀/q₀ = a₀/1
159
+ let pCurr = a0, qCurr = Fraction(1);
160
+ let pPrev = Fraction(1), qPrev = Fraction(0);
161
+
162
+ // run through a₁… up to `steps-1` more
163
+ for (let k = 1; k < steps; k++) {
164
+ const { value, done } = generator['next']();
165
+ if (done) break;
166
+
167
+ const ak = Fraction(value['a'] ?? value),
168
+ bk = Fraction(value['b'] ?? 1);
169
+
170
+ // pₖ = aₖ·pₖ₋₁ + bₖ·pₖ₋₂
171
+ // qₖ = aₖ·qₖ₋₁ + bₖ·qₖ₋₂
172
+ [pPrev, pCurr] = [pCurr, ak['mul'](pCurr)['add'](bk['mul'](pPrev))];
173
+ [qPrev, qCurr] = [qCurr, ak['mul'](qCurr)['add'](bk['mul'](qPrev))];
174
+ }
175
+
176
+ // final convergent = pCurr/qCurr
177
+ return pCurr['div'](qCurr);
178
+ }
179
+ }
@@ -0,0 +1,50 @@
1
+
2
+ const ContinuedFraction = require("continuedfraction.js");
3
+ const assert = require('assert');
4
+
5
+ function get(gen, num = 10) {
6
+
7
+ const res = [];
8
+ for (let i = 0; i < num; i++) {
9
+ let x = gen.next();
10
+ if (x.done) break;
11
+ res.push(x.value);
12
+ }
13
+ return res;
14
+ }
15
+
16
+ const tests = [
17
+
18
+ { label: "sqrt(2)", generator: ContinuedFraction.sqrt(2), expect: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2] },
19
+ { label: "sqrt(3)", generator: ContinuedFraction.sqrt(3), expect: [1, 1, 2, 1, 2, 1, 2, 1, 2, 1] },
20
+ { label: "sqrt(5)", generator: ContinuedFraction.sqrt(5), expect: [2, 4, 4, 4, 4, 4, 4, 4, 4, 4] },
21
+ { label: "sqrt(7)", generator: ContinuedFraction.sqrt(7), expect: [2, 1, 1, 1, 4, 1, 1, 1, 4, 1] },
22
+
23
+ { label: "fromNumber(23.43)", generator: ContinuedFraction.fromNumber(23.43), expect: [23, 2, 3, 14] },
24
+ { label: "fromNumber(77)", generator: ContinuedFraction.fromNumber(77), expect: [77] },
25
+
26
+ { label: "PHI", generator: ContinuedFraction.PHI(), expect: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] },
27
+ { label: "PI", generator: ContinuedFraction.PI(), expect: [{ a: 3, b: 0 }, { a: 6, b: 1 }, { a: 6, b: 9 }, { a: 6, b: 25 }, { a: 6, b: 49 }, { a: 6, b: 81 }, { a: 6, b: 121 },{ a: 6, b: 169 },{ a: 6, b: 225 },{ a: 6, b: 289 }] },
28
+ { label: "FOUR_OVER_PI", generator: ContinuedFraction.FOUR_OVER_PI(), expect: [{ a: 1, b: 0 }, { a: 2, b: 1 }, { a: 2, b: 9 }, { a: 2, b: 25 }, { a: 2, b: 49 }, { a: 2, b: 81 }, { a: 2, b: 121 },{ a: 2, b: 169 },{ a: 2, b: 225 },{ a: 2, b: 289 }] },
29
+ { label: "E", generator: ContinuedFraction.E(), expect: [2, 1, 2, 1, 1, 4, 1, 1, 6, 1] },
30
+
31
+ { label: "eval(Fraction(271/13))", input: ContinuedFraction.eval(ContinuedFraction.fromFraction("271/13"), 10).toFraction(), expect: "271/13" },
32
+ { label: "eval(PHI)", input: ContinuedFraction.eval(ContinuedFraction.PHI, 21).toFraction(), expect: "17711/10946" }
33
+ ];
34
+
35
+ describe('Continued Fraction', function () {
36
+
37
+ for (var i = 0; i < tests.length; i++) {
38
+
39
+ (function (test) {
40
+
41
+ it(test.label, function () {
42
+ if (test.generator)
43
+ assert.deepEqual(get(test.generator, 10), test.expect);
44
+ else
45
+ assert.deepEqual(test.input, test.expect);
46
+ });
47
+
48
+ })(tests[i]);
49
+ }
50
+ });