mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 16:26:47 +08:00
chore: merge Fix/742 migrate doctest to jest (#749)
* Remove QuickSelect doctest There are more Jest test cases already. * Remove AverageMedian doctest Already migrated to jest * Migrate doctest for BinaryExponentiationRecursive.js (also remove inline "main" test method) * Migrate doctest for EulersTotient.js (also remove inline "main" test method) * Migrate doctest for PrimeFactors.js (also remove inline "main" test method) * Migrate doctest for BogoSort.js Re-write prototype-polluting helper methods, too. (also remove inline test driver code) * Migrate doctest for BeadSort.js (also remove inline test driver code) * Migrate doctest for BucketSort.js (also remove inline test driver code) * Migrate doctest for CocktailShakerSort.js (also remove inline test driver code) * Migrate doctest for MergeSort.js (also remove inline test driver code) * Migrate doctest for QuickSort.js (also remove inline test driver code) * Migrate doctest for ReverseString.js (also remove inline test driver code) * Migrate doctest for ReverseString.js * Migrate doctest for ValidateEmail.js * Migrate doctest for ConwaysGameOfLife.js (remove the animate code, too) * Remove TernarySearch doctest Already migrated to jest * Migrate doctest for BubbleSort.js (also remove inline test driver code) * Remove doctest from CI and from dependencies relates to #742 fixes #586 * Migrate doctest for RgbHsvConversion.js * Add --fix option to "standard" npm script * Migrate doctest for BreadthFirstSearch.js (also remove inline test driver code) * Migrate doctest for BreadthFirstShortestPath.js (also remove inline test driver code) * Migrate doctest for EulerMethod.js (also remove inline test driver code) Move manual test-code for plotting stuff in the browser in a distinct file, too. Those "*.manual-test.js" files are excluded from the UpdateDirectory.mjs script, as well. * Migrate doctest for Mandelbrot.js (also remove inline test driver code & moved manual drawing test into a *.manual-test.js) * Migrate doctest for FloodFill.js * Migrate doctest for KochSnowflake.js (also move manual drawing test into a *.manual-test.js) * Update npm lockfile * Update README and COMMITTING with a few bits & bobs regarding testing & code quality
This commit is contained in:
8
.github/workflows/UpdateDirectory.mjs
vendored
8
.github/workflows/UpdateDirectory.mjs
vendored
@ -76,7 +76,13 @@ function pathsToMarkdown (filePaths) {
|
||||
}
|
||||
|
||||
// get paths of all .js files - excluding node_modules, the .github folder, tests and config stuff
|
||||
globby(['**/*.js', '!(node_modules|.github)/**/*', '!**/*.test.js', '!babel.config.js'])
|
||||
globby([
|
||||
'**/*.js',
|
||||
'!(node_modules|.github)/**/*',
|
||||
'!**/*.test.js',
|
||||
'!**/*.manual-test.js',
|
||||
'!babel.config.js'
|
||||
])
|
||||
// create markdown content
|
||||
.then(pathsToMarkdown)
|
||||
// write markdown to file
|
||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -15,9 +15,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: 🧪 Run tests
|
||||
run: |
|
||||
npm run doctest || true # TODO: Add all doctests
|
||||
npm test
|
||||
run: npm test
|
||||
|
||||
- name: 💄 Code style
|
||||
run: npm run style
|
||||
|
139
CONTRIBUTING.md
139
CONTRIBUTING.md
@ -2,33 +2,39 @@
|
||||
|
||||
## Before contributing
|
||||
|
||||
Welcome to [TheAlgorithms/Javascript](https://github.com/TheAlgorithms/Javascript)! Before sending your pull requests, make sure that you **read the whole guidelines**. If you have any doubt on the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Javascript/issues/new)
|
||||
Welcome to [TheAlgorithms/Javascript](https://github.com/TheAlgorithms/Javascript)! Before sending your pull requests,
|
||||
make sure that you **read the whole guidelines**. If you have any doubt on the contributing guide, please feel free to
|
||||
[state it clearly in an issue](https://github.com/TheAlgorithms/Javascript/issues/new).
|
||||
|
||||
## Contributing
|
||||
|
||||
### Contributor
|
||||
|
||||
We are very happy that you consider implementing algorithms and data structures for others! This repository is referenced and used by learners from around the globe. Being one of our contributors, you agree and confirm that:
|
||||
We are very happy that you consider implementing algorithms and data structures for others! This repository is
|
||||
referenced and used by learners from around the globe. Being one of our contributors, you agree and confirm that:
|
||||
|
||||
- You did your work - plagiarism is not allowed.
|
||||
- Any plagiarized work will not be merged.
|
||||
- Your work will be distributed under [GNU License](LICENSE) once your pull request is merged
|
||||
- Your submitted work must fulfill our styles and standards
|
||||
* You did your work - plagiarism is not allowed.
|
||||
* Any plagiarized work will not be merged.
|
||||
* Your work will be distributed under [GNU License](LICENSE) once your pull request is merged.
|
||||
* Your submitted work must fulfill our styles and standards.
|
||||
|
||||
**New implementation** is welcome! For example, new solutions to a problem, different representations of a graph data structure or algorithm designs with different complexity.
|
||||
**New implementation** is welcome! For example, new solutions to a problem, different representations of a graph data
|
||||
structure or algorithm designs with different complexity.
|
||||
|
||||
**Improving comments** and **writing proper tests** are also highly welcome.
|
||||
|
||||
### Contribution
|
||||
|
||||
We appreciate any contribution, from fixing grammar mistakes to implementing complex algorithms. Please read this section if you are contributing to your work.
|
||||
We appreciate any contribution, from fixing grammar mistakes to implementing complex algorithms. Please read this
|
||||
section if you are contributing to your work.
|
||||
|
||||
|
||||
If you submit a pull request that resolves an open issue, please help us to keep our issue list small by adding `fixes: #{$ISSUE_NO}` to your commit message. GitHub will use this tag to auto-close the issue if your PR is merged.
|
||||
If you submit a pull request that resolves an open issue, please help us to keep our issue list small by adding
|
||||
`fixes: #{$ISSUE_NO}` to your commit message. GitHub will use this tag to auto-close the issue if your PR is merged.
|
||||
|
||||
#### What is an Algorithm?
|
||||
|
||||
An Algorithm is one or more functions (or classes) that:
|
||||
|
||||
* take one or more inputs,
|
||||
* perform some internal calculations or data manipulations,
|
||||
* return one or more outputs,
|
||||
@ -37,66 +43,97 @@ An Algorithm is one or more functions (or classes) that:
|
||||
Algorithms should be packaged in a way that would make it easy for readers to put them into larger programs.
|
||||
|
||||
Algorithms should:
|
||||
|
||||
* have intuitive class and function names that make their purpose clear to readers
|
||||
* use JavaScript naming conventions and intuitive variable names to ease comprehension
|
||||
* be flexible to take different input values
|
||||
* raise JavaScript exceptions (RangeError, etc.) on erroneous input values
|
||||
|
||||
Algorithms in this repo should not be how-to examples for existing JavaScript packages. Instead, they should perform internal calculations or manipulations to convert input values into different output values. Those calculations or manipulations can use data types, classes, or functions of existing JavaScript packages but each algorithm in this repo should add unique value.
|
||||
Algorithms in this repo should not be how-to examples for existing JavaScript packages. Instead, they should perform
|
||||
internal calculations or manipulations to convert input values into different output values. Those calculations or
|
||||
manipulations can use data types, classes, or functions of existing JavaScript packages but each algorithm in this repo
|
||||
should add unique value.
|
||||
|
||||
#### File Naming Convention
|
||||
- filenames should use the UpperCamelCase (PascalCase) style.
|
||||
- There should be no spaces in filenames.
|
||||
**Example:**`UserProfile.js` is allowed but `userprofile.js`,`Userprofile.js`,`user-Profile.js`,`userProfile.js` are not
|
||||
|
||||
* filenames should use the UpperCamelCase (PascalCase) style.
|
||||
* There should be no spaces in filenames.
|
||||
* **Example:**`UserProfile.js` is allowed but `userprofile.js`,`Userprofile.js`,`user-Profile.js`,`userProfile.js` are
|
||||
not.
|
||||
|
||||
#### Testing
|
||||
|
||||
Be confident that your code works. When was the last time you committed a code change, your build failed, and half of your app stopped working? Mine was last week. Writing tests for our Algorithms will help us ensure the implementations are air tight even after multiple fixes and code changes.
|
||||
We use a NPM package [doctest](https://www.npmjs.com/package/doctest) to add basic testing functionality to our code. Doctests are simple structured comments that evaluate a function and ensure a required result.
|
||||
Be confident that your code works. When was the last time you committed a code change, your build failed, and half of
|
||||
your app stopped working? Mine was last week. Writing tests for our Algorithms will help us ensure the implementations
|
||||
are air tight even after multiple fixes and code changes.
|
||||
|
||||
The implementation of doctest is quite simple. You can easily learn it [here](https://www.npmjs.com/package/doctest).
|
||||
We use [Jest](https://jestjs.io/) to run unit tests on our algorithms. It provides a very readable and expressive way to
|
||||
structure your test code.
|
||||
|
||||
It is advised that you add the Doctests in a multiline comment just after the Algorithm description.
|
||||
For Code Structure reference see [this file](https://github.com/TheAlgorithms/Javascript/blob/master/Sorts/BubbleSort.js).
|
||||
It is advised that the algorithm file (module) does not contain any "live" code but rather just exports the function(s)
|
||||
needed to execute the algorithm. Your test code can import those function(s), call them with the appropriate parameters
|
||||
and inspect the outcome. Example: [RatInAMaze.test.js](Backtracking/tests/RatInAMaze.test.js).
|
||||
|
||||
You can run the doctest by using the command
|
||||
Please refrain from using `console` in your implementation AND test code.
|
||||
|
||||
You can (and should!) run all tests locally before committing your changes:
|
||||
|
||||
```shell
|
||||
npm test
|
||||
```
|
||||
$ doctest MyFile.js // if that fails, try: npx doctest MyFile.js
|
||||
|
||||
If you want save some time and just run a specific test:
|
||||
|
||||
```shell
|
||||
# this will run any test file where the filename matches "koch"
|
||||
npm test -- koch
|
||||
```
|
||||
|
||||
You can also start Jest in "watch" mode:
|
||||
|
||||
```shell
|
||||
npm test -- --watchAll
|
||||
```
|
||||
|
||||
This will run all tests and watch source and test files for changes. When a change is made, the tests will run again.
|
||||
|
||||
#### Coding Style
|
||||
|
||||
To maximize the readability and correctness of our code, we require that new submissions follow [JavaScript Standard Style](https://standardjs.com/)
|
||||
- Command to install JavaScript Standard Style
|
||||
```
|
||||
$ npm install standard --save-dev
|
||||
```
|
||||
- Usage
|
||||
```
|
||||
$ standard MyFile.js // if that fails, try: npx standard MyFile.js
|
||||
```
|
||||
To maximize the readability and correctness of our code, we require that new submissions follow the
|
||||
[JavaScript Standard Style](https://standardjs.com/).
|
||||
|
||||
- Use camelCase with the leading character as lowercase for identifier names (variables and functions)
|
||||
- Names start with a letter
|
||||
- follow code indentation
|
||||
- Always use 2 spaces for indentation of code blocks
|
||||
```
|
||||
function sumOfArray (arrayOfNumbers) {
|
||||
let sum = 0
|
||||
for (let i = 0; i < arrayOfNumbers.length; i++) {
|
||||
sum += arrayOfNumbers[i]
|
||||
}
|
||||
return (sum)
|
||||
}
|
||||
Before committing, please run
|
||||
|
||||
```
|
||||
- Avoid using global variables and avoid '=='
|
||||
- Please use 'let' over 'var'
|
||||
- Please use 'console.log()'
|
||||
- We strongly recommend the use of ECMAScript 6
|
||||
- Avoid importing external libraries for basic algorithms. Only use those libraries for complicated algorithms.
|
||||
- Most importantly,
|
||||
- **Be consistent in the use of these guidelines when submitting.**
|
||||
- Happy coding!
|
||||
```shell
|
||||
npm run style
|
||||
```
|
||||
|
||||
in order to apply the coding style (where it can be done automatically). If an error is shown, please figure out what's
|
||||
wrong, fix it and run standard again.
|
||||
|
||||
A few (but not all) of the things to keep in mind:
|
||||
|
||||
* Use camelCase with the leading character as lowercase for identifier names (variables and functions)
|
||||
* Names start with a letter
|
||||
* Follow code indentation: Always use 2 spaces for indentation of code blocks
|
||||
```js
|
||||
function sumOfArray (arrayOfNumbers) {
|
||||
let sum = 0
|
||||
for (let i = 0; i < arrayOfNumbers.length; i++) {
|
||||
sum += arrayOfNumbers[i]
|
||||
}
|
||||
return (sum)
|
||||
}
|
||||
```
|
||||
*
|
||||
* Avoid using global variables and avoid `==`
|
||||
* Please use `let` over `var`
|
||||
* Please refrain from using `console.log` or any other console methods
|
||||
* **Absolutely** don't use `alert`
|
||||
* We strongly recommend the use of ECMAScript 6
|
||||
* Avoid importing external libraries for basic algorithms. Only use those libraries for complicated algorithms
|
||||
* Most importantly:
|
||||
* **Be consistent in the use of these guidelines when submitting**
|
||||
* Happy coding!
|
||||
|
||||
Writer [@itsvinayak](https://github.com/itsvinayak), May 2020.
|
||||
|
@ -8,17 +8,10 @@ The Game of Life is a cellular automaton devised by the British mathematician Jo
|
||||
(example adapted from https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/conways_game_of_life.py )
|
||||
*/
|
||||
|
||||
/*
|
||||
* Doctests
|
||||
*
|
||||
* > newGeneration([[0, 1, 0], [0, 1, 0], [0, 1, 0]])
|
||||
* [ [ 0, 0, 0 ], [ 1, 1, 1 ], [ 0, 0, 0 ] ]
|
||||
*/
|
||||
|
||||
/*
|
||||
* Generates the next generation for a given state of Conway's Game of Life.
|
||||
*/
|
||||
function newGeneration (cells) {
|
||||
/**
|
||||
* Generates the next generation for a given state of Conway's Game of Life.
|
||||
*/
|
||||
export function newGeneration (cells) {
|
||||
const nextGeneration = []
|
||||
for (let i = 0; i < cells.length; i++) {
|
||||
const nextGenerationRow = []
|
||||
@ -46,45 +39,3 @@ function newGeneration (cells) {
|
||||
}
|
||||
return nextGeneration
|
||||
}
|
||||
|
||||
/*
|
||||
* utility function to display a series of generations in the console
|
||||
*/
|
||||
async function animate (cells, steps) {
|
||||
/*
|
||||
* utility function to print one frame
|
||||
*/
|
||||
function printCells (cells) {
|
||||
console.clear()
|
||||
for (let i = 0; i < cells.length; i++) {
|
||||
let line = ''
|
||||
for (let j = 0; j < cells[i].length; j++) {
|
||||
if (cells[i][j] === 1) line += '\u2022'
|
||||
else line += ' '
|
||||
}
|
||||
console.log(line)
|
||||
}
|
||||
}
|
||||
|
||||
printCells(cells)
|
||||
|
||||
for (let i = 0; i < steps; i++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 250)) // sleep
|
||||
cells = newGeneration(cells)
|
||||
printCells(cells)
|
||||
}
|
||||
}
|
||||
|
||||
// Define glider example
|
||||
const glider = [
|
||||
[0, 1, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0]
|
||||
]
|
||||
|
||||
animate(glider, 16)
|
||||
|
8
Cellular-Automata/ConwaysGameOfLife.test.js
Normal file
8
Cellular-Automata/ConwaysGameOfLife.test.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { newGeneration } from './ConwaysGameOfLife'
|
||||
|
||||
describe('newGeneration', () => {
|
||||
it('should produce the next generation according to the rules', () => {
|
||||
expect(newGeneration([[0, 1, 0], [0, 1, 0], [0, 1, 0]]))
|
||||
.toEqual([[0, 0, 0], [1, 1, 1], [0, 0, 0]])
|
||||
})
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* The RGB color model is an additive color model in which red, green, and blue light are added
|
||||
* together in various ways to reproduce a broad array of colors. The name of the model comes from
|
||||
* the initials of the three additive primary colors, red, green, and blue. Meanwhile, the HSV
|
||||
@ -8,57 +8,6 @@
|
||||
* https://en.wikipedia.org/wiki/RGB_color_model and https://en.wikipedia.org/wiki/HSL_and_HSV).
|
||||
*/
|
||||
|
||||
/*
|
||||
Doctests
|
||||
Expected RGB-values taken from https://www.rapidtables.com/convert/color/hsv-to-rgb.html
|
||||
Test hsvToRgb-method
|
||||
|
||||
> hsvToRgb(0, 0, 0)
|
||||
[0, 0, 0]
|
||||
> hsvToRgb(0, 0, 1)
|
||||
[255, 255, 255]
|
||||
> hsvToRgb(0, 1, 1)
|
||||
[255, 0, 0]
|
||||
> hsvToRgb(60, 1, 1)
|
||||
[255, 255, 0]
|
||||
> hsvToRgb(120, 1, 1)
|
||||
[0, 255, 0]
|
||||
> hsvToRgb(240, 1, 1)
|
||||
[0, 0, 255]
|
||||
> hsvToRgb(300, 1, 1)
|
||||
[255, 0, 255]
|
||||
> hsvToRgb(180, 0.5, 0.5)
|
||||
[64, 128, 128]
|
||||
> hsvToRgb(234, 0.14, 0.88)
|
||||
[193, 196, 224]
|
||||
> hsvToRgb(330, 0.75, 0.5)
|
||||
[128, 32, 80]
|
||||
|
||||
Test rgbToHsv-method
|
||||
function "approximatelyEqualHsv" needed because of small deviations due to rounding for the RGB-values.
|
||||
> approximatelyEqualHsv(rgbToHsv(0, 0, 0), [0, 0, 0])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(255, 255, 255), [0, 0, 1])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(255, 0, 0), [0, 1, 1])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(255, 255, 0), [60, 1, 1])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(0, 255, 0), [120, 1, 1])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(0, 0, 255), [240, 1, 1])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(255, 0, 255), [300, 1, 1])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(64, 128, 128), [180, 0.5, 0.5])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(193, 196, 224), [234, 0.14, 0.88])
|
||||
true
|
||||
> approximatelyEqualHsv(rgbToHsv(128, 32, 80), [330, 0.75, 0.5])
|
||||
true
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Conversion from the HSV-representation to the RGB-representation.
|
||||
*
|
||||
@ -67,7 +16,7 @@ true
|
||||
* @param value Brightness-value of the color.
|
||||
* @return The tuple of RGB-components.
|
||||
*/
|
||||
function hsvToRgb (hue, saturation, value) { // eslint-disable-line no-unused-vars
|
||||
export function hsvToRgb (hue, saturation, value) {
|
||||
if (hue < 0 || hue > 360) {
|
||||
throw new Error('hue should be between 0 and 360')
|
||||
}
|
||||
@ -96,7 +45,7 @@ function hsvToRgb (hue, saturation, value) { // eslint-disable-line no-unused-va
|
||||
* @param blue Blue-component of the color.
|
||||
* @return The tuple of HSV-components.
|
||||
*/
|
||||
function rgbToHsv (red, green, blue) { // eslint-disable-line no-unused-vars
|
||||
export function rgbToHsv (red, green, blue) {
|
||||
if (red < 0 || red > 255) {
|
||||
throw new Error('red should be between 0 and 255')
|
||||
}
|
||||
@ -120,7 +69,7 @@ function rgbToHsv (red, green, blue) { // eslint-disable-line no-unused-vars
|
||||
if (chroma === 0) {
|
||||
hue = 0
|
||||
} else if (value === dRed) {
|
||||
hue = 60 * (0 + (dGreen - dBlue) / chroma)
|
||||
hue = 60 * ((dGreen - dBlue) / chroma)
|
||||
} else if (value === dGreen) {
|
||||
hue = 60 * (2 + (dBlue - dRed) / chroma)
|
||||
} else {
|
||||
@ -132,7 +81,7 @@ function rgbToHsv (red, green, blue) { // eslint-disable-line no-unused-vars
|
||||
return [hue, saturation, value]
|
||||
}
|
||||
|
||||
function approximatelyEqualHsv (hsv1, hsv2) { // eslint-disable-line no-unused-vars
|
||||
export function approximatelyEqualHsv (hsv1, hsv2) {
|
||||
const bHue = Math.abs(hsv1[0] - hsv2[0]) < 0.2
|
||||
const bSaturation = Math.abs(hsv1[1] - hsv2[1]) < 0.002
|
||||
const bValue = Math.abs(hsv1[2] - hsv2[2]) < 0.002
|
||||
@ -140,8 +89,7 @@ function approximatelyEqualHsv (hsv1, hsv2) { // eslint-disable-line no-unused-v
|
||||
return bHue && bSaturation && bValue
|
||||
}
|
||||
|
||||
function getRgbBySection (
|
||||
hueSection, chroma, matchValue, secondLargestComponent) {
|
||||
function getRgbBySection (hueSection, chroma, matchValue, secondLargestComponent) {
|
||||
function convertToInt (input) {
|
||||
return Math.round(255 * input)
|
||||
}
|
||||
|
33
Conversions/test/RgbHsvConversion.test.js
Normal file
33
Conversions/test/RgbHsvConversion.test.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { approximatelyEqualHsv, hsvToRgb, rgbToHsv } from '../RgbHsvConversion'
|
||||
|
||||
describe('hsvToRgb', () => {
|
||||
// Expected RGB-values taken from https://www.rapidtables.com/convert/color/hsv-to-rgb.html
|
||||
it('should calculate the correct RGB values', () => {
|
||||
expect(hsvToRgb(0, 0, 0)).toEqual([0, 0, 0])
|
||||
expect(hsvToRgb(0, 0, 1)).toEqual([255, 255, 255])
|
||||
expect(hsvToRgb(0, 1, 1)).toEqual([255, 0, 0])
|
||||
expect(hsvToRgb(60, 1, 1)).toEqual([255, 255, 0])
|
||||
expect(hsvToRgb(120, 1, 1)).toEqual([0, 255, 0])
|
||||
expect(hsvToRgb(240, 1, 1)).toEqual([0, 0, 255])
|
||||
expect(hsvToRgb(300, 1, 1)).toEqual([255, 0, 255])
|
||||
expect(hsvToRgb(180, 0.5, 0.5)).toEqual([64, 128, 128])
|
||||
expect(hsvToRgb(234, 0.14, 0.88)).toEqual([193, 196, 224])
|
||||
expect(hsvToRgb(330, 0.75, 0.5)).toEqual([128, 32, 80])
|
||||
})
|
||||
})
|
||||
|
||||
describe('rgbToHsv', () => {
|
||||
// "approximatelyEqualHsv" needed because of small deviations due to rounding for the RGB-values
|
||||
it('should calculate the correct HSV values', () => {
|
||||
expect(approximatelyEqualHsv(rgbToHsv(0, 0, 0), [0, 0, 0])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(255, 255, 255), [0, 0, 1])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(255, 0, 0), [0, 1, 1])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(255, 255, 0), [60, 1, 1])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(0, 255, 0), [120, 1, 1])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(0, 0, 255), [240, 1, 1])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(255, 0, 255), [300, 1, 1])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(64, 128, 128), [180, 0.5, 0.5])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(193, 196, 224), [234, 0.14, 0.88])).toEqual(true)
|
||||
expect(approximatelyEqualHsv(rgbToHsv(128, 32, 80), [330, 0.75, 0.5])).toEqual(true)
|
||||
})
|
||||
})
|
@ -61,15 +61,4 @@ function Swap (arr, x, y) {
|
||||
[arr[x], arr[y]] = [arr[y], arr[x]]
|
||||
}
|
||||
|
||||
// > QuickSelect([1, 4, 2, -2, 4, 5], 1)
|
||||
// -2
|
||||
// > QuickSelect([1, 4, 2, -2, 4, 5], 5)
|
||||
// 4
|
||||
// > QuickSelect([1, 4, 2, -2, 4, 5], 6)
|
||||
// 5
|
||||
// > QuickSelect([1, 4, 2, -2, 4, 5], 0)
|
||||
// "Index Out of Bound"
|
||||
// > QuickSelect([1, 4, 2, -2, 4, 5], 7)
|
||||
// "Index Out of Bound"
|
||||
|
||||
export { QuickSelect }
|
||||
|
@ -1,20 +1,13 @@
|
||||
/*
|
||||
Breadth-first search is an algorithm for traversing a graph. It's discovers all nodes reachable from the starting position by exploring all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
|
||||
(description adapted from https://en.wikipedia.org/wiki/Breadth-first_search )
|
||||
(see also: https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core )
|
||||
*/
|
||||
|
||||
/*
|
||||
Doctests
|
||||
> Array.from(breadthFirstSearch(graph, "C"))
|
||||
[ 'C', 'D', 'A', 'B', 'E' ]
|
||||
> Array.from(breadthFirstSearch(graph, "A"))
|
||||
[ 'A', 'B', 'D', 'E' ]
|
||||
> Array.from(breadthFirstSearch(graph, "F"))
|
||||
[ 'F', 'G' ]
|
||||
*/
|
||||
|
||||
function breadthFirstSearch (graph, startingNode) {
|
||||
/**
|
||||
* Breadth-first search is an algorithm for traversing a graph.
|
||||
*
|
||||
* It discovers all nodes reachable from the starting position by exploring all of the neighbor nodes at the present
|
||||
* depth prior to moving on to the nodes at the next depth level.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Breadth-first_search)
|
||||
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
|
||||
*/
|
||||
export function breadthFirstSearch (graph, startingNode) {
|
||||
// visited keeps track of all nodes visited
|
||||
const visited = new Set()
|
||||
|
||||
@ -39,26 +32,3 @@ function breadthFirstSearch (graph, startingNode) {
|
||||
|
||||
return visited
|
||||
}
|
||||
|
||||
const graph = {
|
||||
A: ['B', 'D'],
|
||||
B: ['E'],
|
||||
C: ['D'],
|
||||
D: ['A'],
|
||||
E: ['D'],
|
||||
F: ['G'],
|
||||
G: []
|
||||
}
|
||||
/*
|
||||
A <-> B
|
||||
ʌ |
|
||||
| |
|
||||
v v
|
||||
C --> D <-- E
|
||||
|
||||
F --> G
|
||||
*/
|
||||
|
||||
console.log(breadthFirstSearch(graph, 'C'))
|
||||
console.log(breadthFirstSearch(graph, 'A'))
|
||||
console.log(breadthFirstSearch(graph, 'F'))
|
||||
|
@ -1,25 +1,13 @@
|
||||
/*
|
||||
Breadth-first approach can be applied to determine the shortest path between two nodes
|
||||
in an equi-weighted graph. It searches the target node among all neighbors of the
|
||||
starting node, then the process is repeated on the level of the neighbors of the
|
||||
neighbors and so on.
|
||||
(See also: https://en.wikipedia.org/wiki/Breadth-first_search )
|
||||
(see also: https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core )
|
||||
*/
|
||||
|
||||
/*
|
||||
Doctests
|
||||
> breadthFirstShortestPath(graph, 'C', 'E')
|
||||
[ 'C', 'D', 'A', 'B', 'E' ]
|
||||
> breadthFirstShortestPath(graph, 'E', 'B')
|
||||
[ 'E', 'D', 'A', 'B' ]
|
||||
> breadthFirstShortestPath(graph, 'F', 'G')
|
||||
[ 'F', 'G' ]
|
||||
> breadthFirstShortestPath(graph, 'A', 'G')
|
||||
[]
|
||||
*/
|
||||
|
||||
function breadthFirstShortestPath (graph, startNode, targetNode) {
|
||||
/**
|
||||
* Breadth-first approach can be applied to determine the shortest path between two nodes in an equi-weighted graph.
|
||||
*
|
||||
* It searches the target node among all neighbors of the starting node, then the process is repeated on the level of
|
||||
* the neighbors of the neighbors and so on.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Breadth-first_search
|
||||
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
|
||||
*/
|
||||
export function breadthFirstShortestPath (graph, startNode, targetNode) {
|
||||
// check if startNode & targetNode are identical
|
||||
if (startNode === targetNode) {
|
||||
return [startNode]
|
||||
@ -62,27 +50,3 @@ function breadthFirstShortestPath (graph, startNode, targetNode) {
|
||||
// the target node was not reachable
|
||||
return []
|
||||
}
|
||||
|
||||
const graph = {
|
||||
A: ['B', 'D'],
|
||||
B: ['E'],
|
||||
C: ['D'],
|
||||
D: ['A'],
|
||||
E: ['D'],
|
||||
F: ['G'],
|
||||
G: []
|
||||
}
|
||||
/*
|
||||
A <-> B
|
||||
ʌ |
|
||||
| |
|
||||
v v
|
||||
C --> D <-- E
|
||||
|
||||
F --> G
|
||||
*/
|
||||
|
||||
console.log(breadthFirstShortestPath(graph, 'C', 'E'))
|
||||
console.log(breadthFirstShortestPath(graph, 'E', 'B'))
|
||||
console.log(breadthFirstShortestPath(graph, 'F', 'G'))
|
||||
console.log(breadthFirstShortestPath(graph, 'A', 'G'))
|
||||
|
28
Graphs/test/BreadthFirstSearch.test.js
Normal file
28
Graphs/test/BreadthFirstSearch.test.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { breadthFirstSearch } from '../BreadthFirstSearch'
|
||||
|
||||
describe('BreadthFirstSearch', () => {
|
||||
const graph = {
|
||||
A: ['B', 'D'],
|
||||
B: ['E'],
|
||||
C: ['D'],
|
||||
D: ['A'],
|
||||
E: ['D'],
|
||||
F: ['G'],
|
||||
G: []
|
||||
}
|
||||
/*
|
||||
A <-> B
|
||||
ʌ |
|
||||
| |
|
||||
v v
|
||||
C --> D <-- E
|
||||
|
||||
F --> G
|
||||
*/
|
||||
|
||||
it('should return the visited nodes', () => {
|
||||
expect(Array.from(breadthFirstSearch(graph, 'C'))).toEqual(['C', 'D', 'A', 'B', 'E'])
|
||||
expect(Array.from(breadthFirstSearch(graph, 'A'))).toEqual(['A', 'B', 'D', 'E'])
|
||||
expect(Array.from(breadthFirstSearch(graph, 'F'))).toEqual(['F', 'G'])
|
||||
})
|
||||
})
|
29
Graphs/test/BreadthFirstShortestPath.test.js
Normal file
29
Graphs/test/BreadthFirstShortestPath.test.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { breadthFirstShortestPath } from '../BreadthFirstShortestPath'
|
||||
|
||||
describe('BreadthFirstShortestPath', () => {
|
||||
const graph = {
|
||||
A: ['B', 'D'],
|
||||
B: ['E'],
|
||||
C: ['D'],
|
||||
D: ['A'],
|
||||
E: ['D'],
|
||||
F: ['G'],
|
||||
G: []
|
||||
}
|
||||
/*
|
||||
A <-> B
|
||||
ʌ |
|
||||
| |
|
||||
v v
|
||||
C --> D <-- E
|
||||
|
||||
F --> G
|
||||
*/
|
||||
|
||||
it('should return the visited nodes', () => {
|
||||
expect(breadthFirstShortestPath(graph, 'C', 'E')).toEqual(['C', 'D', 'A', 'B', 'E'])
|
||||
expect(breadthFirstShortestPath(graph, 'E', 'B')).toEqual(['E', 'D', 'A', 'B'])
|
||||
expect(breadthFirstShortestPath(graph, 'F', 'G')).toEqual(['F', 'G'])
|
||||
expect(breadthFirstShortestPath(graph, 'A', 'G')).toEqual([])
|
||||
})
|
||||
})
|
@ -8,19 +8,9 @@
|
||||
* else if the length of the array is odd number, the median value will be the middle number in the array
|
||||
*/
|
||||
|
||||
/*
|
||||
* Doctests
|
||||
*
|
||||
* > averageMedian([8, 9, 1, 2, 5, 10, 11])
|
||||
* 8
|
||||
* > averageMedian([15, 18, 3, 9, 13, 5])
|
||||
* 11
|
||||
* > averageMedian([1,2,3,4,6,8])
|
||||
* 3.5
|
||||
*/
|
||||
|
||||
const averageMedian = (numbers) => {
|
||||
let median = 0; const numLength = numbers.length
|
||||
let median = 0
|
||||
const numLength = numbers.length
|
||||
numbers = numbers.sort(sortNumbers)
|
||||
|
||||
if (numLength % 2 === 0) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
https://en.wikipedia.org/wiki/Exponentiation_by_squaring
|
||||
*/
|
||||
|
||||
const binaryExponentiation = (a, n) => {
|
||||
export const binaryExponentiation = (a, n) => {
|
||||
// input: a: int, n: int
|
||||
// returns: a^n: int
|
||||
if (n === 0) {
|
||||
@ -18,14 +18,3 @@ const binaryExponentiation = (a, n) => {
|
||||
return b * b
|
||||
}
|
||||
}
|
||||
|
||||
const main = () => {
|
||||
// binary_exponentiation(2, 10)
|
||||
// > 1024
|
||||
console.log(binaryExponentiation(2, 10))
|
||||
// binary_exponentiation(3, 9)
|
||||
// > 19683
|
||||
console.log(binaryExponentiation(3, 9))
|
||||
}
|
||||
|
||||
main()
|
||||
|
@ -1,28 +1,19 @@
|
||||
/*
|
||||
In mathematics and computational science, the Euler method (also called forward Euler method) is a first-order numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. It is the most basic explicit method for numerical integration of ordinary differential equations. The method proceeds in a series of steps. At each step the y-value is calculated by evaluating the differential equation at the previous step, multiplying the result with the step-size and adding it to the last y-value: y_n+1 = y_n + stepSize * f(x_n, y_n).
|
||||
(description adapted from https://en.wikipedia.org/wiki/Euler_method )
|
||||
(see also: https://www.geeksforgeeks.org/euler-method-solving-differential-equation/ )
|
||||
*/
|
||||
|
||||
/*
|
||||
Doctests
|
||||
> eulerStep(0, 0.1, 0, function(x, y){return x})
|
||||
0
|
||||
> eulerStep(2, 1, 1, function(x, y){return x * x})
|
||||
5
|
||||
> eulerFull(0, 3, 1, 0, function(x, y){return x})
|
||||
[{"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 2, "y": 1}, {"x": 3, "y": 3}]
|
||||
> eulerFull(3, 4, 0.5, 1, function(x, y){return x * x})
|
||||
[{"x": 3, "y": 1}, {"x": 3.5, "y": 5.5}, {"x": 4, "y": 11.625}]
|
||||
*/
|
||||
|
||||
function eulerStep (xCurrent, stepSize, yCurrent, differentialEquation) {
|
||||
/**
|
||||
* In mathematics and computational science, the Euler method (also called forward Euler method) is a first-order
|
||||
* numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. It is the most
|
||||
* basic explicit method for numerical integration of ordinary differential equations. The method proceeds in a series
|
||||
* of steps. At each step the y-value is calculated by evaluating the differential equation at the previous step,
|
||||
* multiplying the result with the step-size and adding it to the last y-value: y_n+1 = y_n + stepSize * f(x_n, y_n).
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Euler_method)
|
||||
* @see https://www.geeksforgeeks.org/euler-method-solving-differential-equation/
|
||||
*/
|
||||
export function eulerStep (xCurrent, stepSize, yCurrent, differentialEquation) {
|
||||
// calculates the next y-value based on the current value of x, y and the stepSize
|
||||
const yNext = yCurrent + stepSize * differentialEquation(xCurrent, yCurrent)
|
||||
return yNext
|
||||
return yCurrent + stepSize * differentialEquation(xCurrent, yCurrent)
|
||||
}
|
||||
|
||||
function eulerFull (xStart, xEnd, stepSize, yStart, differentialEquation) {
|
||||
export function eulerFull (xStart, xEnd, stepSize, yStart, differentialEquation) {
|
||||
// loops through all the steps until xEnd is reached, adds a point for each step and then returns all the points
|
||||
const points = [{ x: xStart, y: yStart }]
|
||||
let yCurrent = yStart
|
||||
@ -37,72 +28,3 @@ function eulerFull (xStart, xEnd, stepSize, yStart, differentialEquation) {
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
function plotLine (label, points, width, height) {
|
||||
// utility function to plot the results
|
||||
|
||||
// container needed to control the size of the canvas
|
||||
const container = document.createElement('div')
|
||||
container.style.width = width + 'px'
|
||||
container.style.height = height + 'px'
|
||||
document.body.append(container)
|
||||
|
||||
// the canvas for plotting
|
||||
const canvas = document.createElement('canvas')
|
||||
container.append(canvas)
|
||||
|
||||
// Chart-class from chartjs
|
||||
const chart = new Chart(canvas, { // eslint-disable-line
|
||||
type: 'scatter',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: label,
|
||||
data: points,
|
||||
showLine: true,
|
||||
fill: false,
|
||||
tension: 0,
|
||||
borderColor: 'black'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function exampleEquation1 (x, y) {
|
||||
return x
|
||||
}
|
||||
|
||||
// example from https://en.wikipedia.org/wiki/Euler_method
|
||||
function exampleEquation2 (x, y) {
|
||||
return y
|
||||
}
|
||||
|
||||
// example from https://www.geeksforgeeks.org/euler-method-solving-differential-equation/
|
||||
function exampleEquation3 (x, y) {
|
||||
return x + y + x * y
|
||||
}
|
||||
|
||||
const points1 = eulerFull(0, 4, 0.1, 0, exampleEquation1)
|
||||
const points2 = eulerFull(0, 4, 0.1, 1, exampleEquation2)
|
||||
const points3 = eulerFull(0, 0.1, 0.025, 1, exampleEquation3)
|
||||
|
||||
console.log(points1)
|
||||
console.log(points2)
|
||||
console.log(points3)
|
||||
|
||||
// plot the results if the script is executed in a browser with a window-object
|
||||
if (typeof window !== 'undefined') {
|
||||
const script = document.createElement('script')
|
||||
|
||||
// using chartjs
|
||||
script.src = 'https://www.chartjs.org/dist/2.9.4/Chart.min.js'
|
||||
script.onload = function () {
|
||||
plotLine('example 1: dy/dx = x', points1, 600, 400)
|
||||
plotLine('example 2: dy/dx = y', points2, 600, 400)
|
||||
plotLine('example 3: dy/dx = x + y + x * y', points3, 600, 400)
|
||||
}
|
||||
document.body.append(script)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
O(sqrt(n))
|
||||
*/
|
||||
|
||||
const EulersTotient = (n) => {
|
||||
export const EulersTotient = (n) => {
|
||||
// input: n: int
|
||||
// output: phi(n): count of numbers b/w 1 and n that are coprime to n
|
||||
let res = n
|
||||
@ -27,14 +27,3 @@ const EulersTotient = (n) => {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const main = () => {
|
||||
// EulersTotient(9) = 6 as 1, 2, 4, 5, 7, and 8 are coprime to 9
|
||||
// > 6
|
||||
console.log(EulersTotient(9))
|
||||
// EulersTotient(10) = 4 as 1, 3, 7, 9 are coprime to 10
|
||||
// > 4
|
||||
console.log(EulersTotient(10))
|
||||
}
|
||||
|
||||
main()
|
||||
|
@ -1,43 +1,22 @@
|
||||
/**
|
||||
* The Mandelbrot set is the set of complex numbers "c" for which the series "z_(n+1) = z_n * z_n +
|
||||
* c" does not diverge, i.e. remains bounded. Thus, a complex number "c" is a member of the
|
||||
* Mandelbrot set if, when starting with "z_0 = 0" and applying the iteration repeatedly, the
|
||||
* absolute value of "z_n" remains bounded for all "n > 0". Complex numbers can be written as "a +
|
||||
* b*i": "a" is the real component, usually drawn on the x-axis, and "b*i" is the imaginary
|
||||
* component, usually drawn on the y-axis. Most visualizations of the Mandelbrot set use a
|
||||
* color-coding to indicate after how many steps in the series the numbers outside the set cross the
|
||||
* divergence threshold. Images of the Mandelbrot set exhibit an elaborate and infinitely
|
||||
* complicated boundary that reveals progressively ever-finer recursive detail at increasing
|
||||
* magnifications, making the boundary of the Mandelbrot set a fractal curve. (description adapted
|
||||
* from https://en.wikipedia.org/wiki/Mandelbrot_set ) (see also
|
||||
* https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set )
|
||||
*/
|
||||
|
||||
/*
|
||||
Doctests
|
||||
Test black and white
|
||||
Pixel outside the Mandelbrot set should be white.
|
||||
Pixel inside the Mandelbrot set should be black.
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, false)[0][0]
|
||||
[255, 255, 255]
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, false)[400][300]
|
||||
[0, 0, 0]
|
||||
|
||||
Test color-coding
|
||||
Pixel distant to the Mandelbrot set should be red.
|
||||
Pixel inside the Mandelbrot set should be black.
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, true)[0][0]
|
||||
[255, 0, 0]
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, true)[400][300]
|
||||
[0, 0, 0]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method to generate the image of the Mandelbrot set. Two types of coordinates are used:
|
||||
* image-coordinates that refer to the pixels and figure-coordinates that refer to the complex
|
||||
* numbers inside and outside the Mandelbrot set. The figure-coordinates in the arguments of this
|
||||
* method determine which section of the Mandelbrot set is viewed. The main area of the Mandelbrot
|
||||
* set is roughly between "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates.
|
||||
* Method to generate the image of the Mandelbrot set.
|
||||
*
|
||||
* Two types of coordinates are used: image-coordinates that refer to the pixels and figure-coordinates that refer to
|
||||
* the complex numbers inside and outside the Mandelbrot set. The figure-coordinates in the arguments of this method
|
||||
* determine which section of the Mandelbrot set is viewed. The main area of the Mandelbrot set is roughly between
|
||||
* "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates.
|
||||
*
|
||||
* The Mandelbrot set is the set of complex numbers "c" for which the series "z_(n+1) = z_n * z_n + c" does not diverge,
|
||||
* i.e. remains bounded. Thus, a complex number "c" is a member of the Mandelbrot set if, when starting with "z_0 = 0"
|
||||
* and applying the iteration repeatedly, the absolute value of "z_n" remains bounded for all "n > 0". Complex numbers
|
||||
* can be written as "a + b*i": "a" is the real component, usually drawn on the x-axis, and "b*i" is the imaginary
|
||||
* component, usually drawn on the y-axis. Most visualizations of the Mandelbrot set use a color-coding to indicate
|
||||
* after how many steps in the series the numbers outside the set cross the divergence threshold. Images of the
|
||||
* Mandelbrot set exhibit an elaborate and infinitely complicated boundary that reveals progressively ever-finer
|
||||
* recursive detail at increasing magnifications, making the boundary of the Mandelbrot set a fractal curve.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Mandelbrot_set)
|
||||
* @see https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set
|
||||
*
|
||||
* @param {number} imageWidth The width of the rendered image.
|
||||
* @param {number} imageHeight The height of the rendered image.
|
||||
@ -45,10 +24,10 @@ Pixel inside the Mandelbrot set should be black.
|
||||
* @param {number} figureCenterY The y-coordinate of the center of the figure.
|
||||
* @param {number} figureWidth The width of the figure.
|
||||
* @param {number} maxStep Maximum number of steps to check for divergent behavior.
|
||||
* @param {number} useDistanceColorCoding Render in color or black and white.
|
||||
* @param {boolean} useDistanceColorCoding Render in color or black and white.
|
||||
* @return {object} The RGB-data of the rendered Mandelbrot set.
|
||||
*/
|
||||
function getRGBData (
|
||||
export function getRGBData (
|
||||
imageWidth = 800,
|
||||
imageHeight = 600,
|
||||
figureCenterX = -0.6,
|
||||
@ -83,9 +62,9 @@ function getRGBData (
|
||||
|
||||
// color the corresponding pixel based on the selected coloring-function
|
||||
rgbData[imageX][imageY] =
|
||||
useDistanceColorCoding
|
||||
? colorCodedColorMap(distance)
|
||||
: blackAndWhiteColorMap(distance)
|
||||
useDistanceColorCoding
|
||||
? colorCodedColorMap(distance)
|
||||
: blackAndWhiteColorMap(distance)
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,8 +72,9 @@ function getRGBData (
|
||||
}
|
||||
|
||||
/**
|
||||
* Black and white color-coding that ignores the relative distance. The Mandelbrot set is black,
|
||||
* everything else is white.
|
||||
* Black and white color-coding that ignores the relative distance.
|
||||
*
|
||||
* The Mandelbrot set is black, everything else is white.
|
||||
*
|
||||
* @param {number} distance Distance until divergence threshold
|
||||
* @return {object} The RGB-value corresponding to the distance.
|
||||
@ -104,7 +84,9 @@ function blackAndWhiteColorMap (distance) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Color-coding taking the relative distance into account. The Mandelbrot set is black.
|
||||
* Color-coding taking the relative distance into account.
|
||||
*
|
||||
* The Mandelbrot set is black.
|
||||
*
|
||||
* @param {number} distance Distance until divergence threshold
|
||||
* @return {object} The RGB-value corresponding to the distance.
|
||||
@ -145,11 +127,12 @@ function colorCodedColorMap (distance) {
|
||||
|
||||
/**
|
||||
* Return the relative distance (ratio of steps taken to maxStep) after which the complex number
|
||||
* constituted by this x-y-pair diverges. Members of the Mandelbrot set do not diverge so their
|
||||
* distance is 1.
|
||||
* constituted by this x-y-pair diverges.
|
||||
*
|
||||
* Members of the Mandelbrot set do not diverge so their distance is 1.
|
||||
*
|
||||
* @param {number} figureX The x-coordinate within the figure.
|
||||
* @param {number} figureX The y-coordinate within the figure.
|
||||
* @param {number} figureY The y-coordinate within the figure.
|
||||
* @param {number} maxStep Maximum number of steps to check for divergent behavior.
|
||||
* @return {number} The relative distance as the ratio of steps taken to maxStep.
|
||||
*/
|
||||
@ -171,22 +154,3 @@ function getDistance (figureX, figureY, maxStep) {
|
||||
}
|
||||
return currentStep / (maxStep - 1)
|
||||
}
|
||||
|
||||
// plot the results if the script is executed in a browser with a window-object
|
||||
if (typeof window !== 'undefined') {
|
||||
const rgbData = getRGBData()
|
||||
const width = rgbData.length
|
||||
const height = rgbData[0].length
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
const ctx = canvas.getContext('2d')
|
||||
for (let x = 0; x < width; x++) {
|
||||
for (let y = 0; y < height; y++) {
|
||||
const rgb = rgbData[x][y]
|
||||
ctx.fillStyle = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'
|
||||
ctx.fillRect(x, y, 1, 1)
|
||||
}
|
||||
}
|
||||
document.body.append(canvas)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
https://github.com/TheAlgorithms/Python/blob/master/maths/prime_factors.py
|
||||
*/
|
||||
|
||||
const PrimeFactors = (n) => {
|
||||
export const PrimeFactors = (n) => {
|
||||
// input: n: int
|
||||
// output: primeFactors: Array of all prime factors of n
|
||||
const primeFactors = []
|
||||
@ -20,14 +20,3 @@ const PrimeFactors = (n) => {
|
||||
}
|
||||
return primeFactors
|
||||
}
|
||||
|
||||
const main = () => {
|
||||
// PrimeFactors(100)
|
||||
// > [ 2, 2, 5, 5 ]
|
||||
console.log(PrimeFactors(100))
|
||||
// PrimeFactors(2560)
|
||||
// > [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 5 ]
|
||||
console.log(PrimeFactors(2560))
|
||||
}
|
||||
|
||||
main()
|
||||
|
11
Maths/test/BinaryExponentiationRecursive.test.js
Normal file
11
Maths/test/BinaryExponentiationRecursive.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
const { binaryExponentiation } = require('../BinaryExponentiationRecursive')
|
||||
|
||||
describe('BinaryExponentiationRecursive', () => {
|
||||
it('should calculate 2 to the power of 10 correctly', () => {
|
||||
expect(binaryExponentiation(2, 10)).toBe(1024)
|
||||
})
|
||||
|
||||
it('should calculate 3 to the power of 9 correctly', () => {
|
||||
expect(binaryExponentiation(3, 9)).toBe(19683)
|
||||
})
|
||||
})
|
66
Maths/test/EulerMethod.manual-test.js
Normal file
66
Maths/test/EulerMethod.manual-test.js
Normal file
@ -0,0 +1,66 @@
|
||||
import { eulerFull } from '../EulerMethod'
|
||||
|
||||
function plotLine (label, points, width, height) {
|
||||
// utility function to plot the results
|
||||
|
||||
// container needed to control the size of the canvas
|
||||
const container = document.createElement('div')
|
||||
container.style.width = width + 'px'
|
||||
container.style.height = height + 'px'
|
||||
document.body.append(container)
|
||||
|
||||
// the canvas for plotting
|
||||
const canvas = document.createElement('canvas')
|
||||
container.append(canvas)
|
||||
|
||||
// Chart-class from chartjs
|
||||
const chart = new Chart(canvas, { // eslint-disable-line
|
||||
type: 'scatter',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: label,
|
||||
data: points,
|
||||
showLine: true,
|
||||
fill: false,
|
||||
tension: 0,
|
||||
borderColor: 'black'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function exampleEquation1 (x, y) {
|
||||
return x
|
||||
}
|
||||
|
||||
// example from https://en.wikipedia.org/wiki/Euler_method
|
||||
function exampleEquation2 (x, y) {
|
||||
return y
|
||||
}
|
||||
|
||||
// example from https://www.geeksforgeeks.org/euler-method-solving-differential-equation/
|
||||
function exampleEquation3 (x, y) {
|
||||
return x + y + x * y
|
||||
}
|
||||
|
||||
// plot the results if the script is executed in a browser with a window-object
|
||||
if (typeof window !== 'undefined') {
|
||||
const points1 = eulerFull(0, 4, 0.1, 0, exampleEquation1)
|
||||
const points2 = eulerFull(0, 4, 0.1, 1, exampleEquation2)
|
||||
const points3 = eulerFull(0, 0.1, 0.025, 1, exampleEquation3)
|
||||
|
||||
const script = document.createElement('script')
|
||||
|
||||
// using chartjs
|
||||
script.src = 'https://www.chartjs.org/dist/2.9.4/Chart.min.js'
|
||||
script.onload = function () {
|
||||
plotLine('example 1: dy/dx = x', points1, 600, 400)
|
||||
plotLine('example 2: dy/dx = y', points2, 600, 400)
|
||||
plotLine('example 3: dy/dx = x + y + x * y', points3, 600, 400)
|
||||
}
|
||||
document.body.append(script)
|
||||
}
|
18
Maths/test/EulerMethod.test.js
Normal file
18
Maths/test/EulerMethod.test.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { eulerFull, eulerStep } from '../EulerMethod'
|
||||
|
||||
describe('eulerStep', () => {
|
||||
it('should calculate the next y value correctly', () => {
|
||||
expect(eulerStep(0, 0.1, 0, function (x, y) { return x })).toBe(0)
|
||||
expect(eulerStep(2, 1, 1, function (x, y) { return x * x })).toBe(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('eulerFull', () => {
|
||||
it('should return all the points found', () => {
|
||||
expect(eulerFull(0, 3, 1, 0, function (x, y) { return x }))
|
||||
.toEqual([{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 2, y: 1 }, { x: 3, y: 3 }])
|
||||
|
||||
expect(eulerFull(3, 4, 0.5, 1, function (x, y) { return x * x }))
|
||||
.toEqual([{ x: 3, y: 1 }, { x: 3.5, y: 5.5 }, { x: 4, y: 11.625 }])
|
||||
})
|
||||
})
|
11
Maths/test/EulersTotient.test.js
Normal file
11
Maths/test/EulersTotient.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { EulersTotient } from '../EulersTotient'
|
||||
|
||||
describe('EulersTotient', () => {
|
||||
it('should return 6 as 1, 2, 4, 5, 7, and 8 are coprime to 9', () => {
|
||||
expect(EulersTotient(9)).toBe(6)
|
||||
})
|
||||
|
||||
it('should return 4 as 1, 3, 7, and 9 are coprime to 10', () => {
|
||||
expect(EulersTotient(10)).toBe(4)
|
||||
})
|
||||
})
|
20
Maths/test/Mandelbrot.manual-test.js
Normal file
20
Maths/test/Mandelbrot.manual-test.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { getRGBData } from '../Mandelbrot'
|
||||
|
||||
// plot the results if the script is executed in a browser with a window-object
|
||||
if (typeof window !== 'undefined') {
|
||||
const rgbData = getRGBData()
|
||||
const width = rgbData.length
|
||||
const height = rgbData[0].length
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
const ctx = canvas.getContext('2d')
|
||||
for (let x = 0; x < width; x++) {
|
||||
for (let y = 0; y < height; y++) {
|
||||
const rgb = rgbData[x][y]
|
||||
ctx.fillStyle = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'
|
||||
ctx.fillRect(x, y, 1, 1)
|
||||
}
|
||||
}
|
||||
document.body.append(canvas)
|
||||
}
|
21
Maths/test/Mandelbrot.test.js
Normal file
21
Maths/test/Mandelbrot.test.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { getRGBData } from '../Mandelbrot'
|
||||
|
||||
describe('Mandelbrot', () => {
|
||||
it('should produce black pixels inside the set', () => {
|
||||
const blackAndWhite = getRGBData(800, 600, -0.6, 0, 3.2, 50, false)
|
||||
expect(blackAndWhite[400][300]).toEqual([0, 0, 0]) // black
|
||||
|
||||
const colorCoded = getRGBData(800, 600, -0.6, 0, 3.2, 50, true)
|
||||
expect(colorCoded[400][300]).toEqual([0, 0, 0]) // black
|
||||
})
|
||||
|
||||
it('should produce white pixels outside of the set', () => {
|
||||
const blackAndWhite = getRGBData(800, 600, -0.6, 0, 3.2, 50, false)
|
||||
expect(blackAndWhite[0][0]).toEqual([255, 255, 255]) // black
|
||||
})
|
||||
|
||||
it('should produce colored pixels distant to the set', () => {
|
||||
const colorCoded = getRGBData(800, 600, -0.6, 0, 3.2, 50, true)
|
||||
expect(colorCoded[0][0]).toEqual([255, 0, 0]) // red
|
||||
})
|
||||
})
|
11
Maths/test/PrimeFactors.test.js
Normal file
11
Maths/test/PrimeFactors.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { PrimeFactors } from '../PrimeFactors'
|
||||
|
||||
describe('EulersTotient', () => {
|
||||
it('should return the prime factors for 100', () => {
|
||||
expect(PrimeFactors(100)).toEqual([2, 2, 5, 5])
|
||||
})
|
||||
|
||||
it('should return the prime factors for 2560', () => {
|
||||
expect(PrimeFactors(2560)).toEqual([2, 2, 2, 2, 2, 2, 2, 2, 2, 5])
|
||||
})
|
||||
})
|
12
README.md
12
README.md
@ -1,19 +1,19 @@
|
||||
# The Algorithms - JavaScript
|
||||
|
||||
|
||||
[](https://github.com/TheAlgorithms/Javascript/blob/master/CONTRIBUTING.md)
|
||||
[](CONTRIBUTING.md)
|
||||
[](https://lgtm.com/projects/g/TheAlgorithms/Javascript/context:javascript)
|
||||

|
||||

|
||||
[](https://discord.gg/c7MnfGFGa6)
|
||||

|
||||

|
||||
[](https://standardjs.com/)
|
||||
|
||||
## All algorithms implemented in JavaScript (for educational purposes only)
|
||||
|
||||
### All algorithms implemented in JavaScript (for educational purposes only)
|
||||
[](https://gitpod.io/#https://github.com/TheAlgorithms/Javascript)
|
||||
[](https://gitpod.io/#https://github.com/TheAlgorithms/Javascript)
|
||||
|
||||
These are for demonstration purposes only. There are many implementations of sorts in the JavaScript standard library that are much better for performance reasons.
|
||||
These are for demonstration purposes only. There are many implementations of sorts in the JavaScript standard library
|
||||
that are much better for performance reasons.
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
|
@ -1,44 +1,25 @@
|
||||
/**
|
||||
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected
|
||||
* to a given node in a multi-dimensional array with some matching attribute. It is used in the
|
||||
* "bucket" fill tool of paint programs to fill connected, similarly-colored areas with a different
|
||||
* color. (description adapted from https://en.wikipedia.org/wiki/Flood_fill) (see also:
|
||||
* https://www.techiedelight.com/flood-fill-algorithm/).
|
||||
* Flood fill.
|
||||
*
|
||||
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected to a given node in a
|
||||
* multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill
|
||||
* connected, similarly-colored areas with a different color.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Flood_fill)
|
||||
* @see https://www.techiedelight.com/flood-fill-algorithm/
|
||||
*/
|
||||
|
||||
const neighbors = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
|
||||
|
||||
const black = [0, 0, 0]
|
||||
const green = [0, 255, 0]
|
||||
const violet = [255, 0, 255]
|
||||
const white = [255, 255, 255]
|
||||
const orange = [255, 128, 0] // eslint-disable-line
|
||||
|
||||
/*
|
||||
Doctests
|
||||
> testBreadthFirst([1, 1], green, orange, [1, 1]);
|
||||
orange
|
||||
> testBreadthFirst([1, 1], green, orange, [0, 1]);
|
||||
violet
|
||||
> testBreadthFirst([1, 1], green, orange, [6, 4]);
|
||||
white
|
||||
> testDepthFirst([1, 1], green, orange, [1, 1]);
|
||||
orange
|
||||
> testDepthFirst([1, 1], green, orange, [0, 1]);
|
||||
violet
|
||||
> testDepthFirst([1, 1], green, orange, [6, 4]);
|
||||
white
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements the flood fill algorithm through a breadth-first approach using a queue.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
* Implements the flood fill algorithm through a breadth-first approach using a queue.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
export function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
if (location[0] < 0 ||
|
||||
location[0] >= rgbData.length ||
|
||||
location[1] < 0 ||
|
||||
@ -55,14 +36,14 @@ function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the flood fill algorithm through a depth-first approach using recursion.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
* Implements the flood fill algorithm through a depth-first approach using recursion.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
export function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
if (location[0] < 0 ||
|
||||
location[0] >= rgbData.length ||
|
||||
location[1] < 0 ||
|
||||
@ -74,14 +55,14 @@ function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to implement the breadth-first loop
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param queue The locations that still need to be visited.
|
||||
*/
|
||||
* Utility-function to implement the breadth-first loop.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param queue The locations that still need to be visited.
|
||||
*/
|
||||
function breadthFirstFill (rgbData, location, targetColor, replacementColor, queue) {
|
||||
const currentLocation = queue[0]
|
||||
queue.shift()
|
||||
@ -100,13 +81,13 @@ function breadthFirstFill (rgbData, location, targetColor, replacementColor, que
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to implement the depth-first loop
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
* Utility-function to implement the depth-first loop.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
function depthFirstFill (rgbData, location, targetColor, replacementColor) {
|
||||
if (rgbData[location[0]][location[1]] === targetColor) {
|
||||
rgbData[location[0]][location[1]] = replacementColor
|
||||
@ -120,61 +101,3 @@ function depthFirstFill (rgbData, location, targetColor, replacementColor) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the rgbData-matrix for the tests
|
||||
*
|
||||
* @return example rgbData-matrix
|
||||
*/
|
||||
function generateTestRgbData () {
|
||||
const layout = [
|
||||
[violet, violet, green, green, black, green, green],
|
||||
[violet, green, green, black, green, green, green],
|
||||
[green, green, green, black, green, green, green],
|
||||
[black, black, green, black, white, white, green],
|
||||
[violet, violet, black, violet, violet, white, white],
|
||||
[green, green, green, violet, violet, violet, violet],
|
||||
[violet, violet, violet, violet, violet, violet, violet]
|
||||
]
|
||||
|
||||
// transpose layout-matrix so the x-index comes before the y-index
|
||||
const transposed = []
|
||||
for (let x = 0; x < layout[0].length; x++) {
|
||||
transposed[x] = []
|
||||
for (let y = 0; y < layout.length; y++) {
|
||||
transposed[x][y] = layout[y][x]
|
||||
}
|
||||
}
|
||||
|
||||
return transposed
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to test the function "breadthFirstSearch"
|
||||
*
|
||||
* @param fillLocation The start location on the image where the flood fill is applied.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param testLocation The location of the color to be checked.
|
||||
* @return The color at testLocation
|
||||
*/
|
||||
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
|
||||
const rgbData = generateTestRgbData()
|
||||
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
|
||||
return rgbData[testLocation[0]][testLocation[1]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to test the function "depthFirstSearch"
|
||||
*
|
||||
* @param fillLocation The start location on the image where the flood fill is applied.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param testLocation The location of the color to be checked.
|
||||
* @return The color at testLocation
|
||||
*/
|
||||
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
|
||||
const rgbData = generateTestRgbData()
|
||||
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
|
||||
return rgbData[testLocation[0]][testLocation[1]]
|
||||
}
|
||||
|
80
Recursive/FloodFill.test.js
Normal file
80
Recursive/FloodFill.test.js
Normal file
@ -0,0 +1,80 @@
|
||||
import { breadthFirstSearch, depthFirstSearch } from './FloodFill'
|
||||
|
||||
// some constants
|
||||
const black = [0, 0, 0]
|
||||
const green = [0, 255, 0]
|
||||
const violet = [255, 0, 255]
|
||||
const white = [255, 255, 255]
|
||||
const orange = [255, 128, 0]
|
||||
|
||||
describe('FloodFill', () => {
|
||||
it('should calculate the correct colors using breadth-first approach', () => {
|
||||
expect(testBreadthFirst([1, 1], green, orange, [1, 1])).toEqual(orange)
|
||||
expect(testBreadthFirst([1, 1], green, orange, [0, 1])).toEqual(violet)
|
||||
expect(testBreadthFirst([1, 1], green, orange, [6, 4])).toEqual(white)
|
||||
})
|
||||
|
||||
it('should calculate the correct colors using depth-first approach', () => {
|
||||
expect(testDepthFirst([1, 1], green, orange, [1, 1])).toEqual(orange)
|
||||
expect(testDepthFirst([1, 1], green, orange, [0, 1])).toEqual(violet)
|
||||
expect(testDepthFirst([1, 1], green, orange, [6, 4])).toEqual(white)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Utility-function to test the function "breadthFirstSearch".
|
||||
*
|
||||
* @param fillLocation The start location on the image where the flood fill is applied.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param testLocation The location of the color to be checked.
|
||||
* @return The color at testLocation.
|
||||
*/
|
||||
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {
|
||||
const rgbData = generateTestRgbData()
|
||||
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
|
||||
return rgbData[testLocation[0]][testLocation[1]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to test the function "depthFirstSearch".
|
||||
*
|
||||
* @param fillLocation The start location on the image where the flood fill is applied.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param testLocation The location of the color to be checked.
|
||||
* @return The color at testLocation.
|
||||
*/
|
||||
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
|
||||
const rgbData = generateTestRgbData()
|
||||
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
|
||||
return rgbData[testLocation[0]][testLocation[1]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the rgbData-matrix for the tests.
|
||||
*
|
||||
* @return example rgbData-matrix.
|
||||
*/
|
||||
function generateTestRgbData () {
|
||||
const layout = [
|
||||
[violet, violet, green, green, black, green, green],
|
||||
[violet, green, green, black, green, green, green],
|
||||
[green, green, green, black, green, green, green],
|
||||
[black, black, green, black, white, white, green],
|
||||
[violet, violet, black, violet, violet, white, white],
|
||||
[green, green, green, violet, violet, violet, violet],
|
||||
[violet, violet, violet, violet, violet, violet, violet]
|
||||
]
|
||||
|
||||
// transpose layout-matrix so the x-index comes before the y-index
|
||||
const transposed = []
|
||||
for (let x = 0; x < layout[0].length; x++) {
|
||||
transposed[x] = []
|
||||
for (let y = 0; y < layout.length; y++) {
|
||||
transposed[x][y] = layout[y][x]
|
||||
}
|
||||
}
|
||||
|
||||
return transposed
|
||||
}
|
@ -1,34 +1,20 @@
|
||||
/**
|
||||
* The Koch snowflake is a fractal curve and one of the earliest fractals to have been described.
|
||||
* The Koch snowflake can be built up iteratively, in a sequence of stages. The first stage is an
|
||||
* equilateral triangle, and each successive stage is formed by adding outward bends to each side of
|
||||
* the previous stage, making smaller equilateral triangles. This can be achieved through the
|
||||
* following steps for each line: 1. divide the line segment into three segments of equal length. 2.
|
||||
* draw an equilateral triangle that has the middle segment from step 1 as its base and points
|
||||
* outward. 3. remove the line segment that is the base of the triangle from step 2. (description
|
||||
* adapted from https://en.wikipedia.org/wiki/Koch_snowflake ) (for a more detailed explanation and
|
||||
* an implementation in the Processing language, see
|
||||
* https://natureofcode.com/book/chapter-8-fractals/ #84-the-koch-curve-and-the-arraylist-technique
|
||||
* ).
|
||||
*
|
||||
* The Koch snowflake can be built up iteratively, in a sequence of stages. The first stage is an equilateral triangle,
|
||||
* and each successive stage is formed by adding outward bends to each side of the previous stage, making smaller
|
||||
* equilateral triangles. This can be achieved through the following steps for each line:
|
||||
* 1. divide the line segment into three segments of equal length.
|
||||
* 2. draw an equilateral triangle that has the middle segment from step 1 as its base and points outward.
|
||||
* 3. remove the line segment that is the base of the triangle from step 2.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Koch_snowflake)
|
||||
* (for a more detailed explanation and an implementation in the Processing language, see
|
||||
* https://natureofcode.com/book/chapter-8-fractals/ #84-the-koch-curve-and-the-arraylist-technique).
|
||||
*/
|
||||
|
||||
/*
|
||||
Doctests
|
||||
Test iterate-method
|
||||
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0];
|
||||
{"x": 0, "y": 0}
|
||||
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1];
|
||||
{"x": 1/3, "y": 0}
|
||||
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2];
|
||||
{"x": 1/2, "y": Math.sin(Math.PI / 3) / 3}
|
||||
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3];
|
||||
{"x": 2/3, "y": 0}
|
||||
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4];
|
||||
{"x": 1, "y": 0}
|
||||
*/
|
||||
|
||||
/** Class to handle the vector calculations. */
|
||||
class Vector2 {
|
||||
export class Vector2 {
|
||||
constructor (x, y) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
@ -87,66 +73,15 @@ class Vector2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to render the Koch snowflake to a canvas.
|
||||
* Go through the number of iterations determined by the argument "steps".
|
||||
*
|
||||
* @param canvasWidth The width of the canvas.
|
||||
* @param steps The number of iterations.
|
||||
* @returns The canvas of the rendered Koch snowflake.
|
||||
*/
|
||||
function getKochSnowflake (canvasWidth = 600, steps = 5) {
|
||||
if (canvasWidth <= 0) {
|
||||
throw new Error('canvasWidth should be greater than zero')
|
||||
}
|
||||
|
||||
const offsetX = canvasWidth / 10.0
|
||||
const offsetY = canvasWidth / 3.7
|
||||
const vector1 = new Vector2(offsetX, offsetY)
|
||||
const vector2 =
|
||||
new Vector2(canvasWidth / 2, Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY)
|
||||
const vector3 = new Vector2(canvasWidth - offsetX, offsetY)
|
||||
const initialVectors = []
|
||||
initialVectors.push(vector1)
|
||||
initialVectors.push(vector2)
|
||||
initialVectors.push(vector3)
|
||||
initialVectors.push(vector1)
|
||||
const vectors = iterate(initialVectors, steps)
|
||||
return drawToCanvas(vectors, canvasWidth, canvasWidth)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-method to render the Koch snowflake to a canvas.
|
||||
*
|
||||
* @param vectors The vectors defining the edges to be rendered.
|
||||
* @param canvasWidth The width of the canvas.
|
||||
* @param canvasHeight The height of the canvas.
|
||||
* @returns The canvas of the rendered edges.
|
||||
*/
|
||||
function drawToCanvas (vectors, canvasWidth, canvasHeight) {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = canvasWidth
|
||||
canvas.height = canvasHeight
|
||||
|
||||
// Draw the edges
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(vectors[0].x, vectors[0].y)
|
||||
for (let i = 1; i < vectors.length; i++) {
|
||||
ctx.lineTo(vectors[i].x, vectors[i].y)
|
||||
}
|
||||
ctx.stroke()
|
||||
|
||||
return canvas
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the number of iterations determined by the argument "steps". Be careful with high
|
||||
* values (above 5) since the time to calculate increases exponentially.
|
||||
* Be careful with high values (above 5) since the time to calculate increases exponentially.
|
||||
*
|
||||
* @param initialVectors The vectors composing the shape to which the algorithm is applied.
|
||||
* @param steps The number of iterations.
|
||||
* @returns The transformed vectors after the iteration-steps.
|
||||
*/
|
||||
function iterate (initialVectors, steps) {
|
||||
export function iterate (initialVectors, steps) {
|
||||
let vectors = initialVectors
|
||||
for (let i = 0; i < steps; i++) {
|
||||
vectors = iterationStep(vectors)
|
||||
@ -156,9 +91,10 @@ function iterate (initialVectors, steps) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through each pair of adjacent vectors. Each line between two adjacent vectors is divided
|
||||
* into 4 segments by adding 3 additional vectors in-between the original two vectors. The vector
|
||||
* in the middle is constructed through a 60 degree rotation so it is bent outwards.
|
||||
* Loops through each pair of adjacent vectors.
|
||||
*
|
||||
* Each line between two adjacent vectors is divided into 4 segments by adding 3 additional vectors in-between the
|
||||
* original two vectors. The vector in the middle is constructed through a 60 degree rotation so it is bent outwards.
|
||||
*
|
||||
* @param vectors The vectors composing the shape to which the algorithm is applied.
|
||||
* @returns The transformed vectors after the iteration-step.
|
||||
@ -178,9 +114,3 @@ function iterationStep (vectors) {
|
||||
newVectors.push(vectors[vectors.length - 1])
|
||||
return newVectors
|
||||
}
|
||||
|
||||
// plot the results if the script is executed in a browser with a window-object
|
||||
if (typeof window !== 'undefined') {
|
||||
const canvas = getKochSnowflake()
|
||||
document.body.append(canvas)
|
||||
}
|
||||
|
58
Recursive/KochSnowflake.manual-test.js
Normal file
58
Recursive/KochSnowflake.manual-test.js
Normal file
@ -0,0 +1,58 @@
|
||||
import { Vector2, iterate } from './KochSnowflake'
|
||||
|
||||
/**
|
||||
* Method to render the Koch snowflake to a canvas.
|
||||
*
|
||||
* @param canvasWidth The width of the canvas.
|
||||
* @param steps The number of iterations.
|
||||
* @returns The canvas of the rendered Koch snowflake.
|
||||
*/
|
||||
function getKochSnowflake (canvasWidth = 600, steps = 5) {
|
||||
if (canvasWidth <= 0) {
|
||||
throw new Error('canvasWidth should be greater than zero')
|
||||
}
|
||||
|
||||
const offsetX = canvasWidth / 10.0
|
||||
const offsetY = canvasWidth / 3.7
|
||||
const vector1 = new Vector2(offsetX, offsetY)
|
||||
const vector2 = new Vector2(canvasWidth / 2, Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY)
|
||||
const vector3 = new Vector2(canvasWidth - offsetX, offsetY)
|
||||
const initialVectors = []
|
||||
initialVectors.push(vector1)
|
||||
initialVectors.push(vector2)
|
||||
initialVectors.push(vector3)
|
||||
initialVectors.push(vector1)
|
||||
const vectors = iterate(initialVectors, steps)
|
||||
return drawToCanvas(vectors, canvasWidth, canvasWidth)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-method to render the Koch snowflake to a canvas.
|
||||
*
|
||||
* @param vectors The vectors defining the edges to be rendered.
|
||||
* @param canvasWidth The width of the canvas.
|
||||
* @param canvasHeight The height of the canvas.
|
||||
* @returns The canvas of the rendered edges.
|
||||
*/
|
||||
function drawToCanvas (vectors, canvasWidth, canvasHeight) {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = canvasWidth
|
||||
canvas.height = canvasHeight
|
||||
|
||||
// Draw the edges
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(vectors[0].x, vectors[0].y)
|
||||
for (let i = 1; i < vectors.length; i++) {
|
||||
ctx.lineTo(vectors[i].x, vectors[i].y)
|
||||
}
|
||||
ctx.stroke()
|
||||
|
||||
return canvas
|
||||
}
|
||||
|
||||
// plot the results if the script is executed in a browser with a window-object
|
||||
if (typeof window !== 'undefined') {
|
||||
const canvas = getKochSnowflake()
|
||||
document.body.append(canvas)
|
||||
}
|
20
Recursive/KochSnowflake.test.js
Normal file
20
Recursive/KochSnowflake.test.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { iterate, Vector2 } from './KochSnowflake'
|
||||
|
||||
describe('KochSnowflake', () => {
|
||||
it('should produce the correctly-transformed vectors', () => {
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0])
|
||||
.toEqual({ x: 0, y: 0 })
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1])
|
||||
.toEqual({ x: 1 / 3, y: 0 })
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2])
|
||||
.toEqual({ x: 1 / 2, y: Math.sin(Math.PI / 3) / 3 })
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3])
|
||||
.toEqual({ x: 2 / 3, y: 0 })
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4])
|
||||
.toEqual({ x: 1, y: 0 })
|
||||
})
|
||||
})
|
@ -11,25 +11,6 @@
|
||||
* Reference: https://www.geeksforgeeks.org/ternary-search/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Doctests
|
||||
*
|
||||
* > ternarySearchRecursive([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)
|
||||
* 2
|
||||
* > ternarySearchIterative([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 8)
|
||||
* 7
|
||||
* > ternarySearchRecursive([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0)
|
||||
* -1
|
||||
* > ternarySearchIterative([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 12)
|
||||
* -1
|
||||
* > ternarySearchRecursive(["Ali", "Cathrynli", "Josuke", "Thomas"], "Cathrynli")
|
||||
* 1
|
||||
* > ternarySearchRecursive(["Ali", "Cathrynli", "Josuke", "Thomas"], "Josuke")
|
||||
* 2
|
||||
* > ternarySearchRecursive(["Ali", "Cathrynli", "Josuke", "Thomas"], "Angela")
|
||||
* -1
|
||||
*/
|
||||
|
||||
function ternarySearchRecursive (arr, key, low = 0, high = arr.length - 1) {
|
||||
if (high >= low) {
|
||||
// find the mid1 and mid2
|
||||
@ -65,6 +46,7 @@ function ternarySearchRecursive (arr, key, low = 0, high = arr.length - 1) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
function ternarySearchIterative (arr, key, low = 0, high = arr.length - 1) {
|
||||
while (high >= low) {
|
||||
// find the mid1 and mid2
|
||||
|
@ -1,25 +1,14 @@
|
||||
/**
|
||||
* Bead Sort, also known as Gravity sort, this algorithm was
|
||||
* inspired from natural phenomenons and was designed keeping in mind objects(or beads)
|
||||
* falling under the influence of gravity.
|
||||
* Bead Sort, also known as Gravity sort.
|
||||
*
|
||||
* This algorithm was inspired from natural phenomena and was designed keeping in mind objects (or beads) falling under
|
||||
* the influence of gravity.
|
||||
*
|
||||
* NOTE: It only works for arrays of positive integers.
|
||||
*
|
||||
* Wikipedia: https://en.wikipedia.org/wiki/Bead_sort
|
||||
*/
|
||||
|
||||
/**
|
||||
* Doctests
|
||||
*
|
||||
* > beadSort([5, 4, 3, 2, 1])
|
||||
* [1, 2, 3, 4, 5]
|
||||
* > beadSort([7, 9, 4, 3, 5])
|
||||
* [3, 4, 5, 7, 9]
|
||||
* > beadSort([-1, 5, 8, 4, 3, 19])
|
||||
* ! RangeError: Sequence must be a list of positive integers!
|
||||
*/
|
||||
|
||||
function beadSort (sequence) {
|
||||
export function beadSort (sequence) {
|
||||
/* Let's ensure our sequence has only Positive Integers */
|
||||
if (sequence.some((integer) => integer < 0)) {
|
||||
throw RangeError('Sequence must be a list of Positive integers Only!')
|
||||
@ -60,23 +49,8 @@ function beadSort (sequence) {
|
||||
}
|
||||
|
||||
/* Finally, let's turn our Bead rows into their Respective Numbers */
|
||||
const sortedSequence = grid.map((beadArray) => {
|
||||
return grid.map((beadArray) => {
|
||||
const beadsArray = beadArray.filter(bead => bead === '*')
|
||||
|
||||
return beadsArray.length
|
||||
})
|
||||
|
||||
return sortedSequence
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Bead Sort
|
||||
*/
|
||||
const array = [5, 4, 3, 2, 1]
|
||||
// Before Sort
|
||||
console.log('\n- Before Sort | Implementation of Bead Sort -')
|
||||
console.log(array)
|
||||
// After Sort
|
||||
console.log('- After Sort | Implementation of Bead Sort -')
|
||||
console.log(beadSort(array))
|
||||
console.log('\n')
|
||||
|
@ -1,56 +1,38 @@
|
||||
/*
|
||||
* A simple helper function that checks, if the array is
|
||||
* sorted in ascending order.
|
||||
/**
|
||||
* Checks whether the given array is sorted in ascending order.
|
||||
*/
|
||||
|
||||
// > [].isSorted()
|
||||
// true
|
||||
// > [1].isSorted()
|
||||
// true
|
||||
// > [1,2,3].isSorted()
|
||||
// true
|
||||
// > [3,2,1].isSorted()
|
||||
// false
|
||||
/* eslint no-extend-native: ["off", { "exceptions": ["Object"] }] */
|
||||
Array.prototype.isSorted = function () {
|
||||
const length = this.length
|
||||
export function isSorted (array) {
|
||||
const length = array.length
|
||||
for (let i = 0; i < length - 1; i++) {
|
||||
if (this[i] > this[i + 1]) {
|
||||
if (array[i] > array[i + 1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* A simple helper function to shuffle the array randomly in place.
|
||||
/**
|
||||
* Shuffles the given array randomly in place.
|
||||
*/
|
||||
Array.prototype.shuffle = function () {
|
||||
for (let i = this.length - 1; i; i--) {
|
||||
function shuffle (array) {
|
||||
for (let i = array.length - 1; i; i--) {
|
||||
const m = Math.floor(Math.random() * i)
|
||||
const n = this[i - 1]
|
||||
this[i - 1] = this[m]
|
||||
this[m] = n
|
||||
const n = array[i - 1]
|
||||
array[i - 1] = array[m]
|
||||
array[m] = n
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of the bogosort algorithm. This sorting algorithm randomly
|
||||
* rearranges the array until it is sorted.
|
||||
/**
|
||||
* Implementation of the bogosort algorithm.
|
||||
*
|
||||
* This sorting algorithm randomly rearranges the array until it is sorted.
|
||||
*
|
||||
* For more information see: https://en.wikipedia.org/wiki/Bogosort
|
||||
*/
|
||||
function bogoSort (items) {
|
||||
while (!items.isSorted()) {
|
||||
items.shuffle()
|
||||
export function bogoSort (items) {
|
||||
while (!isSorted(items)) {
|
||||
shuffle(items)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Implementation of bogoSort
|
||||
|
||||
const ar = [5, 6, 7, 8, 1, 2, 12, 14]
|
||||
// Array before Sort
|
||||
console.log(ar)
|
||||
bogoSort(ar)
|
||||
// Array after sort
|
||||
console.log(ar)
|
||||
|
@ -12,28 +12,10 @@
|
||||
* Animated Visual: https://www.toptal.com/developers/sorting-algorithms/bubble-sort
|
||||
*/
|
||||
|
||||
/*
|
||||
* Doctests
|
||||
*
|
||||
* > bubbleSort([5, 4, 1, 2, 3])
|
||||
* [1, 2, 3, 4, 5]
|
||||
* > bubbleSort([])
|
||||
* []
|
||||
* > bubbleSort([1, 2, 3])
|
||||
* [1, 2, 3]
|
||||
*
|
||||
* > alternativeBubbleSort([5, 4, 1, 2, 3])
|
||||
* [1, 2, 3, 4, 5]
|
||||
* > alternativeBubbleSort([])
|
||||
* []
|
||||
* > alternativeBubbleSort([1, 2, 3])
|
||||
* [1, 2, 3]
|
||||
*/
|
||||
|
||||
/*
|
||||
* Using 2 for loops
|
||||
*/
|
||||
function bubbleSort (items) {
|
||||
/**
|
||||
* Using 2 for loops.
|
||||
*/
|
||||
export function bubbleSort (items) {
|
||||
const length = items.length
|
||||
|
||||
for (let i = (length - 1); i > 0; i--) {
|
||||
@ -50,22 +32,10 @@ function bubbleSort (items) {
|
||||
return items
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of 2 for loops method
|
||||
*/
|
||||
const array1 = [5, 6, 7, 8, 1, 2, 12, 14]
|
||||
// Before Sort
|
||||
console.log('\n- Before Sort | Implementation using 2 for loops -')
|
||||
console.log(array1)
|
||||
// After Sort
|
||||
console.log('- After Sort | Implementation using 2 for loops -')
|
||||
console.log(bubbleSort(array1))
|
||||
console.log('\n')
|
||||
|
||||
/*
|
||||
* Using a while loop and a for loop
|
||||
*/
|
||||
function alternativeBubbleSort (arr) {
|
||||
/**
|
||||
* Using a while loop and a for loop.
|
||||
*/
|
||||
export function alternativeBubbleSort (arr) {
|
||||
let swapped = true
|
||||
|
||||
while (swapped) {
|
||||
@ -80,15 +50,3 @@ function alternativeBubbleSort (arr) {
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of a while loop and a for loop method
|
||||
*/
|
||||
const array2 = [5, 6, 7, 8, 1, 2, 12, 14]
|
||||
// Before Sort
|
||||
console.log('\n- Before Sort | Implementation using a while loop and a for loop -')
|
||||
console.log(array2)
|
||||
// After Sort
|
||||
console.log('- After Sort | Implementation using a while loop and a for loop -')
|
||||
console.log(alternativeBubbleSort(array2))
|
||||
console.log('\n')
|
||||
|
@ -1,25 +1,23 @@
|
||||
/*
|
||||
[Wikipedia says](https://en.wikipedia.org/wiki/Bucket_sort#:~:text=Bucket%20sort%2C%20or%20bin%20sort,applying%20the%20bucket%20sorting%20algorithm.&text=Sort%20each%20non%2Dempty%20bucket.): Bucket sort, or bin sort, is a sorting algorithm that works by distributing the
|
||||
elements of an array into a number of buckets. Each bucket is then sorted individually, either using
|
||||
a different sorting algorithm, or by recursively applying the bucket sorting algorithm. It is a
|
||||
distribution sort, and is a cousin of radix sort in the most to least significant digit flavour.
|
||||
Bucket sort is a generalization of pigeonhole sort. Bucket sort can be implemented with comparisons
|
||||
and therefore can also be considered a comparison sort algorithm. The computational complexity estimates
|
||||
involve the number of buckets.
|
||||
|
||||
Time Complexity of Solution:
|
||||
Best Case O(n); Average Case O(n); Worst Case O(n)
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* bucketSort returns an array of numbers sorted in increasing order.
|
||||
* BucketSort implementation.
|
||||
*
|
||||
* Wikipedia says: Bucket sort, or bin sort, is a sorting algorithm that works by distributing the elements of an array
|
||||
* into a number of buckets. Each bucket is then sorted individually, either using a different sorting algorithm, or by
|
||||
* recursively applying the bucket sorting algorithm. It is a distribution sort, and is a cousin of radix sort in the
|
||||
* most to least significant digit flavour. Bucket sort is a generalization of pigeonhole sort. Bucket sort can be
|
||||
* implemented with comparisons and therefore can also be considered a comparison sort algorithm. The computational
|
||||
* complexity estimates involve the number of buckets.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Bucket_sort#:~:text=Bucket%20sort%2C%20or%20bin%20sort,applying%20the%20bucket%20sorting%20algorithm.&text=Sort%20each%20non%2Dempty%20bucket.
|
||||
*
|
||||
* Time Complexity of Solution:
|
||||
* Best Case O(n); Average Case O(n); Worst Case O(n)
|
||||
*
|
||||
* @param {number[]} list The array of numbers to be sorted.
|
||||
* @param {number} size The size of the buckets used. If not provided, size will be 5.
|
||||
* @return {number[]} An array of numbers sorted in increasing order.
|
||||
*/
|
||||
function bucketSort (list, size) {
|
||||
export function bucketSort (list, size) {
|
||||
if (undefined === size) {
|
||||
size = 5
|
||||
}
|
||||
@ -60,5 +58,3 @@ function bucketSort (list, size) {
|
||||
}
|
||||
return sorted
|
||||
}
|
||||
|
||||
export { bucketSort }
|
||||
|
@ -1,26 +1,14 @@
|
||||
/*
|
||||
/**
|
||||
* Cocktail Shaker Sort is an algorithm that is a Bidirectional Bubble Sort.
|
||||
*
|
||||
* The algorithm extends bubble sort by operating in two directions.
|
||||
* While it improves on bubble sort by more quickly moving items to the beginning of the list,
|
||||
* it provides only marginal performance improvements.
|
||||
* While it improves on bubble sort by more quickly moving items to the beginning of the list, it provides only marginal
|
||||
* performance improvements.
|
||||
*
|
||||
* Wikipedia (Cocktail Shaker Sort): https://en.wikipedia.org/wiki/Cocktail_shaker_sort
|
||||
* Wikipedia (Bubble Sort): https://en.wikipedia.org/wiki/Bubble_sort
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Doctests
|
||||
*
|
||||
* > cocktailShakerSort([5, 4, 1, 2, 3])
|
||||
* [1, 2, 3, 4, 5]
|
||||
* > cocktailShakerSort([])
|
||||
* []
|
||||
* > cocktailShakerSort([1, 2, 3])
|
||||
* [1, 2, 3]
|
||||
*/
|
||||
|
||||
function cocktailShakerSort (items) {
|
||||
export function cocktailShakerSort (items) {
|
||||
for (let i = items.length - 1; i > 0; i--) {
|
||||
let j
|
||||
|
||||
@ -41,15 +29,3 @@ function cocktailShakerSort (items) {
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Cocktail Shaker Sort
|
||||
*/
|
||||
const array = [5, 6, 7, 8, 1, 2, 12, 14]
|
||||
// Before Sort
|
||||
console.log('\n- Before Sort | Implementation of Cocktail Shaker Sort -')
|
||||
console.log(array)
|
||||
// After Sort
|
||||
console.log('- After Sort | Implementation of Cocktail Shaker Sort -')
|
||||
console.log(cocktailShakerSort(array))
|
||||
console.log('\n')
|
||||
|
@ -1,40 +1,21 @@
|
||||
/**
|
||||
* Merge Sort is an algorithm where the main list is divided down into two half
|
||||
* sized lists, which then have merge sort called on these two smaller lists
|
||||
* recursively until there is only a sorted list of one.
|
||||
/*
|
||||
* MergeSort implementation.
|
||||
*
|
||||
* Merge Sort is an algorithm where the main list is divided down into two half sized lists, which then have merge sort
|
||||
* called on these two smaller lists recursively until there is only a sorted list of one.
|
||||
*
|
||||
* On the way up the recursive calls, the lists will be merged together inserting
|
||||
* the smaller value first, creating a larger sorted list.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sort and merge two given arrays
|
||||
* @param {Array} list1 - sublist to break down
|
||||
* @param {Array} list2 - sublist to break down
|
||||
* @return {Array} merged list
|
||||
* Sort and merge two given arrays.
|
||||
*
|
||||
* @param {Array} list1 Sublist to break down.
|
||||
* @param {Array} list2 Sublist to break down.
|
||||
* @return {Array} The merged list.
|
||||
*/
|
||||
/*
|
||||
* Doctests
|
||||
* > merge([5, 4],[ 1, 2, 3])
|
||||
* [1, 2, 3, 5, 4]
|
||||
* > merge([],[1, 2])
|
||||
* [1, 2]
|
||||
* > merge([1, 2, 3], [1])
|
||||
* [1, 1, 2, 3]
|
||||
* > merge([], [])
|
||||
* []
|
||||
*
|
||||
* > mergeSort([5, 4])
|
||||
* [4, 5]
|
||||
* > mergeSort([8, 4, 10, 15, 9])
|
||||
* [4, 8, 9, 10, 15]
|
||||
* > mergeSort([1, 2, 3])
|
||||
* [1, 2, 3]
|
||||
* > mergeSort([ ])
|
||||
* [ ]
|
||||
*/
|
||||
|
||||
function merge (list1, list2) {
|
||||
export function merge (list1, list2) {
|
||||
const results = []
|
||||
let i = 0
|
||||
let j = 0
|
||||
@ -51,11 +32,12 @@ function merge (list1, list2) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Break down the lists into smaller pieces to be merged
|
||||
* @param {Array} list - list to be sorted
|
||||
* @return {Array} sorted list
|
||||
* Break down the lists into smaller pieces to be merged.
|
||||
*
|
||||
* @param {Array} list List to be sorted.
|
||||
* @return {Array} The sorted list.
|
||||
*/
|
||||
function mergeSort (list) {
|
||||
export function mergeSort (list) {
|
||||
if (list.length < 2) return list
|
||||
|
||||
const listHalf = Math.floor(list.length / 2)
|
||||
@ -64,9 +46,3 @@ function mergeSort (list) {
|
||||
|
||||
return merge(mergeSort(subList1), mergeSort(subList2))
|
||||
}
|
||||
|
||||
// Merge Sort Example
|
||||
const unsortedArray = [10, 5, 3, 8, 2, 6, 4, 7, 9, 1]
|
||||
const sortedArray = mergeSort(unsortedArray)
|
||||
|
||||
console.log('Before:', unsortedArray, 'After:', sortedArray)
|
||||
|
@ -1,22 +1,9 @@
|
||||
/*
|
||||
/**
|
||||
* Quick sort is a comparison sorting algorithm that uses a divide and conquer strategy.
|
||||
*
|
||||
* For more information see here: https://en.wikipedia.org/wiki/Quicksort
|
||||
*/
|
||||
|
||||
/*
|
||||
* Doctests
|
||||
*
|
||||
* > quickSort([5, 4, 3, 10, 2, 1])
|
||||
* [1, 2, 3, 4, 5, 10]
|
||||
* > quickSort([])
|
||||
* []
|
||||
* > quickSort([5, 4])
|
||||
* [4, 5]
|
||||
* > quickSort([1, 2, 3])
|
||||
* [1, 2, 3]
|
||||
*/
|
||||
|
||||
function quickSort (items) {
|
||||
export function quickSort (items) {
|
||||
const length = items.length
|
||||
|
||||
if (length <= 1) {
|
||||
@ -40,12 +27,3 @@ function quickSort (items) {
|
||||
|
||||
return sorted
|
||||
}
|
||||
|
||||
// Implementation of quick sort
|
||||
|
||||
let ar = [0, 5, 3, 2, 2]
|
||||
// Array before Sort
|
||||
console.log(ar)
|
||||
ar = quickSort(ar)
|
||||
// Array after sort
|
||||
console.log(ar)
|
||||
|
12
Sorts/test/BeadSort.test.js
Normal file
12
Sorts/test/BeadSort.test.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { beadSort } from '../BeadSort'
|
||||
|
||||
describe('BeadSort', () => {
|
||||
it('should sort arrays correctly', () => {
|
||||
expect(beadSort([5, 4, 3, 2, 1])).toEqual([1, 2, 3, 4, 5])
|
||||
expect(beadSort([7, 9, 4, 3, 5])).toEqual([3, 4, 5, 7, 9])
|
||||
})
|
||||
|
||||
it('should throw a RangeError when the array contains negative integers', () => {
|
||||
expect(() => beadSort([-1, 5, 8, 4, 3, 19])).toThrow(RangeError)
|
||||
})
|
||||
})
|
25
Sorts/test/BogoSort.test.js
Normal file
25
Sorts/test/BogoSort.test.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { bogoSort, isSorted } from '../BogoSort'
|
||||
|
||||
describe('isSorted', () => {
|
||||
it('should return true for empty arrays', () => {
|
||||
expect(isSorted([])).toBe(true)
|
||||
})
|
||||
|
||||
it('should return true for single-element arrays', () => {
|
||||
expect(isSorted([1])).toBe(true)
|
||||
})
|
||||
|
||||
it('should return true for arrays that are properly sorted', () => {
|
||||
expect(isSorted([1, 2, 3])).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for arrays that are not properly sorted', () => {
|
||||
expect(isSorted([3, 2, 1])).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('bogoSort', () => {
|
||||
it('should (eventually) sort the array', () => {
|
||||
expect(bogoSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
|
||||
})
|
||||
})
|
19
Sorts/test/BubbleSort.test.js
Normal file
19
Sorts/test/BubbleSort.test.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { alternativeBubbleSort, bubbleSort } from '../BubbleSort'
|
||||
|
||||
describe('bubbleSort', () => {
|
||||
it('should sort arrays correctly', () => {
|
||||
expect(bubbleSort([5, 4, 1, 2, 3])).toEqual([1, 2, 3, 4, 5])
|
||||
expect(bubbleSort([])).toEqual([])
|
||||
expect(bubbleSort([1, 2, 3])).toEqual([1, 2, 3])
|
||||
expect(bubbleSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
|
||||
})
|
||||
})
|
||||
|
||||
describe('alternativeBubbleSort', () => {
|
||||
it('should sort arrays correctly', () => {
|
||||
expect(alternativeBubbleSort([5, 4, 1, 2, 3])).toEqual([1, 2, 3, 4, 5])
|
||||
expect(alternativeBubbleSort([])).toEqual([])
|
||||
expect(alternativeBubbleSort([1, 2, 3])).toEqual([1, 2, 3])
|
||||
expect(alternativeBubbleSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
|
||||
})
|
||||
})
|
13
Sorts/test/CocktailShakerSort.test.js
Normal file
13
Sorts/test/CocktailShakerSort.test.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { cocktailShakerSort } from '../CocktailShakerSort'
|
||||
|
||||
describe('CocktailShakerSort', () => {
|
||||
it('should sort arrays correctly', () => {
|
||||
expect(cocktailShakerSort([5, 4, 1, 2, 3])).toEqual([1, 2, 3, 4, 5])
|
||||
expect(cocktailShakerSort([1, 2, 3])).toEqual([1, 2, 3])
|
||||
expect(cocktailShakerSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
|
||||
})
|
||||
|
||||
it('should work for empty arrays, too', () => {
|
||||
expect(cocktailShakerSort([])).toEqual([])
|
||||
})
|
||||
})
|
23
Sorts/test/MergeSort.test.js
Normal file
23
Sorts/test/MergeSort.test.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { merge, mergeSort } from '../MergeSort'
|
||||
|
||||
describe('merge', () => {
|
||||
it('should merge arrays correctly', () => {
|
||||
expect(merge([5, 4], [1, 2, 3])).toEqual([1, 2, 3, 5, 4])
|
||||
expect(merge([], [1, 2])).toEqual([1, 2])
|
||||
expect(merge([1, 2, 3], [1])).toEqual([1, 1, 2, 3])
|
||||
expect(merge([], [])).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('MergeSort', () => {
|
||||
it('should work for empty arrays', () => {
|
||||
expect(mergeSort([])).toEqual([])
|
||||
})
|
||||
|
||||
it('should sort arrays correctly', () => {
|
||||
expect(mergeSort([5, 4])).toEqual([4, 5])
|
||||
expect(mergeSort([8, 4, 10, 15, 9])).toEqual([4, 8, 9, 10, 15])
|
||||
expect(mergeSort([1, 2, 3])).toEqual([1, 2, 3])
|
||||
expect(mergeSort([10, 5, 3, 8, 2, 6, 4, 7, 9, 1])).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
})
|
||||
})
|
14
Sorts/test/QuickSort.test.js
Normal file
14
Sorts/test/QuickSort.test.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { quickSort } from '../QuickSort'
|
||||
|
||||
describe('QuickSort', () => {
|
||||
it('should work for empty arrays', () => {
|
||||
expect(quickSort([])).toEqual([])
|
||||
})
|
||||
|
||||
it('should sort arrays correctly', () => {
|
||||
expect(quickSort([5, 4, 3, 10, 2, 1])).toEqual([1, 2, 3, 4, 5, 10])
|
||||
expect(quickSort([5, 4])).toEqual([4, 5])
|
||||
expect(quickSort([1, 2, 3])).toEqual([1, 2, 3])
|
||||
expect(quickSort([0, 5, 3, 2, 2])).toEqual([0, 2, 2, 3, 5])
|
||||
})
|
||||
})
|
@ -1,26 +1,5 @@
|
||||
/**
|
||||
* A short example showing how to reverse a string
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new string and append
|
||||
* @complexity O(n)
|
||||
*
|
||||
* Doctests
|
||||
*
|
||||
* > ReverseStringIterative('some')
|
||||
* 'emos'
|
||||
* > ReverseStringIterative('string')
|
||||
* 'gnirts'
|
||||
* > ReverseStringIterative('The Algorithms Javascript')
|
||||
* 'tpircsavaJ smhtiroglA ehT'
|
||||
* > ReverseStringIterative([])
|
||||
* ! TypeError
|
||||
* > ReverseStringIterative({})
|
||||
* ! TypeError
|
||||
* > ReverseStringIterative(null)
|
||||
* ! TypeError
|
||||
* A short example showing how to reverse a string.
|
||||
*/
|
||||
function ReverseStringIterative (string) {
|
||||
if (typeof string !== 'string') {
|
||||
@ -40,34 +19,17 @@ function ReverseStringIterative (string) {
|
||||
* JS disallows string mutation so we're actually a bit slower.
|
||||
*
|
||||
* @complexity O(n)
|
||||
*
|
||||
* 'some' -> 'eoms' -> 'emos'
|
||||
*
|
||||
* Doctests
|
||||
*
|
||||
* > ReverseStringIterativeInplace('some')
|
||||
* 'emos'
|
||||
* > ReverseStringIterativeInplace('string')
|
||||
* 'gnirts'
|
||||
* > ReverseStringIterativeInplace('The Algorithms Javascript')
|
||||
* 'tpircsavaJ smhtiroglA ehT'
|
||||
* > ReverseStringIterativeInplace([])
|
||||
* ! TypeError
|
||||
* > ReverseStringIterativeInplace({})
|
||||
* ! TypeError
|
||||
* > ReverseStringIterativeInplace(null)
|
||||
* ! TypeError
|
||||
*/
|
||||
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++) {
|
||||
const first = _string[i]
|
||||
const second = _string[_string.length - 1 - i]
|
||||
_string[i] = second
|
||||
_string[i] = _string[_string.length - 1 - i]
|
||||
_string[_string.length - 1 - i] = first
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,3 @@
|
||||
/*
|
||||
* Doctests
|
||||
*
|
||||
* > reverseWords('I Love JS')
|
||||
* 'JS Love I'
|
||||
* > reverseWords('Hello World')
|
||||
* 'World Hello'
|
||||
* > reverseWords('The Algorithms Javascript')
|
||||
* 'Javascript Algorithms The'
|
||||
* > reverseWords([])
|
||||
* ! TypeError
|
||||
* > reverseWords({})
|
||||
* ! TypeError
|
||||
* > reverseWords(null)
|
||||
* ! TypeError
|
||||
*/
|
||||
const reverseWords = (str) => {
|
||||
if (typeof str !== 'string') {
|
||||
throw new TypeError('The given value is not a string')
|
||||
|
@ -1,22 +1,6 @@
|
||||
/*
|
||||
Function that takes a string input and return either true or false
|
||||
If it is a valid email address
|
||||
*/
|
||||
|
||||
/*
|
||||
* Doctests
|
||||
*
|
||||
* > validateEmail('mahfoudh.arous@gmail.com')
|
||||
* true
|
||||
* > validateEmail('mahfoudh.arous@helsinki.edu')
|
||||
* true
|
||||
* > validateEmail('mahfoudh.arous.com')
|
||||
* false
|
||||
* > validateEmail('')
|
||||
* ! TypeError
|
||||
* > validateEmail(null)
|
||||
* ! TypeError
|
||||
*/
|
||||
/**
|
||||
* Returns whether the given string is a valid email address or not.
|
||||
*/
|
||||
const validateEmail = (str) => {
|
||||
if (str === '' || str === null) {
|
||||
throw new TypeError('Email Address String Null or Empty.')
|
||||
|
@ -1,63 +1,65 @@
|
||||
import {
|
||||
ReverseStringIterative,
|
||||
ReverseStringIterativeInplace
|
||||
} from '../ReverseString'
|
||||
import { ReverseStringIterative, ReverseStringIterativeInplace } from '../ReverseString'
|
||||
|
||||
describe('ReverseStringIterative', () => {
|
||||
it('expects to reverse a simple string', () => {
|
||||
const SUT = ReverseStringIterative('reverse')
|
||||
expect(SUT).toEqual('esrever')
|
||||
expect(ReverseStringIterative('reverse')).toEqual('esrever')
|
||||
expect(ReverseStringIterative('some')).toEqual('emos')
|
||||
expect(ReverseStringIterative('string')).toEqual('gnirts')
|
||||
expect(ReverseStringIterative('The Algorithms Javascript')).toEqual('tpircsavaJ smhtiroglA ehT')
|
||||
})
|
||||
|
||||
it('expects to reverse a string with spaces in between', () => {
|
||||
const SUT = ReverseStringIterative('reverse me')
|
||||
expect(SUT).toEqual('em esrever')
|
||||
expect(ReverseStringIterative('reverse me')).toEqual('em esrever')
|
||||
})
|
||||
|
||||
it('expects to reverse a simple string without capitalizing the first letter', () => {
|
||||
const SUT = ReverseStringIterative('Javascript')
|
||||
expect(SUT).toEqual('tpircsavaJ')
|
||||
expect(ReverseStringIterative('Javascript')).toEqual('tpircsavaJ')
|
||||
})
|
||||
|
||||
it.each`
|
||||
input
|
||||
${123456}
|
||||
${[1, 2, 3, 4, 5, 6]}
|
||||
${{ test: 'test' }}
|
||||
${null}
|
||||
`(
|
||||
'expects to throw a type error given a value that is $input',
|
||||
({ input }) => {
|
||||
expect(() => {
|
||||
ReverseStringIterative(input)
|
||||
}).toThrow('The given value is not a string')
|
||||
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('')
|
||||
expect(ReverseStringIterative('')).toEqual('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ReverseStringIterativeInplace', () => {
|
||||
it('expects to reverse a simple string', () => {
|
||||
const SUT = ReverseStringIterativeInplace('reverse')
|
||||
expect(SUT).toEqual('esrever')
|
||||
expect(ReverseStringIterativeInplace('reverse')).toEqual('esrever')
|
||||
expect(ReverseStringIterativeInplace('some')).toEqual('emos')
|
||||
expect(ReverseStringIterativeInplace('string')).toEqual('gnirts')
|
||||
expect(ReverseStringIterativeInplace('The Algorithms Javascript')).toEqual('tpircsavaJ smhtiroglA ehT')
|
||||
})
|
||||
|
||||
it('expects to reverse a simple string without capitalizing the first letter', () => {
|
||||
const SUT = ReverseStringIterativeInplace('Javascript')
|
||||
expect(SUT).toEqual('tpircsavaJ')
|
||||
expect(ReverseStringIterativeInplace('Javascript')).toEqual('tpircsavaJ')
|
||||
})
|
||||
|
||||
it('expects to return an empty string given an empty string', () => {
|
||||
const SUT = ReverseStringIterativeInplace('Javascript')
|
||||
expect(SUT).toEqual('tpircsavaJ')
|
||||
expect(ReverseStringIterativeInplace('Javascript')).toEqual('tpircsavaJ')
|
||||
})
|
||||
|
||||
it.each`
|
||||
input
|
||||
${123456}
|
||||
${[1, 2, 3, 4, 5, 6]}
|
||||
${{ test: 'test' }}
|
||||
${null}
|
||||
`(
|
||||
'expects to throw a type error given a value that is $input',
|
||||
({ input }) => {
|
||||
expect(() => {
|
||||
ReverseStringIterativeInplace(input)
|
||||
}).toThrow('The given value is not a string')
|
||||
expect(() => ReverseStringIterativeInplace(input)).toThrow('The given value is not a string')
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -2,14 +2,17 @@ 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')
|
||||
expect(reverseWords('I Love JS')).toBe('JS Love I')
|
||||
expect(reverseWords('Hello World')).toBe('World Hello')
|
||||
expect(reverseWords('The Algorithms Javascript')).toBe('Javascript Algorithms The')
|
||||
})
|
||||
|
||||
it.each`
|
||||
input
|
||||
${123456}
|
||||
${[1, 2, 3, 4, 5, 6]}
|
||||
${{ test: 'test' }}
|
||||
${null}
|
||||
`(
|
||||
'expects to throw a type error given a value that is $input',
|
||||
({ input }) => {
|
||||
|
@ -19,5 +19,6 @@ describe('Validation of an Email Address', () => {
|
||||
|
||||
it('expects to throw a type error', () => {
|
||||
expect(() => { validateEmail('') }).toThrow('Email Address String Null or Empty.')
|
||||
expect(() => { validateEmail(null) }).toThrow('Email Address String Null or Empty.')
|
||||
})
|
||||
})
|
||||
|
12483
package-lock.json
generated
12483
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,8 @@
|
||||
"description": "A repository for All algorithms implemented in Javascript (for educational purposes only)",
|
||||
"main": "",
|
||||
"scripts": {
|
||||
"doctest": "doctest **/*.js",
|
||||
"test": "jest --no-cache",
|
||||
"style": "standard"
|
||||
"style": "standard --fix"
|
||||
},
|
||||
"author": "TheAlgorithms",
|
||||
"license": "GPL-3.0",
|
||||
@ -26,9 +25,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "^26.3.0",
|
||||
"doctest": "^0.17.1",
|
||||
"globby": "^12.0.2",
|
||||
"jest": "^26.4.2",
|
||||
"standard": "^14.3.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user