Skip to content

Commit 2be0fdc

Browse files
lvltegithub-actionsraklaptudirm
authored
merge: Decimal Expansion (TheAlgorithms#787)
* Add Math function for representing the decimal expansion of a given fraction (decimal or any base from 2 to 10). * Auto-update DIRECTORY.md * DecimalExpansion Jest tests. * chore: trigger update * Auto-update DIRECTORY.md * Auto-update DIRECTORY.md * chore: trigger checks Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Rak Laptudirm <[email protected]> Co-authored-by: Rak Laptudirm <[email protected]>
1 parent 6f33f99 commit 2be0fdc

File tree

3 files changed

+267
-0
lines changed

3 files changed

+267
-0
lines changed

DIRECTORY.md

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
* [LFUCache](https://github.com/TheAlgorithms/Javascript/blob/master/Cache/LFUCache.js)
1919
* [LRUCache](https://github.com/TheAlgorithms/Javascript/blob/master/Cache/LRUCache.js)
2020
* [Memoize](https://github.com/TheAlgorithms/Javascript/blob/master/Cache/Memoize.js)
21+
* test
22+
* [cacheTest](https://github.com/TheAlgorithms/Javascript/blob/master/Cache/test/cacheTest.js)
2123

2224
## Cellular-Automata
2325
* [ConwaysGameOfLife](https://github.com/TheAlgorithms/Javascript/blob/master/Cellular-Automata/ConwaysGameOfLife.js)
@@ -65,11 +67,13 @@
6567
* Graph
6668
* [Graph](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Graph/Graph.js)
6769
* [Graph2](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Graph/Graph2.js)
70+
* [Graph3](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Graph/Graph3.js)
6871
* Heap
6972
* [MaxHeap](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Heap/MaxHeap.js)
7073
* [MinHeap](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Heap/MinHeap.js)
7174
* [MinPriorityQueue](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Heap/MinPriorityQueue.js)
7275
* Linked-List
76+
* [AddTwoNumbers](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Linked-List/AddTwoNumbers.js)
7377
* [CycleDetection](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Linked-List/CycleDetection.js)
7478
* [DoublyLinkedList](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Linked-List/DoublyLinkedList.js)
7579
* [RotateListRight](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Linked-List/RotateListRight.js)
@@ -143,6 +147,7 @@
143147
* [Abs](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Abs.js)
144148
* [AliquotSum](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/AliquotSum.js)
145149
* [Area](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Area.js)
150+
* [ArithmeticGeometricMean](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/ArithmeticGeometricMean.js)
146151
* [ArmstrongNumber](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/ArmstrongNumber.js)
147152
* [AverageMean](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/AverageMean.js)
148153
* [AverageMedian](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/AverageMedian.js)
@@ -153,6 +158,7 @@
153158
* [CheckKishnamurthyNumber](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/CheckKishnamurthyNumber.js)
154159
* [Coordinate](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Coordinate.js)
155160
* [CoPrimeCheck](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/CoPrimeCheck.js)
161+
* [DecimalExpansion](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/DecimalExpansion.js)
156162
* [DecimalIsolate](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/DecimalIsolate.js)
157163
* [DegreeToRadian](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/DegreeToRadian.js)
158164
* [EulerMethod](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/EulerMethod.js)
@@ -168,6 +174,7 @@
168174
* [FindHcf](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindHcf.js)
169175
* [FindLcm](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindLcm.js)
170176
* [FindMin](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindMin.js)
177+
* [FindMinIterator](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindMinIterator.js)
171178
* [GetEuclidGCD](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/GetEuclidGCD.js)
172179
* [GridGet](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/GridGet.js)
173180
* [IsDivisible](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/IsDivisible.js)
@@ -290,6 +297,7 @@
290297
* [AlternativeStringArrange](https://github.com/TheAlgorithms/Javascript/blob/master/String/AlternativeStringArrange.js)
291298
* [CheckAnagram](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckAnagram.js)
292299
* [CheckCamelCase](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckCamelCase.js)
300+
* [CheckExceeding](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckExceeding.js)
293301
* [CheckFlatCase](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckFlatCase.js)
294302
* [CheckKebabCase](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckKebabCase.js)
295303
* [CheckPalindrome](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckPalindrome.js)

Maths/DecimalExpansion.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @author Eric Lavault <https://github.com/lvlte>
3+
*
4+
* Represents the decimal (or binary, octal, any base from 2 to 10) expansion
5+
* of a/b using euclidean division.
6+
*
7+
* Because this function is recursive, it may throw an error when reaching the
8+
* maximum call stack size.
9+
*
10+
* Returns an array containing : [
11+
* 0: integer part of the division
12+
* 1: array of decimals (if any, or an empty array)
13+
* 2: indexOf 1st cycle digit in decimals array if a/b is periodic, or undef.
14+
* ]
15+
*
16+
* @see https://mathworld.wolfram.com/DecimalExpansion.html
17+
*
18+
* @param {number} a
19+
* @param {number} b
20+
* @param {number} [base=10]
21+
* @returns {array}
22+
*/
23+
export function decExp (a, b, base = 10, exp = [], d = {}, dlen = 0) {
24+
if (base < 2 || base > 10) {
25+
throw new RangeError('Unsupported base. Must be in range [2, 10]')
26+
}
27+
28+
if (a === 0) {
29+
return [0, [], undefined]
30+
}
31+
32+
if (a === b && dlen === 0) {
33+
return [1, [], undefined]
34+
}
35+
36+
// d contains the dividends used so far and the corresponding index of its
37+
// euclidean division by b in the expansion array.
38+
d[a] = dlen++
39+
40+
if (a < b) {
41+
exp.push(0)
42+
return decExp(a * base, b, base, exp, d, dlen)
43+
}
44+
45+
// Euclid's division lemma : a = bq + r
46+
const r = a % b
47+
const q = (a - r) / b
48+
49+
// Decimal expansion (1st element is the integer part)
50+
exp.push(+q.toString(base))
51+
52+
if (r === 0) {
53+
// got a regular number (division terminates)
54+
return [exp[0], exp.slice(1), undefined]
55+
}
56+
57+
// For the next iteration
58+
a = r * base
59+
60+
// Check if `a` has already been used as a dividend, in which case it means
61+
// the expansion is periodic.
62+
if (a in d) {
63+
return [exp[0], exp.slice(1), d[a] - 1]
64+
}
65+
66+
return decExp(a, b, base, exp, d, dlen)
67+
}

Maths/test/DecimalExpansion.test.js

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { decExp } from '../DecimalExpansion'
2+
3+
/**
4+
* Decimal
5+
*/
6+
7+
describe('Finite Decimal Expansion', () => {
8+
it('1/2 = 0.5', () => {
9+
const [integer, decimals, cycleIndex] = decExp(1, 2)
10+
expect(integer).toBe(0)
11+
expect(decimals).toEqual([5])
12+
expect(cycleIndex).toBeUndefined()
13+
})
14+
15+
it('1/5 = 0.2', () => {
16+
const [integer, decimals, cycleIndex] = decExp(1, 5)
17+
expect(integer).toBe(0)
18+
expect(decimals).toEqual([2])
19+
expect(cycleIndex).toBeUndefined()
20+
})
21+
22+
it('1/8 = 0.125', () => {
23+
const [integer, decimals, cycleIndex] = decExp(1, 8)
24+
expect(integer).toBe(0)
25+
expect(decimals).toEqual([1, 2, 5])
26+
expect(cycleIndex).toBeUndefined()
27+
})
28+
29+
it('255/40 = 6.375', () => {
30+
const [integer, decimals, cycleIndex] = decExp(255, 40)
31+
expect(integer).toBe(6)
32+
expect(decimals).toEqual([3, 7, 5])
33+
expect(cycleIndex).toBeUndefined()
34+
})
35+
})
36+
37+
describe('Repeating Decimal Expansion', () => {
38+
it('1/3 = 0.(3)', () => {
39+
expect(decExp(1, 3)).toStrictEqual([0, [3], 0])
40+
})
41+
42+
it('1/6 = 0.1(6)', () => {
43+
expect(decExp(1, 6)).toStrictEqual([0, [1, 6], 1])
44+
})
45+
46+
it('1/7 = 0.(142857)', () => {
47+
expect(decExp(1, 7)).toStrictEqual([0, [1, 4, 2, 8, 5, 7], 0])
48+
})
49+
})
50+
51+
/**
52+
* Binary
53+
*/
54+
55+
describe('Finite Binary Expansion', () => {
56+
it('1/2 = 0.1₂', () => {
57+
const [integer, decimals, cycleIndex] = decExp(1, 2, 2)
58+
expect(integer).toBe(0)
59+
expect(decimals).toEqual([1])
60+
expect(cycleIndex).toBeUndefined()
61+
})
62+
63+
it('1/8 = 0.001₂', () => {
64+
const [integer, decimals, cycleIndex] = decExp(1, 8, 2)
65+
expect(integer).toBe(0)
66+
expect(decimals).toEqual([0, 0, 1])
67+
expect(cycleIndex).toBeUndefined()
68+
})
69+
70+
it('255/40 = 110.011₂', () => {
71+
const [integer, decimals, cycleIndex] = decExp(255, 40, 2)
72+
expect(integer).toBe(110)
73+
expect(decimals).toEqual([0, 1, 1])
74+
expect(cycleIndex).toBeUndefined()
75+
})
76+
})
77+
78+
describe('Repeating Binary Expansion', () => {
79+
it('1/3 = 0.(01)₂', () => {
80+
expect(decExp(1, 3, 2)).toStrictEqual([0, [0, 1], 0])
81+
})
82+
83+
it('1/5 = 0.(0011)₂', () => {
84+
expect(decExp(1, 5, 2)).toStrictEqual([0, [0, 0, 1, 1], 0])
85+
})
86+
87+
it('1/6 = 0.0(01)₂', () => {
88+
expect(decExp(1, 6, 2)).toStrictEqual([0, [0, 0, 1], 1])
89+
})
90+
91+
it('1/7 = 0.(001)₂', () => {
92+
expect(decExp(1, 7, 2)).toStrictEqual([0, [0, 0, 1], 0])
93+
})
94+
})
95+
96+
/**
97+
* Octal
98+
*/
99+
100+
describe('Finite Octal Expansion', () => {
101+
it('1/2 = 0.4₈', () => {
102+
const [integer, decimals, cycleIndex] = decExp(1, 2, 8)
103+
expect(integer).toBe(0)
104+
expect(decimals).toEqual([4])
105+
expect(cycleIndex).toBeUndefined()
106+
})
107+
108+
it('1/8 = 0.1₈', () => {
109+
const [integer, decimals, cycleIndex] = decExp(1, 8, 8)
110+
expect(integer).toBe(0)
111+
expect(decimals).toEqual([1])
112+
expect(cycleIndex).toBeUndefined()
113+
})
114+
115+
it('255/40 = 6.3₈', () => {
116+
const [integer, decimals, cycleIndex] = decExp(255, 40, 8)
117+
expect(integer).toBe(6)
118+
expect(decimals).toEqual([3])
119+
expect(cycleIndex).toBeUndefined()
120+
})
121+
})
122+
123+
describe('Repeating Octal Expansion', () => {
124+
it('1/3 = 0.(25)₈', () => {
125+
expect(decExp(1, 3, 8)).toStrictEqual([0, [2, 5], 0])
126+
})
127+
128+
it('1/5 = 0.(1463)₈', () => {
129+
expect(decExp(1, 5, 8)).toStrictEqual([0, [1, 4, 6, 3], 0])
130+
})
131+
132+
it('1/6 = 0.1(25)₈', () => {
133+
expect(decExp(1, 6, 8)).toStrictEqual([0, [1, 2, 5], 1])
134+
})
135+
136+
it('1/7 = 0.(1)₈', () => {
137+
expect(decExp(1, 7, 8)).toStrictEqual([0, [1], 0])
138+
})
139+
})
140+
141+
/**
142+
* Integers
143+
*/
144+
145+
describe('Integers', () => {
146+
it('1/1 = 1', () => {
147+
const [integer, decimals, cycleIndex] = decExp(1, 1)
148+
expect(integer).toBe(1)
149+
expect(decimals).toStrictEqual([])
150+
expect(cycleIndex).toBeUndefined()
151+
})
152+
153+
it('5/5 = 1', () => {
154+
const [integer, decimals, cycleIndex] = decExp(5, 5)
155+
expect(integer).toBe(1)
156+
expect(decimals).toStrictEqual([])
157+
expect(cycleIndex).toBeUndefined()
158+
})
159+
160+
it('2/1 = 2', () => {
161+
const [integer, decimals, cycleIndex] = decExp(2, 1)
162+
expect(integer).toBe(2)
163+
expect(decimals).toStrictEqual([])
164+
expect(cycleIndex).toBeUndefined()
165+
})
166+
167+
it('9/3 = 3', () => {
168+
const [integer, decimals, cycleIndex] = decExp(9, 3)
169+
expect(integer).toBe(3)
170+
expect(decimals).toStrictEqual([])
171+
expect(cycleIndex).toBeUndefined()
172+
})
173+
})
174+
175+
/**
176+
* Unsupported base
177+
*/
178+
179+
describe('Throws on unsupported base', () => {
180+
it('negative base', () => {
181+
expect(() => decExp(1, 1, -2)).toThrow(RangeError)
182+
})
183+
it('base 0', () => {
184+
expect(() => decExp(1, 1, 0)).toThrow(RangeError)
185+
})
186+
it('base 1', () => {
187+
expect(() => decExp(1, 1, 1)).toThrow(RangeError)
188+
})
189+
it('base 11', () => {
190+
expect(() => decExp(1, 1, 11)).toThrow(RangeError)
191+
})
192+
})

0 commit comments

Comments
 (0)