Merge branch 'master' into cycle-detection-in-linkedlist

This commit is contained in:
Omkarnath Parida
2020-10-04 23:50:56 +05:30
committed by GitHub
25 changed files with 8555 additions and 34 deletions

View File

@ -1,8 +1,10 @@
## back-tracking
* [KnightTour](https://github.com/TheAlgorithms/Javascript/blob/master/back-tracking/KnightTour.js)
* [NQueen](https://github.com/TheAlgorithms/Javascript/blob/master/back-tracking/NQueen.js)
* [Sudoku](https://github.com/TheAlgorithms/Javascript/blob/master/back-tracking/Sudoku.js)
## [babel](https://github.com/TheAlgorithms/Javascript/blob/master//babel.config.js)
## Backtracking
* [KnightTour](https://github.com/TheAlgorithms/Javascript/blob/master/Backtracking/KnightTour.js)
* [NQueen](https://github.com/TheAlgorithms/Javascript/blob/master/Backtracking/NQueen.js)
* [Sudoku](https://github.com/TheAlgorithms/Javascript/blob/master/Backtracking/Sudoku.js)
## Cache
* [LFUCache](https://github.com/TheAlgorithms/Javascript/blob/master/Cache/LFUCache.js)
@ -45,12 +47,17 @@
* [Trie](https://github.com/TheAlgorithms/Javascript/blob/master/Data-Structures/Tree/Trie.js)
## Dynamic-Programming
* [ClimbingStairs](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/ClimbingStairs.js)
* [CoinChange](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/CoinChange.js)
* [EditDistance](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/EditDistance.js)
* [KadaneAlgo](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/KadaneAlgo.js)
* [LevenshteinDistance](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/LevenshteinDistance.js)
* [LongestCommonSubsequence](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/LongestCommonSubsequence.js)
* [LongestIncreasingSubsequence](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/LongestIncreasingSubsequence.js)
* [MaxNonAdjacentSum](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/MaxNonAdjacentSum.js)
* [NumberOfSubsetEqualToGivenSum](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/NumberOfSubsetEqualToGivenSum.js)
* [SieveOfEratosthenes](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/SieveOfEratosthenes.js)
* [ZeroOneKnapsack](https://github.com/TheAlgorithms/Javascript/blob/master/Dynamic-Programming/ZeroOneKnapsack.js)
## Graphs
* [ConnectedComponents](https://github.com/TheAlgorithms/Javascript/blob/master/Graphs/ConnectedComponents.js)
@ -84,12 +91,13 @@
* [Palindrome](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Palindrome.js)
* [PascalTriangle](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/PascalTriangle.js)
* [PiApproximationMonteCarlo](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/PiApproximationMonteCarlo.js)
* [Polynomial](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Polynomial.js)
* [PrimeCheck](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/PrimeCheck.js)
* [ReversePolishNotation](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/ReversePolishNotation.js)
* [SieveOfEratosthenes](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/SieveOfEratosthenes.js)
## Project Euler
* [Problem1](https://github.com/TheAlgorithms/Javascript/blob/master/Project%20Euler/Problem1.js)
## Project-Euler
* [Problem1](https://github.com/TheAlgorithms/Javascript/blob/master/Project-Euler/Problem1.js)
## Recursive
* [EucledianGCD](https://github.com/TheAlgorithms/Javascript/blob/master/Recursive/EucledianGCD.js)
@ -98,6 +106,7 @@
## Search
* [BinarySearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/BinarySearch.js)
* [ExponentialSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/ExponentialSearch.js)
* [FibonacciSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/FibonacciSearch.js)
* [InterpolationSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/InterpolationSearch.js)
* [JumpSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/JumpSearch.js)
* [LinearSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/LinearSearch.js)
@ -127,13 +136,19 @@
## String
* [CheckAnagram](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckAnagram.js)
* [CheckAnagram](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckAnagram.test.js)
* [CheckPalindrome](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckPalindrome.js)
* [CheckPalindrome](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckPalindrome.test.js)
* [CheckRearrangePalindrome](https://github.com/TheAlgorithms/Javascript/blob/master/String/CheckRearrangePalindrome.js)
* [PatternMatching](https://github.com/TheAlgorithms/Javascript/blob/master/String/PatternMatching.js)
* [PatternMatching](https://github.com/TheAlgorithms/Javascript/blob/master/String/PatternMatching.test.js)
* [ReverseString](https://github.com/TheAlgorithms/Javascript/blob/master/String/ReverseString.js)
* [ReverseString](https://github.com/TheAlgorithms/Javascript/blob/master/String/ReverseString.test.js)
* [ReverseWords](https://github.com/TheAlgorithms/Javascript/blob/master/String/ReverseWords.js)
* [ReverseWords](https://github.com/TheAlgorithms/Javascript/blob/master/String/ReverseWords.test.js)
## TimingFunctions
* [IntervalTimer](https://github.com/TheAlgorithms/Javascript/blob/master/TimingFunctions/IntervalTimer.js)
## Timing-Functions
* [IntervalTimer](https://github.com/TheAlgorithms/Javascript/blob/master/Timing-Functions/IntervalTimer.js)
## Trees
* [DepthFirstSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Trees/DepthFirstSearch.js)

View File

@ -0,0 +1,26 @@
/*
* You are climbing a stair case. It takes n steps to reach to the top.
* Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
*/
const climbStairs = (n) => {
let prev = 0
let cur = 1
let temp
for (let i = 0; i < n; i++) {
temp = prev
prev = cur
cur += temp
}
return cur
}
const main = () => {
const number = 5
console.log('Number of ways to climb ' + number + ' stairs in ' + climbStairs(5))
}
// testing
main()

View File

@ -0,0 +1,61 @@
/*
Wikipedia -> https://en.wikipedia.org/wiki/Edit_distance
Q. -> Given two strings `word1` and `word2`. You can perform these operations on any of the string to make both strings similar.
- Insert
- Remove
- Replace
Find the minimum operation cost required to make both same. Each operation cost is 1.
Algorithm details ->
time complexity - O(n*m)
space complexity - O(n*m)
*/
const minimumEditDistance = (word1, word2) => {
const n = word1.length
const m = word2.length
const dp = new Array(m + 1).fill(0).map(item => [])
/*
fill dp matrix with default values -
- first row is filled considering no elements in word2.
- first column filled considering no elements in word1.
*/
for (let i = 0; i < n + 1; i++) {
dp[0][i] = i
}
for (let i = 0; i < m + 1; i++) {
dp[i][0] = i
}
/*
indexing is 1 based for dp matrix as we defined some known values at first row and first column/
*/
for (let i = 1; i < m + 1; i++) {
for (let j = 1; j < n + 1; j++) {
const letter1 = word1[j - 1]
const letter2 = word2[i - 1]
if (letter1 === letter2) {
dp[i][j] = dp[i - 1][j - 1]
} else {
dp[i][j] = Math.min(dp[i - 1][j], dp[i - 1][j - 1], dp[i][j - 1]) + 1
}
}
}
return dp[m][n]
}
const main = () => {
console.log(minimumEditDistance('horse', 'ros'))
console.log(minimumEditDistance('cat', 'cut'))
console.log(minimumEditDistance('', 'abc'))
console.log(minimumEditDistance('google', 'glgool'))
}
main()

View File

@ -1,3 +1,4 @@
'use strict'
/*
author: PatOnTheBack
license: GPL-3.0 or later
@ -11,12 +12,12 @@
https://en.wikipedia.org/wiki/Mean
*/
function mean (nums) {
'use strict'
const mean = (nums) => {
// This is a function returns average/mean of array
var sum = 0
var avg
// This loop sums all values in the 'nums' array.
// This loop sums all values in the 'nums' array using forEach loop
nums.forEach(function (current) {
sum += current
})

82
Maths/Polynomial.js Normal file
View File

@ -0,0 +1,82 @@
/**
* Polynomials are algebraic expressions consisting of two or more algebraic terms.
* Terms of a polynomial are:
* 1. Coefficients e.g. 5, 4 in 5x^0, 4x^3 respectively
* 2. Variables e.g. y in 3y^2
* 3. Exponents e.g. 5 in y^5
*
* Class Polynomial constructs the polynomial using Array as an argument.
* The members of array are coefficients and their indexes as exponents.
*/
class Polynomial {
constructor (array) {
this.coefficientArray = array // array of coefficients
this.polynomial = '' // in terms of x e.g. (2x) + (1)
this.construct()
}
/**
* Function to construct the polynomial in terms of x using the coefficientArray
*/
construct () {
this.polynomial = this.coefficientArray.map((coefficient, exponent) => {
if (coefficient === 0) {
return '0'
}
if (exponent === 0) {
return `(${coefficient})`
} else if (exponent === 1) {
return `(${coefficient}x)`
} else {
return `(${coefficient}x^${exponent})`
}
})
.filter((x) => {
if (x !== '0') {
return x
}
})
.reverse()
.join(' + ')
}
/**
* Function to display polynomial in terms of x
* @returns {String} of polynomial representation in terms of x
*/
display () {
return this.polynomial
}
/**
* Function to calculate the value of the polynomial by substituting variable x
* @param {Number} value
*/
evaluate (value) {
return this.coefficientArray.reduce((result, coefficient, exponent) => {
return result + coefficient * (Math.pow(value, exponent))
}, 0)
}
}
/**
* Function to perform tests
*/
const tests = () => {
const polynomialOne = new Polynomial([1, 2, 3, 4])
console.log('Test 1: [1,2,3,4]')
console.log('Display Polynomial ', polynomialOne.display())
// (4x^3) + (3x^2) + (2x) + (1)
console.log('Evaluate Polynomial value=2 ', polynomialOne.evaluate(2))
// 49
const polynomialTwo = new Polynomial([5, 0, 0, -4, 3])
console.log('Test 2: [5,0,0,-4,3]')
console.log('Display Polynomial ', polynomialTwo.display())
// (3x^4) + (-4x^3) + (5)
console.log('Evaluate Polynomial value=1 ', polynomialTwo.evaluate(1))
// 4
}
tests()

77
Search/FibonacciSearch.js Normal file
View File

@ -0,0 +1,77 @@
/****************************************************************************
* Fibonacci Search JavaScript Implementation
* Author Alhassan Atama Isiaka
* Version v1.0.0
* Copyright 2020
* https://github.com/komputarist
*
* This implementation is based on Generalizing the Fibonacci search we
* define the Fibonacci search of degree K. Like the Fibonacci search,
* which it reduces to for K = 2, the Fibonacci search of degree K
* involves only addition and subtraction.
* Capocelli R.M. (1991) A Generalization of the Fibonacci Search. In:
* Bergum G.E., Philippou A.N., Horadam A.F. (eds) Applications of Fibonacci
* Numbers. Springer, Dordrecht. https://doi.org/10.1007/978-94-011-3586-3_9
*
* This snippet is free. Feel free to improve on it
*
* We define a function fibonacciSearch() that takes an array of numbers,
* the item (number) to be searched for and the length of the items in the array
****************************************************************************/
const fibonacciSearch = (arr, x, n) => {
let fib2 = 0 // (K-2)'th Fibonacci Number
let fib1 = 1 // (K-1)'th Fibonacci Number.
let fibK = fib2 + fib1 // Kth Fibonacci
/* We want to store the smallest fibonacci number smaller such that
number is greater than or equal to n, we use fibK for this */
while (fibK < n) {
fib2 = fib1
fib1 = fibK
fibK = fib2 + fib1
}
// This marks the eliminated range from front
let offset = -1
/* while there are elements to be checked. We compare arr[fib2] with x.
When fibM becomes 1, fib2 becomes 0 */
while (fibK > 1) {
// Check if fibK is a valid location
const i = Math.min(offset + fib2, n - 1)
/* If x is greater than the value at
index fib2, Partition the subarray array
from offset to i */
if (arr[i] < x) {
fibK = fib1
fib1 = fib2
fib2 = fibK - fib1
offset = i
/* If x is greater than the value at
index fib2, cut the subarray array
from offset to i */
} else if (arr[i] > x) {
fibK = fib2
fib1 = fib1 - fib2
fib2 = fibK - fib1
} else {
// return index for found element
return i
}
}
// comparing the last element with x */
if (fib1 && arr[offset + 1] === x) {
return offset + 1
}
// element not found. return -1
return -1
}
// Example
const myArray = [10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100]
const n = myArray.length
const x = 90
const fibFinder = fibonacciSearch(myArray, x, n)
console.log('Element found at index:', fibFinder)

View File

@ -8,7 +8,7 @@ const checkAnagram = (str1, str2) => {
// If both strings have not same lengths then they can not be anagram.
if (str1.length !== str2.length) {
return 'Not Anagram'
return 'Not anagrams'
}
// Use hashmap to keep count of characters in str1
@ -44,7 +44,4 @@ const checkAnagram = (str1, str2) => {
return 'Anagrams'
}
console.log(checkAnagram('abcd', 'bcad')) // should print anagram
console.log(checkAnagram('abcd', 'abef')) // should print not anagram
console.log(checkAnagram(10, 'abcd'))// should print Not String(s).
console.log(checkAnagram('abs', 'abds'))// should print not anagram
export { checkAnagram }

View File

@ -0,0 +1,31 @@
import { checkAnagram } from './CheckAnagram'
describe('checkAnagram', () => {
it.each`
inputOne | inputTwo
${123456} | ${'abcd'}
${[1, 2, 3, 4, 5, 6]} | ${'abcd'}
${{ test: 'test' }} | ${'abcd'}
${'abcd'} | ${123456}
${'abcd'} | ${[1, 2, 3, 4, 5, 6]}
${'abcd'} | ${{ test: 'test' }}
`(
'expects to return "Not string(s)" given values $inputOne and $inputTwo',
({ inputOne, inputTwo }) => {
const SUT = checkAnagram(inputOne, inputTwo)
expect(SUT).toBe('Not string(s)')
}
)
it('expects to return "Not anagram" if the arguments have different lengths', () => {
const SUT = checkAnagram('abs', 'abds')
expect(SUT).toBe('Not Anagram')
})
it('expects to return "Not anagram" if the arguments are not anagrams', () => {
const SUT = checkAnagram('abcs', 'abds')
expect(SUT).toBe('Not anagrams')
})
it('expects to return "Anagram" if the arguments are anagram', () => {
const SUT = checkAnagram('abcd', 'bcad')
expect(SUT).toBe('Anagrams')
})
})

View File

@ -21,5 +21,4 @@ const checkPalindrome = (str) => {
return 'Palindrome'
}
console.log(checkPalindrome('madam'))
console.log(checkPalindrome('abcd'))
export { checkPalindrome }

View File

@ -0,0 +1,16 @@
import { checkPalindrome } from './CheckPalindrome'
describe('checkPalindrome', () => {
it('expects to return "Palindrome" if the given string is a palindrome', () => {
const SUT = checkPalindrome('madam')
expect(SUT).toBe('Palindrome')
})
it('expects to return "Empty string" if the given string is empty', () => {
const SUT = checkPalindrome('')
expect(SUT).toBe('Empty string')
})
it('expects to return "Not a string" if the given string is not a string', () => {
const SUT = checkPalindrome(123)
expect(SUT).toBe('Not a string')
})
})

View File

@ -0,0 +1,31 @@
/**
* What is a palindrome? https://en.wikipedia.org/wiki/Palindrome
* Receives a string and returns whether it can be rearranged to become a palindrome or not
* The string can only be a palindrome if the count of ALL characters is even or if the ONLY ONE character count is odd
* Input is a string
*
**/
const palindromeRearranging = (str) => {
// check that input is a string
if (typeof str !== 'string') {
return 'Not a string'
}
// Check if is a empty string
if (str.length === 0) {
return 'Empty string'
}
// First obtain the character count for each character in the string and store it in an object.
// Filter the object's values to only the odd character counts.
const charCounts = [...str].reduce((counts, char) => {
counts[char] = counts[char] ? counts[char] + 1 : 1
return counts
}, {})
// If the length of the resulting array is 0 or 1, the string can be a palindrome.
return Object.values(charCounts).filter(count => count % 2 !== 0).length <= 1
}
// testing
console.log(palindromeRearranging('aaeccrr')) // true
console.log(palindromeRearranging('leve')) // false

View File

@ -8,6 +8,9 @@ return the starting index if the given pattern is
available in the text
*/
const checkIfPatternExists = (text, pattern) => {
if (typeof text !== 'string' || typeof pattern !== 'string') {
throw new TypeError('Given input is not a string')
}
const textLength = text.length // Store the length of the text in a variable
const patternLength = pattern.length // Store the length of the pattern in a variable
@ -22,15 +25,10 @@ const checkIfPatternExists = (text, pattern) => {
// j + 1 is equal to the length of the pattern
if (j + 1 === patternLength) {
console.log(`Given pattern is found at index ${i}`)
return `Given pattern is found at index ${i}`
}
}
}
}
const main = () => {
const text = 'AABAACAADAABAAAABAA'
const pattern = 'AABA'
checkIfPatternExists(text.toLowerCase(), pattern.toLowerCase())
}
main()
export { checkIfPatternExists }

View File

@ -0,0 +1,26 @@
import { checkIfPatternExists } from './PatternMatching'
describe('checkIfPatternExists', () => {
it('expects to find a pattern with correct input', () => {
const text = 'AABAACAADAABAAAABAA'
const pattern = 'AABA'
const SUT = checkIfPatternExists(text.toLowerCase(), pattern.toLowerCase())
expect(SUT).toBe('Given pattern is found at index 0')
})
it('expects to return a message when there is no pattern', () => {
const text = 'ABCDEFG'
const pattern = 'AEG'
const SUT = checkIfPatternExists(text.toLowerCase(), pattern.toLowerCase())
expect(SUT).toBe(undefined)
})
it('expects to find a pattern independent of casing', () => {
const text = 'AbCAAAAAAB'
const pattern = 'abc'
const SUT = checkIfPatternExists(text, pattern)
expect(SUT).toBe(undefined)
})
it('expects to throw an error message when given inpuut is not a string', () => {
const text = 123444456
const pattern = 123
expect(() => checkIfPatternExists(text, pattern)).toThrow('Given input is not a string')
})
})

View File

@ -9,6 +9,9 @@
*/
function ReverseStringIterative (string) {
if (typeof string !== 'string') {
throw new TypeError('The given value is not a string')
}
let reversedString = ''
let index
@ -28,6 +31,9 @@ function ReverseStringIterative (string) {
*/
function ReverseStringIterativeInplace (string) {
if (typeof string !== 'string') {
throw new TypeError('The given value is not a string')
}
const _string = string.split('')
for (let i = 0; i < Math.floor(_string.length / 2); i++) {
@ -40,6 +46,4 @@ function ReverseStringIterativeInplace (string) {
return _string.join('')
}
// testing
console.log(ReverseStringIterative('Javascript'))
console.log(ReverseStringIterativeInplace('Javascript'))
export { ReverseStringIterative, ReverseStringIterativeInplace }

View File

@ -0,0 +1,63 @@
import {
ReverseStringIterative,
ReverseStringIterativeInplace
} from './ReverseString'
describe('ReverseStringIterative', () => {
it('expects to reverse a simple string', () => {
const SUT = ReverseStringIterative('reverse')
expect(SUT).toEqual('esrever')
})
it('expects to reverse a string with spaces in between', () => {
const SUT = ReverseStringIterative('reverse me')
expect(SUT).toEqual('em esrever')
})
it('expects to reverse a simple string without capitalizing the first letter', () => {
const SUT = ReverseStringIterative('Javascript')
expect(SUT).toEqual('tpircsavaJ')
})
it.each`
input
${123456}
${[1, 2, 3, 4, 5, 6]}
${{ test: 'test' }}
`(
'expects to throw a type error given a value that is $input',
({ input }) => {
expect(() => {
ReverseStringIterative(input)
}).toThrow('The given value is not a string')
}
)
it('expects to return a empty string with an empty string is given', () => {
const SUT = ReverseStringIterative('')
expect(SUT).toEqual('')
})
})
describe('ReverseStringIterativeInplace', () => {
it('expects to reverse a simple string', () => {
const SUT = ReverseStringIterativeInplace('reverse')
expect(SUT).toEqual('esrever')
})
it('expects to reverse a simple string without capitalizing the first letter', () => {
const SUT = ReverseStringIterativeInplace('Javascript')
expect(SUT).toEqual('tpircsavaJ')
})
it('expects to return an empty string given an empty string', () => {
const SUT = ReverseStringIterativeInplace('Javascript')
expect(SUT).toEqual('tpircsavaJ')
})
it.each`
input
${123456}
${[1, 2, 3, 4, 5, 6]}
${{ test: 'test' }}
`(
'expects to throw a type error given a value that is $input',
({ input }) => {
expect(() => {
ReverseStringIterativeInplace(input)
}).toThrow('The given value is not a string')
}
)
})

View File

@ -1,4 +1,7 @@
const reverseWords = (str) => {
if (typeof str !== 'string') {
throw new TypeError('The given value is not a string')
}
// Split string into words
// Ex. "I Love JS" => ["I", "Love", "JS"]
const words = str.split(' ')
@ -10,6 +13,4 @@ const reverseWords = (str) => {
return reversedWords.join(' ')
}
// testing
console.log(reverseWords('I Love JS'))
console.log(reverseWords('My Name Is JavaScript'))
export { reverseWords }

View File

@ -0,0 +1,21 @@
import { reverseWords } from './ReverseWords'
describe('reverseWords', () => {
it('expects to reverse words to return a joined word', () => {
const SUT = reverseWords('I Love JS')
expect(SUT).toBe('JS Love I')
})
it.each`
input
${123456}
${[1, 2, 3, 4, 5, 6]}
${{ test: 'test' }}
`(
'expects to throw a type error given a value that is $input',
({ input }) => {
expect(() => {
reverseWords(input)
}).toThrow('The given value is not a string')
}
)
})

12
babel.config.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
esmodules: true
}
}
]
]
}

8051
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,17 +2,26 @@
"name": "javascript",
"version": "1.0.0",
"description": "A repository for All algorithms implemented in Javascript (for educational purposes only)",
"main": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest --no-cache"
},
"author": "TheAlgorithms",
"license": "GPL-3.0",
"dependencies": {
"@babel/core": "^7.11.6",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"jsdom": "^16.3.0",
"node-fetch": "2.6.1"
},
"standard": {
"env": [ "jest" ]
},
"devDependencies": {
"standard": "^14.3.4",
"doctest": "^0.17.1"
"babel-jest": "^26.3.0",
"doctest": "^0.17.1",
"jest": "^26.4.2",
"standard": "^14.3.4"
}
}