diff --git a/.babelrc b/.babelrc
index 002b4aa0d5..1320b9a327 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,3 +1,3 @@
 {
-  "presets": ["env"]
+  "presets": ["@babel/preset-env"]
 }
diff --git a/.editorconfig b/.editorconfig
index 78c6ddee23..086af2d930 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,3 +1,4 @@
+# @see: https://editorconfig.org/
 root = true
 
 [*]
@@ -6,3 +7,5 @@ insert_final_newline = true
 charset = utf-8
 indent_style = space
 indent_size = 2
+trim_trailing_whitespace = true
+quote_type = single
diff --git a/.eslintrc b/.eslintrc
index 076399682c..88af3ecf32 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -11,5 +11,11 @@
     "class-methods-use-this": "off",
     "arrow-body-style": "off",
     "no-loop-func": "off"
+  },
+  "ignorePatterns": ["*.md", "*.png", "*.jpeg", "*.jpg"],
+  "settings": {
+    "react": {
+      "version": "18.2.0"
+    }
   }
 }
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000..deda41e7d6
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# @see: https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository
+github: trekhleb
+patreon: trekhleb
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
new file mode 100644
index 0000000000..e007b6c910
--- /dev/null
+++ b/.github/workflows/CI.yml
@@ -0,0 +1,35 @@
+name: CI
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [ 16.x ]
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+
+      - name: Setup Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+
+      - name: Install dependencies
+        run: npm i
+
+      - name: Run linting
+        run: npm run lint
+
+      - name: Run tests
+        run: npm run coverage
+
+      - name: Upload coverage to Codecov
+        uses: codecov/codecov-action@v1
diff --git a/.husky/.gitignore b/.husky/.gitignore
new file mode 100644
index 0000000000..31354ec138
--- /dev/null
+++ b/.husky/.gitignore
@@ -0,0 +1 @@
+_
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000000..598a5dceaa
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,5 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npm run lint
+# npm run test
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000000..b6f27f1359
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000000..7fd023741b
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+v16.15.0
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6fbf14c097..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-sudo: required
-dist: trusty
-language: node_js
-node_js:
-  - node
-script:
-  - npm run ci
-  - npm run codecov
-notifications:
-  email: false
diff --git a/BACKERS.md b/BACKERS.md
new file mode 100644
index 0000000000..a244d3bed3
--- /dev/null
+++ b/BACKERS.md
@@ -0,0 +1,48 @@
+# Project Backers
+
+> You may support this project via ❤️️ [GitHub](https://github.com/sponsors/trekhleb) or ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+## `O(2ⁿ)` Backers
+
+`null`
+
+## `O(n²)` Backers
+
+`null`
+
+## `O(n×log(n))` Backers
+
+`null`
+
+<!--
+<table>
+  <tr>
+    <td align="center">
+      <a href="[PROFILE_URL]">
+        <img
+          src="[PROFILE_IMG_SRC]"
+          width="50"
+          height="50"
+        />
+      </a>
+      <br />
+      <a href="[PROFILE_URL]">[PROFILE_NAME]</a>
+    </td>
+  </tr>
+</table>
+-->
+
+<!--
+<ul>
+  <li>
+    <a href="[PROFILE_URL]">
+      <img
+        src="[PROFILE_IMG_SRC]"
+        width="30"
+        height="30"
+      /></a>
+    &thinsp;
+    <a href="[PROFILE_URL]">[PROFILE_NAME]</a>
+  </li>
+</ul>
+-->
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f486394c18..8f6bf07913 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,7 +9,7 @@
 **Contributing New Translation**
 
 - Create new `README.xx-XX.md` file with translation alongside with 
-main `README.md` file where `xx-XX` is locale and country/region codes.
+main `README.md` file where `xx-XX` is [locale and country/region codes](http://www.lingoes.net/en/translator/langcode.htm).
 For example `en-US`, `zh-CN`, `zh-TW`, `ko-KR` etc.
 - You may also translate all other sub-folders by creating 
 related `README.xx-XX.md` files in each of them.
diff --git a/README.ar-AR.md b/README.ar-AR.md
new file mode 100644
index 0000000000..2ff0baddbd
--- /dev/null
+++ b/README.ar-AR.md
@@ -0,0 +1,328 @@
+# جافا سكريبت خوارزميات  وهياكل البيانات
+
+[![Build Status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+تحتوي هذه المقالة على أمثلة عديدة تستند إلى الخوارزميات الشائعة وهياكل البيانات في الجافا سكريبت.
+
+كل خوارزمية وهياكل البيانات لها برنامج README منفصل خاص بها
+مع التفسيرات والروابط ذات الصلة لمزيد من القراءة (بما في ذلك تلك
+إلى مقاطع فيديو YouTube).
+
+
+_اقرأ هذا في لغات أخرى:_
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+ ☝ ملاحضة هذا المشروع مخصص للاستخدام لأغراض التعلم والبحث
+فقط ، و ** ليست ** معدة للاستخدام في **الإنتاج**
+
+## هياكل البيانات
+
+هياكل البيانات هي طريقة خاصة لتنظيم البيانات وتخزينها في جهاز الكمبيوتر بحيث
+يمكن الوصول إليها وتعديلها بكفاءة. بتعبير أدق ، هيكل البيانات هو مجموعة من البيانات
+القيم والعلاقات فيما بينها والوظائف أو العمليات التي يمكن تطبيقها عليها
+البيانات.
+
+
+`B` - مبتدئ, `A` - المتقدمة
+
+* `B` [قائمة مرتبطة](src/data-structures/linked-list)
+* `B` [قائمة مرتبطة بشكل مضاعف](src/data-structures/doubly-linked-list)
+* `B` [طابور, Queue](src/data-structures/queue)
+* `B` [كومة](src/data-structures/stack)
+* `B` [جدول التجزئة](src/data-structures/hash-table)
+* `B` [كومة](src/data-structures/heap) -الحد الأقصى والحد الأدنى من إصدارات الكومة
+* `B` [طابور الأولوية](src/data-structures/priority-queue)
+* `A` [تري](src/data-structures/trie)
+* `A` [شجرة](src/data-structures/tree)
+  * `A` [شجرة البحث الثنائية](src/data-structures/tree/binary-search-tree)
+  * `A` [شجرة AVL](src/data-structures/tree/avl-tree)
+  * `A` [شجرة الأحمر والأسود](src/data-structures/tree/red-black-tree)
+  * `A` [شجرة القطعة](src/data-structures/tree/segment-tree) - مع أمثلة على استفسارات النطاق الأدنى / الأقصى / المجموع
+  * `A` [شجرة فينويك](src/data-structures/tree/fenwick-tree) (شجرة ثنائية مفهرسة)
+* `A` [Graph](src/data-structures/graph) (كلاهما موجه وغير موجه)
+* `A` [مجموعة منفصلة](src/data-structures/disjoint-set)
+* `A` [مرشح بلوم](src/data-structures/bloom-filter)
+
+
+## الخوارزميات
+
+الخوارزمية هي تحديد لا لبس فيه لكيفية حل فئة من المشاكل. أنه
+مجموعة من القواعد التي تحدد بدقة تسلسل العمليات.
+
+`B` - مبتدئ ، `A` - متقدم
+
+
+### الخوارزميات حسب الموضوع
+
+* **رياضيات**
+  * `B` [معالجة البت](src/algorithms/math/bits)
+  * `B` [عاملي](src/algorithms/math/factorial)
+  * `B` [رقم فيبوناتشي](src/algorithms/math/fibonacci) - الإصدارات الكلاسيكية والمغلقة
+  * `B` [اختبار البدائية](src/algorithms/math/primality-test) (طريقة تقسيم المحاكمة)
+  * `B` [الخوارزمية الإقليدية](src/algorithms/math/euclidean-algorithm) - احسب القاسم المشترك الأكبر (GCD)
+  * `B` [أقل مضاعف مشترك](src/algorithms/math/least-common-multiple) (LCM)
+  * `B` [منخل إراتوستينس](src/algorithms/math/sieve-of-eratosthenes) - إيجاد جميع الأعداد الأولية حتى أي حد معين
+  * `B` [هي قوة اثنين](src/algorithms/math/is-power-of-two) - تحقق مما إذا كان الرقم هو قوة اثنين (الخوارزميات الساذجة والبتية)
+  * `B` [مثلث باسكال](src/algorithms/math/pascal-triangle)
+  * `B` [عدد مركب](src/algorithms/math/complex-number) - الأعداد المركبة والعمليات الأساسية معهم
+  * `B` [راديان ودرجة](src/algorithms/math/radian) - راديان لدرجة التحويل والعكس
+  * `B` [تشغيل سريع](src/algorithms/math/fast-powering)
+  * `B` [طريقة هورنر](src/algorithms/math/horner-method) - تقييم متعدد الحدود
+  * `A` [قسم صحيح](src/algorithms/math/integer-partition)
+  * `A` [الجذر التربيعي](src/algorithms/math/square-root) - طريقة نيوتن
+  * `A` [خوارزمية ليو هوي π](src/algorithms/math/liu-hui) - π حسابات تقريبية على أساس N-gons
+  * `A` [تحويل فورييه المنفصل](src/algorithms/math/fourier-transform) - حلل وظيفة الوقت (إشارة) في الترددات التي يتكون منها
+* **مجموعات**
+  * `B` [المنتج الديكارتي](src/algorithms/sets/cartesian-product) - منتج من مجموعات متعددة
+  * `B` [فيشر ييتس شافل](src/algorithms/sets/fisher-yates) - التقليب العشوائي لتسلسل محدود
+  * `A` [مجموعة الطاقة](src/algorithms/sets/power-set) - جميع المجموعات الفرعية للمجموعة (حلول البت والتتبع التراجعي)
+  * `A` [التباديل](src/algorithms/sets/permutations) (مع وبدون التكرار)
+  * `A` [مجموعات](src/algorithms/sets/combinations) (مع وبدون التكرار)
+  * `A` [أطول نتيجة مشتركة](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [أطول زيادة متتالية](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [أقصر تسلسل فائق مشترك](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [مشكلة حقيبة الظهر](src/algorithms/sets/knapsack-problem) - "0/1" و "غير منضم"
+  * `A` [الحد الأقصى من Subarray](src/algorithms/sets/maximum-subarray) -إصدارات "القوة الغاشمة" و "البرمجة الديناميكية" (كادان)
+  * `A` [مجموع الجمع](src/algorithms/sets/combination-sum) - ابحث عن جميع التركيبات التي تشكل مبلغًا محددًا
+* **سلاسل**
+  * `B` [مسافة هامنج](src/algorithms/string/hamming-distance) - عدد المواقف التي تختلف فيها الرموز
+  * `A` [المسافة ليفنشتاين](src/algorithms/string/levenshtein-distance) - الحد الأدنى لمسافة التحرير بين تسلسلين
+  * `A` [خوارزمية كنوث - موريس - برات](src/algorithms/string/knuth-morris-pratt) (خوارزمية KMP) - بحث السلسلة الفرعية (مطابقة النمط)
+  * `A` [خوارزمية Z](src/algorithms/string/z-algorithm) - بحث سلسلة فرعية (مطابقة النمط)
+  * `A` [خوارزمية رابين كارب](src/algorithms/string/rabin-karp) - بحث السلسلة الفرعية
+  * `A` [أطول سلسلة فرعية مشتركة](src/algorithms/string/longest-common-substring)
+  * `A` [مطابقة التعبير العادي](src/algorithms/string/regular-expression-matching)
+* **عمليات البحث**
+  * `B` [البحث الخطي](src/algorithms/search/linear-search)
+  * `B` [بحث سريع](src/algorithms/search/jump-search) (أو حظر البحث) - ابحث في مصفوفة مرتبة
+  * `B` [بحث ثنائي](src/algorithms/search/binary-search) - البحث في مجموعة مرتبة
+  * `B` [بحث الاستيفاء](src/algorithms/search/interpolation-search) - البحث في مجموعة مرتبة موزعة بشكل موحد
+
+* **فرز**
+  * `B` [Bubble Sort](src/algorithms/sorting/bubble-sort)
+  * `B` [Selection Sort](src/algorithms/sorting/selection-sort)
+  * `B` [Insertion Sort](src/algorithms/sorting/insertion-sort)
+  * `B` [Heap Sort](src/algorithms/sorting/heap-sort)
+  * `B` [Merge Sort](src/algorithms/sorting/merge-sort)
+  * `B` [Quicksort](src/algorithms/sorting/quick-sort) - عمليات التنفيذ في المكان وغير في المكان
+  * `B` [Shellsort](src/algorithms/sorting/shell-sort)
+  * `B` [Counting Sort](src/algorithms/sorting/counting-sort)
+  * `B` [Radix Sort](src/algorithms/sorting/radix-sort)
+* **القوائم المرتبطة**
+  * `B` [Straight Traversal](src/algorithms/linked-list/traversal)
+  * `B` [Reverse Traversal](src/algorithms/linked-list/reverse-traversal)
+* **الأشجار**
+  * `B` [Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Breadth-First Search](src/algorithms/tree/breadth-first-search) (BFS)
+* **الرسوم البيانية**
+  * `B` [Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Breadth-First Search](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - إيجاد الحد الأدنى من شجرة الامتداد (MST) للرسم البياني الموزون غير الموجه
+  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) -إيجاد أقصر المسارات لجميع رؤوس الرسم البياني من رأس واحد
+  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - إيجاد أقصر المسارات لجميع رؤوس الرسم البياني من رأس واحد
+  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - إيجاد أقصر المسارات بين جميع أزواج الرؤوس
+  * `A` [Detect Cycle](src/algorithms/graph/detect-cycle) - لكل من الرسوم البيانية الموجهة وغير الموجهة (الإصدارات القائمة على DFS و Disjoint Set)
+  * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - إيجاد الحد الأدنى من شجرة الامتداد (MST) للرسم البياني الموزون غير الموجه
+  * `A` [Topological Sorting](src/algorithms/graph/topological-sorting) - طريقة البحث العمق الأول (DFS)
+  * `A` [Articulation Points](src/algorithms/graph/articulation-points) - خوارزمية تارجان (تعتمد على DFS)
+  * `A` [Bridges](src/algorithms/graph/bridges) - خوارزمية تعتمد على DFS
+  * `A` [Eulerian Path and Eulerian Circuit](src/algorithms/graph/eulerian-path) - خوارزمية فلوري - قم بزيارة كل حافة مرة واحدة بالضبط
+  * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - قم بزيارة كل قمة مرة واحدة بالضبط
+  * `A` [Strongly Connected Components](src/algorithms/graph/strongly-connected-components) - خوارزمية Kosaraju
+  * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - أقصر طريق ممكن يزور كل مدينة ويعود إلى المدينة الأصلية
+* **التشفير
+  * `B` [Polynomial Hash](src/algorithms/cryptography/polynomial-hash) - المتداول دالة التجزئة على أساس متعدد الحدود
+  * `B` [Caesar Cipher](src/algorithms/cryptography/caesar-cipher) - استبدال بسيط للشفرات
+* **التعلم الالي**
+  * `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 وظائف JS بسيطة توضح كيف يمكن للآلات أن تتعلم بالفعل (الانتشار إلى الأمام / الخلف)
+* **غير مصنف**
+  * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - خوارزمية في المكان
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - التراجع ، البرمجة الديناميكية (من أعلى إلى أسفل + من أسفل إلى أعلى) والأمثلة الجشعة
+  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths) - التراجع والبرمجة الديناميكية والأمثلة القائمة على مثلث باسكال
+  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - محاصرة مشكلة مياه الأمطار (البرمجة الديناميكية وإصدارات القوة الغاشمة)
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - احسب عدد الطرق للوصول إلى القمة (4 حلول)
+  * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
+  * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
+
+### الخوارزميات حسب النموذج
+
+النموذج الحسابي هو طريقة أو نهج عام يكمن وراء تصميم الفصل
+من الخوارزميات. إنه تجريد أعلى من مفهوم الخوارزمية ، تمامًا مثل
+الخوارزمية هي تجريد أعلى من برنامج الكمبيوتر.
+
+* **القوة الغاشمة** - انظر في جميع الاحتمالات وحدد الحل الأفضل
+  * `B` [Linear Search](src/algorithms/search/linear-search)
+  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - محاصرة مشكلة مياه الأمطار
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - احسب عدد الطرق للوصول إلى القمة
+  * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
+  * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - أقصر طريق ممكن يزور كل مدينة ويعود إلى المدينة الأصلية
+  * `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - حلل وظيفة الوقت (إشارة) في الترددات التي يتكون منها
+* **جشع** - اختر الخيار الأفضل في الوقت الحالي ، دون أي اعتبار للمستقبل
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `A` [Unbound Knapsack Problem](src/algorithms/sets/knapsack-problem)
+  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - إيجاد أقصر مسار لجميع رؤوس الرسم البياني
+  * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - إيجاد الحد الأدنى من شجرة الامتداد (MST) للرسم البياني الموزون غير الموجه
+  * `A` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - إيجاد الحد الأدنى من شجرة الامتداد (MST) للرسم البياني الموزون غير الموجه
+* **فرق تسد** - قسّم المشكلة إلى أجزاء أصغر ثم حل تلك الأجزاء
+  * `B` [Binary Search](src/algorithms/search/binary-search)
+  * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Pascal's Triangle](src/algorithms/math/pascal-triangle)
+  * `B` [Euclidean Algorithm](src/algorithms/math/euclidean-algorithm) - حساب القاسم المشترك الأكبر (GCD)
+  * `B` [Merge Sort](src/algorithms/sorting/merge-sort)
+  * `B` [Quicksort](src/algorithms/sorting/quick-sort)
+  * `B` [Tree Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Graph Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `B` [Fast Powering](src/algorithms/math/fast-powering)
+  * `A` [Permutations](src/algorithms/sets/permutations) (مع التكرار وبدونه)
+  * `A` [Combinations](src/algorithms/sets/combinations) (مع التكرار وبدونه)
+* **البرمجة الديناميكية** - بناء حل باستخدام الحلول الفرعية التي تم العثور عليها مسبقًا
+  * `B` [Fibonacci Number](src/algorithms/math/fibonacci)
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
+  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - محاصرة مشكلة مياه الأمطار
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - احسب عدد الطرق للوصول إلى القمة
+  * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - الحد الأدنى لمسافة التحرير بين تسلسلين
+  * `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Longest Common Substring](src/algorithms/string/longest-common-substring)
+  * `A` [Longest Increasing Subsequence](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Shortest Common Supersequence](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [0/1 Knapsack Problem](src/algorithms/sets/knapsack-problem)
+  * `A` [Integer Partition](src/algorithms/math/integer-partition)
+  * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
+  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - إيجاد أقصر مسار لجميع رؤوس الرسم البياني
+  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - إيجاد أقصر المسارات بين جميع أزواج الرؤوس
+  * `A` [Regular Expression Matching](src/algorithms/string/regular-expression-matching)
+* **التراجع** - على غرار القوة الغاشمة ، حاول إنشاء جميع الحلول الممكنة ، ولكن في كل مرة تقوم فيها بإنشاء الحل التالي الذي تختبره
+إذا استوفت جميع الشروط ، وعندها فقط استمر في إنشاء الحلول اللاحقة. خلاف ذلك ، تراجع ، واذهب إلى
+طريق مختلف لإيجاد حل. عادةً ما يتم استخدام اجتياز DFS لمساحة الدولة.
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
+  * `B` [Power Set](src/algorithms/sets/power-set) - جميع المجموعات الفرعية للمجموعة
+  * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - قم بزيارة كل قمة مرة واحدة بالضبط
+  * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
+  * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
+  * `A` [Combination Sum](src/algorithms/sets/combination-sum) - ابحث عن جميع التركيبات التي تشكل مبلغًا محددًا
+
+
+* ** Branch & Bound ** - تذكر الحل الأقل تكلفة الموجود في كل مرحلة من مراحل التراجع
+البحث ، واستخدام تكلفة الحل الأقل تكلفة الموجود حتى الآن بحد أدنى لتكلفة
+الحل الأقل تكلفة للمشكلة ، من أجل تجاهل الحلول الجزئية بتكاليف أكبر من
+تم العثور على حل بأقل تكلفة حتى الآن. اجتياز BFS عادةً بالاشتراك مع اجتياز DFS لمساحة الحالة
+يتم استخدام الشجرة.
+
+## كيفية استخدام هذا المستودع
+
+**تثبيت كل التبعيات**
+```
+npm install
+```
+
+**قم بتشغيل ESLint**
+
+قد ترغب في تشغيله للتحقق من جودة الكود.
+
+```
+npm run lint
+```
+
+**قم بإجراء جميع الاختبارات**
+```
+npm test
+```
+
+**قم بإجراء الاختبارات بالاسم**
+```
+npm test -- 'LinkedList'
+```
+
+**ملعب**
+
+يمكنك اللعب بهياكل البيانات والخوارزميات في ملف `. /src/playground/playground.js` والكتابة
+اختبارات لها في `./src/playground/__test__/playground.test.js`.
+
+ثم قم ببساطة بتشغيل الأمر التالي لاختبار ما إذا كان كود الملعب الخاص بك يعمل كما هو متوقع:
+
+```
+npm test -- 'playground'
+```
+
+## معلومات مفيدة
+
+### المراجع
+
+[▶ هياكل البيانات والخوارزميات على موقع يوتيوب](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### Big O Notation
+
+* يتم استخدام **Big O notation** لتصنيف الخوارزميات وفقًا لكيفية نمو متطلبات وقت التشغيل أو المساحة مع نمو حجم الإدخال.
+قد تجد في الرسم البياني أدناه الأوامر الأكثر شيوعًا لنمو الخوارزميات المحددة في تBig O notation.
+
+![Big O graphs](./assets/big-o-graph.png)
+
+مصدر: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+فيما يلي قائمة ببعض رموز Big O notation الأكثر استخدامًا ومقارنات أدائها مقابل أحجام مختلفة من بيانات الإدخال.
+
+| Big O Notation | Computations for 10 elements | Computations for 100 elements | Computations for 1000 elements  |
+| -------------- | ---------------------------- | ----------------------------- | ------------------------------- |
+| **O(1)**       | 1                            | 1                             | 1                               |
+| **O(log N)**   | 3                            | 6                             | 9                               |
+| **O(N)**       | 10                           | 100                           | 1000                            |
+| **O(N log N)** | 30                           | 600                           | 9000                            |
+| **O(N^2)**     | 100                          | 10000                         | 1000000                         |
+| **O(2^N)**     | 1024                         | 1.26e+29                      | 1.07e+301                       |
+| **O(N!)**      | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+
+### تعقيد عمليات بنية البيانات
+
+| Data Structure          | Access    | Search    | Insertion | Deletion  | Comments  |
+| ----------------------- | :-------: | :-------: | :-------: | :-------: | :-------- |
+| **Array**               | 1         | n         | n         | n         |           |
+| **Stack**               | n         | n         | 1         | 1         |           |
+| **Queue**               | n         | n         | 1         | 1         |           |
+| **Linked List**         | n         | n         | 1         | n         |           |
+| **Hash Table**          | -         | n         | n         | n         |في حالة وجود تكاليف دالة تجزئة مثالية ستكون O (1)|
+| **Binary Search Tree**  | n         | n         | n         | n         | في حالة توازن تكاليف الشجرة ستكون O (log (n))|
+| **B-Tree**              | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Red-Black Tree**      | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **AVL Tree**            | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Bloom Filter**        | -         | 1         | 1         | -         |الإيجابيات الكاذبة ممكنة أثناء البحث|
+
+### تعقيد خوارزميات فرز الصفيف
+
+| Name                  | Best            | Average             | Worst               | Memory    | Stable    | Comments  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Bubble sort**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | نعم       |           |
+| **Insertion sort**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | نعم       |           |
+| **Selection sort**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | لا        |           |
+| **Heap sort**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | لا        |           |
+| **Merge sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | نعم       |           |
+| **Quick sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | No        | عادةً ما يتم إجراء الفرز السريع في مكانه مع مساحة مكدس O (log (n))|
+| **Shell sort**        | n&nbsp;log(n)   | depends on gap sequence   | n&nbsp;(log(n))<sup>2</sup>  | 1         | لا       |           |
+| **Counting sort**     | n + r           | n + r               | n + r               | n + r     | Yes       |r - أكبر رقم في المجموعة|
+| **Radix sort**        | n * k           | n * k               | n * k               | n + k     | Yes       | ك - طول أطول مفتاح |
+
+## مؤيدو المشروع
+
+> يمكنك دعم هذا المشروع عبر ❤️️ [GitHub](https://github.com/sponsors/trekhleb) أو ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+[الناس الذين يدعمون هذا المشروع](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 0`
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.de-DE.md b/README.de-DE.md
new file mode 100644
index 0000000000..019e7d6b39
--- /dev/null
+++ b/README.de-DE.md
@@ -0,0 +1,338 @@
+# JavaScript-Algorithmen und Datenstrukturen
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+Dieses Repository enthält JavaScript Beispiele für viele
+gängige Algorithmen und Datenstrukturen.
+
+Jeder Algorithmus und jede Datenstruktur hat eine eigene README
+mit zugehörigen Erklärungen und weiterführenden Links (einschließlich zu YouTube-Videos).
+
+_Lies dies in anderen Sprachen:_
+[_English_](https://github.com/trekhleb/javascript-algorithms/)
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+_☝ Beachte, dass dieses Projekt nur für Lern- und Forschungszwecke gedacht ist und **nicht** für den produktiven Einsatz verwendet werden soll_
+
+## Datenstrukturen
+
+Eine Datenstruktur ist eine bestimmte Art und Weise, Daten in einem Computer so zu organisieren und zu speichern, dass sie
+effizient erreicht und verändert werden können. Genauer gesagt, ist eine Datenstruktur eine Sammlung von Werten,
+den Beziehungen zwischen ihnen und den Funktionen oder Operationen, die auf die Daten angewendet werden können.
+
+`B` - Anfänger:innen, `A` - Fortgeschrittene
+
+* `B` [Verkettete Liste (Linked List)](src/data-structures/linked-list)
+* `B` [Doppelt verkettete Liste (Doubly Linked List)](src/data-structures/doubly-linked-list)
+* `B` [Warteschlange (Queue)](src/data-structures/queue)
+* `B` [Stapelspeicher (Stack)](src/data-structures/stack)
+* `B` [Hashtabelle (Hash Table)](src/data-structures/hash-table)
+* `B` [Heap-Algorithmus (Heap)](src/data-structures/heap) - max und min Heap-Versionen
+* `B` [Vorrangwarteschlange (Priority Queue)](src/data-structures/priority-queue)
+* `A` [Trie (Trie)](src/data-structures/trie)
+* `A` [Baum (Tree)](src/data-structures/tree)
+  * `A` [Binärer Suchbaum (Binary Search Tree)](src/data-structures/tree/binary-search-tree)
+  * `A` [AVL-Baum (AVL Tree)](src/data-structures/tree/avl-tree)
+  * `A` [Rot-Schwarz-Baum (Red-Black Tree)](src/data-structures/tree/red-black-tree)
+  * `A` [Segment-Baum (Segment Tree)](src/data-structures/tree/segment-tree) - mit Min/Max/Summenbereich-Abfrage Beispiel
+  * `A` [Fenwick Baum (Fenwick Tree)](src/data-structures/tree/fenwick-tree) (Binär indizierter Baum / Binary Indexed Tree)
+* `A` [Graph (Graph)](src/data-structures/graph) (sowohl gerichtet als auch ungerichtet)
+* `A` [Union-Find-Struktur (Disjoint Set)](src/data-structures/disjoint-set)
+* `A` [Bloomfilter (Bloom Filter)](src/data-structures/bloom-filter)
+
+## Algorithmen
+
+Ein Algorithmus ist eine eindeutige Spezifikation, wie eine Klasse von Problemen zu lösen ist. Er besteht
+aus einem Satz von Regeln, die eine Abfolge von Operationen genau definieren.
+
+`B` - Anfänger:innen, `A` - Fortgeschrittene
+
+### Algorithmen nach Thema
+
+* **Mathe**
+  * `B` [Bitmanipulation (Bit Manipulation)](src/algorithms/math/bits) - Bits setzen/lesen/aktualisieren/löschen, Multiplikation/Division durch zwei negieren usw..
+  * `B` [Faktoriell (Factorial)](src/algorithms/math/factorial)
+  * `B` [Fibonacci-Zahl (Fibonacci Number)](src/algorithms/math/fibonacci) - Klassische und geschlossene Version
+  * `B` [Primfaktoren (Prime Factors)](src/algorithms/math/prime-factors) - Auffinden von Primfaktoren und deren Zählung mit Hilfe des Satz von Hardy-Ramanujan (Hardy-Ramanujan's theorem)
+  * `B` [Primzahl-Test (Primality Test)](src/algorithms/math/primality-test) (Probedivision / trial division method)
+  * `B` [Euklidischer Algorithmus (Euclidean Algorithm)](src/algorithms/math/euclidean-algorithm) - Berechnen des größten gemeinsamen Teilers (ggT)
+  * `B` [Kleinstes gemeinsames Vielfaches (Least Common Multiple)](src/algorithms/math/least-common-multiple) (kgV)
+  * `B` [Sieb des Eratosthenes (Sieve of Eratosthenes)](src/algorithms/math/sieve-of-eratosthenes) - Finden aller Primzahlen bis zu einer bestimmten Grenze
+  * `B` [Power of two (Is Power of Two)](src/algorithms/math/is-power-of-two) - Prüft, ob die Zahl eine Zweierpotenz ist (naive und bitweise Algorithmen)
+  * `B` [Pascalsches Dreieck (Pascal's Triangle)](src/algorithms/math/pascal-triangle)
+  * `B` [Komplexe Zahlen (Complex Number)](src/algorithms/math/complex-number) - Komplexe Zahlen und Grundoperationen mit ihnen
+  * `B` [Bogenmaß & Grad (Radian & Degree)](src/algorithms/math/radian) - Umrechnung von Bogenmaß in Grad und zurück
+  * `B` [Fast Powering Algorithmus (Fast Powering)](src/algorithms/math/fast-powering)
+  * `B` [Horner-Schema (Horner's method)](src/algorithms/math/horner-method) - Polynomauswertung
+  * `B` [Matrizen (Matrices)](src/algorithms/math/matrix) - Matrizen und grundlegende Matrixoperationen (Multiplikation, Transposition usw.)
+  * `B` [Euklidischer Abstand (Euclidean Distance)](src/algorithms/math/euclidean-distance) - Abstand zwischen zwei Punkten/Vektoren/Matrizen
+  * `A` [Ganzzahlige Partitionierung (Integer Partition)](src/algorithms/math/integer-partition)
+  * `A` [Quadratwurzel (Square Root)](src/algorithms/math/square-root) - Newtonverfahren (Newton's method)
+  * `A` [Liu Hui π Algorithmus (Liu Hui π Algorithm)](src/algorithms/math/liu-hui) - Näherungsweise π-Berechnungen auf Basis von N-gons
+  * `A` [Diskrete Fourier-Transformation (Discrete Fourier Transform)](src/algorithms/math/fourier-transform) - Eine Funktion der Zeit (ein Signal) in die Frequenzen zerlegen, aus denen sie sich zusammensetzt
+* **Sets**
+  * `B` [Kartesisches Produkt (Cartesian Product)](src/algorithms/sets/cartesian-product) - Produkt aus mehreren Mengen
+  * `B` [Fisher-Yates-Verfahren (Fisher–Yates Shuffle)](src/algorithms/sets/fisher-yates) - Zufällige Permutation einer endlichen Folge
+  * `A` [Potenzmenge (Power Set)](src/algorithms/sets/power-set) - Alle Teilmengen einer Menge (Bitweise und Rücksetzverfahren Lösungen(backtracking solutions))
+  * `A` [Permutation (Permutations)](src/algorithms/sets/permutations) (mit und ohne Wiederholungen)
+  * `A` [Kombination (Combinations)](src/algorithms/sets/combinations) (mit und ohne Wiederholungen)
+  * `A` [Problem der längsten gemeinsamen Teilsequenz (Longest Common Subsequence)](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Längste gemeinsame Teilsequenz (Longest Increasing Subsequence)](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Der kürzeste gemeinsame String (Shortest Common Supersequence)](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [Rucksackproblem (Knapsack Problem)](src/algorithms/sets/knapsack-problem) - "0/1" und "Ungebunden"
+  * `A` [Das Maximum-Subarray Problem (Maximum Subarray)](src/algorithms/sets/maximum-subarray) - "Brute-Force-Methode" und "Dynamische Programmierung" (Kadane' Algorithmus)
+  * `A` [Kombinationssumme (Combination Sum)](src/algorithms/sets/combination-sum) - Alle Kombinationen finden, die eine bestimmte Summe bilden
+* **Zeichenketten (Strings)**
+  * `B` [Hamming-Abstand (Hamming Distance)](src/algorithms/string/hamming-distance) - Anzahl der Positionen, an denen die Symbole unterschiedlich sind
+  * `A` [Levenshtein-Distanz (Levenshtein Distance)](src/algorithms/string/levenshtein-distance) - Minimaler Editierabstand zwischen zwei Sequenzen
+  * `A` [Knuth-Morris-Pratt-Algorithmus (Knuth–Morris–Pratt Algorithm)](src/algorithms/string/knuth-morris-pratt) (KMP Algorithmus) - Teilstringsuche (Mustervergleich / Pattern Matching)
+  * `A` [Z-Algorithmus (Z Algorithm)](src/algorithms/string/z-algorithm) - Teilstringsuche (Mustervergleich / Pattern Matching)
+  * `A` [Rabin-Karp-Algorithmus (Rabin Karp Algorithm)](src/algorithms/string/rabin-karp) - Teilstringsuche
+  * `A` [Längstes häufiges Teilzeichenfolgenproblem (Longest Common Substring)](src/algorithms/string/longest-common-substring)
+  * `A` [Regulärer Ausdruck (Regular Expression Matching)](src/algorithms/string/regular-expression-matching)
+* **Suchen**
+  * `B` [Lineare Suche (Linear Search)](src/algorithms/search/linear-search)
+  * `B` [Sprungsuche (Jump Search)](src/algorithms/search/jump-search) (oder Blocksuche) - Suche im sortierten Array
+  * `B` [Binäre Suche (Binary Search)](src/algorithms/search/binary-search) - Suche in einem sortierten Array
+  * `B` [Interpolationssuche (Interpolation Search)](src/algorithms/search/interpolation-search) - Suche in gleichmäßig verteilt sortiertem Array
+* **Sortieren**
+  * `B` [Bubblesort (Bubble Sort)](src/algorithms/sorting/bubble-sort)
+  * `B` [Selectionsort (Selection Sort)](src/algorithms/sorting/selection-sort)
+  * `B` [Einfügesortierenmethode (Insertion Sort)](src/algorithms/sorting/insertion-sort)
+  * `B` [Haldensortierung (Heap Sort)](src/algorithms/sorting/heap-sort)
+  * `B` [Mergesort (Merge Sort)](src/algorithms/sorting/merge-sort)
+  * `B` [Quicksort (Quicksort)](src/algorithms/sorting/quick-sort) - in-place und non-in-place Implementierungen
+  * `B` [Shellsort (Shellsort)](src/algorithms/sorting/shell-sort)
+  * `B` [Countingsort (Counting Sort)](src/algorithms/sorting/counting-sort)
+  * `B` [Fachverteilen (Radix Sort)](src/algorithms/sorting/radix-sort)
+* **Verkettete Liste (Linked List)**
+  * `B` [Gerade Traversierung (Straight Traversal)](src/algorithms/linked-list/traversal)
+  * `B` [Umgekehrte Traversierung (Reverse Traversal)](src/algorithms/linked-list/reverse-traversal)
+* **Bäume**
+  * `B` [Tiefensuche (Depth-First Search)](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Breitensuche (Breadth-First Search)](src/algorithms/tree/breadth-first-search) (BFS)
+* **Graphen**
+  * `B` [Tiefensuche (Depth-First Search)](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Breitensuche (Breadth-First Search)](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [Algorithmus von Kruskal (Kruskal’s Algorithm)](src/algorithms/graph/kruskal) - Finden des Spannbaum (Minimum Spanning Tree / MST) für einen gewichteten ungerichteten Graphen
+  * `A` [Dijkstra-Algorithmus (Dijkstra Algorithm)](src/algorithms/graph/dijkstra) - Finden der kürzesten Wege zu allen Knoten des Graphen von einem einzelnen Knotenpunkt aus
+  * `A` [Bellman-Ford-Algorithmus (Bellman-Ford Algorithm)](src/algorithms/graph/bellman-ford) - Finden der kürzesten Wege zu allen Knoten des Graphen von einem einzelnen Knotenpunkt aus
+  * `A` [Algorithmus von Floyd und Warshall (Floyd-Warshall Algorithm)](src/algorithms/graph/floyd-warshall) - Die kürzesten Wege zwischen allen Knotenpaaren finden
+  * `A` [Zykluserkennung (Detect Cycle)](src/algorithms/graph/detect-cycle) - Sowohl für gerichtete als auch für ungerichtete Graphen (DFS- und Disjoint-Set-basierte Versionen)
+  * `A` [Algorithmus von Prim (Prim’s Algorithm)](src/algorithms/graph/prim) - Finden des Spannbaums (Minimum Spanning Tree / MST) für einen gewichteten ungerichteten Graphen
+  * `A` [Topologische Sortierung (Topological Sorting)](src/algorithms/graph/topological-sorting) - DFS-Verfahren
+  * `A` [Artikulationspunkte (Articulation Points)](src/algorithms/graph/articulation-points) - Algorithmus von Tarjan (Tarjan's algorithm) (DFS basiert)
+  * `A` [Brücke (Bridges)](src/algorithms/graph/bridges) - DFS-basierter Algorithmus
+  * `A` [Eulerkreisproblem (Eulerian Path and Eulerian Circuit)](src/algorithms/graph/eulerian-path) - Algorithmus von Fleury (Fleury's algorithm) - Jede Kante genau einmal durchlaufen.
+  * `A` [Hamiltonkreisproblem (Hamiltonian Cycle)](src/algorithms/graph/hamiltonian-cycle) - Jeden Eckpunkt genau einmal durchlaufen.
+  * `A` [Starke Zusammenhangskomponente (Strongly Connected Components)](src/algorithms/graph/strongly-connected-components) - Kosarajus Algorithmus
+  * `A` [Problem des Handlungsreisenden (Travelling Salesman Problem)](src/algorithms/graph/travelling-salesman) - Kürzestmögliche Route, die jede Stadt besucht und zur Ausgangsstadt zurückkehrt
+* **Kryptographie**
+  * `B` [Polynomiale Streuwertfunktion(Polynomial Hash)](src/algorithms/cryptography/polynomial-hash) - Rollierende Streuwert-Funktion basierend auf Polynom
+  * `B` [Schienenzaun Chiffre (Rail Fence Cipher)](src/algorithms/cryptography/rail-fence-cipher) - Ein Transpositionsalgorithmus zur Verschlüsselung von Nachrichten
+  * `B` [Caesar-Verschlüsselung (Caesar Cipher)](src/algorithms/cryptography/caesar-cipher) - Einfache Substitutions-Chiffre
+  * `B` [Hill-Chiffre (Hill Cipher)](src/algorithms/cryptography/hill-cipher) - Substitutionschiffre basierend auf linearer Algebra
+* **Maschinelles Lernen**
+  * `B` [Künstliches Neuron (NanoNeuron)](https://github.com/trekhleb/nano-neuron) - 7 einfache JS-Funktionen, die veranschaulichen, wie Maschinen tatsächlich lernen können (Vorwärts-/Rückwärtspropagation)
+  * `B` [Nächste-Nachbarn-Klassifikation (k-NN)](src/algorithms/ml/knn) - k-nächste-Nachbarn-Algorithmus
+  * `B` [k-Means (k-Means)](src/algorithms/ml/k-means) - k-Means-Algorithmus
+* **Image Processing**
+  * `B` [Inhaltsabhängige Bildverzerrung (Seam Carving)](src/algorithms/image-processing/seam-carving) - Algorithmus zur inhaltsabhängigen Bildgrößenänderung
+* **Unkategorisiert**
+  * `B` [Türme von Hanoi (Tower of Hanoi)](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Rotationsmatrix (Square Matrix Rotation)](src/algorithms/uncategorized/square-matrix-rotation) - In-Place-Algorithmus
+  * `B` [Jump Game (Jump Game)](src/algorithms/uncategorized/jump-game) - Backtracking, dynamische Programmierung (Top-down + Bottom-up) und gierige Beispiele
+  * `B` [Eindeutige Pfade (Unique Paths)](src/algorithms/uncategorized/unique-paths) - Backtracking, dynamische Programmierung und Pascalsches Dreieck basierte Beispiele
+  * `B` [Regenterrassen (Rain Terraces)](src/algorithms/uncategorized/rain-terraces) - Auffangproblem für Regenwasser (trapping rain water problem) (dynamische Programmierung und Brute-Force-Versionen)
+  * `B` [Rekursive Treppe (Recursive Staircase)](src/algorithms/uncategorized/recursive-staircase) - Zählen der Anzahl der Wege, die nach oben führen (4 Lösungen)
+  * `B` [Beste Zeit zum Kaufen/Verkaufen von Aktien (Best Time To Buy Sell Stocks)](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - Beispiele für "Teile und Herrsche" und Beispiele für den One-Pass-Algorithmus
+  * `A` [Damenproblem (N-Queens Problem)](src/algorithms/uncategorized/n-queens)
+  * `A` [Springerproblem (Knight's Tour)](src/algorithms/uncategorized/knight-tour)
+
+### Algorithmen nach Paradigma
+
+Ein algorithmisches Paradigma ist eine generische Methode oder ein Ansatz, der dem Entwurf einer Klasse von Algorithmen zugrunde liegt. Es ist eine Abstraktion, die höher ist als der Begriff des Algorithmus. Genauso wie ein Algorithmus eine Abstraktion ist, die höher ist als ein Computerprogramm.
+
+* **Brachiale Gewalt (Brute Force)** - schaut sich alle Möglichkeiten an und wählt die beste Lösung aus
+  * `B` [Lineare Suche (Linear Search)](src/algorithms/search/linear-search)
+  * `B` [Regenterrassen (Rain Terraces)](src/algorithms/uncategorized/rain-terraces) - Auffangproblem für Regenwasser (trapping rain water problem) (dynamische Programmierung und Brute-Force-Versionen)
+  * `B` [Rekursive Treppe (Recursive Staircase)](src/algorithms/uncategorized/recursive-staircase) - Zählen der Anzahl der Wege, die nach oben führen (4 Lösungen)
+  * `A` [Das Maximum-Subarray Problem (Maximum Subarray)](src/algorithms/sets/maximum-subarray)
+  * `A` [Problem des Handlungsreisenden (Travelling Salesman Problem)](src/algorithms/graph/travelling-salesman) - Kürzestmögliche Route, die jede Stadt besucht und zur Ausgangsstadt zurückkehrt
+  * `A` [Diskrete Fourier-Transformation (Discrete Fourier Transform)](src/algorithms/math/fourier-transform) - Eine Funktion der Zeit (ein Signal) in die Frequenzen zerlegen, aus denen sie sich zusammensetzt
+* **Gierig (Greedy)** - Wählt die beste Option zum aktuellen Zeitpunkt, ohne Rücksicht auf die Zukunft
+  * `B` [Jump Game (Jump Game)](src/algorithms/uncategorized/jump-game)
+  * `A` [Rucksackproblem (Unbound Knapsack Problem)](src/algorithms/sets/knapsack-problem)
+  * `A` [Dijkstra-Algorithmus (Dijkstra Algorithm)](src/algorithms/graph/dijkstra) - Finden der kürzesten Wege zu allen Knoten des Graphen von einem einzelnen Knotenpunkt aus
+  * `A` [Algorithmus von Prim (Prim’s Algorithm)](src/algorithms/graph/prim) - Finden des Spannbaums (Minimum Spanning Tree / MST) für einen gewichteten ungerichteten Graphen
+  * `B` [Algorithmus von Kruskal (Kruskal’s Algorithm)](src/algorithms/graph/kruskal) - Finden des Spannbaum (Minimum Spanning Tree / MST) für einen gewichteten ungerichteten Graphen
+* **Teile und herrsche** - Das Problem in kleinere Teile aufteilen und diese Teile dann lösen
+  * `B` [Binäre Suche (Binary Search)](src/algorithms/search/binary-search)
+  * `B` [Türme von Hanoi (Tower of Hanoi)](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Pascalsches Dreieck (Pascal's Triangle)](src/algorithms/math/pascal-triangle)
+  * `B` [Euklidischer Algorithmus (Euclidean Algorithm)](src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
+  * `B` [Mergesort (Merge Sort)](src/algorithms/sorting/merge-sort)
+  * `B` [Quicksort (Quicksort)](src/algorithms/sorting/quick-sort)
+  * `B` [Tiefensuche (Depth-First Search)](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Breitensuche (Breadth-First Search)](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Matrizen (Matrices)](src/algorithms/math/matrix) - Matrizen und grundlegende Matrixoperationen (Multiplikation, Transposition usw.)
+  * `B` [Jump Game (Jump Game)](src/algorithms/uncategorized/jump-game)
+  * `B` [Fast Powering Algorithmus (Fast Powering)](src/algorithms/math/fast-powering)
+  * `B` [Beste Zeit zum Kaufen/Verkaufen von Aktien (Best Time To Buy Sell Stocks)](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - Beispiele für "Teile und Herrsche" und Beispiele für den One-Pass-Algorithmus
+  * `A` [Permutation (Permutations)](src/algorithms/sets/permutations) (mit und ohne Wiederholungen)
+  * `A` [Kombination (Combinations)](src/algorithms/sets/combinations) (mit und ohne Wiederholungen)
+* **Dynamische Programmierung** - Eine Lösung aus zuvor gefundenen Teillösungen aufbauen
+  * `B` [Fibonacci-Zahl (Fibonacci Number)](src/algorithms/math/fibonacci)
+  * `B` [Jump Game (Jump Game)](src/algorithms/uncategorized/jump-game)
+  * `B` [Eindeutige Pfade (Unique Paths)](src/algorithms/uncategorized/unique-paths)
+  * `B` [Regenterrassen (Rain Terraces)](src/algorithms/uncategorized/rain-terraces) - Auffangproblem für Regenwasser (trapping rain water problem) (dynamische Programmierung und Brute-Force-Versionen)
+  * `B` [Rekursive Treppe (Recursive Staircase)](src/algorithms/uncategorized/recursive-staircase) - Zählen der Anzahl der Wege, die nach oben führen (4 Lösungen)
+  * `B` [Inhaltsabhängige Bildverzerrung (Seam Carving)](src/algorithms/image-processing/seam-carving) - Algorithmus zur inhaltsabhängigen Bildgrößenänderung
+  * `A` [Levenshtein-Distanz (Levenshtein Distance)](src/algorithms/string/levenshtein-distance) - Minimaler Editierabstand zwischen zwei Sequenzen
+  * `A` [Problem der längsten gemeinsamen Teilsequenz (Longest Common Subsequence)](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Längstes häufiges Teilzeichenfolgenproblem (Longest Common Substring)](src/algorithms/string/longest-common-substring)
+  * `A` [Längste gemeinsame Teilsequenz (Longest Increasing Subsequence)](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Der kürzeste gemeinsame String (Shortest Common Supersequence)](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Rucksackproblem (0/1 Knapsack Problem)](src/algorithms/sets/knapsack-problem)
+  * `A` [Ganzzahlige Partitionierung (Integer Partition)](src/algorithms/math/integer-partition)
+  * `A` [Das Maximum-Subarray Problem (Maximum Subarray)](src/algorithms/sets/maximum-subarray)
+  * `A` [Bellman-Ford-Algorithmus (Bellman-Ford Algorithm)](src/algorithms/graph/bellman-ford) - Finden der kürzesten Wege zu allen Knoten des Graphen von einem einzelnen Knotenpunkt aus
+  * `A` [Algorithmus von Floyd und Warshall (Floyd-Warshall Algorithm)](src/algorithms/graph/floyd-warshall) - Die kürzesten Wege zwischen allen Knotenpaaren finden
+  * `A` [Regulärer Ausdruck (Regular Expression Matching)](src/algorithms/string/regular-expression-matching)
+* **Zurückverfolgung** - Ähnlich wie bei Brute-Force versuchen Sie, alle möglichen Lösungen zu generieren, aber jedes Mal, wenn Sie die nächste Lösung generieren, testen Sie, ob sie alle Bedingungen erfüllt, und fahren erst dann mit der Generierung weiterer Lösungen fort. Andernfalls gehen Sie zurück und nehmen einen anderen Weg, um eine Lösung zu finden. Normalerweise wird das DFS-Traversal des Zustandsraums verwendet.
+  * `B` [Jump Game (Jump Game)](src/algorithms/uncategorized/jump-game)
+  * `B` [Eindeutige Pfade (Unique Paths)](src/algorithms/uncategorized/unique-paths)
+  * `A` [Potenzmenge (Power Set)](src/algorithms/sets/power-set) - Alle Teilmengen einer Menge
+  * `A` [Hamiltonkreisproblem (Hamiltonian Cycle)](src/algorithms/graph/hamiltonian-cycle) - Jeden Eckpunkt genau einmal durchlaufen.
+  * `A` [Damenproblem (N-Queens Problem)](src/algorithms/uncategorized/n-queens)
+  * `A` [Springerproblem (Knight's Tour)](src/algorithms/uncategorized/knight-tour)
+  * `A` [Kombinationssumme (Combination Sum)](src/algorithms/sets/combination-sum) - Alle Kombinationen finden, die eine bestimmte Summe bilden
+* **Verzweigung & Bindung** - Merkt sich die Lösung mit den niedrigsten Kosten, die in jeder Phase der Backtracking-Suche gefunden wurde, und verwendet die Kosten der bisher gefundenen Lösung mit den niedrigsten Kosten als untere Schranke für die Kosten einer Lösung des Problems mit den geringsten Kosten, um Teillösungen zu verwerfen, deren Kosten größer sind als die der bisher gefundenen Lösung mit den niedrigsten Kosten. Normalerweise wird das BFS-Traversal in Kombination mit dem DFS-Traversal des Zustandsraumbaums verwendet.
+
+## So verwendest du dieses Repository
+
+**Alle Abhängigkeiten installieren**
+
+```
+npm install
+```
+
+**ESLint ausführen**
+
+You may want to run it to check code quality.
+
+```
+npm run lint
+```
+
+**Alle Tests ausführen**
+
+```
+npm test
+```
+
+**Tests nach Namen ausführen**
+
+```
+npm test -- 'LinkedList'
+```
+
+**Fehlerbehebung**
+
+Falls das Linting oder Testen fehlschlägt, versuche, den Ordner "node_modules" zu löschen und die npm-Pakete neu zu installieren:
+
+```
+rm -rf ./node_modules
+npm i
+```
+
+**Spielwiese**
+
+Du kannst mit Datenstrukturen und Algorithmen in der Datei `./src/playground/playground.js` herumspielen und
+dir in dieser Datei Tests schreiben `./src/playground/__test__/playground.test.js`.
+
+Dann führe einfach folgenden Befehl aus, um zu testen, ob dein Spielwiesencode wie erwartet funktioniert:
+
+```
+npm test -- 'playground'
+```
+
+## Nützliche Informationen
+
+### Referenzen
+
+[▶ Datenstrukturen und Algorithmen auf YouTube(Englisch)](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### O-Notation (_Big O Notation_)
+
+Die O-Notation wird verwendet, um Algorithmen danach zu klassifizieren, wie ihre Laufzeit oder ihr Platzbedarf mit zunehmender Eingabegröße wächst. In der folgenden Tabelle finden Sie die häufigsten Wachstumsordnungen von Algorithmen, die in Big-O-Notation angegeben sind.
+
+![O-Notation Graphen](./assets/big-o-graph.png)
+
+Quelle: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+Nachfolgend finden Sie eine Liste einiger der am häufigsten verwendeten Big O-Notationen und deren Leistungsvergleiche für unterschiedliche Größen der Eingabedaten.
+
+| Big O Notation | Berechnungen für 10 Elemente | Berechnungen für 100 Elemente | Berechnungen für 1000 Elemente |
+| -------------- | ---------------------------- | ----------------------------- | ------------------------------ |
+| **O(1)**       | 1                            | 1                             | 1                              |
+| **O(log N)**   | 3                            | 6                             | 9                              |
+| **O(N)**       | 10                           | 100                           | 1000                           |
+| **O(N log N)** | 30                           | 600                           | 9000                           |
+| **O(N^2)**     | 100                          | 10000                         | 1000000                        |
+| **O(2^N)**     | 1024                         | 1.26e+29                      | 1.07e+301                      |
+| **O(N!)**      | 3628800                      | 9.3e+157                      | 4.02e+2567                     |
+
+### Komplexität von Datenstrukturoperationen
+
+| Datenstruktur          | Zugriff auf | Suche  | Einfügen | Löschung | Kommentare                                                      |
+| ---------------------- | :---------: | :----: | :------: | :------: | :-------------------------------------------------------------- |
+| **Array**              |      1      |   n    |    n     |    n     |                                                                 |
+| **Stack**              |      n      |   n    |    1     |    1     |                                                                 |
+| **Queue**              |      n      |   n    |    1     |    1     |                                                                 |
+| **Linked List**        |      n      |   n    |    1     |    n     |                                                                 |
+| **Hash Table**         |      -      |   n    |    n     |    n     | Im Falle einer perfekten Hash-Funktion wären die Kosten O(1)    |
+| **Binary Search Tree** |      n      |   n    |    n     |    n     | Im Falle eines ausgeglichenen Baumes wären die Kosten O(log(n)) |
+| **B-Tree**             |   log(n)    | log(n) |  log(n)  |  log(n)  |                                                                 |
+| **Red-Black Tree**     |   log(n)    | log(n) |  log(n)  |  log(n)  |                                                                 |
+| **AVL Tree**           |   log(n)    | log(n) |  log(n)  |  log(n)  |                                                                 |
+| **Bloom Filter**       |      -      |   1    |    1     |    -     | Falschpostive sind bei der Suche möglichen                      |
+
+### Komplexität von Array-Sortieralgorithmen
+
+| Name               |    Bester     |      Durchschnitt       |        Schlechtester        | Speicher | Stabil | Kommentar                                                                  |
+| ------------------ | :-----------: | :---------------------: | :-------------------------: | :------: | :----: | :------------------------------------------------------------------------- |
+| **Bubble sort**    |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |    1     |   JA   |                                                                            |
+| **Insertion sort** |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |    1     |   Ja   |                                                                            |
+| **Selection sort** | n<sup>2</sup> |      n<sup>2</sup>      |        n<sup>2</sup>        |    1     |  Nein  |                                                                            |
+| **Heap sort**      | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |    1     |  Nein  |                                                                            |
+| **Merge sort**     | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |    n     |   Ja   |                                                                            |
+| **Quick sort**     | n&nbsp;log(n) |      n&nbsp;log(n)      |        n<sup>2</sup>        |  log(n)  |  Nein  | Quicksort wird normalerweise in-place mit O(log(n)) Stapelplatz ausgeführt |
+| **Shell sort**     | n&nbsp;log(n) | abhängig von Spaltfolge | n&nbsp;(log(n))<sup>2</sup> |    1     |  Nein  |                                                                            |
+| **Counting sort**  |     n + r     |          n + r          |            n + r            |  n + r   |   Ja   | r - größte Zahl im Array                                                   |
+| **Radix sort**     |    n \* k     |         n \* k          |           n \* k            |  n + k   |   Ja   | k - Länge des längsten Schlüssels                                          |
+
+## Projekt-Unterstützer
+
+> Du kannst dieses Projekt unterstützen über ❤️️ [GitHub](https://github.com/sponsors/trekhleb) or ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+[Leute, die dieses Projekt unterstützen](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 0`
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
+
diff --git a/README.es-ES.md b/README.es-ES.md
index 2f2389e5ac..3a15ada4bd 100644
--- a/README.es-ES.md
+++ b/README.es-ES.md
@@ -1,12 +1,12 @@
-# Algoritmos JavaScript y Estructuras de Datos
+# Algoritmos y Estructuras de Datos en JavaScript
 
-[![Build Status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
 
 Este repositorio contiene ejemplos basados en JavaScript de muchos
-algoritmos populares y estructuras de datos.
+algoritmos y estructuras de datos populares.
 
-Cada algoritmo y estructura de datos tiene su propio LÉAME con explicaciones relacionadas y 
+Cada algoritmo y estructura de datos tiene su propio LÉAME con explicaciones relacionadas y
 enlaces para lecturas adicionales (incluyendo algunas a vídeos de YouTube).
 
 _Léelo en otros idiomas:_
@@ -14,181 +14,201 @@ _Léelo en otros idiomas:_
 [_简体中文_](README.zh-CN.md),
 [_繁體中文_](README.zh-TW.md),
 [_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
 [_Polski_](README.pl-PL.md),
 [_Français_](README.fr-FR.md),
-[_Português_](README.pt-BR.md)
-
-> Estamos escribiendo un libro que explicará claramente, en detalle, los principales algoritmos.
-Si desea ser notificado cuando se publique el libro "Algoritmos JavaScript
-lanzamientos, [haga clic aquí](https://upscri.be/402324/).
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+*☝ Nótese que este proyecto está pensado con fines de aprendizaje e investigación,
+y **no** para ser usado en producción.*
 
 ## Estructuras de Datos
 
-Una estructura de datos es una forma particular de organizar y almacenar datos en un ordenador para que pueda
-y modificarse de forma eficiente. Más concretamente, una estructura de datos es un conjunto de datos
-los valores, las relaciones entre ellos y las funciones u operaciones que se pueden aplicar a
+Una estructura de datos es una forma particular de organizar y almacenar datos en un ordenador para que puedan accederse
+y modificarse de forma eficiente. Más concretamente, una estructura de datos es un conjunto de valores
+de datos, las relaciones entre ellos y las funciones u operaciones que se pueden aplicar a
 los datos.
 
 `P` - Principiante, `A` - Avanzado
 
-* `P` [Lista Enlazada](src/data-structures/linked-list)
-* `P` [Lista Doblemente Enlazada](src/data-structures/doubly-linked-list)
+* `P` [Lista enlazada](src/data-structures/linked-list)
+* `P` [Lista doblemente enlazada](src/data-structures/doubly-linked-list)
 * `P` [Cola](src/data-structures/queue)
-* `P` [Pila - Stack](src/data-structures/stack)
-* `P` [Hash Table](src/data-structures/hash-table)
-* `P` [Pila - Heap](src/data-structures/heap)
-* `P` [Cola de Prioridad](src/data-structures/priority-queue)
+* `P` [Pila](src/data-structures/stack)
+* `P` [Tabla hash](src/data-structures/hash-table)
+* `P` [Heap](src/data-structures/heap) - versiones máx y mín
+* `P` [Cola de prioridad](src/data-structures/priority-queue)
 * `A` [Trie](src/data-structures/trie)
-* `A` [Arbol](src/data-structures/tree)
-  * `A` [Arbol de busqueda binaria](src/data-structures/tree/binary-search-tree)
-  * `A` [Arbol AVL ](src/data-structures/tree/avl-tree)
+* `A` [Árbol](src/data-structures/tree)
+  * `A` [Árbol de búsqueda binaria](src/data-structures/tree/binary-search-tree)
+  * `A` [Árbol AVL](src/data-structures/tree/avl-tree)
   * `A` [Árbol Rojo-Negro](src/data-structures/tree/red-black-tree)
   * `A` [Árbol de segmentos](src/data-structures/tree/segment-tree) - con ejemplos de consultas de rango mín/máx/suma
-  * `A` [Arbol de Fenwick ](src/data-structures/tree/fenwick-tree) (Árbol binario indexado)
-* `A` [Grafo](src/data-structures/graph) (con dirección y sin dirección)
-* `A` [Conjunto disjunto](src/data-structures/disjoint-set)
-* `A` [Filtro Bloom](src/data-structures/bloom-filter)
+  * `A` [Árbol de Fenwick](src/data-structures/tree/fenwick-tree) (Árbol binario indexado)
+* `A` [Grafo](src/data-structures/graph) (dirigido y no dirigido)
+* `A` [Conjuntos disjuntos](src/data-structures/disjoint-set)
+* `A` [Filtro de Bloom](src/data-structures/bloom-filter)
 
 ## Algoritmos
 
-Un algoritmo es una especificación inequívoca de cómo resolver una clase de problemas. Es un conjunto de reglas que 
+Un algoritmo es una especificación inequívoca de cómo resolver una clase de problemas. Es un conjunto de reglas que
 definen con precisión una secuencia de operaciones.
 
 `P` - Principiante, `A` - Avanzado
 
 ### Algoritmos por Tema
 
-* **Matematica**
-  * `P` [Manipulacion de bits](src/algorithms/math/bits) - setear/obtener/actualizar/limpiar bits, multiplication/division por dos, hacer negativo, etc.
+* **Matemáticas**
+  * `P` [Manipulación de bits](src/algorithms/math/bits) - asignar/obtener/actualizar/limpiar bits, multiplicación/división por dos, hacer negativo, etc.
   * `P` [Factorial](src/algorithms/math/factorial)
-  * `P` [Numero de Fibonacci](src/algorithms/math/fibonacci)
-  * `P` [Prueba de Primacia](src/algorithms/math/primality-test) (metodo de division de prueba)
-  * `P` [Algoritmo Euclideo](src/algorithms/math/euclidean-algorithm) - Calcular el maximo comun divisor (MCD)
-  * `P` [Minimo comun multiplo](src/algorithms/math/least-common-multiple) (MCM)
-  * `P` [Tamiz de Eratosthenes](src/algorithms/math/sieve-of-eratosthenes) - encontrar todos los números primos hasta cualquier límite dado
-  * `P` [Es la potencia de dos](src/algorithms/math/is-power-of-two) - comprueba si el número es la potencia de dos (algoritmos ingenuos y de bits)
-  * `P` [Triangulo de Pascal](src/algorithms/math/pascal-triangle)
-  * `A` [Particion Entera](src/algorithms/math/integer-partition)
-  * `A` [Algortimo π de Liu Hui ](src/algorithms/math/liu-hui) - aproximado π cálculos basados en N-gons
+  * `P` [Sucesión de Fibonacci](src/algorithms/math/fibonacci)
+  * `P` [Prueba de primalidad](src/algorithms/math/primality-test) (método de división de prueba)
+  * `P` [Algoritmo de Euclides](src/algorithms/math/euclidean-algorithm) - calcular el Máximo común divisor (MCD)
+  * `P` [Mínimo común múltiplo](src/algorithms/math/least-common-multiple) (MCM)
+  * `P` [Criba de Eratóstenes](src/algorithms/math/sieve-of-eratosthenes) - encontrar todos los números primos hasta un límite dado
+  * `P` [Es una potencia de dos?](src/algorithms/math/is-power-of-two) - comprobar si el número es una potencia de dos (algoritmos ingenuos y de bits)
+  * `P` [Triángulo de Pascal](src/algorithms/math/pascal-triangle)
+  * `P` [Números complejos](src/algorithms/math/complex-number) - números complejos y operaciones con ellos
+  * `P` [Radianes & Grados](src/algorithms/math/radian) - conversión de radianes a grados y viceversa
+  * `P` [Exponenciación rápida](src/algorithms/math/fast-powering)
+  * `A` [Partición entera](src/algorithms/math/integer-partition)
+  * `A` [Algoritmo π de Liu Hui](src/algorithms/math/liu-hui) - aproximar el cálculo de  π basado en polígonos de N lados
+  * `A` [Transformada discreta de Fourier](src/algorithms/math/fourier-transform) - descomponer una función de tiempo (señal) en las frecuencias que la componen
 * **Conjuntos**
-  * `P` [Producto Cartesiano](src/algorithms/sets/cartesian-product) - pproducto de múltiples conjuntos
+  * `P` [Producto cartesiano](src/algorithms/sets/cartesian-product) - producto de múltiples conjuntos
   * `P` [Permutación de Fisher–Yates](src/algorithms/sets/fisher-yates) - permutación aleatoria de una secuencia finita
-  * `A` [Conjunto Potencia](src/algorithms/sets/power-set) - todos los subconjuntos de un conjunto
+  * `A` [Conjunto potencia](src/algorithms/sets/power-set) - todos los subconjuntos de un conjunto
   * `A` [Permutaciones](src/algorithms/sets/permutations) (con y sin repeticiones)
   * `A` [Combinaciones](src/algorithms/sets/combinations) (con y sin repeticiones)
-  * `A` [Subsecuencia Común más Larga](src/algorithms/sets/longest-common-subsequence) (SCL)
-  * `A` [Subsecuencia Creciente más Larga](src/algorithms/sets/longest-increasing-subsequence)
-  * `A` [Supersequencia Común Más Corta](src/algorithms/sets/shortest-common-supersequence) (SCS)
-  * `A` [Problema de la Mochila](src/algorithms/sets/knapsack-problem) - "0/1" y unos sin consolidar
-  * `A` [Maxima Subarreglo](src/algorithms/sets/maximum-subarray) - versiones de "Fuerza Bruta" y "Programación Dinámica" ( de Kadane's)
-  * `A` [Suma Combinada](src/algorithms/sets/combination-sum) - encuentra todas las combinaciones que forman una suma específica.
-* **Cadenas de Caracteres**
+  * `A` [Subsecuencia común más larga](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Subsecuencia creciente más larga](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Supersecuencia común más corta](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [Problema de la mochila](src/algorithms/sets/knapsack-problem) - "0/1" y "sin límite"
+  * `A` [Máximo subarreglo](src/algorithms/sets/maximum-subarray) - versiones de "fuerza bruta" y "programación dinámica" (de Kadane)
+  * `A` [Suma combinada](src/algorithms/sets/combination-sum) - encuentra todas las combinaciones que forman una suma específica
+* **Cadenas de caracteres**
   * `P` [Distancia de Hamming](src/algorithms/string/hamming-distance) - número de posiciones en las que los símbolos son diferentes
   * `A` [Distancia de Levenshtein](src/algorithms/string/levenshtein-distance) - distancia mínima de edición entre dos secuencias
-  * `A` [Algoritmo Knuth-Morris-Prattm](src/algorithms/string/knuth-morris-pratt) (Algoritmo KMP) - búsqueda por subcadenas (coincidencia de patrones)
-  * `A` [Algoritmo Z](src/algorithms/string/z-algorithm) - úsqueda de subcadenas (coincidencia de patrones)
-  * `A` [Algoritmo de Rabin Karp ](src/algorithms/string/rabin-karp) - búsqueda por subcadenas
-  * `A` [Subcadena Común Más Larga](src/algorithms/string/longest-common-substring)
-  * `A` [Coincidencia por Expresiones Regulares](src/algorithms/string/regular-expression-matching)
-* **Busquedas**
-  * `P` [Búsqueda Lineal](src/algorithms/search/linear-search)
-  * `P` [Búsqueda de Salto](src/algorithms/search/jump-search) (o Búsqueda de Bloque) - búsqueda en una lista ordenada
+  * `A` [Algoritmo Knuth-Morris-Pratt](src/algorithms/string/knuth-morris-pratt) (Algoritmo KMP) - búsqueda de subcadenas (coincidencia de patrones)
+  * `A` [Algoritmo Z](src/algorithms/string/z-algorithm) - búsqueda de subcadenas (coincidencia de patrones)
+  * `A` [Algoritmo de Rabin Karp](src/algorithms/string/rabin-karp) - búsqueda de subcadenas
+  * `A` [Subcadena común más larga](src/algorithms/string/longest-common-substring)
+  * `A` [Coincidencia por expresiones regulares](src/algorithms/string/regular-expression-matching)
+* **Búsquedas**
+  * `P` [Búsqueda lineal](src/algorithms/search/linear-search)
+  * `P` [Búsqueda de salto](src/algorithms/search/jump-search) (o Búsqueda de bloque) - búsqueda en una lista ordenada
   * `P` [Búsqueda binaria](src/algorithms/search/binary-search) - búsqueda en una lista ordenada
-  * `P` [ Búsqueda por Interpolación](src/algorithms/search/interpolation-search) - búsqueda en una lista ordenada yd uniformemente distribuida
+  * `P` [Búsqueda por interpolación](src/algorithms/search/interpolation-search) - búsqueda en una lista ordenada uniformemente distribuida
 * **Ordenamiento**
-  * `P` [Ordenamiento de Burbuja](src/algorithms/sorting/bubble-sort)
-  * `P` [Ordenamiento por Selección](src/algorithms/sorting/selection-sort)
-  * `P` [Ordenamiento por Inserción](src/algorithms/sorting/insertion-sort)
-  * `P` [ Ordenamiento en Pilas](src/algorithms/sorting/heap-sort)
-  * `P` [Ordenamiento por Fusion](src/algorithms/sorting/merge-sort)
+  * `P` [Ordenamiento de burbuja](src/algorithms/sorting/bubble-sort)
+  * `P` [Ordenamiento por selección](src/algorithms/sorting/selection-sort)
+  * `P` [Ordenamiento por inserción](src/algorithms/sorting/insertion-sort)
+  * `P` [Ordenamiento por Heap](src/algorithms/sorting/heap-sort)
+  * `P` [Ordenamiento por mezcla](src/algorithms/sorting/merge-sort)
   * `P` [Quicksort](src/algorithms/sorting/quick-sort) - implementaciones in situ y no in situ
   * `P` [Shellsort](src/algorithms/sorting/shell-sort)
-  * `P` [Clasificación de Recuento](src/algorithms/sorting/counting-sort)
+  * `P` [Ordenamiento por cuentas](src/algorithms/sorting/counting-sort)
   * `P` [Ordenamiento Radix](src/algorithms/sorting/radix-sort)
-* **Arboles**
+* **Listas enlazadas**
+  * `P` [Recorrido directo](src/algorithms/linked-list/traversal)
+  * `P` [Recorrido inverso](src/algorithms/linked-list/reverse-traversal)
+* **Árboles**
   * `P` [Búsqueda en profundidad](src/algorithms/tree/depth-first-search) (DFS)
   * `P` [Búsqueda en anchura](src/algorithms/tree/breadth-first-search) (BFS)
 * **Grafos**
   * `P` [Búsqueda en profundidad](src/algorithms/graph/depth-first-search) (DFS)
   * `P` [Búsqueda en anchura](src/algorithms/graph/breadth-first-search) (BFS)
-  * `P` [Algoritmo de Kruskal’s](src/algorithms/graph/kruskal) - encontrar el árbol de expansión mínima (MST) para el grafo no dirigido ponderado
-  * `A` [Algoritmo de Dijkstra](src/algorithms/graph/dijkstra) - encontrar las trayectorias más cortas a todos los vértices del grafo desde un solo vértice
-  * `A` [Algoritmo de Bellman-Ford](src/algorithms/graph/bellman-ford) - encontrar las trayectorias más cortas a todos los vértices del gráfico desde un solo vértice
-  * `A` [Algortimo de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - encuentra los caminos más cortos entre todos los pares de vértices
-  * `A` [Ciclo de detección](src/algorithms/graph/detect-cycle) - para gráficos dirigidos y no dirigidos (versiones basadas en DFS y Conjuntos Disjuntos)
-  * `A` [Algoritmo de Prim](src/algorithms/graph/prim) - encontrar el árbol de expansión mínima (MST) para una grafo no dirigido ponderada
-  * `A` [Clasificación topológica](src/algorithms/graph/topological-sorting) - método DFS
-  * `A` [Puntos de Articulación](src/algorithms/graph/articulation-points) - Algoritmo de Tarjan (basado en DFS)
-  * `A` [Puentes](src/algorithms/graph/bridges) - Algoritmo basado en DFS
-  * `A` [Senda Euleriana y un Circuito Euleriano](src/algorithms/graph/eulerian-path) - algoritmo de Fleury - Visite cada borde exactamente una vez
-  * `A` [Ciclo Hamiltoniano](src/algorithms/graph/hamiltonian-cycle) - visite cada vértice exactamente una vez
-  * `A` [Componentes Fuertemente Conectados](src/algorithms/graph/strongly-connected-components) - Algoritmo de Kosaraju
-  * `A` [Problema del Vendedor Itinerante](src/algorithms/graph/travelling-salesman) - la ruta más corta posible que visita cada ciudad y vuelve a la ciudad de origen
-* **Criptografia**
-  * `P` [Hash Polinomial](src/algorithms/cryptography/polynomial-hash) - función de hash rodante basada en polinomio
-* **Sin Categoria**
-  * `P` [Torre de Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `P` [Algoritmo de Kruskal](src/algorithms/graph/kruskal) - encontrar el árbol de cubrimiento mínimo (MST) para un grafo no dirigido ponderado
+  * `A` [Algoritmo de Dijkstra](src/algorithms/graph/dijkstra) - encontrar los caminos más cortos a todos los vértices del grafo desde un solo vértice
+  * `A` [Algoritmo de Bellman-Ford](src/algorithms/graph/bellman-ford) - encontrar los caminos más cortos a todos los vértices del grafo desde un solo vértice
+  * `A` [Algoritmo de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - encontrar los caminos más cortos entre todos los pares de vértices
+  * `A` [Detectar ciclos](src/algorithms/graph/detect-cycle) - para grafos dirigidos y no dirigidos (versiones basadas en DFS y conjuntos disjuntos)
+  * `A` [Algoritmo de Prim](src/algorithms/graph/prim) - encontrar el árbol de cubrimiento mínimo (MST) para un grafo no dirigido ponderado
+  * `A` [Ordenamiento topológico](src/algorithms/graph/topological-sorting) - método DFS
+  * `A` [Puntos de articulación](src/algorithms/graph/articulation-points) - algoritmo de Tarjan (basado en DFS)
+  * `A` [Puentes](src/algorithms/graph/bridges) - algoritmo basado en DFS
+  * `A` [Camino euleriano y circuito euleriano](src/algorithms/graph/eulerian-path) - algoritmo de Fleury - visitar cada arista exactamente una vez
+  * `A` [Ciclo hamiltoniano](src/algorithms/graph/hamiltonian-cycle) - visitar cada vértice exactamente una vez
+  * `A` [Componentes fuertemente conexos](src/algorithms/graph/strongly-connected-components) - algoritmo de Kosaraju
+  * `A` [Problema del viajante](src/algorithms/graph/travelling-salesman) - la ruta más corta posible que visita cada ciudad y vuelve a la ciudad de origen
+* **Criptografía**
+  * `P` [Hash polinomial](src/algorithms/cryptography/polynomial-hash) - función de hash rodante basada en polinomio
+* **Sin categoría**
+  * `P` [Torre de Hanói](src/algorithms/uncategorized/hanoi-tower)
   * `P` [Rotación de matriz cuadrada](src/algorithms/uncategorized/square-matrix-rotation) - algoritmo in situ
-  * `P` [Juego de los saltos](src/algorithms/uncategorized/jump-game) - retroceso, programación dinámica (de arriba hacia abajo + de abajo hacia arriba) y ejemplos codiciosos
-  * `P` [Caminos Unicos](src/algorithms/uncategorized/unique-paths) -etroceso, programación dinámica y ejemplos basados en el Triángulo de Pascal
-  * `P` [Terrazas Pluviales](src/algorithms/uncategorized/rain-terraces) - problema de atrapamiento de aguas pluviales (programación dinámica y versiones de fuerza bruta)
-  * `A` [Problema de N-Reinas](src/algorithms/uncategorized/n-queens)
-  * `A` [Una gira de Caballeros](src/algorithms/uncategorized/knight-tour)
+  * `P` [Juego de los saltos](src/algorithms/uncategorized/jump-game) - ejemplos de backtracking, programación dinámica (de arriba hacia abajo + de abajo hacia arriba) y voraces
+  * `P` [Caminos únicos](src/algorithms/uncategorized/unique-paths) - ejemplos de backtracking, programación dinámica y basados en el Triángulo de Pascal
+  * `P` [Terrazas pluviales](src/algorithms/uncategorized/rain-terraces) - el problema de la retención del agua de lluvia (programación dinámica y fuerza bruta)
+  * `A` [Problema de las N Reinas](src/algorithms/uncategorized/n-queens)
+  * `A` [Problema del caballo (Knight tour)](src/algorithms/uncategorized/knight-tour)
 
-### Algoritmos por Paradigma
+### Algoritmos por paradigma
 
 Un paradigma algorítmico es un método o enfoque genérico que subyace al diseño de una clase de algoritmos.
 Es una abstracción superior a la noción de algoritmo, del mismo modo que un algoritmo es una abstracción superior a un programa de ordenador.
 
 * **Fuerza Bruta** - mira todas las posibilidades y selecciona la mejor solución
-  * `P` [Busqueda Lienal](src/algorithms/search/linear-search)
-  * `P` [Terrazas Pluviales](src/algorithms/uncategorized/rain-terraces) - el problema de la retención del agua de lluvia
-  * `A` [Subcoleción maxima](src/algorithms/sets/maximum-subarray)
-  * `A` [Problema del Vendedor Itinerante](src/algorithms/graph/travelling-salesman) - la ruta más corta posible que visita cada ciudad y vuelve a la ciudad de origen
-* **Codiciosos** - escoja la mejor opción en el momento actual, sin ninguna consideración para el futuro.
-  * `P` [El juego de los saltos](src/algorithms/uncategorized/jump-game)
-  * `A` [Problema de la Mochila no Consolidada](src/algorithms/sets/knapsack-problem)
-  * `A` [Algoritmo de Dijkstra](src/algorithms/graph/dijkstra) - encontrar la ruta más corta a todos los vértices del gráfico
-  * `A` [Algortimo de Prim](src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
-  * `A` [Algoritmo de Kruskal](src/algorithms/graph/kruskal) - encontrar el árbol de expansión mínima (MST) para una gráfica no dirigida ponderada
-* **Divide y Venceras** - divide el problema en partes más pequeñas y luego resuelve esas partes.
-  * `P` [Búsqueda Binaria](src/algorithms/search/binary-search)
-  * `P` [Torre de Hanoi](src/algorithms/uncategorized/hanoi-tower)
-  * `P` [Triangulo de Pascal](src/algorithms/math/pascal-triangle)
-  * `P` [Algoritmo Euclideo](src/algorithms/math/euclidean-algorithm) - ccalcular el Maximo Comun Divisor (MCD)
-  * `P` [Clasificacion por Fusión](src/algorithms/sorting/merge-sort)
+  * `P` [Búsqueda lineal](src/algorithms/search/linear-search)
+  * `P` [Terrazas pluviales](src/algorithms/uncategorized/rain-terraces) - el problema de la retención del agua de lluvia
+  * `A` [Máximo subarreglo](src/algorithms/sets/maximum-subarray)
+  * `A` [Problema del viajante](src/algorithms/graph/travelling-salesman) - la ruta más corta posible que visita cada ciudad y vuelve a la ciudad de origen
+  * `A` [Transformada discreta de Fourier](src/algorithms/math/fourier-transform) - descomponer una función de tiempo (señal) en las frecuencias que la componen
+* **Voraces** - escoge la mejor opción en el momento actual, sin ninguna consideración sobre el futuro
+  * `P` [Juego de los saltos](src/algorithms/uncategorized/jump-game)
+  * `A` [Problema de la mochila sin límite](src/algorithms/sets/knapsack-problem)
+  * `A` [Algoritmo de Dijkstra](src/algorithms/graph/dijkstra) - encontrar los caminos más cortos a todos los vértices del grafo desde un solo vértice
+  * `A` [Algoritmo de Prim](src/algorithms/graph/prim) - encontrar el árbol de cubrimiento mínimo (MST) para un grafo no dirigido ponderado
+  * `A` [Algoritmo de Kruskal](src/algorithms/graph/kruskal) - encontrar el árbol de cubrimiento mínimo (MST) para un grafo no dirigido ponderado
+* **Divide y Vencerás** - divide el problema en partes más pequeñas y luego resuelve esas partes
+  * `P` [Búsqueda binaria](src/algorithms/search/binary-search)
+  * `P` [Torre de Hanói](src/algorithms/uncategorized/hanoi-tower)
+  * `P` [Triángulo de Pascal](src/algorithms/math/pascal-triangle)
+  * `P` [Algoritmo de Euclides](src/algorithms/math/euclidean-algorithm) - calcular el Máximo Común Divisor (MCD)
+  * `P` [Ordenamiento por mezcla](src/algorithms/sorting/merge-sort)
   * `P` [Quicksort](src/algorithms/sorting/quick-sort)
-  * `P` [Busqueda por Profundidad](src/algorithms/tree/depth-first-search) - (DFS)
-  * `P` [Busqueda por Anchura](src/algorithms/graph/depth-first-search) - (DFS)
-  * `P` [Juego de los Saltos](src/algorithms/uncategorized/jump-game)
+  * `P` [Búsqueda en profundidad (árboles)](src/algorithms/tree/depth-first-search) - (DFS)
+  * `P` [Búsqueda en profundidad (grafos)](src/algorithms/graph/depth-first-search) - (DFS)
+  * `P` [Juego de los saltos](src/algorithms/uncategorized/jump-game)
+  * `P` [Exponenciación rápida](src/algorithms/math/fast-powering)
   * `A` [Permutaciones](src/algorithms/sets/permutations) - (con y sin repeticiones)
   * `A` [Combinaciones](src/algorithms/sets/combinations) - (con y sin repeticiones)
-* **Programacion Dinamica** - onstruya una solución usando sub-soluciones previamente encontradas
-  * `P` [Numero de Fibonacci](src/algorithms/math/fibonacci)
-  * `P` [Juego de los Saltos](src/algorithms/uncategorized/jump-game)
-  * `P` [Unicos Caminos](src/algorithms/uncategorized/unique-paths)
-  * `P` [Terrazas Pluviales](src/algorithms/uncategorized/rain-terraces) - el problema de la retención del agua de lluvia
+* **Programación Dinámica** - construye una solución usando sub-soluciones previamente encontradas
+  * `P` [Número de Fibonacci](src/algorithms/math/fibonacci)
+  * `P` [Juego de los saltos](src/algorithms/uncategorized/jump-game)
+  * `P` [Caminos únicos](src/algorithms/uncategorized/unique-paths)
+  * `P` [Terrazas pluviales](src/algorithms/uncategorized/rain-terraces) - el problema de la retención del agua de lluvia
   * `A` [Distancia de Levenshtein](src/algorithms/string/levenshtein-distance) - distancia mínima de edición entre dos secuencias
-  * `A` [Subsecuencia Comun más Larga](src/algorithms/sets/longest-common-subsequence) (LCS)
-  * `A` [Subcadena de Caracteres Comun más larga](src/algorithms/string/longest-common-substring)
-  * `A` [Subsecuencia Creciente más Larga](src/algorithms/sets/longest-increasing-subsequence)
-  * `A` [Susecuencia Comun más Corta](src/algorithms/sets/shortest-common-supersequence)
-  * `A` [0/1 Problema de la Mochila](src/algorithms/sets/knapsack-problem)
-  * `A` [Particion Entera](src/algorithms/math/integer-partition)
-  * `A` [Subarreglo Macima](src/algorithms/sets/maximum-subarray)
-  * `A` [Algoritmo de Bellman-Ford](src/algorithms/graph/bellman-ford) - encontrar el camino más corto a todos los vértices del grafo
-  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) -encuentra los caminos más cortos entre todos los pares de vértices
-  * `A` [Coincidencia por Expresiones regulares](src/algorithms/string/regular-expression-matching)
-* **De Retorceso** - de manera similar a la fuerza bruta, trate de generar todas las soluciones posibles, pero cada vez que genere la siguiente solución, compruebe si cumple con todas las condiciones, y sólo entonces continúe generando soluciones posteriores. De lo contrario, retroceda y siga un camino diferente para encontrar una solución. Normalmente se utiliza la travesía del espacio estatal.
-  * `P` [Juego de Saltos](src/algorithms/uncategorized/jump-game)
-  * `P` [Camino Unico](src/algorithms/uncategorized/unique-paths)
-  * `A` [Ciclo Hamiltoniano](src/algorithms/graph/hamiltonian-cycle) - Visite cada vértice exactamente una vez
-  * `A` [Problema de las N-Reinas](src/algorithms/uncategorized/n-queens)
-  * `A` [Gira de Caballeros](src/algorithms/uncategorized/knight-tour)
-  * `A` [Suma Combinada](src/algorithms/sets/combination-sum) - encuentra todas las combinaciones que forman una suma específica.
-* **Ramas y Limites** - recuerde la solución de menor costo encontrada en cada etapa de la búsqueda de rastreo, y utilice el costo de la solución de menor costo encontrada hasta el momento como un límite más bajo en el costo de una solución de menor costo para el problema, a fin de descartar soluciones parciales con costos mayores que la solución de menor costo encontrada hasta el momento. Normalmente se utiliza la travesía BFS en combinación con la travesía DFS del árbol del espacio de estado.
-
-## Como usar este repositorio
+  * `A` [Subsecuencia común más larga](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Subcadena común más larga](src/algorithms/string/longest-common-substring)
+  * `A` [Subsecuencia creciente más larga](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Supersecuencia común más corta](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Problema de la mochila 0/1](src/algorithms/sets/knapsack-problem)
+  * `A` [Partición entera](src/algorithms/math/integer-partition)
+  * `A` [Máximo subarreglo](src/algorithms/sets/maximum-subarray)
+  * `A` [Algoritmo de Bellman-Ford](src/algorithms/graph/bellman-ford) - encontrar los caminos más cortos a todos los vértices del grafo desde un solo vértice
+  * `A` [Algoritmo de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - encontrar los caminos más cortos entre todos los pares de vértices
+  * `A` [Coincidencia por expresiones regulares](src/algorithms/string/regular-expression-matching)
+* **De Retorceso (Backtracking)** - De manera similar a la fuerza bruta, trata de generar todas las soluciones posibles, pero cada vez que genere la siguiente solución, comprueba si cumple con todas las condiciones, y sólo entonces continúa generando soluciones posteriores. De lo contrario, retrocede y sigue un camino diferente para encontrar una solución. Normalmente se utiliza un recorrido en profundidad (DFS) del espacio de estados.
+  * `P` [Juego de los saltos](src/algorithms/uncategorized/jump-game)
+  * `P` [Caminos únicos](src/algorithms/uncategorized/unique-paths)
+  * `P` [Conjunto potencia](src/algorithms/sets/power-set) - todos los subconjuntos de un conjunto
+  * `A` [Ciclo hamiltoniano](src/algorithms/graph/hamiltonian-cycle) - visitar cada vértice exactamente una vez
+  * `A` [Problema de las N Reinas](src/algorithms/uncategorized/n-queens)
+  * `A` [Problema del caballo (Knight tour)](src/algorithms/uncategorized/knight-tour)
+  * `A` [Suma combinada](src/algorithms/sets/combination-sum) - encuentra todas las combinaciones que forman una suma específica
+* **Ramas y Límites** - recuerda la solución de menor costo encontrada en cada etapa de la búsqueda de rastreo, y utilizar el costo de la solución de menor costo encontrada hasta el momento como un límite inferior del costo de una solución de menor costo para el problema, a fin de descartar soluciones parciales con costos mayores que la solución de menor costo encontrada hasta el momento. Normalmente se utiliza un recorrido BFS en combinación con un recorrido DFS del árbol del espacio de estados.
+
+## Cómo usar este repositorio
 
 **Instalar las dependencias**
 
@@ -216,9 +236,9 @@ npm test
 npm test -- 'LinkedList'
 ```
 
-**Campo de Juegos**
+**Campo de juegos**
 
-Puede jugar con estructuras de datos y algoritmos en el archivo `./src/playground/playground.js` y escribir 
+Puede jugar con estructuras de datos y algoritmos en el archivo `./src/playground/playground.js` y escribir
 pruebas para ello en `./src/playground/__test__/playground.test.js`.
 
 A continuación, simplemente ejecute el siguiente comando para comprobar si el código funciona como se espera:
@@ -227,21 +247,21 @@ A continuación, simplemente ejecute el siguiente comando para comprobar si el c
 npm test -- 'playground'
 ```
 
-## Información Util
+## Información útil
 
-### Refrencias
+### Referencias
 
-[▶ Estructuras de datos y Algoritmos en YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+[▶ Estructuras de datos y algoritmos en YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
 
 ### Notación O Grande
 
 Orden de crecimiento de los algoritmos especificados en la notación O grande.
 
-![Graficas de Notación O grande ](./assets/big-o-graph.png)
+![Gráficas de Notación O grande ](./assets/big-o-graph.png)
 
-Fuente: [Notación O grande, Hoja de atajos](http://bigocheatsheet.com/).
+Fuente: [Big O Cheat Sheet](http://bigocheatsheet.com/).
 
-A continuación se muestra la lista de algunas de las notaciones de Big O más utilizadas y sus comparaciones de rendimiento 
+A continuación se muestra la lista de algunas de las notaciones de Big O más utilizadas y sus comparaciones de rendimiento
 frente a diferentes tamaños de los datos de entrada.
 
 | Notación O grande | Cálculos para 10 elementos | Cálculos para 100 elementos | Cálculos para 1000 elementos |
@@ -254,31 +274,33 @@ frente a diferentes tamaños de los datos de entrada.
 | **O(2^N)**        | 1024                       | 1.26e+29                    | 1.07e+301                    |
 | **O(N!)**         | 3628800                    | 9.3e+157                    | 4.02e+2567                   |
 
-### Complejidad de las operaciones de estructura de datos
+### Complejidad de las operaciones de estructuras de datos
 
 | Estructura de Datos            | Accesso | Busqueda | Inserción | Borrado | Comentarios                                                     |
 | ------------------------------ | :-----: | :------: | :-------: | :-----: | :-------------------------------------------------------------- |
-| **Coleción**                   |    1    |    n     |     n     |    n    |                                                                 |
+| **Colección**                  |    1    |    n     |     n     |    n    |                                                                 |
 | **Stack**                      |    n    |    n     |     1     |    1    |                                                                 |
 | **Cola**                       |    n    |    n     |     1     |    1    |                                                                 |
-| **Lista Enlazada**             |    n    |    n     |     1     |    1    |                                                                 |
-| **Tabla de Hash**              |    -    |    n     |     n     |    n    | En caso de función hash perfecta los costos serían O(1)         |
-| **Búsqueda por Arbol Binario** |    n    |    n     |     n     |    n    | En el caso de un árbol equilibrado, los costes serían O(log(n)) |
+| **Lista enlazada**             |    n    |    n     |     1     |    1    |                                                                 |
+| **Tabla hash**                 |    -    |    n     |     n     |    n    | En caso de función hash perfecta los costos serían O(1)         |
+| **Búsqueda por Árbol binario** |    n    |    n     |     n     |    n    | En el caso de un árbol equilibrado, los costos serían O(log(n)) |
 | **Árbol B**                    | log(n)  |  log(n)  |  log(n)   | log(n)  |                                                                 |
 | **Árbol Rojo-Negro**           | log(n)  |  log(n)  |  log(n)   | log(n)  |                                                                 |
 | **Árbol AVL**                  | log(n)  |  log(n)  |  log(n)   | log(n)  |                                                                 |
 | **Filtro de Bloom**            |    -    |    1     |     1     |    -    | Falsos positivos son posibles durante la búsqueda               |
 
-### Complejidad de Algoritmos de Clasificación de Arreglos
+### Complejidad de algoritmos de ordenamiento de arreglos
 
 | Nombre                           | Mejor         | Promedio                | Pero                        | Memorya | Estable | Comentarios                                                   |
 | -------------------------------- | :-----------: | :---------------------: | :-------------------------: | :-----: | :-----: | :------------------------------------------------------------ |
-| **Clasificación de Burbujas**    |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |   1     |  Si     |                                                               |
-| **Clasificación por Inserción**  |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |   1     |  Si     |                                                               |
-| **Clasificacion por Selección**  | n<sup>2</sup> |      n<sup>2</sup>      |        n<sup>2</sup>        |   1     |   No    |                                                               |
-| **Classificacion por Pila**      | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |   1     |   No    |                                                               |
-| **Clasificacion por Fusion**     | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |   n     |  Si     |                                                               |
-| **Quick sort**                   | n&nbsp;log(n) |      n&nbsp;log(n)      |        n<sup>2</sup>        | log(n)  |   No    | Quicksort es utilizqado con O(log(n)) espacio en el stack  |
-| **Shell sort**                   | n&nbsp;log(n) | depende de la secuencia de huecos | n&nbsp;(log(n))<sup>2</sup> |   1   |   No   |                                                               |
-| **Clasificacion por Conteo**     |     n + r     |          n + r          |            n + r            | n + r   |  Si     | r - mayor numero en arreglo                                   |
-| **Radix sort**                   |    n \* k     |         n \* k          |           n \* k            | n + k   |  Si     | k - largo de la llave más larga                                     |
+| **Ordenamiento de burbuja**      |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |   1     |  Si     |                                                               |
+| **Ordenamiento por inserción**   |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |   1     |  Si     |                                                               |
+| **Ordenamiento por selección**   | n<sup>2</sup> |      n<sup>2</sup>      |        n<sup>2</sup>        |   1     |   No    |                                                               |
+| **Ordenamiento por Heap**        | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |   1     |   No    |                                                               |
+| **Ordenamiento por mezcla**      | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |   n     |  Si     |                                                               |
+| **Quicksort**                    | n&nbsp;log(n) |      n&nbsp;log(n)      |        n<sup>2</sup>        | log(n)  |   No    | Quicksort utiliza O(log(n)) de espacio en el stack         |
+| **Shellsort**                    | n&nbsp;log(n) | depende de la secuencia de huecos | n&nbsp;(log(n))<sup>2</sup> |   1   |   No   |                                                               |
+| **Ordenamiento por cuentas**     |     n + r     |          n + r          |            n + r            | n + r   |  Si     | r - mayor número en el arreglo                                |
+| **Ordenamiento Radix**           |    n \* k     |         n \* k          |           n \* k            | n + k   |  Si     | k - largo de la llave más larga                                     |
+
+> ℹ️ Algunos otros [proyectos](https://trekhleb.dev/projects/) y [artículos](https://trekhleb.dev/blog/) sobre JavaScript y algoritmos en [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.fr-FR.md b/README.fr-FR.md
index e56de9f34f..cb30d1833d 100644
--- a/README.fr-FR.md
+++ b/README.fr-FR.md
@@ -1,13 +1,13 @@
 # Algorithmes et Structures de Données en JavaScript
 
-[![Build Status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
 
-Ce dépôt contient des exemples d'implémentation en JavaScript de plusieurs 
+Ce dépôt contient des exemples d'implémentation en JavaScript de plusieurs
 algorithmes et structures de données populaires.
 
-Chaque algorithme et structure de donnée possède son propre README contenant 
-les explications détaillées et liens (incluant aussi des vidéos Youtube) pour 
+Chaque algorithme et structure de donnée possède son propre README contenant
+les explications détaillées et liens (incluant aussi des vidéos Youtube) pour
 complément d'informations.
 
 _Lisez ceci dans d'autres langues:_
@@ -15,184 +15,195 @@ _Lisez ceci dans d'autres langues:_
 [_简体中文_](README.zh-CN.md),
 [_繁體中文_](README.zh-TW.md),
 [_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
 [_Polski_](README.pl-PL.md),
 [_Español_](README.es-ES.md),
-[_Português_](README.pt-BR.md)
-
-> Nous écrivons actuellement un livre qui expliquera clairement, en détail, les 
-principaux algorithmes. Si vous souhaitez être notifié lorsque le livre 
-“JavaScript Algorithms” paraîtra, [cliquez ici](https://upscri.be/402324/).
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
 
 ## Data Structures
 
-Une structure de données est une manière spéciale d'organiser et de stocker 
-des données dans un ordinateur de manière à ce que l'on puisse accéder à 
-cette information et la modifier de manière efficiente. De manière plus 
-spécifique, une structure de données est un ensemble composé d'une collection 
-de valeurs, des relations entre ces valeurs ainsi que d'un ensemble de 
+Une structure de données est une manière spéciale d'organiser et de stocker
+des données dans un ordinateur de manière à ce que l'on puisse accéder à
+cette information et la modifier de manière efficiente. De manière plus
+spécifique, une structure de données est un ensemble composé d'une collection
+de valeurs, des relations entre ces valeurs ainsi que d'un ensemble de
 fonctions ou d'opérations pouvant être appliquées sur ces données.
 
 `B` - Débutant, `A` - Avancé
 
-* `B` [Liste Chaînée](src/data-structures/linked-list)
-* `B` [Liste Doublement Chaînée](src/data-structures/doubly-linked-list)
-* `B` [Queue](src/data-structures/queue)
-* `B` [Pile](src/data-structures/stack)
-* `B` [Table de Hachage](src/data-structures/hash-table)
-* `B` [Tas](src/data-structures/heap)
-* `B` [Queue de Priorité](src/data-structures/priority-queue)
-* `A` [Trie](src/data-structures/trie)
-* `A` [Arbre](src/data-structures/tree)
-  * `A` [Arbre de recherche Binaire](src/data-structures/tree/binary-search-tree)
-  * `A` [Arbre AVL](src/data-structures/tree/avl-tree)
-  * `A` [Arbre Red-Black](src/data-structures/tree/red-black-tree)
-  * `A` [Arbre de Segments](src/data-structures/tree/segment-tree) - avec exemples de requêtes de type min/max/somme sur intervalles
-  * `A` [Arbre de Fenwick](src/data-structures/tree/fenwick-tree) (Arbre Binaire Indexé)
-* `A` [Graphe](src/data-structures/graph) (orienté et non orienté)
-* `A` [Ensembles Disjoints](src/data-structures/disjoint-set)
-* `A` [Filtre de Bloom](src/data-structures/bloom-filter)
+- `B` [Liste Chaînée](src/data-structures/linked-list)
+- `B` [Liste Doublement Chaînée](src/data-structures/doubly-linked-list)
+- `B` [Queue](src/data-structures/queue)
+- `B` [Pile](src/data-structures/stack)
+- `B` [Table de Hachage](src/data-structures/hash-table)
+- `B` [Tas](src/data-structures/heap)
+- `B` [Queue de Priorité](src/data-structures/priority-queue)
+- `A` [Trie](src/data-structures/trie)
+- `A` [Arbre](src/data-structures/tree)
+  - `A` [Arbre de recherche Binaire](src/data-structures/tree/binary-search-tree)
+  - `A` [Arbre AVL](src/data-structures/tree/avl-tree)
+  - `A` [Arbre Red-Black](src/data-structures/tree/red-black-tree)
+  - `A` [Arbre de Segments](src/data-structures/tree/segment-tree) - avec exemples de requêtes de type min/max/somme sur intervalles
+  - `A` [Arbre de Fenwick](src/data-structures/tree/fenwick-tree) (Arbre Binaire Indexé)
+- `A` [Graphe](src/data-structures/graph) (orienté et non orienté)
+- `A` [Ensembles Disjoints](src/data-structures/disjoint-set)
+- `A` [Filtre de Bloom](src/data-structures/bloom-filter)
 
 ## Algorithmes
 
-Un algorithme est une démarche non ambigüe expliquant comment résoudre une 
-classe de problèmes. C'est un ensemble de règles décrivant de manière précise 
+Un algorithme est une démarche non ambigüe expliquant comment résoudre une
+classe de problèmes. C'est un ensemble de règles décrivant de manière précise
 une séquence d'opérations.
 
 `B` - Débutant, `A` - Avancé
 
 ### Algorithmes par topic
 
-* **Math**
-  * `B` [Manipulation de Bit](src/algorithms/math/bits) - définir/obtenir/mettre à jour/effacer les bits, multiplication/division par deux, négativiser etc.
-  * `B` [Factorielle](src/algorithms/math/factorial) 
-  * `B` [Nombre de Fibonacci](src/algorithms/math/fibonacci)
-  * `B` [Test de Primalité](src/algorithms/math/primality-test) (méthode du test de division)
-  * `B` [Algorithme d'Euclide](src/algorithms/math/euclidean-algorithm) - calcule le Plus Grand Commun Diviseur (PGCD)
-  * `B` [Plus Petit Commun Multiple](src/algorithms/math/least-common-multiple) (PPCM)
-  * `B` [Crible d'Eratosthène](src/algorithms/math/sieve-of-eratosthenes) - trouve tous les nombres premiers inférieurs à une certaine limite
-  * `B` [Puissance de Deux](src/algorithms/math/is-power-of-two) - teste si un nombre donné est une puissance de deux (algorithmes naif et basé sur les opérations bit-à-bit)
-  * `B` [Triangle de Pascal](src/algorithms/math/pascal-triangle)
-  * `A` [Partition Entière](src/algorithms/math/integer-partition)
-  * `A` [Approximation de π par l'algorithme de Liu Hui](src/algorithms/math/liu-hui) - approximation du calcul de π basé sur les N-gons
-* **Ensembles**
-  * `B` [Produit Cartésien](src/algorithms/sets/cartesian-product) - produit de plusieurs ensembles
-  * `B` [Mélange de Fisher–Yates](src/algorithms/sets/fisher-yates) - permulation aléatoire d'une séquence finie
-  * `A` [Ensemble des parties d'un ensemble](src/algorithms/sets/power-set) - tous les sous-ensembles d'un ensemble
-  * `A` [Permutations](src/algorithms/sets/permutations) (avec et sans répétitions)
-  * `A` [Combinaisons](src/algorithms/sets/combinations) (avec et sans répétitions)
-  * `A` [Plus Longue Sous-séquence Commune](src/algorithms/sets/longest-common-subsequence)
-  * `A` [Plus Longue Sous-suite strictement croissante](src/algorithms/sets/longest-increasing-subsequence)
-  * `A` [Plus Courte Super-séquence Commune](src/algorithms/sets/shortest-common-supersequence)
-  * `A` [Problème du Sac à Dos](src/algorithms/sets/knapsack-problem) - versions "0/1" et "Sans Contraintes"
-  * `A` [Sous-partie Maximum](src/algorithms/sets/maximum-subarray) - versions "Force Brute" et "Programmation Dynamique" (Kadane)
-  * `A` [Somme combinatoire](src/algorithms/sets/combination-sum) - trouve toutes les combinaisons qui forment une somme spécifique
-* **Chaînes de Caractères**
-  * `B` [Distance de Hamming](src/algorithms/string/hamming-distance) - nombre de positions auxquelles les symboles sont différents
-  * `A` [Distance de Levenshtein](src/algorithms/string/levenshtein-distance) - distance minimale d'édition entre deux séquences
-  * `A` [Algorithme de Knuth–Morris–Pratt](src/algorithms/string/knuth-morris-pratt) (Algorithme KMP) - recherche de sous-chaîne (pattern matching)
-  * `A` [Algorithme Z](src/algorithms/string/z-algorithm) - recherche de sous-chaîne (pattern matching)
-  * `A` [Algorithme de Rabin Karp](src/algorithms/string/rabin-karp) - recherche de sous-chaîne
-  * `A` [Plus Longue Sous-chaîne Commune](src/algorithms/string/longest-common-substring)
-  * `A` [Expression Régulière](src/algorithms/string/regular-expression-matching)
-* **Recherche**
-  * `B` [Recherche Linéaire](src/algorithms/search/linear-search)
-  * `B` [Jump Search](src/algorithms/search/jump-search) Recherche par saut (ou par bloc) - recherche dans une liste triée
-  * `B` [Recherche Binaire](src/algorithms/search/binary-search) - recherche dans une liste triée
-  * `B` [Recherche par Interpolation](src/algorithms/search/interpolation-search) - recherche dans une liste triée et uniformément distribuée
-* **Tri**
-  * `B` [Tri Bullet](src/algorithms/sorting/bubble-sort)
-  * `B` [Tri Sélection](src/algorithms/sorting/selection-sort)
-  * `B` [Tri Insertion](src/algorithms/sorting/insertion-sort)
-  * `B` [Tri Par Tas](src/algorithms/sorting/heap-sort)
-  * `B` [Tri Fusion](src/algorithms/sorting/merge-sort)
-  * `B` [Tri Rapide](src/algorithms/sorting/quick-sort) - implémentations *in-place* et *non in-place*
-  * `B` [Tri Shell](src/algorithms/sorting/shell-sort)
-  * `B` [Tri Comptage](src/algorithms/sorting/counting-sort)
-  * `B` [Tri Radix](src/algorithms/sorting/radix-sort)
-* **Arbres**
-  * `B` [Parcours en Profondeur](src/algorithms/tree/depth-first-search) (DFS)
-  * `B` [Parcours en Largeur](src/algorithms/tree/breadth-first-search) (BFS)
-* **Graphes**
-  * `B` [Parcours en Profondeur](src/algorithms/graph/depth-first-search) (DFS)
-  * `B` [Parcours en Largeur](src/algorithms/graph/breadth-first-search) (BFS)
-  * `B` [Algorithme de Kruskal](src/algorithms/graph/kruskal) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
-  * `A` [Algorithme de Dijkstra](src/algorithms/graph/dijkstra) - trouver tous les plus courts chemins  partant d'un noeud vers tous les autres noeuds dans un graphe
-  * `A` [Algorithme de Bellman-Ford](src/algorithms/graph/bellman-ford) - trouver tous les plus courts chemins  partant d'un noeud vers tous les autres noeuds dans un graphe
-  * `A` [Algorithme de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - trouver tous les plus courts chemins  entre toutes les paires de noeuds dans un graphe
-  * `A` [Détection de Cycle](src/algorithms/graph/detect-cycle) - pour les graphes dirigés et non dirigés (implémentations basées sur l'algorithme de Parcours en Profondeur et sur les Ensembles Disjoints)
-  * `A` [Algorithme de Prim](src/algorithms/graph/prim) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
-  * `A` [Tri Topologique](src/algorithms/graph/topological-sorting) - méthode DFS
-  * `A` [Point d'Articulation](src/algorithms/graph/articulation-points) - algorithme de Tarjan (basé sur l'algorithme de Parcours en Profondeur)
-  * `A` [Bridges](src/algorithms/graph/bridges) - algorithme basé sur le Parcours en Profondeur
-  * `A` [Chemin Eulérien et Circuit Eulérien](src/algorithms/graph/eulerian-path) - algorithme de Fleury - visite chaque arc exactement une fois
-  * `A` [Cycle Hamiltonien](src/algorithms/graph/hamiltonian-cycle) - visite chaque noeud exactement une fois
-  * `A` [Composants Fortements Connexes](src/algorithms/graph/strongly-connected-components) - algorithme de Kosaraju
-  * `A` [Problème du Voyageur de Commerce](src/algorithms/graph/travelling-salesman) - chemin le plus court visitant chaque cité et retournant à la cité d'origine
-* **Non catégorisé**
-  * `B` [Tours de Hanoi](src/algorithms/uncategorized/hanoi-tower)
-  * `B` [Rotation de Matrice Carrée](src/algorithms/uncategorized/square-matrix-rotation) - algorithme *in place*
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - retour sur trace, programmation dynamique (haut-bas + bas-haut) et exemples gourmands 
-  * `B` [Chemins Uniques](src/algorithms/uncategorized/unique-paths) - retour sur trace, programmation dynamique (haut-bas + bas-haut) et exemples basés sur le Triangle de Pascal 
-  * `A` [Problème des N-Dames](src/algorithms/uncategorized/n-queens)
-  * `A` [Problème du Cavalier](src/algorithms/uncategorized/knight-tour)
+- **Math**
+  - `B` [Manipulation de Bit](src/algorithms/math/bits/README.fr-FR.md) - définir/obtenir/mettre à jour/effacer les bits, multiplication/division par deux, négativiser etc.
+  - `B` [Factorielle](src/algorithms/math/factorial/README.fr-FR.md)
+  - `B` [Nombre de Fibonacci](src/algorithms/math/fibonacci/README.fr-FR.md)
+  - `B` [Test de Primalité](src/algorithms/math/primality-test) (méthode du test de division)
+  - `B` [Algorithme d'Euclide](src/algorithms/math/euclidean-algorithm/README.fr-FR.md) - calcule le Plus Grand Commun Diviseur (PGCD)
+  - `B` [Plus Petit Commun Multiple](src/algorithms/math/least-common-multiple) (PPCM)
+  - `B` [Crible d'Eratosthène](src/algorithms/math/sieve-of-eratosthenes) - trouve tous les nombres premiers inférieurs à une certaine limite
+  - `B` [Puissance de Deux](src/algorithms/math/is-power-of-two) - teste si un nombre donné est une puissance de deux (algorithmes naif et basé sur les opérations bit-à-bit)
+  - `B` [Triangle de Pascal](src/algorithms/math/pascal-triangle)
+  - `B` [Nombre complexe](src/algorithms/math/complex-number/README.fr-FR.md) - nombres complexes et opérations de bases
+  - `A` [Partition Entière](src/algorithms/math/integer-partition)
+  - `A` [Approximation de π par l'algorithme de Liu Hui](src/algorithms/math/liu-hui) - approximation du calcul de π basé sur les N-gons
+  - `B` [Exponentiation rapide](src/algorithms/math/fast-powering/README.fr-FR.md)
+  - `A` [Transformée de Fourier Discrète](src/algorithms/math/fourier-transform/README.fr-FR.md) - décomposer une fonction du temps (un signal) en fréquences qui la composent
+- **Ensembles**
+  - `B` [Produit Cartésien](src/algorithms/sets/cartesian-product) - produit de plusieurs ensembles
+  - `B` [Mélange de Fisher–Yates](src/algorithms/sets/fisher-yates) - permulation aléatoire d'une séquence finie
+  - `A` [Ensemble des parties d'un ensemble](src/algorithms/sets/power-set) - tous les sous-ensembles d'un ensemble
+  - `A` [Permutations](src/algorithms/sets/permutations) (avec et sans répétitions)
+  - `A` [Combinaisons](src/algorithms/sets/combinations) (avec et sans répétitions)
+  - `A` [Plus Longue Sous-séquence Commune](src/algorithms/sets/longest-common-subsequence)
+  - `A` [Plus Longue Sous-suite strictement croissante](src/algorithms/sets/longest-increasing-subsequence)
+  - `A` [Plus Courte Super-séquence Commune](src/algorithms/sets/shortest-common-supersequence)
+  - `A` [Problème du Sac à Dos](src/algorithms/sets/knapsack-problem) - versions "0/1" et "Sans Contraintes"
+  - `A` [Sous-partie Maximum](src/algorithms/sets/maximum-subarray) - versions "Force Brute" et "Programmation Dynamique" (Kadane)
+  - `A` [Somme combinatoire](src/algorithms/sets/combination-sum) - trouve toutes les combinaisons qui forment une somme spécifique
+- **Chaînes de Caractères**
+  - `B` [Distance de Hamming](src/algorithms/string/hamming-distance) - nombre de positions auxquelles les symboles sont différents
+  - `A` [Distance de Levenshtein](src/algorithms/string/levenshtein-distance) - distance minimale d'édition entre deux séquences
+  - `A` [Algorithme de Knuth–Morris–Pratt](src/algorithms/string/knuth-morris-pratt) (Algorithme KMP) - recherche de sous-chaîne (pattern matching)
+  - `A` [Algorithme Z](src/algorithms/string/z-algorithm) - recherche de sous-chaîne (pattern matching)
+  - `A` [Algorithme de Rabin Karp](src/algorithms/string/rabin-karp) - recherche de sous-chaîne
+  - `A` [Plus Longue Sous-chaîne Commune](src/algorithms/string/longest-common-substring)
+  - `A` [Expression Régulière](src/algorithms/string/regular-expression-matching)
+- **Recherche**
+  - `B` [Recherche Linéaire](src/algorithms/search/linear-search)
+  - `B` [Jump Search](src/algorithms/search/jump-search) Recherche par saut (ou par bloc) - recherche dans une liste triée
+  - `B` [Recherche Binaire](src/algorithms/search/binary-search) - recherche dans une liste triée
+  - `B` [Recherche par Interpolation](src/algorithms/search/interpolation-search) - recherche dans une liste triée et uniformément distribuée
+- **Tri**
+  - `B` [Tri Bullet](src/algorithms/sorting/bubble-sort)
+  - `B` [Tri Sélection](src/algorithms/sorting/selection-sort)
+  - `B` [Tri Insertion](src/algorithms/sorting/insertion-sort)
+  - `B` [Tri Par Tas](src/algorithms/sorting/heap-sort)
+  - `B` [Tri Fusion](src/algorithms/sorting/merge-sort)
+  - `B` [Tri Rapide](src/algorithms/sorting/quick-sort) - implémentations _in-place_ et _non in-place_
+  - `B` [Tri Shell](src/algorithms/sorting/shell-sort)
+  - `B` [Tri Comptage](src/algorithms/sorting/counting-sort)
+  - `B` [Tri Radix](src/algorithms/sorting/radix-sort)
+- **Arbres**
+  - `B` [Parcours en Profondeur](src/algorithms/tree/depth-first-search) (DFS)
+  - `B` [Parcours en Largeur](src/algorithms/tree/breadth-first-search) (BFS)
+- **Graphes**
+  - `B` [Parcours en Profondeur](src/algorithms/graph/depth-first-search) (DFS)
+  - `B` [Parcours en Largeur](src/algorithms/graph/breadth-first-search) (BFS)
+  - `B` [Algorithme de Kruskal](src/algorithms/graph/kruskal) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
+  - `A` [Algorithme de Dijkstra](src/algorithms/graph/dijkstra) - trouver tous les plus courts chemins partant d'un noeud vers tous les autres noeuds dans un graphe
+  - `A` [Algorithme de Bellman-Ford](src/algorithms/graph/bellman-ford) - trouver tous les plus courts chemins partant d'un noeud vers tous les autres noeuds dans un graphe
+  - `A` [Algorithme de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - trouver tous les plus courts chemins entre toutes les paires de noeuds dans un graphe
+  - `A` [Détection de Cycle](src/algorithms/graph/detect-cycle) - pour les graphes dirigés et non dirigés (implémentations basées sur l'algorithme de Parcours en Profondeur et sur les Ensembles Disjoints)
+  - `A` [Algorithme de Prim](src/algorithms/graph/prim) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
+  - `A` [Tri Topologique](src/algorithms/graph/topological-sorting) - méthode DFS
+  - `A` [Point d'Articulation](src/algorithms/graph/articulation-points) - algorithme de Tarjan (basé sur l'algorithme de Parcours en Profondeur)
+  - `A` [Bridges](src/algorithms/graph/bridges) - algorithme basé sur le Parcours en Profondeur
+  - `A` [Chemin Eulérien et Circuit Eulérien](src/algorithms/graph/eulerian-path) - algorithme de Fleury - visite chaque arc exactement une fois
+  - `A` [Cycle Hamiltonien](src/algorithms/graph/hamiltonian-cycle) - visite chaque noeud exactement une fois
+  - `A` [Composants Fortements Connexes](src/algorithms/graph/strongly-connected-components) - algorithme de Kosaraju
+  - `A` [Problème du Voyageur de Commerce](src/algorithms/graph/travelling-salesman) - chemin le plus court visitant chaque cité et retournant à la cité d'origine
+- **Non catégorisé**
+  - `B` [Tours de Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  - `B` [Rotation de Matrice Carrée](src/algorithms/uncategorized/square-matrix-rotation) - algorithme _in place_
+  - `B` [Jump Game](src/algorithms/uncategorized/jump-game) - retour sur trace, programmation dynamique (haut-bas + bas-haut) et exemples gourmands
+  - `B` [Chemins Uniques](src/algorithms/uncategorized/unique-paths) - retour sur trace, programmation dynamique (haut-bas + bas-haut) et exemples basés sur le Triangle de Pascal
+  - `A` [Problème des N-Dames](src/algorithms/uncategorized/n-queens)
+  - `A` [Problème du Cavalier](src/algorithms/uncategorized/knight-tour)
 
 ### Algorithmes par Paradigme
 
-Un paradigme algorithmique est une méthode générique ou une approche qui 
-sous-tend la conception d'une classe d'algorithmes. C'est une abstraction 
-au-dessus de la notion d'algorithme, tout comme l'algorithme est une abstraction 
+Un paradigme algorithmique est une méthode générique ou une approche qui
+sous-tend la conception d'une classe d'algorithmes. C'est une abstraction
+au-dessus de la notion d'algorithme, tout comme l'algorithme est une abstraction
 supérieure à un programme informatique.
 
-* **Force Brute** - cherche parmi toutes les possibilités et retient la meilleure
-  * `B` [Recherche Linéaire](src/algorithms/search/linear-search)
-  * `A` [Sous-partie Maximum](src/algorithms/sets/maximum-subarray)
-  * `A` [Problème du Voyageur de Commerce](src/algorithms/graph/travelling-salesman) - chemin le plus court visitant chaque cité et retournant à la cité d'origine
-* **Gourmand** - choisit la meilleure option à l'instant courant, sans tenir compte de la situation future
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `A` [Problème du Sac à Dos Sans Contraintes](src/algorithms/sets/knapsack-problem)
-  * `A` [Algorithme de Dijkstra](src/algorithms/graph/dijkstra) - trouver tous les plus courts chemins  partant d'un noeud vers tous les autres noeuds dans un graphe
-  * `A` [Algorithme de Prim](src/algorithms/graph/prim) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
-  * `A` [Algorithme de Kruskal](src/algorithms/graph/kruskal) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
-* **Diviser et Régner** - divise le problème en sous problèmes (plus simples) et résoud ces sous problèmes
-  * `B` [Recherche Binaire](src/algorithms/search/binary-search)
-  * `B` [Tours de Hanoi](src/algorithms/uncategorized/hanoi-tower)
-  * `B` [Triangle de Pascal](src/algorithms/math/pascal-triangle)
-  * `B` [Algorithme d'Euclide](src/algorithms/math/euclidean-algorithm) - calcule le Plus Grand Commun Diviseur (PGCD)
-  * `B` [Tri Fusion](src/algorithms/sorting/merge-sort)
-  * `B` [Tri Rapide](src/algorithms/sorting/quick-sort)
-  * `B` [Arbre de Parcours en Profondeur](src/algorithms/tree/depth-first-search) (DFS)
-  * `B` [Graphe de Parcours en Profondeur](src/algorithms/graph/depth-first-search) (DFS)
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `A` [Permutations](src/algorithms/sets/permutations) (avec et sans répétitions)
-  * `A` [Combinations](src/algorithms/sets/combinations) (avec et sans répétitions)
-* **Programmation Dynamique** - construit une solution en utilisant les solutions précédemment trouvées
-  * `B` [Nombre de Fibonacci](src/algorithms/math/fibonacci)
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `B` [Chemins Uniques](src/algorithms/uncategorized/unique-paths)
-  * `A` [Distance de Levenshtein](src/algorithms/string/levenshtein-distance) - distance minimale d'édition entre deux séquences
-  * `A` [Plus Longue Sous-séquence Commune](src/algorithms/sets/longest-common-subsequence)
-  * `A` [Plus Longue Sous-chaîne Commune](src/algorithms/string/longest-common-substring)
-  * `A` [Plus Longue Sous-suite strictement croissante](src/algorithms/sets/longest-increasing-subsequence)
-  * `A` [Plus Courte Super-séquence Commune](src/algorithms/sets/shortest-common-supersequence)
-  * `A` [Problème de Sac à Dos](src/algorithms/sets/knapsack-problem)
-  * `A` [Partition Entière](src/algorithms/math/integer-partition)
-  * `A` [Sous-partie Maximum](src/algorithms/sets/maximum-subarray)
-  * `A` [Algorithme de Bellman-Ford](src/algorithms/graph/bellman-ford) - trouver tous les plus courts chemins  partant d'un noeud vers tous les autres noeuds dans un graphe
-  * `A` [Algorithme de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - trouver tous les plus courts chemins  entre toutes les paires de noeuds dans un graphe
-  * `A` [Expression Régulière](src/algorithms/string/regular-expression-matching)
-* **Retour sur trace** - de même que la version "Force Brute", essaie de générer toutes les solutions possibles, mais pour chaque solution générée, on teste si elle satisfait toutes les conditions, et seulement ensuite continuer à générer des solutions ultérieures. Sinon, l'on revient en arrière, et l'on essaie un
-chemin différent pour tester d'autres solutions. Normalement, la traversée en profondeur de l'espace d'états est utilisée.
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
-  * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Visit every vertex exactly once
-  * `A` [Problème des N-Dames](src/algorithms/uncategorized/n-queens)
-  * `A` [Problème du Cavalier](src/algorithms/uncategorized/knight-tour)
-  * `A` [Somme combinatoire](src/algorithms/sets/combination-sum) - trouve toutes les combinaisons qui forment une somme spécifique
-* **Séparation et Evaluation** - pemet de retenir une solution à moindre coût dans un ensemble. Pour chaque étape, l'on garde une trace de la solution la moins coûteuse trouvée jusqu'à présent en tant que borne inférieure du coût. Cela afin d'éliminer les solutions partielles dont les coûts sont plus élevés que celui de la solution actuelle retenue. Normalement, la traversée en largeur en combinaison avec la traversée en profondeur de l'espace d'états de l'arbre est utilisée.
+- **Force Brute** - cherche parmi toutes les possibilités et retient la meilleure
+  - `B` [Recherche Linéaire](src/algorithms/search/linear-search)
+  - `A` [Sous-partie Maximum](src/algorithms/sets/maximum-subarray)
+  - `A` [Problème du Voyageur de Commerce](src/algorithms/graph/travelling-salesman) - chemin le plus court visitant chaque cité et retournant à la cité d'origine
+- **Gourmand** - choisit la meilleure option à l'instant courant, sans tenir compte de la situation future
+  - `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  - `A` [Problème du Sac à Dos Sans Contraintes](src/algorithms/sets/knapsack-problem)
+  - `A` [Algorithme de Dijkstra](src/algorithms/graph/dijkstra) - trouver tous les plus courts chemins partant d'un noeud vers tous les autres noeuds dans un graphe
+  - `A` [Algorithme de Prim](src/algorithms/graph/prim) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
+  - `A` [Algorithme de Kruskal](src/algorithms/graph/kruskal) - trouver l'arbre couvrant de poids minimal sur un graphe pondéré non dirigé
+- **Diviser et Régner** - divise le problème en sous problèmes (plus simples) et résoud ces sous problèmes
+  - `B` [Recherche Binaire](src/algorithms/search/binary-search)
+  - `B` [Tours de Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  - `B` [Triangle de Pascal](src/algorithms/math/pascal-triangle)
+  - `B` [Algorithme d'Euclide](src/algorithms/math/euclidean-algorithm) - calcule le Plus Grand Commun Diviseur (PGCD)
+  - `B` [Tri Fusion](src/algorithms/sorting/merge-sort)
+  - `B` [Tri Rapide](src/algorithms/sorting/quick-sort)
+  - `B` [Arbre de Parcours en Profondeur](src/algorithms/tree/depth-first-search) (DFS)
+  - `B` [Graphe de Parcours en Profondeur](src/algorithms/graph/depth-first-search) (DFS)
+  - `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  - `A` [Permutations](src/algorithms/sets/permutations) (avec et sans répétitions)
+  - `A` [Combinations](src/algorithms/sets/combinations) (avec et sans répétitions)
+- **Programmation Dynamique** - construit une solution en utilisant les solutions précédemment trouvées
+  - `B` [Nombre de Fibonacci](src/algorithms/math/fibonacci)
+  - `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  - `B` [Chemins Uniques](src/algorithms/uncategorized/unique-paths)
+  - `A` [Distance de Levenshtein](src/algorithms/string/levenshtein-distance) - distance minimale d'édition entre deux séquences
+  - `A` [Plus Longue Sous-séquence Commune](src/algorithms/sets/longest-common-subsequence)
+  - `A` [Plus Longue Sous-chaîne Commune](src/algorithms/string/longest-common-substring)
+  - `A` [Plus Longue Sous-suite strictement croissante](src/algorithms/sets/longest-increasing-subsequence)
+  - `A` [Plus Courte Super-séquence Commune](src/algorithms/sets/shortest-common-supersequence)
+  - `A` [Problème de Sac à Dos](src/algorithms/sets/knapsack-problem)
+  - `A` [Partition Entière](src/algorithms/math/integer-partition)
+  - `A` [Sous-partie Maximum](src/algorithms/sets/maximum-subarray)
+  - `A` [Algorithme de Bellman-Ford](src/algorithms/graph/bellman-ford) - trouver tous les plus courts chemins partant d'un noeud vers tous les autres noeuds dans un graphe
+  - `A` [Algorithme de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - trouver tous les plus courts chemins entre toutes les paires de noeuds dans un graphe
+  - `A` [Expression Régulière](src/algorithms/string/regular-expression-matching)
+- **Retour sur trace** - de même que la version "Force Brute", essaie de générer toutes les solutions possibles, mais pour chaque solution générée, on teste si elle satisfait toutes les conditions, et seulement ensuite continuer à générer des solutions ultérieures. Sinon, l'on revient en arrière, et l'on essaie un
+  chemin différent pour tester d'autres solutions. Normalement, la traversée en profondeur de l'espace d'états est utilisée.
+  - `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  - `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
+  - `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Visit every vertex exactly once
+  - `A` [Problème des N-Dames](src/algorithms/uncategorized/n-queens)
+  - `A` [Problème du Cavalier](src/algorithms/uncategorized/knight-tour)
+  - `A` [Somme combinatoire](src/algorithms/sets/combination-sum) - trouve toutes les combinaisons qui forment une somme spécifique
+- **Séparation et Evaluation** - pemet de retenir une solution à moindre coût dans un ensemble. Pour chaque étape, l'on garde une trace de la solution la moins coûteuse trouvée jusqu'à présent en tant que borne inférieure du coût. Cela afin d'éliminer les solutions partielles dont les coûts sont plus élevés que celui de la solution actuelle retenue. Normalement, la traversée en largeur en combinaison avec la traversée en profondeur de l'espace d'états de l'arbre est utilisée.
 
 ## Comment utiliser ce dépôt
 
 **Installer toutes les dépendances**
+
 ```
 npm install
 ```
@@ -206,22 +217,24 @@ npm run lint
 ```
 
 **Exécuter tous les tests**
+
 ```
 npm test
 ```
 
 **Exécuter les tests par nom**
+
 ```
 npm test -- 'LinkedList'
 ```
 
 **Tests personnalisés**
 
-Vous pouvez manipuler les structures de données et algorithmes présents dans ce 
-dépôt avec le fichier `./src/playground/playground.js` et écrire vos propres 
+Vous pouvez manipuler les structures de données et algorithmes présents dans ce
+dépôt avec le fichier `./src/playground/playground.js` et écrire vos propres
 tests dans file `./src/playground/__test__/playground.test.js`.
 
-Vous pourrez alors simplement exécuter la commande suivante afin de tester si 
+Vous pourrez alors simplement exécuter la commande suivante afin de tester si
 votre code fonctionne comme escompté
 
 ```
@@ -242,44 +255,46 @@ Comparaison de la performance d'algorithmes en notation Grand O.
 
 Source: [Big O Cheat Sheet](http://bigocheatsheet.com/).
 
-Voici la liste de certaines des notations Grand O les plus utilisées et de leurs 
+Voici la liste de certaines des notations Grand O les plus utilisées et de leurs
 comparaisons de performance suivant différentes tailles pour les données d'entrée.
 
-| Notation Grand O | Opérations pour 10 éléments  | Opérations pour 100 éléments  | Opérations pour 1000 éléments   |
-| ---------------- | ---------------------------- | ----------------------------- | ------------------------------- |
-| **O(1)**         | 1                            | 1                             | 1                               |
-| **O(log N)**     | 3                            | 6                             | 9                               |
-| **O(N)**         | 10                           | 100                           | 1000                            |
-| **O(N log N)**   | 30                           | 600                           | 9000                            |
-| **O(N^2)**       | 100                          | 10000                         | 1000000                         |
-| **O(2^N)**       | 1024                         | 1.26e+29                      | 1.07e+301                       |
-| **O(N!)**        | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+| Notation Grand O | Opérations pour 10 éléments | Opérations pour 100 éléments | Opérations pour 1000 éléments |
+| ---------------- | --------------------------- | ---------------------------- | ----------------------------- |
+| **O(1)**         | 1                           | 1                            | 1                             |
+| **O(log N)**     | 3                           | 6                            | 9                             |
+| **O(N)**         | 10                          | 100                          | 1000                          |
+| **O(N log N)**   | 30                          | 600                          | 9000                          |
+| **O(N^2)**       | 100                         | 10000                        | 1000000                       |
+| **O(2^N)**       | 1024                        | 1.26e+29                     | 1.07e+301                     |
+| **O(N!)**        | 3628800                     | 9.3e+157                     | 4.02e+2567                    |
 
 ### Complexité des Opérations suivant les Structures de Données
 
-| Structure de donnée             | Accès     | Recherche | Insertion | Suppression  | Commentaires  |
-| ------------------------------- | :-------: | :-------: | :-------: | :----------: | :------------ |
-| **Liste**                       | 1         | n         | n         | n            |               |
-| **Pile**                        | n         | n         | 1         | 1            |               |
-| **Queue**                       | n         | n         | 1         | 1            |               |
-| **Liste Liée**                  | n         | n         | 1         | 1            |               |
-| **Table de Hachage**            | -         | n         | n         | n            | Dans le cas des fonctions de hachage parfaites, les couts seraient de O(1) |
-| **Arbre de Recherche Binaire**  | n         | n         | n         | n            | Dans le cas des arbre équilibrés, les coûts seraient de O(log(n)) |
-| **Arbre B**                     | log(n)    | log(n)    | log(n)    | log(n)       |               |
-| **Arbre Red-Black**             | log(n)    | log(n)    | log(n)    | log(n)       |               |
-| **Arbre AVL**                   | log(n)    | log(n)    | log(n)    | log(n)       |               |
-| **Filtre de Bloom**             | -         | 1         | 1         | -            | Les faux positifs sont possibles lors de la recherche |
+| Structure de donnée            | Accès  | Recherche | Insertion | Suppression | Commentaires                                                               |
+| ------------------------------ | :----: | :-------: | :-------: | :---------: | :------------------------------------------------------------------------- |
+| **Liste**                      |   1    |     n     |     n     |      n      |                                                                            |
+| **Pile**                       |   n    |     n     |     1     |      1      |                                                                            |
+| **Queue**                      |   n    |     n     |     1     |      1      |                                                                            |
+| **Liste Liée**                 |   n    |     n     |     1     |      1      |                                                                            |
+| **Table de Hachage**           |   -    |     n     |     n     |      n      | Dans le cas des fonctions de hachage parfaites, les couts seraient de O(1) |
+| **Arbre de Recherche Binaire** |   n    |     n     |     n     |      n      | Dans le cas des arbre équilibrés, les coûts seraient de O(log(n))          |
+| **Arbre B**                    | log(n) |  log(n)   |  log(n)   |   log(n)    |                                                                            |
+| **Arbre Red-Black**            | log(n) |  log(n)   |  log(n)   |   log(n)    |                                                                            |
+| **Arbre AVL**                  | log(n) |  log(n)   |  log(n)   |   log(n)    |                                                                            |
+| **Filtre de Bloom**            |   -    |     1     |     1     |      -      | Les faux positifs sont possibles lors de la recherche                      |
 
 ### Complexité des Algorithmes de Tri de Liste
 
-| Nom                     | Meilleur        | Moyenne                | Pire                | Mémoire   | Stable    | Commentaires  |
-| ----------------------- | :-------------: | :--------------------: | :-----------------: | :-------: | :-------: | :------------ |
-| **Tri Bulle**           | n               | n<sup>2</sup>          | n<sup>2</sup>       | 1         | Oui       |               |
-| **Tri Insertion**       | n               | n<sup>2</sup>          | n<sup>2</sup>       | 1         | Oui       |               |
-| **Tri Sélection**       | n<sup>2</sup>   | n<sup>2</sup>          | n<sup>2</sup>       | 1         | Non       |               |
-| **Tri par Tas**         | n&nbsp;log(n)   | n&nbsp;log(n)          | n&nbsp;log(n)       | 1         | Non       |               |
-| **Merge sort**          | n&nbsp;log(n)   | n&nbsp;log(n)          | n&nbsp;log(n)       | n         | Oui       |               |
-| **Tri Rapide**          | n&nbsp;log(n)   | n&nbsp;log(n)          | n<sup>2</sup>       | log(n)    | Non       | le Tri Rapide est généralement effectué *in-place* avec une pile de taille O(log(n)) |
-| **Tri Shell**           | n&nbsp;log(n)   | dépend du gap séquence | n&nbsp;(log(n))<sup>2</sup>  | 1         | Non         |           |
-| **Tri Comptage**        | n + r           | n + r                  | n + r               | n + r     | Oui       | r - le plus grand nombre dans la liste |
-| **Tri Radix**           | n * k           | n * k                  | n * k               | n + k     | Non       | k - longueur du plus long index |
+| Nom               |   Meilleur    |        Moyenne         |            Pire             | Mémoire | Stable | Commentaires                                                                         |
+| ----------------- | :-----------: | :--------------------: | :-------------------------: | :-----: | :----: | :----------------------------------------------------------------------------------- |
+| **Tri Bulle**     |       n       |     n<sup>2</sup>      |        n<sup>2</sup>        |    1    |  Oui   |                                                                                      |
+| **Tri Insertion** |       n       |     n<sup>2</sup>      |        n<sup>2</sup>        |    1    |  Oui   |                                                                                      |
+| **Tri Sélection** | n<sup>2</sup> |     n<sup>2</sup>      |        n<sup>2</sup>        |    1    |  Non   |                                                                                      |
+| **Tri par Tas**   | n&nbsp;log(n) |     n&nbsp;log(n)      |        n&nbsp;log(n)        |    1    |  Non   |                                                                                      |
+| **Merge sort**    | n&nbsp;log(n) |     n&nbsp;log(n)      |        n&nbsp;log(n)        |    n    |  Oui   |                                                                                      |
+| **Tri Rapide**    | n&nbsp;log(n) |     n&nbsp;log(n)      |        n<sup>2</sup>        | log(n)  |  Non   | le Tri Rapide est généralement effectué _in-place_ avec une pile de taille O(log(n)) |
+| **Tri Shell**     | n&nbsp;log(n) | dépend du gap séquence | n&nbsp;(log(n))<sup>2</sup> |    1    |  Non   |                                                                                      |
+| **Tri Comptage**  |     n + r     |         n + r          |            n + r            |  n + r  |  Oui   | r - le plus grand nombre dans la liste                                               |
+| **Tri Radix**     |    n \* k     |         n \* k         |           n \* k            |  n + k  |  Non   | k - longueur du plus long index                                                      |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.he-IL.md b/README.he-IL.md
new file mode 100644
index 0000000000..43ef196e60
--- /dev/null
+++ b/README.he-IL.md
@@ -0,0 +1,370 @@
+# אלגוריתמים ומבני נתונים ב-JavaScript
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+![גודל המאגר](https://img.shields.io/github/repo-size/trekhleb/javascript-algorithms.svg)
+
+מאגר זה מכיל דוגמאות מבוססות JavaScript של אלגוריתמים ומבני נתונים פופולריים רבים.
+
+לכל אלגוריתם ומבנה נתונים יש README משלו עם הסברים קשורים וקישורים לקריאה נוספת (כולל קישורים לסרטוני YouTube).
+
+_קרא זאת בשפות אחרות:_
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türkçe_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+
+*☝ שים לב שפרויקט זה מיועד למטרות לימוד ומחקר בלבד, ואינו מיועד לשימוש בייצור.*
+
+## מבני נתונים
+
+מבנה נתונים הוא דרך מסוימת לארגן ולאחסן נתונים במחשב כך שניתן לגשת אליהם ולשנות אותם ביעילות. ליתר דיוק, מבנה נתונים הוא אוסף של ערכי נתונים, היחסים ביניהם, והפונקציות או הפעולות שניתן ליישם על הנתונים.
+
+זכור שלכל מבנה נתונים יש את היתרונות והחסרונות שלו. חשוב לשים לב יותר לסיבה שבגללה אתה בוחר מבנה נתונים מסוים מאשר לאופן היישום שלו.
+
+`B` - מתחיל, `A` - מתקדם
+
+* `B` [רשימה מקושרת](src/data-structures/linked-list)
+* `B` [רשימה מקושרת כפולה](src/data-structures/doubly-linked-list)
+* `B` [תור](src/data-structures/queue)
+* `B` [מחסנית](src/data-structures/stack)
+* `B` [טבלת גיבוב](src/data-structures/hash-table)
+* `B` [ערימה](src/data-structures/heap) - גרסאות מקסימום ומינימום
+* `B` [תור עדיפויות](src/data-structures/priority-queue)
+* `A` [עץ תחיליות](src/data-structures/trie)
+* `A` [עץ](src/data-structures/tree)
+  * `A` [עץ חיפוש בינארי](src/data-structures/tree/binary-search-tree)
+  * `A` [עץ AVL](src/data-structures/tree/avl-tree)
+  * `A` [עץ אדום-שחור](src/data-structures/tree/red-black-tree)
+  * `A` [עץ מקטעים](src/data-structures/tree/segment-tree) - עם דוגמאות לשאילתות מינימום/מקסימום/סכום של טווח
+  * `A` [עץ פנוויק](src/data-structures/tree/fenwick-tree) (עץ בינארי מאונדקס)
+* `A` [גרף](src/data-structures/graph) (מכוון ולא מכוון)
+* `A` [קבוצה מופרדת](src/data-structures/disjoint-set) - מבנה נתונים של איחוד-מציאה או מיזוג-מציאה
+* `A` [מסנן בלום](src/data-structures/bloom-filter)
+* `A` [מטמון LRU](src/data-structures/lru-cache/) - מטמון פחות שימוש לאחרונה (LRU)
+
+## אלגוריתמים
+
+אלגוריתם הוא מפרט חד משמעי כיצד לפתור סוג של בעיות. זוהי קבוצה של כללים המגדירים במדויק רצף של פעולות.
+
+`B` - מתחיל, `A` - מתקדם
+
+### אלגוריתמים לפי נושא
+
+* **מתמטיקה**
+  * `B` [מניפולציה על ביטים](src/algorithms/math/bits) - קביעה/עדכון/ניקוי ביטים, הכפלה/חילוק ב-2, הפיכה לשלילי וכו'
+  * `B` [נקודה צפה בינארית](src/algorithms/math/binary-floating-point) - ייצוג בינארי של מספרים בנקודה צפה
+  * `B` [פקטוריאל](src/algorithms/math/factorial)
+  * `B` [מספר פיבונאצ'י](src/algorithms/math/fibonacci) - גרסאות קלאסיות וסגורות
+  * `B` [גורמים ראשוניים](src/algorithms/math/prime-factors) - מציאת גורמים ראשוניים וספירתם באמצעות משפט הארדי-רמנוג'אן
+  * `B` [בדיקת ראשוניות](src/algorithms/math/primality-test) (שיטת החלוקה הניסיונית)
+  * `B` [אלגוריתם אוקלידס](src/algorithms/math/euclidean-algorithm) - חישוב המחלק המשותף הגדול ביותר (GCD)
+  * `B` [המכפיל המשותף הקטן ביותר](src/algorithms/math/least-common-multiple) (LCM)
+  * `B` [נפה של ארטוסתנס](src/algorithms/math/sieve-of-eratosthenes) - מציאת כל המספרים הראשוניים עד לגבול כלשהו
+  * `B` [האם חזקה של שתיים](src/algorithms/math/is-power-of-two) - בדיקה אם מספר הוא חזקה של שתיים (אלגוריתמים נאיביים וביטיים)
+  * `B` [משולש פסקל](src/algorithms/math/pascal-triangle)
+  * `B` [מספר מרוכב](src/algorithms/math/complex-number) - מספרים מרוכבים ופעולות בסיסיות עליהם
+  * `B` [רדיאן ומעלות](src/algorithms/math/radian) - המרה מרדיאנים למעלות ובחזרה
+  * `B` [חזקה מהירה](src/algorithms/math/fast-powering)
+  * `B` [שיטת הורנר](src/algorithms/math/horner-method) - הערכת פולינום
+  * `B` [מטריצות](src/algorithms/math/matrix) - מטריצות ופעולות בסיסיות על מטריצות (כפל, טרנספוזיציה וכו')
+  * `B` [מרחק אוקלידי](src/algorithms/math/euclidean-distance) - מרחק בין שתי נקודות/וקטורים/מטריצות
+  * `A` [חלוקת מספר שלם](src/algorithms/math/integer-partition)
+  * `A` [שורש ריבועי](src/algorithms/math/square-root) - שיטת ניוטון
+  * `A` [אלגוריתם π של ליו הוי](src/algorithms/math/liu-hui) - חישובי π מקורבים על בסיס N-גונים
+  * `A` [התמרת פורייה הבדידה](src/algorithms/math/fourier-transform) - פירוק פונקציה של זמן (אות) לתדרים המרכיבים אותה
+
+* **קבוצות**
+  * `B` [מכפלה קרטזית](src/algorithms/sets/cartesian-product) - מכפלה של מספר קבוצות
+  * `B` [ערבוב פישר-ייטס](src/algorithms/sets/fisher-yates) - תמורה אקראית של רצף סופי
+  * `A` [קבוצת חזקה](src/algorithms/sets/power-set) - כל תתי הקבוצות של קבוצה (פתרונות ביטיים, מעקב לאחור וקסקדה)
+  * `A` [תמורות](src/algorithms/sets/permutations) (עם ובלי חזרות)
+  * `A` [צירופים](src/algorithms/sets/combinations) (עם ובלי חזרות)
+  * `A` [תת-רצף משותף ארוך ביותר](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [תת-רצף עולה ארוך ביותר](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [על-רצף משותף קצר ביותר](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [בעיית התרמיל](src/algorithms/sets/knapsack-problem) - "0/1" ו"לא מוגבל"
+  * `A` [תת-מערך מקסימלי](src/algorithms/sets/maximum-subarray) - "כוח ברוטלי" ו"תכנות דינמי" (Kadane) גרסאות
+  * `A` [סכום צירוף](src/algorithms/sets/combination-sum) - מציאת כל הצירופים שיוצרים סכום ספציפי
+
+* **מחרוזות**
+  * `B` [מרחק המינג](src/algorithms/string/hamming-distance) - מספר העמדות שבהן הסמלים שונים
+  * `B` [פלינדרום](src/algorithms/string/palindrome) - בדיקה אם המחרוזת זהה בקריאה לאחור
+  * `A` [מרחק לוונשטיין](src/algorithms/string/levenshtein-distance) - מרחק העריכה המינימלי בין שתי רצפים
+  * `A` [אלגוריתם קנות'-מוריס-פראט](src/algorithms/string/knuth-morris-pratt) (אלגוריתם KMP) - חיפוש תת-מחרוזת (התאמת תבנית)
+  * `A` [אלגוריתם Z](src/algorithms/string/z-algorithm) - חיפוש תת-מחרוזת (התאמת תבנית)
+  * `A` [אלגוריתם רבין קארפ](src/algorithms/string/rabin-karp) - חיפוש תת-מחרוזת
+  * `A` [תת-מחרוזת משותפת ארוכה ביותר](src/algorithms/string/longest-common-substring)
+  * `A` [התאמת ביטוי רגולרי](src/algorithms/string/regular-expression-matching)
+
+* **חיפושים**
+  * `B` [חיפוש לינארי](src/algorithms/search/linear-search)
+  * `B` [חיפוש קפיצות](src/algorithms/search/jump-search) (או חיפוש בלוקים) - חיפוש במערך ממוין
+  * `B` [חיפוש בינארי](src/algorithms/search/binary-search) - חיפוש במערך ממוין
+  * `B` [חיפוש אינטרפולציה](src/algorithms/search/interpolation-search) - חיפוש במערך ממוין עם התפלגות אחידה
+
+* **מיון**
+  * `B` [מיון בועות](src/algorithms/sorting/bubble-sort)
+  * `B` [מיון בחירה](src/algorithms/sorting/selection-sort)
+  * `B` [מיון הכנסה](src/algorithms/sorting/insertion-sort)
+  * `B` [מיון ערימה](src/algorithms/sorting/heap-sort)
+  * `B` [מיון מיזוג](src/algorithms/sorting/merge-sort)
+  * `B` [מיון מהיר](src/algorithms/sorting/quick-sort) - יישומים במקום ולא במקום
+  * `B` [מיון צדפות](src/algorithms/sorting/shell-sort)
+  * `B` [מיון ספירה](src/algorithms/sorting/counting-sort)
+  * `B` [מיון בסיס](src/algorithms/sorting/radix-sort)
+  * `B` [מיון דלי](src/algorithms/sorting/bucket-sort)
+
+* **רשימות מקושרות**
+  * `B` [מעבר ישר](src/algorithms/linked-list/traversal)
+  * `B` [מעבר הפוך](src/algorithms/linked-list/reverse-traversal)
+
+* **עצים**
+  * `B` [חיפוש לעומק](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [חיפוש לרוחב](src/algorithms/tree/breadth-first-search) (BFS)
+
+* **גרפים**
+  * `B` [חיפוש לעומק](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [חיפוש לרוחב](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [אלגוריתם קרוסקל](src/algorithms/graph/kruskal) - מציאת עץ פורש מינימלי (MST) עבור גרף לא מכוון משוקלל
+  * `A` [אלגוריתם דייקסטרה](src/algorithms/graph/dijkstra) - מציאת המסלולים הקצרים ביותר לכל קודקודי הגרף מקודקוד יחיד
+  * `A` [אלגוריתם בלמן-פורד](src/algorithms/graph/bellman-ford) - מציאת המסלולים הקצרים ביותר לכל קודקודי הגרף מקודקוד יחיד
+  * `A` [אלגוריתם פלויד-וורשל](src/algorithms/graph/floyd-warshall) - מציאת המסלולים הקצרים ביותר בין כל זוגות הקודקודים
+  * `A` [זיהוי מעגל](src/algorithms/graph/detect-cycle) - עבור גרפים מכוונים ולא מכוונים (גרסאות מבוססות DFS וקבוצה מופרדת)
+  * `A` [אלגוריתם פרים](src/algorithms/graph/prim) - מציאת עץ פורש מינימלי (MST) עבור גרף לא מכוון משוקלל
+  * `A` [מיון טופולוגי](src/algorithms/graph/topological-sorting) - שיטת DFS
+  * `A` [נקודות חיתוך](src/algorithms/graph/articulation-points) - אלגוריתם טרג'ן (מבוסס DFS)
+  * `A` [גשרים](src/algorithms/graph/bridges) - אלגוריתם מבוסס DFS
+  * `A` [מסלול ומעגל אוילר](src/algorithms/graph/eulerian-path) - אלגוריתם פלרי - ביקור בכל קשת בדיוק פעם אחת
+  * `A` [מעגל המילטון](src/algorithms/graph/hamiltonian-cycle) - ביקור בכל קודקוד בדיוק פעם אחת
+  * `A` [רכיבים קשירים חזק](src/algorithms/graph/strongly-connected-components) - אלגוריתם קוסרג'ו
+  * `A` [בעיית הסוכן הנוסע](src/algorithms/graph/travelling-salesman) - המסלול הקצר ביותר האפשרי שמבקר בכל עיר וחוזר לעיר המוצא
+
+* **הצפנה**
+  * `B` [גיבוב פולינומי](src/algorithms/cryptography/polynomial-hash) - פונקציית גיבוב מתגלגלת המבוססת על פולינום
+  * `B` [צופן גדר מסילה](src/algorithms/cryptography/rail-fence-cipher) - אלגוריתם הצפנת טרנספוזיציה להצפנת הודעות
+  * `B` [צופן קיסר](src/algorithms/cryptography/caesar-cipher) - צופן החלפה פשוט
+  * `B` [צופן היל](src/algorithms/cryptography/hill-cipher) - צופן החלפה המבוסס על אלגברה לינארית
+
+* **למידת מכונה**
+  * `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 פונקציות JS פשוטות שמדגימות כיצד מכונות יכולות ללמוד באמת (תפוצה קדימה/אחורה)
+  * `B` [k-NN](src/algorithms/ml/knn) - אלגוריתם סיווג k-השכנים הקרובים ביותר
+  * `B` [k-Means](src/algorithms/ml/k-means) - אלגוריתם אשכול k-Means
+
+* **עיבוד תמונה**
+  * `B` [Seam Carving](src/algorithms/image-processing/seam-carving) - אלגוריתם שינוי גודל תמונה מודע תוכן
+
+* **סטטיסטיקה**
+  * `B` [משקל אקראי](src/algorithms/statistics/weighted-random) - בחירת פריט אקראי מהרשימה על בסיס משקלי הפריטים
+
+* **אלגוריתמים אבולוציוניים**
+  * `A` [אלגוריתם גנטי](https://github.com/trekhleb/self-parking-car-evolution) - דוגמה לאופן שבו ניתן ליישם אלגוריתם גנטי לאימון מכוניות בחניה עצמית
+
+* **לא מסווג**
+  * `B` [מגדלי האנוי](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [סיבוב מטריצה ריבועית](src/algorithms/uncategorized/square-matrix-rotation) - אלגוריתם במקום
+  * `B` [משחק הקפיצות](src/algorithms/uncategorized/jump-game) - דוגמאות למעקב לאחור, תכנות דינמי (מלמעלה למטה + מלמטה למעלה) וחמדני
+  * `B` [מסלולים ייחודיים](src/algorithms/uncategorized/unique-paths) - דוגמאות למעקב לאחור, תכנות דינמי ומבוססות על משולש פסקל
+  * `B` [מדרגות גשם](src/algorithms/uncategorized/rain-terraces) - בעיית לכידת מי גשם (גרסאות תכנות דינמי וכוח ברוטלי)
+  * `B` [מדרגות רקורסיביות](src/algorithms/uncategorized/recursive-staircase) - ספירת מספר הדרכים להגיע לראש (4 פתרונות)
+  * `B` [הזמן הטוב ביותר לקנות ולמכור מניות](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - דוגמאות לחלוקה וכיבוש ומעבר אחד
+  * `A` [בעיית N-המלכות](src/algorithms/uncategorized/n-queens)
+  * `A` [סיור הפרש](src/algorithms/uncategorized/knight-tour)
+
+### אלגוריתמים לפי פרדיגמה
+
+פרדיגמה אלגוריתמית היא שיטה או גישה כללית המונחת בבסיס התכנון של מחלקת אלגוריתמים. זוהי הפשטה גבוהה יותר מהמושג של אלגוריתם, בדיוק כפי שאלגוריתם הוא הפשטה גבוהה יותר מתוכנית מחשב.
+
+* **כוח ברוטלי** - בודק את כל האפשרויות ובוחר את הפתרון הטוב ביותר
+  * `B` [חיפוש לינארי](src/algorithms/search/linear-search)
+  * `B` [מדרגות גשם](src/algorithms/uncategorized/rain-terraces) - בעיית לכידת מי גשם
+  * `B` [מדרגות רקורסיביות](src/algorithms/uncategorized/recursive-staircase) - ספירת מספר הדרכים להגיע לראש
+  * `A` [תת-מערך מקסימלי](src/algorithms/sets/maximum-subarray)
+  * `A` [בעיית הסוכן הנוסע](src/algorithms/graph/travelling-salesman) - המסלול הקצר ביותר האפשרי שמבקר בכל עיר וחוזר לעיר המוצא
+  * `A` [התמרת פורייה הבדידה](src/algorithms/math/fourier-transform) - פירוק פונקציה של זמן (אות) לתדרים המרכיבים אותה
+
+* **חמדני** - בוחר את האפשרות הטובה ביותר בזמן הנוכחי, ללא כל התחשבות בעתיד
+  * `B` [משחק הקפיצות](src/algorithms/uncategorized/jump-game)
+  * `A` [בעיית התרמיל הלא מוגבל](src/algorithms/sets/knapsack-problem)
+  * `A` [אלגוריתם דייקסטרה](src/algorithms/graph/dijkstra) - מציאת המסלולים הקצרים ביותר לכל קודקודי הגרף
+  * `A` [אלגוריתם פרים](src/algorithms/graph/prim) - מציאת עץ פורש מינימלי (MST) עבור גרף לא מכוון משוקלל
+  * `A` [אלגוריתם קרוסקל](src/algorithms/graph/kruskal) - מציאת עץ פורש מינימלי (MST) עבור גרף לא מכוון משוקלל
+
+* **חלוקה וכיבוש** - מחלק את הבעיה לחלקים קטנים יותר ואז פותר חלקים אלה
+  * `B` [חיפוש בינארי](src/algorithms/search/binary-search)
+  * `B` [מגדלי האנוי](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [משולש פסקל](src/algorithms/math/pascal-triangle)
+  * `B` [אלגוריתם אוקלידס](src/algorithms/math/euclidean-algorithm) - חישוב המחלק המשותף הגדול ביותר (GCD)
+  * `B` [מיון מיזוג](src/algorithms/sorting/merge-sort)
+  * `B` [מיון מהיר](src/algorithms/sorting/quick-sort)
+  * `B` [חיפוש לעומק בעץ](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [חיפוש לעומק בגרף](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [מטריצות](src/algorithms/math/matrix) - יצירה ומעבר על מטריצות בצורות שונות
+  * `B` [משחק הקפיצות](src/algorithms/uncategorized/jump-game)
+  * `B` [חזקה מהירה](src/algorithms/math/fast-powering)
+  * `B` [הזמן הטוב ביותר לקנות ולמכור מניות](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - דוגמאות לחלוקה וכיבוש ומעבר אחד
+  * `A` [תמורות](src/algorithms/sets/permutations) (עם ובלי חזרות)
+  * `A` [צירופים](src/algorithms/sets/combinations) (עם ובלי חזרות)
+  * `A` [תת-מערך מקסימלי](src/algorithms/sets/maximum-subarray)
+
+* **תכנות דינמי** - בניית פתרון באמצעות תת-פתרונות שנמצאו קודם לכן
+  * `B` [מספר פיבונאצ'י](src/algorithms/math/fibonacci)
+  * `B` [משחק הקפיצות](src/algorithms/uncategorized/jump-game)
+  * `B` [מסלולים ייחודיים](src/algorithms/uncategorized/unique-paths)
+  * `B` [מדרגות גשם](src/algorithms/uncategorized/rain-terraces) - בעיית לכידת מי גשם
+  * `B` [מדרגות רקורסיביות](src/algorithms/uncategorized/recursive-staircase) - ספירת מספר הדרכים להגיע לראש
+  * `B` [Seam Carving](src/algorithms/image-processing/seam-carving) - אלגוריתם שינוי גודל תמונה מודע תוכן
+  * `A` [מרחק לוונשטיין](src/algorithms/string/levenshtein-distance) - מרחק העריכה המינימלי בין שתי רצפים
+  * `A` [תת-רצף משותף ארוך ביותר](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [תת-מחרוזת משותפת ארוכה ביותר](src/algorithms/string/longest-common-substring)
+  * `A` [תת-רצף עולה ארוך ביותר](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [על-רצף משותף קצר ביותר](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [בעיית התרמיל 0/1](src/algorithms/sets/knapsack-problem)
+  * `A` [חלוקת מספר שלם](src/algorithms/math/integer-partition)
+  * `A` [תת-מערך מקסימלי](src/algorithms/sets/maximum-subarray)
+  * `A` [אלגוריתם בלמן-פורד](src/algorithms/graph/bellman-ford) - מציאת המסלולים הקצרים ביותר לכל קודקודי הגרף
+  * `A` [אלגוריתם פלויד-וורשל](src/algorithms/graph/floyd-warshall) - מציאת המסלולים הקצרים ביותר בין כל זוגות הקודקודים
+  * `A` [התאמת ביטוי רגולרי](src/algorithms/string/regular-expression-matching)
+
+* **מעקב לאחור** - בדומה לכוח ברוטלי, מנסה לייצר את כל הפתרונות האפשריים, אך בכל פעם שאתה מייצר פתרון הבא אתה בודק אם הוא עומד בכל התנאים, ורק אז ממשיך לייצר פתרונות הבאים. אחרת, חוזר אחורה, והולך בנתיב אחר של מציאת פתרון. בדרך כלל מעבר DFS של מרחב המצבים משמש.
+  * `B` [משחק הקפיצות](src/algorithms/uncategorized/jump-game)
+  * `B` [מסלולים ייחודיים](src/algorithms/uncategorized/unique-paths)
+  * `B` [קבוצת חזקה](src/algorithms/sets/power-set) - כל תתי הקבוצות של קבוצה
+  * `A` [מעגל המילטון](src/algorithms/graph/hamiltonian-cycle) - ביקור בכל קודקוד בדיוק פעם אחת
+  * `A` [בעיית N-המלכות](src/algorithms/uncategorized/n-queens)
+  * `A` [סיור הפרש](src/algorithms/uncategorized/knight-tour)
+  * `A` [סכום צירוף](src/algorithms/sets/combination-sum) - מציאת כל הצירופים שיוצרים סכום ספציפי
+
+* **סניף וחסום** - זוכר את הפתרון בעלות הנמוכה ביותר שנמצא בכל שלב של החיפוש המעקב לאחור, ומשתמש בעלות של הפתרון בעלות הנמוכה ביותר שנמצא עד כה כגבול תחתון על העלות של פתרון בעלות מינימלית לבעיה, על מנת לפסול פתרונות חלקיים עם עלויות גדולות יותר מהפתרון בעלות הנמוכה ביותר שנמצא עד כה. בדרך כלל מעבר BFS בשילוב עם מעבר DFS של עץ מרחב המצבים משמש.
+
+## כיצד להשתמש במאגר זה
+
+**התקנת כל התלויות**
+
+```
+npm install
+```
+
+**הרצת ESLint**
+
+ייתכן שתרצה להריץ אותו כדי לבדוק את איכות הקוד.
+
+```
+npm run lint
+```
+
+**הרצת כל הבדיקות**
+
+```
+npm test
+```
+
+**הרצת בדיקות לפי שם**
+
+```
+npm test -- 'LinkedList'
+```
+
+**פתרון בעיות**
+
+אם הלינטינג או הבדיקות נכשלים, נסה למחוק את התיקייה `node_modules` ולהתקין מחדש את חבילות npm:
+
+```
+rm -rf ./node_modules
+npm i
+```
+
+בנוסף, ודא שאתה משתמש בגרסת Node נכונה (`>=16`). אם אתה משתמש ב-[nvm](https://github.com/nvm-sh/nvm) לניהול גרסאות Node, תוכל להריץ `nvm use` מתיקיית השורש של הפרויקט והגרסה הנכונה תיבחר.
+
+**שטח משחקים**
+
+אתה יכול לשחק עם מבני נתונים ואלגוריתמים בקובץ `./src/playground/playground.js` ולכתוב
+בדיקות עבורו ב-`./src/playground/__test__/playground.test.js`.
+
+לאחר מכן פשוט הרץ את הפקודה הבאה כדי לבדוק אם קוד שטח המשחקים שלך עובד כמצופה:
+
+```
+npm test -- 'playground'
+```
+
+## מידע שימושי
+
+### הפניות
+
+- [▶ מבני נתונים ואלגוריתמים ב-YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [✍🏻 סקיצות של מבני נתונים](https://okso.app/showcase/data-structures)
+
+### סימון ה-O הגדול
+
+סימון *ה-O הגדול* משמש לסיווג אלגוריתמים לפי כיצד זמן הריצה או דרישות המרחב שלהם גדלים ככל שגודל הקלט גדל.
+בתרשים שלהלן תוכל למצוא את הסדרים הנפוצים ביותר של צמיחת אלגוריתמים המצוינים בסימון ה-O הגדול.
+
+![גרפי ה-O הגדול](./assets/big-o-graph.png)
+
+מקור: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+להלן רשימה של כמה מסימוני ה-O הגדול הנפוצים ביותר והשוואות הביצועים שלהם מול גדלים שונים של נתוני קלט.
+
+| סימון ה-O הגדול | חישובים ל-10 אלמנטים | חישובים ל-100 אלמנטים | חישובים ל-1000 אלמנטים |
+| ---------------- | --------------------- | ---------------------- | ----------------------- |
+| **O(1)**         | 1                     | 1                      | 1                       |
+| **O(log N)**     | 3                     | 6                      | 9                       |
+| **O(N)**         | 10                    | 100                    | 1000                    |
+| **O(N log N)**   | 30                    | 600                    | 9000                    |
+| **O(N^2)**       | 100                   | 10000                  | 1000000                 |
+| **O(2^N)**       | 1024                  | 1.26e+29               | 1.07e+301               |
+| **O(N!)**        | 3628800               | 9.3e+157               | 4.02e+2567              |
+
+### מורכבות פעולות מבני נתונים
+
+| מבנה נתונים          | גישה    | חיפוש   | הכנסה   | מחיקה   | הערות  |
+| --------------------- | :-----: | :-----: | :-----: | :-----: | :------ |
+| **מערך**              | 1       | n       | n       | n       |         |
+| **מחסנית**            | n       | n       | 1       | 1       |         |
+| **תור**               | n       | n       | 1       | 1       |         |
+| **רשימה מקושרת**      | n       | n       | 1       | n       |         |
+| **טבלת גיבוב**        | -       | n       | n       | n       | במקרה של פונקציית גיבוב מושלמת, העלויות יהיו O(1) |
+| **עץ חיפוש בינארי**   | n       | n       | n       | n       | במקרה של עץ מאוזן, העלויות יהיו O(log(n)) |
+| **עץ B**              | log(n)  | log(n)  | log(n)  | log(n)  |         |
+| **עץ אדום-שחור**      | log(n)  | log(n)  | log(n)  | log(n)  |         |
+| **עץ AVL**            | log(n)  | log(n)  | log(n)  | log(n)  |         |
+| **מסנן בלום**         | -       | 1       | 1       | -       | תוצאות חיוביות שגויות אפשריות בעת חיפוש |
+
+### מורכבות אלגוריתמי מיון מערכים
+
+| שם                  | הטוב ביותר        | ממוצע               | הגרוע ביותר         | זיכרון  | יציב    | הערות  |
+| ------------------- | :----------------: | :-----------------: | :------------------: | :-----: | :-----: | :------ |
+| **מיון בועות**      | n                  | n<sup>2</sup>       | n<sup>2</sup>        | 1       | כן      |         |
+| **מיון הכנסה**      | n                  | n<sup>2</sup>       | n<sup>2</sup>        | 1       | כן      |         |
+| **מיון בחירה**      | n<sup>2</sup>      | n<sup>2</sup>       | n<sup>2</sup>        | 1       | לא      |         |
+| **מיון ערימה**      | n&nbsp;log(n)      | n&nbsp;log(n)       | n&nbsp;log(n)        | 1       | לא      |         |
+| **מיון מיזוג**      | n&nbsp;log(n)      | n&nbsp;log(n)       | n&nbsp;log(n)        | n       | כן      |         |
+| **מיון מהיר**       | n&nbsp;log(n)      | n&nbsp;log(n)       | n<sup>2</sup>        | log(n)  | לא      | מיון מהיר בדרך כלל מבוצע במקום עם O(log(n)) שטח מחסנית |
+| **מיון צדפות**      | n&nbsp;log(n)      | תלוי ברצף הפער      | n&nbsp;(log(n))<sup>2</sup>  | 1         | לא      |         |
+| **מיון ספירה**      | n + r              | n + r               | n + r                | n + r   | כן      | r - המספר הגדול ביותר במערך |
+| **מיון בסיס**       | n * k              | n * k               | n * k                | n + k   | כן      | k - אורך המפתח הארוך ביותר |
+
+## תומכי הפרויקט
+
+> אתה יכול לתמוך בפרויקט זה דרך ❤️️ [GitHub](https://github.com/sponsors/trekhleb) או ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+[אנשים שתומכים בפרויקט זה](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 1`
+
+## מחבר
+
+[@trekhleb](https://trekhleb.dev)
+
+כמה [פרויקטים](https://trekhleb.dev/projects/) ו[מאמרים](https://trekhleb.dev/blog/) נוספים על JavaScript ואלגוריתמים ב-[trekhleb.dev](https://trekhleb.dev)* `B` [משחק הקפיצות](src/algorithms/uncategor * `B` [חיפוש בינארי](src/algorithms
diff --git a/README.id-ID.md b/README.id-ID.md
new file mode 100644
index 0000000000..9dd908c2e6
--- /dev/null
+++ b/README.id-ID.md
@@ -0,0 +1,311 @@
+# Algoritme dan Struktur Data Javascript
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+Repositori ini berisi contoh-contoh algoritme dan struktur data yang populer menggunakan JavaScript.
+
+Setiap algoritma dan struktur data memiliki README-nya tersendiri dengan penjelasan yang berkaitan dan tautan untuk bacaan lebih lanjut (termasuk tautan menuju video YouTube).
+
+_Baca ini dalam bahasa yang lain:_
+[_English_](https://github.com/trekhleb/javascript-algorithms/),
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+_☝ Perhatikan bahwa proyek ini hanya dimaksudkan untuk tujuan pembelajaran dan riset, dan **tidak** dimaksudkan untuk digunakan sebagai produksi._
+
+## Struktur Data
+
+Struktur data adalah cara tertentu untuk mengatur dan menyimpan data dalam komputer sehingga dapat diakses dan diubah secara efisien. Lebih tepatnya, struktur data adalah kumpulan dari nilai data, relasi di antara data-data, dan fungsi atau operasi yang dapat diterapkan pada data.
+
+`P` - Pemula, `L` - Lanjutan
+
+- `P` [Senarai Berantai](src/data-structures/linked-list)
+- `P` [Senarai Berantai Ganda](src/data-structures/doubly-linked-list)
+- `P` [Antrean](src/data-structures/queue)
+- `P` [Tumpukan](src/data-structures/stack)
+- `P` [Tabel Hash](src/data-structures/hash-table)
+- `P` [_Heap_](src/data-structures/heap) - versi _heap_ maksimum dan minimum
+- `P` [Antrean Prioritas](src/data-structures/priority-queue)
+- `L` [_Trie_](src/data-structures/trie)
+- `L` [Pohon](src/data-structures/tree)
+  - `L` [Pohon Telusur Biner](src/data-structures/tree/binary-search-tree)
+  - `L` [_AVL Tree_](src/data-structures/tree/avl-tree)
+  - `L` [Pohon Merah Hitam](src/data-structures/tree/red-black-tree)
+  - `L` [_Segment Tree_](src/data-structures/tree/segment-tree) - dengan contoh min/max/sum range query
+  - `L` [Pohon Fenwick](src/data-structures/tree/fenwick-tree) (Binary Indexed Tree)
+- `L` [Graf](src/data-structures/graph) (directed dan undirected)
+- `L` [_Disjoint Set_](src/data-structures/disjoint-set)
+- `L` [_Bloom Filter_](src/data-structures/bloom-filter)
+
+## Algoritma
+
+Algoritma adalah sebuah perincian yang jelas tentang cara untuk memecahkan suatu masalah. Ia adalah sekumpulan aturan yang menjelaskan secara tepat urutan-urutan dari sebuah operasi.
+
+`P` - Pemula, `L` - Lanjutan
+
+### Algoritma Berdasarkanan Topik
+
+- **Matematika**
+  - `P` [Manipulasi Bit](src/algorithms/math/bits) - menetapkan/mendapatkan/memperbarui/menghapus bit, perkalian/pembagian dengan angka 2, membuat bilangan negatif dan lain-lain.
+  - `P` [Faktorial](src/algorithms/math/Faktorial)
+  - `P` [Bilangan Fibonacci](src/algorithms/math/fibonacci) - versi klasik dan bentuk tertutup
+  - `P` [Faktor Prima](src/algorithms/math/prime-factors) - menemukan faktor prima dan menghitungnya menggunakan teorema Hardy-Ramanujan
+  - `P` [Pengujian Bilangan Prima](src/algorithms/math/primality-test) (metode _trial division_)
+  - `P` [Algoritma Euclidean](src/algorithms/math/euclidean-algorithm) - menghitung Faktor Persekutuan Terbesar (FPB)
+  - `P` [_Least Common Multiple_](src/algorithms/math/least-common-multiple) (LCM)
+  - `P` [_Sieve of Eratosthenes_](src/algorithms/math/sieve-of-eratosthenes) - menemukan semua bilangan prima hingga batas yang ditentukan
+  - `P` [_Is Power of Two_](src/algorithms/math/is-power-of-two) - mengecek apakah sebuah bilangan adalah hasil dari pangkat dua (algoritma _naive_ dan _bitwise_)
+  - `P` [Segitiga Pascal](src/algorithms/math/pascal-triangle)
+  - `P` [Bilangan Kompleks](src/algorithms/math/complex-number) - bilangan kompleks dengan operasi dasarnya
+  - `P` [Radian & Derajat](src/algorithms/math/radian) - konversi radian ke derajat dan sebaliknya
+  - `P` [_Fast Powering_](src/algorithms/math/fast-powering)
+  - `P` [Metode Horner](src/algorithms/math/horner-method) - evaluasi polinomial
+  - `L` [Partisi Bilangan Bulat](src/algorithms/math/integer-partition)
+  - `L` [Akar Pangkat Dua](src/algorithms/math/square-root) - metode Newton
+  - `L` [Algoritma π Liu Hui](src/algorithms/math/liu-hui) - perkiraan perhitungan π berdasarkan segibanyak
+  - `L` [Transformasi Diskrit Fourier](src/algorithms/math/fourier-transform) - menguraikan fungsi waktu (sinyal) menjadi frekuensi yang menyusunnya
+- **Himpunan**
+  - `P` [Produk Kartesian](src/algorithms/sets/cartesian-product) - hasil dari beberapa himpunan
+  - `P` [Pengocokan Fisher–Yates](src/algorithms/sets/fisher-yates) - permutasi acak dari sebuah urutan terhingga
+  - `L` [Himpunan Kuasa](src/algorithms/sets/power-set) - semua himpunan bagian dari sebuah himpunan
+  - `L` [Permutasi](src/algorithms/sets/permutations) (dengan dan tanpa pengulangan)
+  - `L` [Kombinasi](src/algorithms/sets/combinations) (dengan dan tanpa pengulangan)
+  - `L` [_Longest Common Subsequence_](src/algorithms/sets/longest-common-subsequence) (LCS)
+  - `L` [_Longest Increasing Subsequence_](src/algorithms/sets/longest-increasing-subsequence)
+  - `L` [_Shortest Common Supersequence_](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  - `L` [Permasalahan Knapsack](src/algorithms/sets/knapsack-problem) - "0/1" dan yang tidak "dibatasi"
+  - `L` [Upalarik Maksimum](src/algorithms/sets/maximum-subarray) - "_Brute Force_" dan "Pemrograman Dinamis" versi Kadane
+  - `L` [_Combination Sum_](src/algorithms/sets/combination-sum) - menemukan semua kombinasi yang membentuk jumlah tertentu
+- **String**
+  - `P` [Jarak Hamming](src/algorithms/string/hamming-distance) - jumlah posisi di mana ditemukan simbol-simbol yang berbeda
+  - `L` [Algoritma Jarak Levenshtein](src/algorithms/string/levenshtein-distance) - _edit distance_ minimum antara dua urutan
+  - `L` [Algoritma Knuth–Morris–Pratt](src/algorithms/string/knuth-morris-pratt) (Algoritma KMP) - pencarian substring (pencocokan pola)
+  - `L` [Algoritma Z](src/algorithms/string/z-algorithm) - pencarian substring (pencocokan pola)
+  - `L` [Algoritma Rabin Karp](src/algorithms/string/rabin-karp) - pencarian substring
+  - `L` [_Longest Common Substring_](src/algorithms/string/longest-common-substring)
+  - `L` [Pencocokan Ekspresi Reguler](src/algorithms/string/regular-expression-matching)
+- **Pencarian**
+  - `P` [Pencarian Linier](src/algorithms/search/linear-search)
+  - `P` [Pencarian Lompat](src/algorithms/search/jump-search) (atau Block Search) - pencarian di larik tersortir
+  - `P` [Pencarian Biner](src/algorithms/search/binary-search) - pencarian di larik tersortir
+  - `P` [Pencarian Interpolasi](src/algorithms/search/interpolation-search) - pencarian di larik tersortir yang terdistribusi seragam
+- **Penyortiran**
+  - `P` [Sortir Gelembung](src/algorithms/sorting/bubble-sort)
+  - `P` [Sortir Seleksi](src/algorithms/sorting/selection-sort)
+  - `P` [Sortir Sisipan](src/algorithms/sorting/insertion-sort)
+  - `P` [Sortir _Heap_](src/algorithms/sorting/heap-sort)
+  - `P` [Sortir Gabungan](src/algorithms/sorting/merge-sort)
+  - `P` [Sortir Cepat](src/algorithms/sorting/quick-sort) - implementasi _in-place_ dan _non-in-place_
+  - `P` [Sortir Shell](src/algorithms/sorting/shell-sort)
+  - `P` [Sortir Perhitungan](src/algorithms/sorting/counting-sort)
+  - `P` [Sortir Akar](src/algorithms/sorting/radix-sort)
+- **Senarai Berantai**
+  - `P` [Lintas Lurus](src/algorithms/linked-list/traversal)
+  - `P` [Lintas Terbalik](src/algorithms/linked-list/reverse-traversal)
+- **Pohon**
+  - `P` [Pencarian Kedalaman Pertama](src/algorithms/tree/depth-first-search) (DFS)
+  - `P` [Pencarian Luas Pertama](src/algorithms/tree/breadth-first-search) (BFS)
+- **Graf**
+  - `P` [Pencarian Kedalaman Pertama](src/algorithms/graph/depth-first-search) (DFS)
+  - `P` [Pencarian Luas Pertama](src/algorithms/graph/breadth-first-search) (BFS)
+  - `P` [Algoritma Kruskal](src/algorithms/graph/kruskal) - mencari rentang pohon minimum untuk graf tidak berarah berbobot
+  - `L` [Algoritma Dijkstra](src/algorithms/graph/dijkstra) - menemukan jalur terpendek ke semua sudut graf dari sudut tunggal
+  - `L` [Algoritma Bellman-Ford](src/algorithms/graph/bellman-ford) - menemukan jalur terpendek ke semua sudut graf dari sudut tunggal
+  - `L` [Algoritma Floyd-Warshall](src/algorithms/graph/floyd-warshall) - menemukan jalur terpendek antara semua pasangan sudut
+  - `L` [Mendeteksi Siklus](src/algorithms/graph/detect-cycle) - untuk graf berarah dan tidak berarah (berdasarkan versi DFS dan _Disjoint Set_)
+  - `L` [ALgoritma Prim](src/algorithms/graph/prim) - mencari rentang pohon minimum untuk graf tidak berarah berbobot
+  - `L` [Sortir Topologi](src/algorithms/graph/topological-sorting) - metode DFS
+  - `L` [Poin Artikulasi](src/algorithms/graph/articulation-points) - Algoritma Tarjan (berdasarkan DFS)
+  - `L` [Jembatan](src/algorithms/graph/bridges) - Algoritma berdasarkan DFS
+  - `L` [Jalur dan Sirkuit Eulerian](src/algorithms/graph/eulerian-path) - Algoritma Fleury - Mengunjungi setiap tepinya tepat satu kali
+  - `L` [Siklus Hamiltonian](src/algorithms/graph/hamiltonian-cycle) - mengunjungi setiap sudutnya tepat satu kali
+  - `L` [Komponen yang Terkoneksi dengan Kuat](src/algorithms/graph/strongly-connected-components) - Algoritma Kosaraju
+  - `L` [Permasalahan Penjual Keliling](src/algorithms/graph/travelling-salesman) - kemungkinan rute terpendek untuk mengunjungi setiap kota dan kembali lagi ke kota asal
+- **Kriptografi**
+  - `P` [Polinomial Hash](src/algorithms/cryptography/polynomial-hash) - fungsi rolling hash berdasarkan polinomial
+  - `P` [Sandi Caesar](src/algorithms/cryptography/caesar-cipher) - sandi pengganti sederhana
+- **Pembelajaran Mesin**
+  - `P` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 fungsi JS sederhana yang mengilustrasikan bagaimana mesin-mesin dapat benar-benar belajar (perambatan maju/mundur)
+- **Tidak Dikategorikan**
+  - `P` [Menara Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  - `P` [Perputaran Matriks Persegi](src/algorithms/uncategorized/square-matrix-rotation) - algoritma _in-place_
+  - `P` [Permainan Melompat](src/algorithms/uncategorized/jump-game) - runut-balik, pemrograman dinamis (atas ke bawah + bawah ke atas) and contoh-contoh _greedy_
+  - `P` [_Unique Paths_](src/algorithms/uncategorized/unique-paths) - runut-balik, pemrograman dinamis and contoh-contoh beradsarkan Segitiga Pascal
+  - `P` [_Rain Terraces_](src/algorithms/uncategorized/rain-terraces) - permasalahan _trapping rain water_ (versi pemrograman dinamis and _brute force_)
+  - `P` [Tangga Rekursif](src/algorithms/uncategorized/recursive-staircase) - menghitung jumlah cara untuk mencapai ke atas tangga (4 solusi)
+  - `L` [Permainan N-Queen](src/algorithms/uncategorized/n-queens)
+  - `L` [Permainan Knight's Tour](src/algorithms/uncategorized/knight-tour)
+
+### Algoritma Berdasarkan Paradigma
+
+Paradigma algoritmik adalah sebuah metode atau pendekatan umum yang mendasari desain sebuah tingkatan algoritma. Paradigma algoritmik merupakan abstraksi yang lebih tinggi dari gagasan sebuah algoritma, seperti halnya sebuah algoritma merupakan abstraksi yang lebih tinggi dari sebuah program komputer.
+
+- **_Brute Force_** - melihat ke semua kemungkinan dan memilih solusi yang terbaik
+  - `P` [Pencarian Linier](src/algorithms/search/linear-search)
+  - `P` [_Rain Terraces_](src/algorithms/uncategorized/rain-terraces) - permasalahan _trapping rain water_
+  - `P` [Tangga Rekursif](src/algorithms/uncategorized/recursive-staircase) - menghitung jumlah cara untuk mencapai ke atas tangga
+  - `L` [Upalarik Maksimum](src/algorithms/sets/maximum-subarray)
+  - `L` [Permasalahan Penjual Keliling](src/algorithms/graph/travelling-salesman) - kemungkinan rute terpendek untuk mengunjungi setiap kota dan kembali lagi ke kota asal
+  - `L` [Transformasi Diskrit Fourier](src/algorithms/math/fourier-transform) - menguraikan fungsi waktu (sinyal) menjadi frekuensi yang menyusunnya
+- **_Greedy_** - memilih pilihan terbaik pada saat ini tanpa mempertimbangkan masa yang akan datang
+  - `P` [Permainan Melompat](src/algorithms/uncategorized/jump-game)
+  - `L` [Permasalahan Knapsack yang Tidak Dibatasi](src/algorithms/sets/knapsack-problem)
+  - `L` [Algoritma Dijkstra](src/algorithms/graph/dijkstra) - menemukan jalur terpendek ke semua sudut graf dari sudut tunggal
+  - `L` [Algoritma Prim](src/algorithms/graph/prim) - mencari rentang pohon minimum untuk graf tidak berarah berbobot
+  - `L` [Algoritma Kruskal](src/algorithms/graph/kruskal) - mencari rentang pohon minimum untuk graf tidak berarah berbobot
+- **Memecah dan Menaklukkan** - membagi masalah menjadi bagian-bagian yang kecil, lalu memcahkan bagian-bagian tersebut
+  - `P` [Pencarian Biner](src/algorithms/search/binary-search)
+  - `P` [Menara Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  - `P` [Segitiga Pascal](src/algorithms/math/pascal-triangle)
+  - `P` [Algoritma Euclidean](src/algorithms/math/euclidean-algorithm) - menghitung Faktor Persekutuan Terbesar (FPB)
+  - `P` [Sortir Gabungan](src/algorithms/sorting/merge-sort)
+  - `P` [Sortir Cepat](src/algorithms/sorting/quick-sort)
+  - `P` [Pencarian Kedalaman Pertama untuk Pohon](src/algorithms/tree/depth-first-search) (DFS)
+  - `P` [Pencarian Kedalaman Pertama untuk Graf](src/algorithms/graph/depth-first-search) (DFS)
+  - `P` [Permainan Melompat](src/algorithms/uncategorized/jump-game)
+  - `P` [_Fast Powering_](src/algorithms/math/fast-powering)
+  - `L` [Permutasi](src/algorithms/sets/permutations) (dengan dan tanpa pengulangan)
+  - `L` [Kombinasi](src/algorithms/sets/combinations) (dengan dan tanpa pengulangan)
+- **Pemrograman Dinamis** - membangun sebuah solusi menggunakan upasolusi yang ditemukan sebelumnya
+  - `P` [Bilangan Fibonacci](src/algorithms/math/fibonacci)
+  - `P` [Permainan Melompat](src/algorithms/uncategorized/jump-game)
+  - `P` [_Unique Paths_](src/algorithms/uncategorized/unique-paths)
+  - `P` [_Rain Terraces_](src/algorithms/uncategorized/rain-terraces) - permasalahan _trapping rain water_
+  - `P` [Tangga Rekursif](src/algorithms/uncategorized/recursive-staircase) - menghitung jumlah cara untuk mencapai ke atas tangga
+  - `L` [Algoritma Jarak Levenshtein](src/algorithms/string/levenshtein-distance) - _edit distance_ minimum antara dua urutan
+  - `L` [_Longest Common Subsquence_](src/algorithms/sets/longest-common-subsequence) (LCS)
+  - `L` [_Longest Common Substring_](src/algorithms/string/longest-common-substring)
+  - `L` [_Longest Increasing Subsequence_](src/algorithms/sets/longest-increasing-subsequence)
+  - `L` [_Shortest Common Supersequence_](src/algorithms/sets/shortest-common-supersequence)
+  - `L` [Permasalahan Knapsack 0/1](src/algorithms/sets/knapsack-problem)
+  - `L` [Partisi Bilangan Bulat](src/algorithms/math/integer-partition)
+  - `L` [Upalarik Maksimum](src/algorithms/sets/maximum-subarray)
+  - `L` [Algoritma Bellman-Ford](src/algorithms/graph/bellman-ford) - menemukan jalur terpendek ke semua sudut graf dari sudut tunggal
+  - `L` [Algoritma Floyd-Warshall](src/algorithms/graph/floyd-warshall) - menemukan jalur terpendek antara semua pasangan sudut
+  - `L` [Pencocokan Ekspresi Reguler](src/algorithms/string/regular-expression-matching)
+- **Runut-balik** - sama halnya dengan _brute force_, algoritma ini mencoba untuk menghasilkan segala kemungkinan solusi, tetapi setiap kali anda menghasilkan solusi selanjutnya, anda akan menguji apakah solusi tersebut memenuhi semua kondisi dan setelah itu baru akan menghasilkan solusi berikutnya. Apabila tidak, maka akan merunut-balik dan mencari solusi di jalur yang berbeda. Biasanya menggunakan lintas DFS dari ruang keadaan.
+  - `P` [Permainan Melompat](src/algorithms/uncategorized/jump-game)
+  - `P` [_Unique Paths_](src/algorithms/uncategorized/unique-paths)
+  - `P` [Himpunan Kuasa](src/algorithms/sets/power-set) - semua himpunan bagian dari sebuah himpunan
+  - `L` [Siklus Hamiltonian](src/algorithms/graph/hamiltonian-cycle) - mengunjungi setiap sudutnya tepat satu kali
+  - `L` [Permainan N-Queen](src/algorithms/uncategorized/n-queens)
+  - `L` [Permainan Knight's Tour](src/algorithms/uncategorized/knight-tour)
+  - `L` [_Combination Sum_](src/algorithms/sets/combination-sum) - menemukan semua kombinasi yang membentuk jumlah tertentu
+- **_Mencabang dan Membatasi_** - digunakan untuk membuang solusi parsial dengan biaya yang lebih besar dari solusi dengan biaya yang terendah yang ditemukan sejauh ini dengan cara mengingat solusi dengan biaya terendah yang ditemukan pada setiap tahap dari pencarian runut-balik dan menggunakan biaya dari solusi dengan biaya terendah sejauh ini sebagai batas bawah pada biaya dari solusi dengan biaya yang paling sedikit untuk permasalahannya. Biasanya menggunakan lintas BFS yang berkombinasi dengan lintas DFS dari pohon ruang keadaan.
+
+## Cara menggunakan repositori ini
+
+**Meng-_install_ semua dependensi**
+
+```
+npm install
+```
+
+**Menjalankan ESLint**
+
+Anda dapat menjalankannya untuk memeriksa kualitas kode.
+
+```
+npm run lint
+```
+
+**Menjalankan semua tes**
+
+```
+npm test
+```
+
+**Menjalankan tes berdasarkan nama**
+
+```
+npm test -- 'LinkedList'
+```
+
+**_Playground_**
+
+Anda dapat bermain dengan algoritma dan struktur data di _file_ `./src/playground/playground.js` dan menuliskan tesnya di `./src/playground/__test__/playground.test.js`.
+
+Lalu, hanya tinggal menjalankan perintah berikut untuk mengetes apakah kode _playground_ anda bekerja sesuai dengan keinginan:
+
+```
+npm test -- 'playground'
+```
+
+## Informasi Bermanfaat
+
+### Referensi
+
+[▶ Algoritma dan Struktur Data di YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### Notasi _Big O_
+
+Notasi _Big O_ digunakan untuk mengklasifikasikan algoritma berdasarkan durasi atau ruang yang dibutuhkan seiring bertambahnya _input_. Pada grafik dibawah, anda dapat menemukan urutan pertumbuhan yang paling umum dari algoritma yang ditentukan dalam notasi _Big O_.
+
+![Big O graphs](./assets/big-o-graph.png)
+
+Sumber: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+Di bawah ini adalah daftar dari beberapa notasi _Big O_ yang sering digunakan dan perbandingan kinerjanya terhadap berbagai ukuran _input data_.
+
+| Notasi _Big O_ | Komputasi untuk 10 elemen | Komputasi untuk 100 elemen | Komputasi untuk 1000 elemen |
+| -------------- | ------------------------- | -------------------------- | --------------------------- |
+| **O(1)**       | 1                         | 1                          | 1                           |
+| **O(log N)**   | 3                         | 6                          | 9                           |
+| **O(N)**       | 10                        | 100                        | 1000                        |
+| **O(N log N)** | 30                        | 600                        | 9000                        |
+| **O(N^2)**     | 100                       | 10000                      | 1000000                     |
+| **O(2^N)**     | 1024                      | 1.26e+29                   | 1.07e+301                   |
+| **O(N!)**      | 3628800                   | 9.3e+157                   | 4.02e+2567                  |
+
+### Kompleksitas Operasi Struktur Data
+
+| Struktur Data                                | Akses  | Pencarian | Penyisipan | Penghapusan | Keterangan                                               |
+| -------------------------------------------- | :----: | :-------: | :--------: | :---------: | :------------------------------------------------------- |
+| **Array (Larik)**                            |   1    |     n     |     n      |      n      |                                                          |
+| **Stack (Tumpukan)**                         |   n    |     n     |     1      |      1      |                                                          |
+| **Queue (Antrean)**                          |   n    |     n     |     1      |      1      |                                                          |
+| **Linked List (Senarai Berantai)**           |   n    |     n     |     1      |      n      |                                                          |
+| **Hash Table**                               |   -    |     n     |     n      |      n      | Apabila fungsi hash sempurna, biayanya akan menjadi O(1) |
+| **Binary Search Tree (Pohon Telusur Biner)** |   n    |     n     |     n      |      n      | Apabila pohon seimbang, biayanya akan menjadi O(log(n))  |
+| **B-Tree**                                   | log(n) |  log(n)   |   log(n)   |   log(n)    |                                                          |
+| **Red-Black Tree (Pohon Merah-Hitam)**       | log(n) |  log(n)   |   log(n)   |   log(n)    |                                                          |
+| **AVL Tree**                                 | log(n) |  log(n)   |   log(n)   |   log(n)    |                                                          |
+| **Bloom Filter**                             |   -    |     1     |     1      |      -      | Positif palsu dimungkinkan saat pencarian                |
+
+### Kompleksitas Algoritma Sortir Larik
+
+| Nama                                   |    Terbaik    |          Rata-rata           |          Terburuk           | Memori | Stabil | Keterangan                                                                        |
+| -------------------------------------- | :-----------: | :--------------------------: | :-------------------------: | :----: | :----: | :-------------------------------------------------------------------------------- |
+| **Bubble sort (Sortir Gelembung)**     |       n       |        n<sup>2</sup>         |        n<sup>2</sup>        |   1    |   Ya   |                                                                                   |
+| **Insertion sort (Sortir Sisipan)**    |       n       |        n<sup>2</sup>         |        n<sup>2</sup>        |   1    |   Ya   |                                                                                   |
+| **Selection sort (Sortir Seleksi)**    | n<sup>2</sup> |        n<sup>2</sup>         |        n<sup>2</sup>        |   1    | Tidak  |                                                                                   |
+| **Heap sort (Sortir _Heap_)**          | n&nbsp;log(n) |        n&nbsp;log(n)         |        n&nbsp;log(n)        |   1    | Tidak  |                                                                                   |
+| **Merge Sort (Sortir Gabungan)**       | n&nbsp;log(n) |        n&nbsp;log(n)         |        n&nbsp;log(n)        |   n    |   Ya   |                                                                                   |
+| **Quick sort (Sortir Cepat)**          | n&nbsp;log(n) |        n&nbsp;log(n)         |        n<sup>2</sup>        | log(n) | Tidak  | Sortir Cepat biasanya dilakukan secara _in-place_ dengan O(log(n)) ruang tumpukan |
+| **Shell sort (Sortir Shell)**          | n&nbsp;log(n) | tergantung pada jarak urutan | n&nbsp;(log(n))<sup>2</sup> |   1    | Tidak  |                                                                                   |
+| **Counting sort (Sortir Perhitungan)** |     n + r     |            n + r             |            n + r            | n + r  |   Ya   | r - angka terbesar dalam larik                                                    |
+| **Radix sort (Sortir Akar)**           |    n \* k     |            n \* k            |           n \* k            | n + k  |   Ya   | k - panjang dari kunci terpanjang                                                 |
+
+## Pendukung Proyek
+
+> Anda dapat mendukung proyek ini via ❤️️ [GitHub](https://github.com/sponsors/trekhleb) atau ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+[Orang-orang yang mendukung proyek ini](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 1`
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.it-IT.md b/README.it-IT.md
new file mode 100644
index 0000000000..d749a6aa7f
--- /dev/null
+++ b/README.it-IT.md
@@ -0,0 +1,304 @@
+# Algoritmi e Strutture Dati in Javascript
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+Questa repository contiene esempi in Javascript dei più popolari algoritmi e strutture dati .
+
+Ogni algortimo e struttura dati ha il suo README separato e la relative spiegazioni e i link per ulteriori approfondimenti (compresi quelli su YouTube).
+
+_Leggilo in altre lingue:_
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+*☝ Si noti che questo progetto è destinato ad essere utilizzato solo per l'apprendimento e la ricerca e non è destinato ad essere utilizzato per il commercio.*
+
+## Strutture Dati
+
+Una struttura dati è un particolare modo di organizzare e memorizzare i dati in un computer che  permeta di accedervi e modificarli in modo efficiente. Più precisamente, una struttura dati è una raccolta di dati, le relazioni tra di essi e le funzioni o operazioni che possono essere applicate ai dati.
+
+`P` - Principiante, `A` - Avanzato
+
+* `P` [Lista Concatenata](src/data-structures/linked-list)
+* `P` [Doppia Lista Concatenata](src/data-structures/doubly-linked-list)
+* `P` [Coda](src/data-structures/queue)
+* `P` [Pila](src/data-structures/stack)
+* `P` [Hash Table](src/data-structures/hash-table)
+* `P` [Heap](src/data-structures/heap) - versione massimo e minimo heap
+* `P` [Coda di priorità](src/data-structures/priority-queue)
+* `A` [Trie](src/data-structures/trie)
+* `A` [Albero](src/data-structures/tree)
+  * `A` [Albero binario di ricerca](src/data-structures/tree/binary-search-tree)
+  * `A` [Albero AVL](src/data-structures/tree/avl-tree)
+  * `A` [RB Albero](src/data-structures/tree/red-black-tree)
+  * `A` [Albero Segmentato](src/data-structures/tree/segment-tree) - con  min/max/sum esempi di query
+  * `A` [Albero di Fenwick](src/data-structures/tree/fenwick-tree) (Albero binario indicizzato)
+* `A` [Grafo](src/data-structures/graph) (direzionale e unidirezionale)
+* `A` [Set Disgiunto](src/data-structures/disjoint-set)
+* `A` [Filtro Bloom](src/data-structures/bloom-filter)
+
+## Algoritmi
+
+Un algoritmo è una specifica univoca per risolvere una classe di problemi. È
+un insieme di regole che definiscono con precisione una sequenza di operazioni.
+
+`P` - Principiante, `A` - Avanzato
+
+### Algoritmi per Topic
+
+* **Matematica**
+  * `P` [Manipolazione dei Bit](src/algorithms/math/bits) - set/get/update/clear bits, moltiplicazione/divisione per due, gestire numeri negativi etc.
+  * `P` [Fattoriale](src/algorithms/math/factorial)
+  * `P` [Numeri di Fibonacci](src/algorithms/math/fibonacci) - classico e forma chiusa
+  * `P` [Test di Primalità](src/algorithms/math/primality-test) (metodo del divisore)
+  * `P` [Algoritmo di Euclide](src/algorithms/math/euclidean-algorithm) - trova il massimo comune divisore (MCD)
+  * `P` [Minimo Comune Multiplo](src/algorithms/math/least-common-multiple) (MCM)
+  * `P` [Crivello di Eratostene](src/algorithms/math/sieve-of-eratosthenes) - trova i numeri i primi fino al limite indicato
+  * `P` [Potenza di due](src/algorithms/math/is-power-of-two) - controlla se il numero è una potenza di due
+  * `P` [Triangolo di Pascal](src/algorithms/math/pascal-triangle)
+  * `P` [Numeri Complessi](src/algorithms/math/complex-number) - numeri complessi e operazioni
+  * `P` [Radiante & Gradi](src/algorithms/math/radian) - conversione da radiante a gradi e viceversa
+  * `P` [Potenza di un Numero](src/algorithms/math/fast-powering)
+  * `A` [Partizione di un Intero](src/algorithms/math/integer-partition)
+  * `A` [Radice Quadrata](src/algorithms/math/square-root) - Metodo di Newton
+  * `A` [Algoritmo di Liu Hui π](src/algorithms/math/liu-hui) - calcolare π usando un poligono
+  * `A` [Trasformata Discreta di Fourier ](src/algorithms/math/fourier-transform) -decomporre una funzione di tempo (un segnale) nelle frequenze che lo compongono
+* **Set**
+  * `P` [Prodotto Cartesiano](src/algorithms/sets/cartesian-product) - moltiplicazione multipla di set
+  * `P` [Fisher–Yates Shuffle](src/algorithms/sets/fisher-yates) - permutazione casuale di un sequenza finita
+  * `A` [Power Set](src/algorithms/sets/power-set) - tutti i sottoinsiemi di un set (soluzioni bitwise e backtracking)
+  * `A` [Permutazioni](src/algorithms/sets/permutations) (con e senza ripetizioni)
+  * `A` [Combinazioni](src/algorithms/sets/combinations) (con e senza ripetizioni)
+  * `A` [Massima Sottosequenza Comune](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Massima Sottosequenza Crescente](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Minima Sottosequenza Diffusa](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [Problema dello Zaino di Knapsack](src/algorithms/sets/knapsack-problem) - "0/1" e "Senza Restrizioni"
+  * `A` [Massimo SubArray](src/algorithms/sets/maximum-subarray) - "Brute Force" e "Programmazione Dinamica" versione Kadane
+  * `A` [Somma di Combinazioni](src/algorithms/sets/combination-sum) - ricerca di tutte le combinazioni di una somma
+* **String**
+  * `P` [Distanza di Hamming](src/algorithms/string/hamming-distance) - numero di posizioni in cui i caratteri sono diversi
+  * `A` [Distanza di Levenshtein](src/algorithms/string/levenshtein-distance) - numero minimo di modifiche per rendere uguali due stringhe
+  * `A` [Algoritmo di Knuth-Morris-Pratt](src/algorithms/string/knuth-morris-pratt) (KMP) - ricerca nella sottostringa (pattern matching)
+  * `A` [Algoritmo Z](src/algorithms/string/z-algorithm) - ricerca nella sottostringa (pattern matching)
+  * `A` [Algoritmo di Rabin Karp ](src/algorithms/string/rabin-karp) - ricerca nella sottostringa
+  * `A` [Sottostringa Comune più lunga](src/algorithms/string/longest-common-substring)
+  * `A` [Espressioni Regolari](src/algorithms/string/regular-expression-matching)
+* **Searches**
+  * `P` [Ricerca Sequenziale](src/algorithms/search/linear-search)
+  * `P` [Ricerca a Salti](src/algorithms/search/jump-search) (o Ricerca a Blocchi) - per la ricerca in array ordinati
+  * `P` [Ricerca Binari](src/algorithms/search/binary-search) - per la ricerca in array ordinati
+  * `P` [Ricerca Interpolata](src/algorithms/search/interpolation-search) - per la ricerca in un array ordinato uniformemente distibuito
+* **Sorting**
+  * `P` [Bubble Sort](src/algorithms/sorting/bubble-sort)
+  * `P` [Selection Sort](src/algorithms/sorting/selection-sort)
+  * `P` [Insertion Sort](src/algorithms/sorting/insertion-sort)
+  * `P` [Heap Sort](src/algorithms/sorting/heap-sort)
+  * `P` [Merge Sort](src/algorithms/sorting/merge-sort)
+  * `P` [Quicksort](src/algorithms/sorting/quick-sort) - con e senza allocazione di ulteriore memoria
+  * `P` [Shellsort](src/algorithms/sorting/shell-sort)
+  * `P` [Counting Sort](src/algorithms/sorting/counting-sort)
+  * `P` [Radix Sort](src/algorithms/sorting/radix-sort)
+* **Lista Concatenatas**
+  * `P` [Attraversamento Lista Concatenata](src/algorithms/linked-list/traversal)
+  * `P` [Attraversamento Lista Concatenata nel senso Contrario](src/algorithms/linked-list/reverse-traversal)
+* **Alberi**
+  * `P` [Ricerca in Profondità su Alberi](src/algorithms/tree/depth-first-search) (DFS)
+  * `P` [Ricerca in Ampiezza su Alberi](src/algorithms/tree/breadth-first-search) (BFS)
+* **Grafi**
+  * `P` [Ricerca in Profondità su Grafi](src/algorithms/graph/depth-first-search) (DFS)
+  * `P` [Breadth-First Search su Grafi](src/algorithms/graph/breadth-first-search) (BFS)
+  * `P` [Algoritmo di Kruskal](src/algorithms/graph/kruskal) - ricerca dell'Albero con Minima Distanza (MST) per grafi pesati unidirezionali
+  * `A` [Algoritmo di Dijkstra](src/algorithms/graph/dijkstra) - ricerca dei percorsi più breve per raggiungere tutti i vertici del grafo da un singolo vertice
+  * `A` [Algoritmo di Bellman-Ford](src/algorithms/graph/bellman-ford) - ricerca dei percorsi più breve per raggiungere tutti i vertici del grafo da un singolo vertice
+  * `A` [Algoritmo di Floyd-Warshall](src/algorithms/graph/floyd-warshall) -  ricerca dei percorsi più brevi tra tutte le coppie di vertici
+  * `A` [Rivelamento dei Cicli](src/algorithms/graph/detect-cycle) - per grafici diretti e non diretti (basate su partizioni DFS e Disjoint Set)
+  * `A` [Algoritmo di Prim](src/algorithms/graph/prim) - ricerca dell'Albero Ricoprente Minimo (MST) per grafi unidirezionali pesati
+  * `A` [Ordinamento Topologico](src/algorithms/graph/topological-sorting) - metodo DFS
+  * `A` [Punti di Articolazione](src/algorithms/graph/articulation-points) - Algoritmo di Tarjan (basato su DFS)
+  * `A` [Bridges](src/algorithms/graph/bridges) - basato su DFS
+  * `A` [Cammino Euleriano e Circuito Euleriano](src/algorithms/graph/eulerian-path) - Algoritmo di Fleury - Visita ogni margine esattamente una volta
+  * `A` [Ciclo di Hamiltonian](src/algorithms/graph/hamiltonian-cycle) - Visita ad ogni vertice solo una volta
+  * `A` [Componenti Fortemente Connessa](src/algorithms/graph/strongly-connected-components) - algoritmo di Kosaraju
+  * `A` [Problema del Commesso Viaggiatore](src/algorithms/graph/travelling-salesman) - il percorso più breve che visita ogni città e ritorna alla città iniziale
+* **Crittografia**
+  * `P` [Hash Polinomiale](src/algorithms/cryptography/polynomial-hash) - Una funzione hash di rolling basata sul polinomio
+* **Senza categoria**
+  * `P` [Torre di Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `P` [Rotazione Matrice Quadrata](src/algorithms/uncategorized/square-matrix-rotation) - algoritmo in memoria
+  * `P` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, programmazione dinamica (top-down + bottom-up) ed esempre di greeedy
+  * `P` [Percorsi Unici](src/algorithms/uncategorized/unique-paths) - backtracking, programmazione dinamica and l'esempio del Triangolo di Pascal
+  * `P` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - problema dell'acqua piovana in trappola(versione con programmazione dinamica e brute force)
+  * `P` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - contare il numero di percorsi per arrivare in vetta(4 soluzioni)
+  * `A` [Rompicapo delle Otto Regine](src/algorithms/uncategorized/n-queens)
+  * `A` [Percorso del Cavallo](src/algorithms/uncategorized/knight-tour)
+
+### Modelli di Algoritmi
+
+ Un modello di algoritmo è un generico metodo o approcio che sta alla base della progettazione di una classe di algoritmi.
+ Si tratta di un'astrazione ancora più alta di un algoritmo, proprio come un algoritmo è un'astrazione di un programma del computer.
+
+* **Brute Force** - controlla tutte le possibilità e seleziona la migliore
+  * `P` [Ricerca Lineare](src/algorithms/search/linear-search)
+  * `P` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - problema dell'acqua piovana in trappola
+  * `P` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - contare il numero di percorsi per arrivare in vetta
+  * `A` [Massimo SubArray](src/algorithms/sets/maximum-subarray)
+  * `A` [Problema del commesso viaggiatore](src/algorithms/graph/travelling-salesman) - il percorso più breve che visita ogni città e ritorna alla città iniziale
+  * `A` [Trasformata Discreta di Fourier](src/algorithms/math/fourier-transform) - scomporre la funzione (segnale) del tempo in frequenze che la compongono
+* **Greedy** - scegliere l'opzione migliore al momento d'eleborazione dell'algoritmo, senza alcuna considerazione per il futuro
+  * `P` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `A` [Problema dello Zaino di Knapsack](src/algorithms/sets/knapsack-problem)
+  * `A` [Algoritmo di Dijkstra](src/algorithms/graph/dijkstra) - ricerca del percorso più breve tra tutti i vertici del grafo
+  * `A` [Algoritmo di Prim](src/algorithms/graph/prim) - ricerca del Minimo Albero Ricoprente per grafi pesati e unidirezionali
+  * `A` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
+* **Divide e Conquista** - divide il problema in piccole parti e risolve ogni parte
+  * `P` [Ricerca Binaria](src/algorithms/search/binary-search)
+  * `P` [Torre di Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `P` [Triangolo di Pascal](src/algorithms/math/pascal-triangle)
+  * `P` [Algoritmo di Euclide](src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
+  * `P` [Merge Sort](src/algorithms/sorting/merge-sort)
+  * `P` [Quicksort](src/algorithms/sorting/quick-sort)
+  * `P` [Albero per Ricerca in Profondità](src/algorithms/tree/depth-first-search) (DFS)
+  * `P` [Grafo per Ricerca in Profondità](src/algorithms/graph/depth-first-search) (DFS)
+  * `P` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `P` [Algoritmo di Elevamento a Potenza](src/algorithms/math/fast-powering)
+  * `A` [Permutazioni](src/algorithms/sets/permutations) (con o senza ripetizioni)
+  * `A` [Combinazioni](src/algorithms/sets/combinations) (con o senza ripetizioni)
+* **Programmazione Dinamica** - creare una soluzione utilizzando le sub-solution trovate in precedenza
+  * `P` [Numero di Fibonacci](src/algorithms/math/fibonacci)
+  * `P` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `P` [Percorsi Unici](src/algorithms/uncategorized/unique-paths)
+  * `P` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - problema dell'acqua piovana in trappola
+  * `P` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - contare il numero di percorsi per arrivare in vetta
+  * `A` [Distanza di Levenshtein](src/algorithms/string/levenshtein-distance) - minima variazione tra due sequenze
+  * `A` [La Più Lunga Frequente SottoSequenza](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [La Più Lunga Frequente SubString](src/algorithms/string/longest-common-substring)
+  * `A` [La Più Lunga SottoSequenza Crescente](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [La Più Corta e Frequente SuperSequenza](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Problema dello zaino](src/algorithms/sets/knapsack-problem)
+  * `A` [Partizione di un Intero](src/algorithms/math/integer-partition)
+  * `A` [Massimo SubArray](src/algorithms/sets/maximum-subarray)
+  * `A` [Algoritmo di Bellman-Ford](src/algorithms/graph/bellman-ford) - ricerca del percorso più breve per tutti i vertici del grafo
+  * `A` [Algoritmo di Floyd-Warshall](src/algorithms/graph/floyd-warshall) - ricerca del percorso più breve tra tutte le coppie di vertici
+  * `A` [Espressioni Regolari](src/algorithms/string/regular-expression-matching)
+* **Backtracking** -  come la brute force, provate a generare tutte le soluzioni possibili, ma ogni volta che generate la prossima soluzione testate se soddisfa tutte le condizioni e solo allora continuare a generare soluzioni successive. Altrimenti, fate marcia indietro, e andate su un percorso diverso per trovare una soluzione. Normalmente si utilizza l'algoritmo DFS.
+  * `P` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `P` [Percorsi Unici](src/algorithms/uncategorized/unique-paths)
+  * `P` [Power Set](src/algorithms/sets/power-set) - tutti i subset di un set
+  * `A` [Ciclo di Hamiltonian](src/algorithms/graph/hamiltonian-cycle) - visita di tutti i vertici solamente una volta
+  * `A` [Problema di N-Queens](src/algorithms/uncategorized/n-queens)
+  * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
+  * `A` [Combinazioni di una Somma](src/algorithms/sets/combination-sum) - trovare tutte le combinazioni che compongono una somma
+* **Branch & Bound** - ricordatevi che la soluzione meno costosa trovata ad ogni step durante il backtracking e
+il costo di usare la soluzione meno costosa trovata fino al limite inferiore al costo minimo della soluzione al problema,
+al fine di scartare soluzioni parziali con costi maggiori della soluzione meno costosa trovata .
+Di solito si usa BFS trasversale in combinazione con DFS trasversale .
+
+## Come usare questa repository
+
+**Installare tutte le dipendenze**
+```
+npm install
+```
+
+**Eseguire ESLint**
+
+Potresti usarlo per controllare la qualità del codice.
+
+```
+npm run lint
+```
+
+**Eseguire tutti i test**
+```
+npm test
+```
+
+**Eseguire un test tramite il nome**
+```
+npm test -- 'LinkedList'
+```
+
+**Playground**
+
+Se vuoi puoi giocare le strutture dati e gli algoritmi nel file ./src/playground/playground.js` e
+scrivere test nel file `./src/playground/__test__/playground.test.js`.
+
+Poi puoi semplicemente eseguire il seguente comando per testare quello che hai scritto :
+
+```
+npm test -- 'playground'
+```
+
+## Informazioni Utili
+
+### Bibliografia
+
+[▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### Notazione Big O
+
+* La notazione Big O* è usata per classificare algoritmi in base al tempo di esecuzione o ai
+requisiti di spazio che crescono in base alla crescita dell'input .
+Nella grafico qua sotto puoi trovare gli ordini di crescita più comuni degli algoritmi usando la notazione Big O.
+
+![Grafi Big O ](./assets/big-o-graph.png)
+
+Riferimento: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+Nella tabella qua sotto ci sono riportate la lista delle notazioni Big O più usate e delle loro prestazioni comparate tra differenti grandezze d'input .
+
+| Notazione Big O | Computazione con 10 elementi | Computazione con 100 elementi | Computazione con 1000 elementi  |
+| --------------- | ---------------------------- | ----------------------------- | ------------------------------- |
+| **O(1)**        | 1                            | 1                             | 1                               |
+| **O(log N)**    | 3                            | 6                             | 9                               |
+| **O(N)**        | 10                           | 100                           | 1000                            |
+| **O(N log N)**  | 30                           | 600                           | 9000                            |
+| **O(N^2)**      | 100                          | 10000                         | 1000000                         |
+| **O(2^N)**      | 1024                         | 1.26e+29                      | 1.07e+301                       |
+| **O(N!)**       | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+
+### Complessità delle Operazion sulle Strutture Dati
+
+| Struttura Dati          | Accesso   | Ricerca   | Inserimento | Rimozione | Commenti  |
+| ----------------------- | :-------: | :-------: | :--------:  | :-------: | :-------- |
+| **Array**               | 1         | n         | n           | n         |           |
+| **Pila**                | n         | n         | 1           | 1         |           |
+| **Coda**                | n         | n         | 1           | 1         |           |
+| **Lista Concatenata**   | n         | n         | 1           | n         |           |
+| **Tabella Hash**        | -         | n         | n           | n         | Nel caso di una funzione di hashing perfetta il costo sarebbe O(1)|
+| **Binary Search Tree**  | n         | n         | n           | n         | Nel caso di albero bilanciato il costo sarebbe O(log(n)) |
+| **B-Tree**              | log(n)    | log(n)    | log(n)      | log(n)    |           |
+| **Red-Black Tree**      | log(n)    | log(n)    | log(n)      | log(n)    |           |
+| **Albero AVL**          | log(n)    | log(n)    | log(n)      | log(n)    |           |
+| **Bloom Filter**        | -         | 1         | 1           | -         | Falsi positivi sono possibili durante la ricerca |
+
+### Complessità degli Algoritmi di Ordinamento di Array
+
+| Nome                  | Milgiore        | Media               | Perggiore           | Memoria   | Stabile   | Commenti  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Bubble sort**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Yes       |           |
+| **Insertion sort**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Yes       |           |
+| **Selection sort**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | No        |           |
+| **Heap sort**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | No        |           |
+| **Merge sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Yes       |           |
+| **Quick sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | No        | Quicksort viene eseguito in memoria solitamente con una pila di O(log(n)) |
+| **Shell sort**        | n&nbsp;log(n)   | dipende dagli spazi vuoti nella sequenza  | n&nbsp;(log(n))<sup>2</sup>  | 1         | No         |           |
+| **Counting sort**     | n + r           | n + r               | n + r               | n + r     | Yes       | r - numero più grande nell'array |
+| **Radix sort**        | n * k           | n * k               | n * k               | n + k     | Yes       | k - lunghezza della chiave più grande |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.ja-JP.md b/README.ja-JP.md
new file mode 100644
index 0000000000..acf9dee8f0
--- /dev/null
+++ b/README.ja-JP.md
@@ -0,0 +1,303 @@
+# JavaScriptアルゴリズムとデータ構造
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+このリポジトリには、JavaScriptベースの一般的なアルゴリズムとデータ構造に関する多数のサンプルが含まれています。
+
+
+各アルゴリズムとデータ構造には独自のREADMEがあります。
+関連する説明、そして参考資料 (YouTube動画)も含まれています。
+
+_Read this in other languages:_
+[_English_](https://github.com/trekhleb/javascript-algorithms/),
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+## データ構造
+
+データ構造は、データ値、データ値との間の関係、
+そして、データを扱うことができる関数と演算の集合で、
+データを特定の方法で構成して保存することで、より効率的に
+アクセスして変更することができます。
+
+`B` - 初心者, `A` - 上級
+
+* `B` [リンクされたリスト](src/data-structures/linked-list)
+* `B` [二重リンクリスト](src/data-structures/doubly-linked-list)
+* `B` [キュー](src/data-structures/queue)
+* `B` [スタック](src/data-structures/stack)
+* `B` [ハッシュ表](src/data-structures/hash-table)
+* `B` [ヒープ](src/data-structures/heap) - max and min heap versions
+* `B` [優先度キュー](src/data-structures/priority-queue)
+* `A` [トライ](src/data-structures/trie)
+* `A` [ツリー](src/data-structures/tree)
+  * `A` [バイナリ検索ツリー](src/data-structures/tree/binary-search-tree)
+  * `A` [AVLツリー](src/data-structures/tree/avl-tree)
+  * `A` [赤黒のツリー](src/data-structures/tree/red-black-tree)
+  * `A` [セグメントツリー](src/data-structures/tree/segment-tree) - with min/max/sum range queries examples
+  * `A` [フェンウィック・ツリー](src/data-structures/tree/fenwick-tree) (Binary Indexed Tree)
+* `A` [グラフ](src/data-structures/graph) (both directed and undirected)
+* `A` [分離集合](src/data-structures/disjoint-set)
+* `A` [ブルームフィルタ](src/data-structures/bloom-filter)
+
+## アルゴリズム
+
+アルゴリズムとは、問題のクラスをどのように解決するかの明確な仕様です。
+一連の操作を正確に定義する一連のルールです。
+
+`B` - 初心者, `A` - 上級
+
+### トピック別アルゴリズム
+
+* **数学**
+  * `B` [ビット操作](src/algorithms/math/bits) - set/get/update/clear bits, 2つの乗算/除算, 否定的にする. 等
+  * `B` [因果関係](src/algorithms/math/factorial)
+  * `B` [フィボナッチ数](src/algorithms/math/fibonacci) - クラシックとクローズドフォームのバージョン
+  * `B` [素数性テスト](src/algorithms/math/primality-test) (trial division 方法)
+  * `B` [ユークリッドアルゴリズム](src/algorithms/math/euclidean-algorithm) - 最大公約数を計算する (GCD)
+  * `B` [最小公倍数](src/algorithms/math/least-common-multiple) (LCM)
+  * `B` [エラトステネスのふるい](src/algorithms/math/sieve-of-eratosthenes) - 与えられた限度まですべての素数を見つける
+  * `B` [Is Power of Two](src/algorithms/math/is-power-of-two) - 数値が2の累乗であるかどうかを調べる（単純なアルゴリズムとビットごとのアルゴリズム）
+  * `B` [パスカルの三角形](src/algorithms/math/pascal-triangle)
+  * `B` [複素数](src/algorithms/math/complex-number) - 複素数とその基本演算
+  * `B` [ラジアン＆度](src/algorithms/math/radian) - 度数と逆方向の変換に対するラジアン
+  * `B` [高速電力供給](src/algorithms/math/fast-powering)
+  * `A` [整数パーティション](src/algorithms/math/integer-partition)
+  * `A` [Liu Hui π アルゴリズム](src/algorithms/math/liu-hui) - N-gonsに基づく近似π計算
+  * `A` [離散フーリエ変換](src/algorithms/math/fourier-transform) - 時間（信号）の関数をそれを構成する周波数に分解する
+* **セット**
+  * `B` [デカルト積 ](src/algorithms/sets/cartesian-product) - 複数の積の積
+  * `B` [Fisher–Yates Shuffle](src/algorithms/sets/fisher-yates) - 有限シーケンスのランダム置換
+  * `A` [パワーセット](src/algorithms/sets/power-set) - セットのすべてのサブセット（ビットごとのソリューションとバックトラッキングソリューション）
+  * `A` [順列](src/algorithms/sets/permutations) （繰り返しの有無にかかわらず）
+  * `A` [組み合わせ](src/algorithms/sets/combinations) （繰返しあり、繰返しなし）
+  * `A` [最長共通部分列](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [最長増加サブシーケンス](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [最短共通スーパーシーケンス](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [ナップザック問題 ](src/algorithms/sets/knapsack-problem) - 「0/1」と「非結合」問題
+  * `A` [最大サブアレイ](src/algorithms/sets/maximum-subarray) - 「ブルートフォース」と「ダイナミックプログラミング」（Kadane's版）
+  * `A` [組み合わせ合計](src/algorithms/sets/combination-sum) - 特定の合計を構成するすべての組み合わせを見つける
+* **文字列**
+  * `B` [ハミング距離](src/algorithms/string/hamming-distance) - シンボルが異なる位置の数
+  * `A` [レーベンシュタイン距離](src/algorithms/string/levenshtein-distance) - 2つのシーケンス間の最小編集距離
+  * `A` [Knuth-Morris-Prattアルゴリズム](src/algorithms/string/knuth-morris-pratt) (KMP Algorithm) - 部分文字列検索 (pattern matching)
+  * `A` [Z アルゴリズム](src/algorithms/string/z-algorithm) - 部分文字列検索 (pattern matching)
+  * `A` [Rabin Karpアルゴリズム](src/algorithms/string/rabin-karp) - 部分文字列検索
+  * `A` [最長共通部分文字列](src/algorithms/string/longest-common-substring)
+  * `A` [正規表現マッチング](src/algorithms/string/regular-expression-matching)
+* **検索**
+  * `B` [リニアサーチ](src/algorithms/search/linear-search)
+  * `B` [ジャンプ検索](src/algorithms/search/jump-search) (Jump Search) - ソートされた配列で検索
+  * `B` [バイナリ検索](src/algorithms/search/binary-search) - ソートされた配列で検索
+  * `B` [補間探索](src/algorithms/search/interpolation-search) - 一様分布のソート配列で検索する
+* **並べ替え**
+  * `B` [バブルソート](src/algorithms/sorting/bubble-sort)
+  * `B` [選択ソート](src/algorithms/sorting/selection-sort)
+  * `B` [挿入ソート](src/algorithms/sorting/insertion-sort)
+  * `B` [ヒープソート](src/algorithms/sorting/heap-sort)
+  * `B` [マージソート](src/algorithms/sorting/merge-sort)
+  * `B` [クイックソート](src/algorithms/sorting/quick-sort) -インプレースおよび非インプレース・インプリメンテーション
+  * `B` [シェルソート](src/algorithms/sorting/shell-sort)
+  * `B` [並べ替えを数える](src/algorithms/sorting/counting-sort)
+  * `B` [基数ソート](src/algorithms/sorting/radix-sort)
+* **リンクされたリスト**
+  * `B` [ストレートトラバーサル](src/algorithms/linked-list/traversal)
+  * `B` [逆方向のトラバーサル](src/algorithms/linked-list/reverse-traversal)
+* **ツリー**
+  * `B` [深度優先検索](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [幅優先検索](src/algorithms/tree/breadth-first-search) (BFS)
+* **グラフ**
+  * `B` [深度優先検索](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [幅優先検索](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [Kruskalのアルゴリズム](src/algorithms/graph/kruskal) - 重み付き無向グラフの最小スパニングツリー（MST）の発見
+  * `A` [Dijkstraアルゴリズム](src/algorithms/graph/dijkstra) - 単一の頂点からすべてのグラフ頂点への最短経路を見つける
+  * `A` [Bellman-Fordアルゴリズム](src/algorithms/graph/bellman-ford) - 単一の頂点からすべてのグラフ頂点への最短経路を見つける
+  * `A` [Floyd-Warshallアルゴリズム](src/algorithms/graph/floyd-warshall) - すべての頂点ペア間の最短経路を見つける
+  * `A` [Detect Cycle](src/algorithms/graph/detect-cycle) - 有向グラフと無向グラフの両方（DFSおよびディスジョイントセットベースのバージョン）
+  * `A` [プリムのアルゴリズム](src/algorithms/graph/prim) - 重み付き無向グラフの最小スパニングツリー（MST）の発見
+  * `A` [トポロジカルソート](src/algorithms/graph/topological-sorting) - DFSメソッド
+  * `A` [アーティキュレーションポイント](src/algorithms/graph/articulation-points) - Tarjanのアルゴリズム（DFSベース）
+  * `A` [ブリッジ ](src/algorithms/graph/bridges) - DFSベースのアルゴリズム
+  * `A` [オイラーパスとオイラー回路](src/algorithms/graph/eulerian-path) - フルリーアルゴリズム - すべてのエッジを正確に1回訪問する
+  * `A` [ハミルトニアンサイクル](src/algorithms/graph/hamiltonian-cycle) - すべての頂点を正確に1回訪問する
+  * `A` [強連結成分](src/algorithms/graph/strongly-connected-components) - コサラジュのアルゴリズム
+  * `A` [トラベリングセールスマン問題](src/algorithms/graph/travelling-salesman) - 各都市を訪問し、起点都市に戻る最短経路
+* **暗号**
+  * `B` [多項式ハッシュ](src/algorithms/cryptography/polynomial-hash) - 関数多項式に基づくハッシュ関数
+* **未分類**
+  * `B` [ハノイの塔](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [正方行列回転](src/algorithms/uncategorized/square-matrix-rotation) - インプレイスアルゴリズム
+  * `B` [ジャンプゲーム](src/algorithms/uncategorized/jump-game) - バックトラック、ダイナミックプログラミング（トップダウン+ボトムアップ）、欲張りの例
+  * `B` [ユニークなパス](src/algorithms/uncategorized/unique-paths) - バックトラック、動的プログラミング、PascalのTriangleベースの例
+  * `B` [レインテラス](src/algorithms/uncategorized/rain-terraces) - トラップ雨水問題（ダイナミックプログラミングとブルートフォースバージョン）
+  * `B` [再帰的階段](src/algorithms/uncategorized/recursive-staircase) - 上に到達する方法の数を数える（4つのソリューション）
+  * `A` [N-クイーンズ問題](src/algorithms/uncategorized/n-queens)
+  * `A` [ナイトツアー](src/algorithms/uncategorized/knight-tour)
+
+### Paradigmによるアルゴリズム
+
+アルゴリズムパラダイムは、あるクラスのアルゴリズムの設計の基礎をなす一般的な方法またはアプローチである。それは、アルゴリズムがコンピュータプログラムよりも高い抽象であるのと同様に、アルゴリズムの概念よりも高い抽象である。
+* **ブルートフォース** - すべての可能性を見て最適なソリューションを選択する
+  * `B` [線形探索](src/algorithms/search/linear-search)
+  * `B` [レインテラス](src/algorithms/uncategorized/rain-terraces) - 雨水問題
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - 先頭に到達する方法の数を数えます
+  * `A` [最大サブアレイ](src/algorithms/sets/maximum-subarray)
+  * `A` [旅行セールスマン問題](src/algorithms/graph/travelling-salesman) - 各都市を訪れ、起点都市に戻る最短ルート
+  * `A` [離散フーリエ変換](src/algorithms/math/fourier-transform) - 時間（信号）の関数をそれを構成する周波数に分解する
+* **欲張り** - 未来を考慮することなく、現時点で最適なオプションを選択する
+  * `B` [ジャンプゲーム](src/algorithms/uncategorized/jump-game)
+  * `A` [結合されていないナップザック問題](src/algorithms/sets/knapsack-problem)
+  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - すべてのグラフ頂点への最短経路を見つける
+  * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - 重み付き無向グラフの最小スパニングツリー（MST）を見つける
+  * `A` [Kruskalのアルゴリズム](src/algorithms/graph/kruskal) - 重み付き無向グラフの最小スパニングツリー（MST）を見つける
+* **分割と征服** - 問題をより小さな部分に分割し、それらの部分を解決する
+  * `B` [バイナリ検索](src/algorithms/search/binary-search)
+  * `B` [ハノイの塔](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [パスカルの三角形](src/algorithms/math/pascal-triangle)
+  * `B` [ユークリッドアルゴリズム](src/algorithms/math/euclidean-algorithm) - GCD（Greatest Common Divisor）を計算する
+  * `B` [マージソート](src/algorithms/sorting/merge-sort)
+  * `B` [クイックソート](src/algorithms/sorting/quick-sort)
+  * `B` [ツリーの深さ優先検索](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [グラフの深さ優先検索](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [ジャンプゲーム](src/algorithms/uncategorized/jump-game)
+  * `B` [高速電力供給](src/algorithms/math/fast-powering)
+  * `A` [順列](src/algorithms/sets/permutations) （繰り返しの有無にかかわらず）
+  * `A` [組み合わせ](src/algorithms/sets/combinations)（繰返しあり、繰返しなし）
+* **動的プログラミング** - 以前に発見されたサブソリューションを使用してソリューションを構築する
+  * `B` [フィボナッチ数](src/algorithms/math/fibonacci)
+  * `B` [ジャンプゲーム](src/algorithms/uncategorized/jump-game)
+  * `B` [ユニークなパス](src/algorithms/uncategorized/unique-paths)
+  * `B` [雨テラス](src/algorithms/uncategorized/rain-terraces) - トラップ雨水問題
+  * `B` [再帰的階段](src/algorithms/uncategorized/recursive-staircase) - 上に到達する方法の数を数える
+  * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - 2つのシーケンス間の最小編集距離
+  * `A` [最長共通部分列](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [最長共通部分文字列](src/algorithms/string/longest-common-substring)
+  * `A` [最長増加サブシーケンス](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [最短共通共通配列](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [0/1ナップザック問題](src/algorithms/sets/knapsack-problem)
+  * `A` [整数パーティション](src/algorithms/math/integer-partition)
+  * `A` [最大サブアレイ](src/algorithms/sets/maximum-subarray)
+  * `A` [Bellman-Fordアルゴリズム](src/algorithms/graph/bellman-ford) - すべてのグラフ頂点への最短経路を見つける
+  * `A` [Floyd-Warshallアルゴリズム](src/algorithms/graph/floyd-warshall) - すべての頂点ペア間の最短経路を見つける
+  * `A` [正規表現マッチング](src/algorithms/string/regular-expression-matching)
+* **バックトラッキング** - ブルートフォースと同様に、可能なすべてのソリューションを生成しようとしますが、
+  次のソリューションを生成するたびにすべての条件を満たすかどうかをテストし、それ以降は引き続きソリューションを生成します。
+  それ以外の場合は、バックトラックして、解決策を見つける別の経路に進みます。
+  通常、状態空間のDFSトラバーサルが使用されています。
+  * `B` [ジャンプゲーム](src/algorithms/uncategorized/jump-game)
+  * `B` [ユニークなパス](src/algorithms/uncategorized/unique-paths)
+  * `B` [パワーセット](src/algorithms/sets/power-set) - セットのすべてのサブセット
+  * `A` [ハミルトニアンサイクル](src/algorithms/graph/hamiltonian-cycle) - すべての頂点を正確に1回訪問する
+  * `A` [N-クイーンズ問題](src/algorithms/uncategorized/n-queens)
+  * `A` [ナイトツアー](src/algorithms/uncategorized/knight-tour)
+  * `A` [組み合わせ合計](src/algorithms/sets/combination-sum) - 特定の合計を構成するすべての組み合わせを見つける
+* **ブランチ＆バウンド** - バックトラック検索の各段階で見つかった最もコストの低いソリューションを覚えておいて、最もコストの低いソリューションのコストを使用します。これまでに発見された最もコストの低いソリューションよりも大きなコストで部分ソリューションを破棄するように指示します。通常、状態空間ツリーのDFSトラバーサルと組み合わせたBFSトラバーサルが使用されています。
+
+## このリポジトリの使い方
+
+**すべての依存関係をインストールする**
+```
+npm install
+```
+
+**ESLintを実行する**
+
+これを実行してコードの品質をチェックすることができます。
+
+```
+npm run lint
+```
+
+**すべてのテストを実行する**
+```
+npm test
+```
+
+**名前でテストを実行する**
+```
+npm test -- 'LinkedList'
+```
+
+**playground**
+
+データ構造とアルゴリズムを `./src/playground/playground.js` ファイルで再生し、
+それに対するテストを書くことができ `./src/playground/__test__/playground.test.js`.
+
+次に、次のコマンドを実行して、遊び場コードが正常に動作するかどうかをテストします。
+
+```
+npm test -- 'playground'
+```
+
+## 有用な情報
+
+### 参考文献
+
+[▶ データ構造とアルゴリズム on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### ビッグO表記
+
+*Big O表記法は* 入力サイズが大きくなるにつれて実行時間やスペース要件がどのように増加するかに応じてアルゴリズムを分類するために使用されます。下のチャートでは、Big O表記で指定されたアルゴリズムの成長の最も一般的な順序を見つけることができます。
+
+![Big Oグラフ](./assets/big-o-graph.png)
+
+出典: [Big Oチートシート](http://bigocheatsheet.com/).
+
+以下は、最も使用されているBig O表記のリストと、入力データのさまざまなサイズに対するパフォーマンス比較です。
+
+| Big O Notation | Computations for 10 elements | Computations for 100 elements | Computations for 1000 elements  |
+| -------------- | ---------------------------- | ----------------------------- | ------------------------------- |
+| **O(1)**       | 1                            | 1                             | 1                               |
+| **O(log N)**   | 3                            | 6                             | 9                               |
+| **O(N)**       | 10                           | 100                           | 1000                            |
+| **O(N log N)** | 30                           | 600                           | 9000                            |
+| **O(N^2)**     | 100                          | 10000                         | 1000000                         |
+| **O(2^N)**     | 1024                         | 1.26e+29                      | 1.07e+301                       |
+| **O(N!)**      | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+
+### データ構造操作の複雑さ
+
+| Data Structure          | Access    | Search    | Insertion | Deletion  | Comments  |
+| ----------------------- | :-------: | :-------: | :-------: | :-------: | :-------- |
+| **Array**               | 1         | n         | n         | n         |           |
+| **Stack**               | n         | n         | 1         | 1         |           |
+| **Queue**               | n         | n         | 1         | 1         |           |
+| **Linked List**         | n         | n         | 1         | 1         |           |
+| **Hash Table**          | -         | n         | n         | n         | In case of perfect hash function costs would be O(1) |
+| **Binary Search Tree**  | n         | n         | n         | n         | In case of balanced tree costs would be O(log(n)) |
+| **B-Tree**              | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Red-Black Tree**      | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **AVL Tree**            | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Bloom Filter**        | -         | 1         | 1         | -         | False positives are possible while searching |
+
+### 配列の並べ替えアルゴリズムの複雑さ
+
+| Name                  | Best            | Average             | Worst               | Memory    | Stable    | Comments  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Bubble sort**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Yes       |           |
+| **Insertion sort**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Yes       |           |
+| **Selection sort**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | No        |           |
+| **Heap sort**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | No        |           |
+| **Merge sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Yes       |           |
+| **Quick sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | No        | Quicksort is usually done in-place with O(log(n)) stack space |
+| **Shell sort**        | n&nbsp;log(n)   | depends on gap sequence   | n&nbsp;(log(n))<sup>2</sup>  | 1         | No         |           |
+| **Counting sort**     | n + r           | n + r               | n + r               | n + r     | Yes       | r - biggest number in array |
+| **Radix sort**        | n * k           | n * k               | n * k               | n + k     | Yes       | k - length of longest key |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.ko-KR.md b/README.ko-KR.md
index 647cda72b0..d4b0d4ef14 100644
--- a/README.ko-KR.md
+++ b/README.ko-KR.md
@@ -1,31 +1,38 @@
 # JavaScript 알고리즘 및 자료 구조
 
-[![Build Status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
 
 이 저장소에는 많이 알려진 알고리즘 및 자료 구조의 Javascript 기반 예제를 담고 있습니다.
 
-각 알고리즘과 자료 구조에 대해 연관되어있는 설명이 README에 작성되어 있으며,
+각 알고리즘과 자료 구조에 대해 연관되어 있는 설명이 README에 작성되어 있으며,
 링크를 통해 더 자세한 설명을 만날 수 있습니다. (관련된 YouTube 영상도 포함).
 
 _Read this in other languages:_
 [_English_](https://github.com/trekhleb/javascript-algorithms/),
 [_简体中文_](README.zh-CN.md),
 [_繁體中文_](README.zh-TW.md),
+[_日本語_](README.ja-JP.md),
 [_Polski_](README.pl-PL.md),
 [_Français_](README.fr-FR.md),
 [_Español_](README.es-ES.md),
-[_Português_](README.pt-BR.md)
-
-> 우리는 주요 알고리즘에 대해 더 자세한 설명을 담은 책을 제작 중입니다.
-만약 “JavaScript Algorithms” 책이 언제 출시되는지 알고 싶다면
-, [여기를 클릭하세요.](https://upscri.be/402324/).
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
 
 ## 자료 구조
 
 자료 구조는 데이터를 특정 방식으로 구성하고 저장함으로써 더 효율적으로
 접근하고 수정할 수 있게 해줍니다. 간단히 말해, 자료 구조는 데이터 값들,
-데이터 간의 관계, 그리고 데이터를 다룰 수 있는 함수와 작업의 모임입니다. 
+데이터 간의 관계, 그리고 데이터를 다룰 수 있는 함수와 작업의 모임입니다.
 
 
 `B` - 입문자, `A` - 숙련자
@@ -50,8 +57,8 @@ _Read this in other languages:_
 
 ## 알고리즘
 
-알고리즘은 어떤 종류의 문제를 풀 수 있는 정확한 방법이며, 
-일련의 작업을 정확하게 정의해 놓은 규칙들입니다. 
+알고리즘은 어떤 종류의 문제를 풀 수 있는 정확한 방법이며,
+일련의 작업을 정확하게 정의해 놓은 규칙들입니다.
 
 `B` - 입문자, `A` - 숙련자
 
@@ -59,7 +66,7 @@ _Read this in other languages:_
 
 * **Math**
   * `B` [Bit Manipulation](src/algorithms/math/bits) - set/get/update/clear bits, 2의 곱 / 나누기, 음수로 만들기 etc.
-  * `B` [팩토리얼](src/algorithms/math/factorial) 
+  * `B` [팩토리얼](src/algorithms/math/factorial)
   * `B` [피보나치 수](src/algorithms/math/fibonacci)
   * `B` [소수 판별](src/algorithms/math/primality-test) (trial division 방식)
   * `B` [유클리드 호제법](src/algorithms/math/euclidean-algorithm) - 최대공약수 (GCD)
@@ -126,7 +133,7 @@ _Read this in other languages:_
 * **Uncategorized**
   * `B` [하노이 탑](src/algorithms/uncategorized/hanoi-tower)
   * `B` [정방 행렬 회전](src/algorithms/uncategorized/square-matrix-rotation) - 제자리(in-place) 알고리즘
-  * `B` [점프 게임](src/algorithms/uncategorized/jump-game) - 백트래킹, 동적계획법 (top-down + bottom-up), 탐욕 알고리즘 예제 
+  * `B` [점프 게임](src/algorithms/uncategorized/jump-game) - 백트래킹, 동적계획법 (top-down + bottom-up), 탐욕 알고리즘 예제
   * `B` [Unique 경로](src/algorithms/uncategorized/unique-paths) - 백트래킹, 동적계획법, 파스칼 삼각형에 기반한 예제
   * `B` [빗물 담기 문제](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem (동적계획법, 브루트포스 버전)
   * `A` [N-Queens 문제](src/algorithms/uncategorized/n-queens)
@@ -134,9 +141,7 @@ _Read this in other languages:_
 
 ### 패러다임별 알고리즘
 
-알고리즘의 패러다임은 어떤 종류의 알고리즘을 설계할 때 기초가 되는 일반적인 방법 혹은 접근법입니다.
-알고리즘이 컴퓨터의 프로그램 보다 더 추상적인 것처럼 알고리즘의 패러다임은 어떤 알고리즘의 
-개념보다 추상적인 것입니다.
+알고리즘 패러다임 이란, 알고리즘이 주어진 문제를 해결하기 위해 채택한 기초가 되는 일반적인 방법 혹은 접근법입니다. 알고리즘이 해결하는 문제나 알고리즘의 동작 방식이 완전히 다르더라도,알고리즘의 동작 원칙이 같으면 같은 패러다음을 사용했다고 말할 수 있으며, 주로 알고리즘을 구분하는 기준으로 쓰인다. 알고리즘이 일반적인 컴퓨터의 프로그램에 대한 개념보다 보다 더 추상적인 개념인 것처럼 알고리즘의 패러다임은 명확히 정의된 수학적 실체가 있는 것이 아니기 때문에 그 어떤 알고리즘의 개념보다도 훨씬 추상적인 개념입니다.
 
 * **브루트 포스(Brute Force)** - 가능한 모든 경우를 탐색한 뒤 최적을 찾아내는 방식입니다.
   * `B` [선형 탐색](src/algorithms/search/linear-search)
@@ -184,11 +189,11 @@ _Read this in other languages:_
   * `A` [N-Queens 문제](src/algorithms/uncategorized/n-queens)
   * `A` [기사의 여행](src/algorithms/uncategorized/knight-tour)
   * `A` [조합 합](src/algorithms/sets/combination-sum) - 특정 합을 구성하는 모든 조합 찾기
-* **분기 한정법** - 백트래킹으로 찾은 각 단계의 최소 비용 해결법을 기억해 두고 있다가, 이 비용을 이용해서 더 낮은 최소 비용을 찾습니다. 기억해둔 최소 비용을 이용해 더 높은 비용이 드는 해결법은 더이상 탐색하지 않습니다. 보통 상태 정보를 사진 DFS 를 이용한 BFS 방식에서 사용됩니다.
+* **분기 한정법** - 백트래킹으로 찾은 각 단계의 최소 비용이 드는 해를 기억해 두고 있다가, 이 비용을 이용해서 더 낮은 최적의 해를 찾습니다. 기억해둔 최소 비용들을 이용해 더 높은 비용이 드는 해결법을 탐색 안함으로써 불필요한 시간 소모를 줄입니다. 보통 상태 공간 트리의 DFS 탐색을 이용한 BFS 탐색 방식에서 사용됩니다.
 
 ## 이 저장소의 사용법
 
-**모든 의존성 설치**
+**모든 종속 모듈들 설치**
 ```
 npm install
 ```
@@ -275,3 +280,5 @@ Source: [Big O Cheat Sheet](http://bigocheatsheet.com/).
 | **셸 정렬**            | n&nbsp;log(n)   | 간격 순서에 영향을 받습니다.   | n&nbsp;(log(n))<sup>2</sup>  | 1         | No         |           |
 | **계수 정렬**          | n + r           | n + r               | n + r               | n + r     | Yes              | r - 배열내 가장 큰 수 |
 | **기수 정렬**          | n * k           | n * k               | n * k               | n + k     | Yes              | k - 키값의 최대 길이 |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.md b/README.md
index f762179666..3e5c5cc096 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,17 @@
 # JavaScript Algorithms and Data Structures
 
-[![Build Status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+> 🇺🇦 UKRAINE [IS BEING ATTACKED](https://war.ukraine.ua/) BY RUSSIAN ARMY. CIVILIANS ARE GETTING KILLED. RESIDENTIAL AREAS ARE GETTING BOMBED.
+> - Help Ukraine via:
+>   - [Serhiy Prytula Charity Foundation](https://prytulafoundation.org/en/)
+>   - [Come Back Alive Charity Foundation](https://savelife.in.ua/en/donate-en/)
+>   - [National Bank of Ukraine](https://bank.gov.ua/en/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi)
+> - More info on [war.ukraine.ua](https://war.ukraine.ua/) and [MFA of Ukraine](https://twitter.com/MFA_Ukraine)
+
+<hr/>
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+![repo size](https://img.shields.io/github/repo-size/trekhleb/javascript-algorithms.svg)
 
 This repository contains JavaScript based examples of many
 popular algorithms and data structures.
@@ -14,14 +24,24 @@ _Read this in other languages:_
 [_简体中文_](README.zh-CN.md),
 [_繁體中文_](README.zh-TW.md),
 [_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
 [_Polski_](README.pl-PL.md),
 [_Français_](README.fr-FR.md),
 [_Español_](README.es-ES.md),
-[_Português_](README.pt-BR.md)
-
-> We’re writing a book that will clearly explain, in detail, the main algorithms. 
-If you’d like to be notified when the “JavaScript Algorithms” book 
-launches, [click here](https://upscri.be/402324/).
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türkçe_](README.tr-TR.md),
+[_Italiano_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md),
+[_עברית_](README.he-IL.md)
+
+*☝ Note that this project is meant to be used for learning and researching purposes
+only, and it is **not** meant to be used for production.*
 
 ## Data Structures
 
@@ -30,6 +50,8 @@ be accessed and modified efficiently. More precisely, a data structure is a coll
 values, the relationships among them, and the functions or operations that can be applied to
 the data.
 
+Remember that each data has its own trade-offs. And you need to pay attention more to why you're choosing a certain data structure than to how to implement it.
+
 `B` - Beginner, `A` - Advanced
 
 * `B` [Linked List](src/data-structures/linked-list)
@@ -47,8 +69,9 @@ the data.
   * `A` [Segment Tree](src/data-structures/tree/segment-tree) - with min/max/sum range queries examples
   * `A` [Fenwick Tree](src/data-structures/tree/fenwick-tree) (Binary Indexed Tree)
 * `A` [Graph](src/data-structures/graph) (both directed and undirected)
-* `A` [Disjoint Set](src/data-structures/disjoint-set)
+* `A` [Disjoint Set](src/data-structures/disjoint-set) - a union–find data structure or merge–find set
 * `A` [Bloom Filter](src/data-structures/bloom-filter)
+* `A` [LRU Cache](src/data-structures/lru-cache/) - Least Recently Used (LRU) cache
 
 ## Algorithms
 
@@ -61,8 +84,10 @@ a set of rules that precisely define a sequence of operations.
 
 * **Math**
   * `B` [Bit Manipulation](src/algorithms/math/bits) - set/get/update/clear bits, multiplication/division by two, make negative etc.
-  * `B` [Factorial](src/algorithms/math/factorial) 
-  * `B` [Fibonacci Number](src/algorithms/math/fibonacci)
+  * `B` [Binary Floating Point](src/algorithms/math/binary-floating-point) - binary representation of the floating-point numbers.
+  * `B` [Factorial](src/algorithms/math/factorial)
+  * `B` [Fibonacci Number](src/algorithms/math/fibonacci) - classic and closed-form versions
+  * `B` [Prime Factors](src/algorithms/math/prime-factors) - finding prime factors and counting them using Hardy-Ramanujan's theorem
   * `B` [Primality Test](src/algorithms/math/primality-test) (trial division method)
   * `B` [Euclidean Algorithm](src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
   * `B` [Least Common Multiple](src/algorithms/math/least-common-multiple) (LCM)
@@ -71,13 +96,18 @@ a set of rules that precisely define a sequence of operations.
   * `B` [Pascal's Triangle](src/algorithms/math/pascal-triangle)
   * `B` [Complex Number](src/algorithms/math/complex-number) - complex numbers and basic operations with them
   * `B` [Radian & Degree](src/algorithms/math/radian) - radians to degree and backwards conversion
+  * `B` [Fast Powering](src/algorithms/math/fast-powering)
+  * `B` [Horner's method](src/algorithms/math/horner-method) - polynomial evaluation
+  * `B` [Matrices](src/algorithms/math/matrix) - matrices and basic matrix operations (multiplication, transposition, etc.)
+  * `B` [Euclidean Distance](src/algorithms/math/euclidean-distance) - distance between two points/vectors/matrices
   * `A` [Integer Partition](src/algorithms/math/integer-partition)
+  * `A` [Square Root](src/algorithms/math/square-root) - Newton's method
   * `A` [Liu Hui π Algorithm](src/algorithms/math/liu-hui) - approximate π calculations based on N-gons
-  * `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - decompose a function of time (a signal) into the frequencies that make it up 
+  * `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - decompose a function of time (a signal) into the frequencies that make it up
 * **Sets**
   * `B` [Cartesian Product](src/algorithms/sets/cartesian-product) - product of multiple sets
   * `B` [Fisher–Yates Shuffle](src/algorithms/sets/fisher-yates) - random permutation of a finite sequence
-  * `A` [Power Set](src/algorithms/sets/power-set) - all subsets of a set (bitwise and backtracking solutions)
+  * `A` [Power Set](src/algorithms/sets/power-set) - all subsets of a set (bitwise, backtracking, and cascading solutions)
   * `A` [Permutations](src/algorithms/sets/permutations) (with and without repetitions)
   * `A` [Combinations](src/algorithms/sets/combinations) (with and without repetitions)
   * `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS)
@@ -88,6 +118,7 @@ a set of rules that precisely define a sequence of operations.
   * `A` [Combination Sum](src/algorithms/sets/combination-sum) - find all combinations that form specific sum
 * **Strings**
   * `B` [Hamming Distance](src/algorithms/string/hamming-distance) - number of positions at which the symbols are different
+  * `B` [Palindrome](src/algorithms/string/palindrome) - check if the string is the same in reverse
   * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences
   * `A` [Knuth–Morris–Pratt Algorithm](src/algorithms/string/knuth-morris-pratt) (KMP Algorithm) - substring search (pattern matching)
   * `A` [Z Algorithm](src/algorithms/string/z-algorithm) - substring search (pattern matching)
@@ -109,6 +140,10 @@ a set of rules that precisely define a sequence of operations.
   * `B` [Shellsort](src/algorithms/sorting/shell-sort)
   * `B` [Counting Sort](src/algorithms/sorting/counting-sort)
   * `B` [Radix Sort](src/algorithms/sorting/radix-sort)
+  * `B` [Bucket Sort](src/algorithms/sorting/bucket-sort)
+* **Linked Lists**
+  * `B` [Straight Traversal](src/algorithms/linked-list/traversal)
+  * `B` [Reverse Traversal](src/algorithms/linked-list/reverse-traversal)
 * **Trees**
   * `B` [Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
   * `B` [Breadth-First Search](src/algorithms/tree/breadth-first-search) (BFS)
@@ -116,9 +151,9 @@ a set of rules that precisely define a sequence of operations.
   * `B` [Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
   * `B` [Breadth-First Search](src/algorithms/graph/breadth-first-search) (BFS)
   * `B` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
-  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - finding shortest paths to all graph vertices from single vertex
-  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - finding shortest paths to all graph vertices from single vertex
-  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - find shortest paths between all pairs of vertices
+  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - finding the shortest paths to all graph vertices from single vertex
+  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - finding the shortest paths to all graph vertices from single vertex
+  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - find the shortest paths between all pairs of vertices
   * `A` [Detect Cycle](src/algorithms/graph/detect-cycle) - for both directed and undirected graphs (DFS and Disjoint Set based versions)
   * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
   * `A` [Topological Sorting](src/algorithms/graph/topological-sorting) - DFS method
@@ -130,12 +165,28 @@ a set of rules that precisely define a sequence of operations.
   * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city
 * **Cryptography**
   * `B` [Polynomial Hash](src/algorithms/cryptography/polynomial-hash) - rolling hash function based on polynomial
+  * `B` [Rail Fence Cipher](src/algorithms/cryptography/rail-fence-cipher) - a transposition cipher algorithm for encoding messages
+  * `B` [Caesar Cipher](src/algorithms/cryptography/caesar-cipher) - simple substitution cipher
+  * `B` [Hill Cipher](src/algorithms/cryptography/hill-cipher) - substitution cipher based on linear algebra
+* **Machine Learning**
+  * `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 simple JS functions that illustrate how machines can actually learn (forward/backward propagation)
+  * `B` [k-NN](src/algorithms/ml/knn) - k-nearest neighbors classification algorithm
+  * `B` [k-Means](src/algorithms/ml/k-means) - k-Means clustering algorithm
+* **Image Processing**
+  * `B` [Seam Carving](src/algorithms/image-processing/seam-carving) - content-aware image resizing algorithm
+* **Statistics**
+  * `B` [Weighted Random](src/algorithms/statistics/weighted-random) - select the random item from the list based on items' weights
+* **Evolutionary algorithms**
+  * `A` [Genetic algorithm](https://github.com/trekhleb/self-parking-car-evolution) - example of how the genetic algorithm may be applied for training the self-parking cars
 * **Uncategorized**
   * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
   * `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - in-place algorithm
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, dynamic programming (top-down + bottom-up) and greedy examples 
-  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths) - backtracking, dynamic programming and Pascal's Triangle based examples 
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, dynamic programming (top-down + bottom-up) and greedy examples
+  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths) - backtracking, dynamic programming and Pascal's Triangle based examples
   * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem (dynamic programming and brute force versions)
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - count the number of ways to reach to the top (4 solutions)
+  * `B` [Best Time To Buy Sell Stocks](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - divide and conquer and one-pass examples
+  * `B` [Valid Parentheses](src/algorithms/stack/valid-parentheses) - check if a string has valid parentheses (using stack)
   * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
   * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
 
@@ -148,13 +199,14 @@ algorithm is an abstraction higher than a computer program.
 * **Brute Force** - look at all the possibilities and selects the best solution
   * `B` [Linear Search](src/algorithms/search/linear-search)
   * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - count the number of ways to reach the top
   * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
   * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city
   * `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - decompose a function of time (a signal) into the frequencies that make it up
 * **Greedy** - choose the best option at the current time, without any consideration for the future
   * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
   * `A` [Unbound Knapsack Problem](src/algorithms/sets/knapsack-problem)
-  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
+  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - finding the shortest path to all graph vertices
   * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
   * `A` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
 * **Divide and Conquer** - divide the problem into smaller parts and then solve those parts
@@ -166,14 +218,20 @@ algorithm is an abstraction higher than a computer program.
   * `B` [Quicksort](src/algorithms/sorting/quick-sort)
   * `B` [Tree Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
   * `B` [Graph Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Matrices](src/algorithms/math/matrix) - generating and traversing the matrices of different shapes
   * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `B` [Fast Powering](src/algorithms/math/fast-powering)
+  * `B` [Best Time To Buy Sell Stocks](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - divide and conquer and one-pass examples
   * `A` [Permutations](src/algorithms/sets/permutations) (with and without repetitions)
   * `A` [Combinations](src/algorithms/sets/combinations) (with and without repetitions)
+  * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
 * **Dynamic Programming** - build up a solution using previously found sub-solutions
   * `B` [Fibonacci Number](src/algorithms/math/fibonacci)
   * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
   * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
   * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - count the number of ways to reach the top
+  * `B` [Seam Carving](src/algorithms/image-processing/seam-carving) - content-aware image resizing algorithm
   * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences
   * `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS)
   * `A` [Longest Common Substring](src/algorithms/string/longest-common-substring)
@@ -182,12 +240,12 @@ algorithm is an abstraction higher than a computer program.
   * `A` [0/1 Knapsack Problem](src/algorithms/sets/knapsack-problem)
   * `A` [Integer Partition](src/algorithms/math/integer-partition)
   * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
-  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
-  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - find shortest paths between all pairs of vertices
+  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - finding the shortest path to all graph vertices
+  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - find the shortest paths between all pairs of vertices
   * `A` [Regular Expression Matching](src/algorithms/string/regular-expression-matching)
-* **Backtracking** - similarly to brute force, try to generate all possible solutions, but each time you generate next solution you test
-if it satisfies all conditions, and only then continue generating subsequent solutions. Otherwise, backtrack, and go on a
-different path of finding a solution. Normally the DFS traversal of state-space is being used.
+* **Backtracking** - similarly to brute force, try to generate all possible solutions, but each time you generate the next solution, you test
+if it satisfies all conditions and only then continue generating subsequent solutions. Otherwise, backtrack and go on a
+different path to finding a solution. Normally the DFS traversal of state-space is being used.
   * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
   * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
   * `B` [Power Set](src/algorithms/sets/power-set) - all subsets of a set
@@ -197,13 +255,14 @@ different path of finding a solution. Normally the DFS traversal of state-space
   * `A` [Combination Sum](src/algorithms/sets/combination-sum) - find all combinations that form specific sum
 * **Branch & Bound** - remember the lowest-cost solution found at each stage of the backtracking
 search, and use the cost of the lowest-cost solution found so far as a lower bound on the cost of
-a least-cost solution to the problem, in order to discard partial solutions with costs larger than the
-lowest-cost solution found so far. Normally BFS traversal in combination with DFS traversal of state-space
+a least-cost solution to the problem in order to discard partial solutions with costs larger than the
+lowest-cost solution found so far. Normally, BFS traversal in combination with DFS traversal of state-space
 tree is being used.
 
 ## How to use this repository
 
 **Install all dependencies**
+
 ```
 npm install
 ```
@@ -217,21 +276,34 @@ npm run lint
 ```
 
 **Run all tests**
+
 ```
 npm test
 ```
 
 **Run tests by name**
+
 ```
 npm test -- 'LinkedList'
 ```
 
+**Troubleshooting**
+
+If linting or testing is failing, try to delete the `node_modules` folder and re-install npm packages:
+
+```
+rm -rf ./node_modules
+npm i
+```
+
+Also, make sure that you're using the correct Node version (`>=16`). If you're using [nvm](https://github.com/nvm-sh/nvm) for Node version management you may run `nvm use` from the root folder of the project and the correct version will be picked up.
+
 **Playground**
 
 You may play with data-structures and algorithms in `./src/playground/playground.js` file and write
 tests for it in `./src/playground/__test__/playground.test.js`.
 
-Then just simply run the following command to test if your playground code works as expected:
+Then just, simply run the following command to test if your playground code works as expected:
 
 ```
 npm test -- 'playground'
@@ -241,11 +313,13 @@ npm test -- 'playground'
 
 ### References
 
-[▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [✍🏻 Data Structure Sketches](https://okso.app/showcase/data-structures)
 
 ### Big O Notation
 
-Order of growth of algorithms specified in Big O notation.
+*Big O notation* is used to classify algorithms according to how their running time or space requirements grow as the input size grows.
+On the chart below, you may find the most common orders of growth of algorithms specified in Big O notation.
 
 ![Big O graphs](./assets/big-o-graph.png)
 
@@ -253,15 +327,15 @@ Source: [Big O Cheat Sheet](http://bigocheatsheet.com/).
 
 Below is the list of some of the most used Big O notations and their performance comparisons against different sizes of the input data.
 
-| Big O Notation | Computations for 10 elements | Computations for 100 elements | Computations for 1000 elements  |
-| -------------- | ---------------------------- | ----------------------------- | ------------------------------- |
-| **O(1)**       | 1                            | 1                             | 1                               |
-| **O(log N)**   | 3                            | 6                             | 9                               |
-| **O(N)**       | 10                           | 100                           | 1000                            |
-| **O(N log N)** | 30                           | 600                           | 9000                            |
-| **O(N^2)**     | 100                          | 10000                         | 1000000                         |
-| **O(2^N)**     | 1024                         | 1.26e+29                      | 1.07e+301                       |
-| **O(N!)**      | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+| Big O Notation | Type        | Computations for 10 elements | Computations for 100 elements | Computations for 1000 elements  |
+| -------------- | ----------- | ---------------------------- | ----------------------------- | ------------------------------- |
+| **O(1)**       | Constant    | 1                            | 1                             | 1                               |
+| **O(log N)**   | Logarithmic | 3                            | 6                             | 9                               |
+| **O(N)**       | Linear      | 10                           | 100                           | 1000                            |
+| **O(N log N)** | n log(n)    | 30                           | 600                           | 9000                            |
+| **O(N^2)**     | Quadratic   | 100                          | 10000                         | 1000000                         |
+| **O(2^N)**     | Exponential | 1024                         | 1.26e+29                      | 1.07e+301                       |
+| **O(N!)**      | Factorial   | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
 
 ### Data Structure Operations Complexity
 
@@ -270,7 +344,7 @@ Below is the list of some of the most used Big O notations and their performance
 | **Array**               | 1         | n         | n         | n         |           |
 | **Stack**               | n         | n         | 1         | 1         |           |
 | **Queue**               | n         | n         | 1         | 1         |           |
-| **Linked List**         | n         | n         | 1         | 1         |           |
+| **Linked List**         | n         | n         | 1         | n         |           |
 | **Hash Table**          | -         | n         | n         | n         | In case of perfect hash function costs would be O(1) |
 | **Binary Search Tree**  | n         | n         | n         | n         | In case of balanced tree costs would be O(log(n)) |
 | **B-Tree**              | log(n)    | log(n)    | log(n)    | log(n)    |           |
@@ -291,3 +365,15 @@ Below is the list of some of the most used Big O notations and their performance
 | **Shell sort**        | n&nbsp;log(n)   | depends on gap sequence   | n&nbsp;(log(n))<sup>2</sup>  | 1         | No         |           |
 | **Counting sort**     | n + r           | n + r               | n + r               | n + r     | Yes       | r - biggest number in array |
 | **Radix sort**        | n * k           | n * k               | n * k               | n + k     | Yes       | k - length of longest key |
+
+## Project Backers
+
+> You may support this project via ❤️️ [GitHub](https://github.com/sponsors/trekhleb) or ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+[Folks who are backing this project](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 1`
+
+## Author
+
+[@trekhleb](https://trekhleb.dev)
+
+A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.pl-PL.md b/README.pl-PL.md
index 84ddd76bff..56617030ab 100644
--- a/README.pl-PL.md
+++ b/README.pl-PL.md
@@ -1,13 +1,13 @@
 # JavaScript Algorytmy i Struktury Danych
 
-[![Build Status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
 
-To repozytorium zawiera wiele przykładów JavaScript opartych na 
+To repozytorium zawiera wiele przykładów JavaScript opartych na
 znanych algorytmach i strukturach danych.
 
 Każdy algorytm i struktura danych zawiera osobny plik README
-wraz z powiązanymi wyjaśnieniami i odnośnikami do dalszego czytania 
+wraz z powiązanymi wyjaśnieniami i odnośnikami do dalszego czytania
 (włącznie z tymi do YouTube videos).
 
 _Read this in other languages:_
@@ -15,21 +15,27 @@ _Read this in other languages:_
 [_简体中文_](README.zh-CN.md),
 [_繁體中文_](README.zh-TW.md),
 [_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
 [_Français_](README.fr-FR.md),
 [_Español_](README.es-ES.md),
-[_Português_](README.pt-BR.md)
-
-> Jesteśmy w trakcie pisania książki, która w jasny i specyficzny sposób, 
-wyjaśni główne algorytmy. Jeżeli chcesz dostać powiadomienie o tym kiedy 
-książka â€œJavaScript Algorithmsâ€ zostanie wydana,[kliknij tutaj](https://upscri.be/402324/).
-
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
 
 ## Struktury Danych
 
-Struktura danych to sposób uporządkowania i przechowywania informacji w 
+Struktura danych to sposób uporządkowania i przechowywania informacji w
 komputerze żeby mogłaby być sprawnie dostępna i efektywnie zmodyfikowana.
-Dokładniej, struktura danych jest zbiorem wartości danych, relacjami 
-pomiędzy nimi, zadaniami lub działaniami, które mogą dotyczyć danych. 
+Dokładniej, struktura danych jest zbiorem wartości danych, relacjami
+pomiędzy nimi, zadaniami lub działaniami, które mogą dotyczyć danych.
 
 `B` - Początkujący, `A` - Zaawansowany
 
@@ -53,8 +59,8 @@ pomiędzy nimi, zadaniami lub działaniami, które mogą dotyczyć danych.
 
 ## Algorytmy
 
-Algorytm jest to skończony ciąg jasno zdefiniowanych czynności, koniecznych 
-do wykonania pewnego rodzaju zadań. Sposób postępowania prowadzący do 
+Algorytm jest to skończony ciąg jasno zdefiniowanych czynności, koniecznych
+do wykonania pewnego rodzaju zadań. Sposób postępowania prowadzący do
 rozwiązania problemu.
 
 `B` - Początkujący, `A` - Zaawansowany
@@ -63,7 +69,7 @@ rozwiązania problemu.
 
 * **Matematyka**
   * `B` [Manipulacja Bitami](src/algorithms/math/bits) - ustaw / uzyskaj / aktualizuj / usuwaj bity, mnożenie / dzielenie przez dwa, tworzenie negatywów itp.
-  * `B` [Silna](src/algorithms/math/factorial) 
+  * `B` [Silnia](src/algorithms/math/factorial)
   * `B` [Ciąg Fibonacciego](src/algorithms/math/fibonacci)
   * `B` [Test Pierwszorzędności](src/algorithms/math/primality-test) (metoda podziału na próby)
   * `B` [Algorytm Euclideana](src/algorithms/math/euclidean-algorithm) - obliczyć Największy Wspólny Dzielnik (GCD)
@@ -82,9 +88,9 @@ rozwiązania problemu.
   * `A` [Najdłuższa Wspólna Podsekwencja](src/algorithms/sets/longest-common-subsequence) (LCS)
   * `A` [Najdłuższa Wzrostająca Podsekwencja](src/algorithms/sets/longest-increasing-subsequence)
   * `A` [Najkrótsza Wspólna Supersekwencja](src/algorithms/sets/shortest-common-supersequence) (SCS)
-  * `A` [Problem Knapsacka](src/algorithms/sets/knapsack-problem) - "0/1" i "Rozwiązany" 
+  * `A` [Problem Knapsacka](src/algorithms/sets/knapsack-problem) - "0/1" i "Rozwiązany"
   * `A` [Maksymalna Podtablica](src/algorithms/sets/maximum-subarray) - "Metoda Siłowa" i "Dynamiczne Programowanie" (Kadane-a) wersje
-  * `A` [Suma Kombinacji](src/algorithms/sets/combination-sum) - 
+  * `A` [Suma Kombinacji](src/algorithms/sets/combination-sum) -
 znajdź wszystkie kombinacje, które tworzą określoną sumę
 * **Łańcuchy**
   * `B` [Odległość Hamminga](src/algorithms/string/hamming-distance) - liczba pozycji, w których symbole są różne
@@ -121,26 +127,26 @@ znajdź wszystkie kombinacje, które tworzą określoną sumę
   * `A` [Algorytm Floyd-Warshalla](src/algorithms/graph/floyd-warshall) - znajdź najkrótsze ścieżki między wszystkimi parami wierzchołków
   * `A` [Detect Cycle](src/algorithms/graph/detect-cycle) - zarówno dla wykresów skierowanych, jak i nieukierunkowanych(wersje oparte na DFS i Rozłączny Zestaw)
   * `A` [Algorytm Prima](src/algorithms/graph/prim) - znalezienie Minimalnego Drzewa Opinającego (MST) dla ważonego nieukierunkowanego wykresu
-  * `A` [Sortowanie Topologiczne](src/algorithms/graph/topological-sorting) - metoda DFS 
+  * `A` [Sortowanie Topologiczne](src/algorithms/graph/topological-sorting) - metoda DFS
   * `A` [Punkty Artykulacji](src/algorithms/graph/articulation-points) - Algorytm Tarjana (oparty o DFS)
-  * `A` [Mosty](src/algorithms/graph/bridges) - Oparty na algorytmie DFS 
+  * `A` [Mosty](src/algorithms/graph/bridges) - Oparty na algorytmie DFS
   * `A` [Ścieżka Euleriana i Obwód Euleriana](src/algorithms/graph/eulerian-path) - Algorytm Fleurya - Odwiedź każdą krawędź dokładnie raz
   * `A` [Cykl Hamiltoniana](src/algorithms/graph/hamiltonian-cycle) - Odwiedź każdy wierzchołek dokładnie raz
-  * `A` [Silnie Połączone Komponenty](src/algorithms/graph/strongly-connected-components) - Algorytm Kosaraja 
+  * `A` [Silnie Połączone Komponenty](src/algorithms/graph/strongly-connected-components) - Algorytm Kosaraja
   * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - najkrótsza ścieżka która odwiedza każde miasto i wraca miasta początkującego
 * **Niezkategorizowane**
   * `B` [Wieża Hanoi](src/algorithms/uncategorized/hanoi-tower)
   * `B` [Kwadratowa Matryca Obrotu](src/algorithms/uncategorized/square-matrix-rotation) - algorytm w miejscu
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - cofanie, dynamiczne programowanie (od góry do dołu + od dołu do góry) i przykłady chciwego  
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - cofanie, dynamiczne programowanie (od góry do dołu + od dołu do góry) i przykłady chciwego
   * `B` [Unikatowe Ścieżki](src/algorithms/uncategorized/unique-paths) - cofanie, dynamiczne programowanie i przykłady oparte na Trójkącie Pascala
   * `A` [Problem N-Queens](src/algorithms/uncategorized/n-queens)
   * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
 
 ### Algorytmy według paradygmatu
 
-Paradygmat algorytmiczny jest ogólną metodą lub podejściem, które jest 
-podstawą projektowania klasy algorytmów. Jest abstrakcją wyższą niż 
-pojęcie algorytmu, podobnie jak algorytm jest abstrakcją wyższą niż 
+Paradygmat algorytmiczny jest ogólną metodą lub podejściem, które jest
+podstawą projektowania klasy algorytmów. Jest abstrakcją wyższą niż
+pojęcie algorytmu, podobnie jak algorytm jest abstrakcją wyższą niż
 program komputerowy.
 
 * **Metoda Siłowa** - Sprawdza wszystkie możliwosci i wybiera  najlepsze rozwiązanie.
@@ -150,7 +156,7 @@ program komputerowy.
 * **Chciwy** - wybierz najlepszą opcję w obecnym czasie, bez względu na przyszłość
   * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
   * `A` [Niezwiązany Problem Knapsacka ](src/algorithms/sets/knapsack-problem)
-  * `A` [Algorytm Dijkstry](src/algorithms/graph/dijkstra) - 
+  * `A` [Algorytm Dijkstry](src/algorithms/graph/dijkstra) -
 znalezienie najkrótszej ścieżki do wszystkich wierzchołków grafu
   * `A` [Algorytm Prima](src/algorithms/graph/prim) - znalezienie Minimalnego Drzewa Opinającego (MST) dla ważonego nieukierunkowanego wykresu
   * `A` [Algorytm Kruskala](src/algorithms/graph/kruskal) - znalezienie Minimalnego Drzewa Opinającego (MST) dla ważonego nieukierunkowanego wykresu
@@ -179,7 +185,7 @@ znalezienie najkrótszej ścieżki do wszystkich wierzchołków grafu
   * `A` [Partycja Całkowita](src/algorithms/math/integer-partition)
   * `A` [Maksymalne Podtablice](src/algorithms/sets/maximum-subarray)
   * `A` [Algorytm Bellman-Forda](src/algorithms/graph/bellman-ford) - znalezienie najkrótszej ścieżki wszystkich wierzchołków wykresu
-  * `A` [Algorytm Floyd-Warshalla](src/algorithms/graph/floyd-warshall) - 
+  * `A` [Algorytm Floyd-Warshalla](src/algorithms/graph/floyd-warshall) -
 znajdź najkrótsze ścieżki między wszystkimi parami wierzchołków
   * `A` [Dopasowanie Wyrażeń Regularnych](src/algorithms/string/regular-expression-matching)
 * **Algorytm z nawrotami** - podobny do metody siłowej, próbuje wygenerować wszystkie możliwe rozwiązania, jednak za każdym razem generujesz następne rozwiązanie które testujesz
@@ -193,7 +199,7 @@ jeżeli zaspokaja wszystkie warunki, tylko wtedy generuje kolejne rozwiązania.
 * **Metoda Podziału i Ograniczeń** - Pamięta o niskonakładowym rozwiązaniu znalezionym na każdym etapie szukania nawrotu,
 używa kosztu niskonakładowego kosztu, które dotychczas zostało znalezione jako niska granica najmniejszego kosztu
 do rozwiązanie problemu, aby odrzucić cząstkowe rozwiązania o kosztach większych niż niskonakładowe
-rozwiązanie znalezione do tej pory. 
+rozwiązanie znalezione do tej pory.
 Zazwyczan trajektoria BFS, w połączeniu z trajektorią Przeszukiwania W Głąb (DFS) drzewa przestrzeni stanów jest użyte.
 
 ## Jak używać repozytorium
@@ -226,7 +232,7 @@ npm test -- 'LinkedList'
 Możesz pociwiczyć ze strukturą danych i algorytmami w `./src/playground/playground.js` zakartotekuj i napisz
 testy do tego w `./src/playground/__test__/playground.test.js`.
 
-Następnie uruchom następującą komendę w celu przetestowania czy twoje kod działa według oczekiwań: 
+Następnie uruchom następującą komendę w celu przetestowania czy twoje kod działa według oczekiwań:
 
 ```
 npm test -- 'playground'
@@ -240,7 +246,7 @@ npm test -- 'playground'
 
 ### Big O Notacja
 
-Kolejność wzrastania algorytmów według Big O notacji. 
+Kolejność wzrastania algorytmów według Big O notacji.
 
 ![Big O grafy](./assets/big-o-graph.png)
 
@@ -286,3 +292,5 @@ Poniżej umieszczamy listę najbardziej używanych Big O notacji i ich porównan
 | **Sortowanie Shella**               | n&nbsp;log(n)   | zależy od luki w układzie   | n&nbsp;(log(n))<sup>2</sup>  | 1         | No         |           |
 | **Sortowanie przez zliczanie**      | n + r           | n + r               | n + r               | n + r     | Yes         | r - największy numer w tablicy|
 | **Sortowanie Radix**                | n * k           | n * k               | n * k               | n + k     | Yes         | k -długość najdłuższego klucza |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.pt-BR.md b/README.pt-BR.md
index 6dd96542c5..5b16798bc9 100644
--- a/README.pt-BR.md
+++ b/README.pt-BR.md
@@ -1,12 +1,12 @@
 # Estrutura de Dados e Algoritmos em JavaScript
 
-[![Build Status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
 
 Este repositório contém exemplos baseados em JavaScript de muitos
-algoritmos e estruturas de dados populares. 
+algoritmos e estruturas de dados populares.
 
-Cada algoritmo e estrutura de dado possui seu próprio README
+Cada algoritmo e estrutura de dados possui seu próprio README
 com explicações relacionadas e links para leitura adicional (incluindo
 vídeos para YouTube)
 
@@ -15,40 +15,45 @@ _Leia isto em outros idiomas:_
 [_简体中文_](README.zh-CN.md),
 [_繁體中文_](README.zh-TW.md),
 [_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
 [_Polski_](README.pl-PL.md),
 [_Français_](README.fr-FR.md),
-[_Español_](README.es-ES.md)
-
-> Nós estamos escrevendo um livro que explicará claramente, em datalhes, os principais algoritmos.
-Se você deseja ser notificado quando o livro "Algoritmos em JavaScript” 
-for lançado, [clique aqui](https://upscri.be/402324/).
-
-## Data Structures
+[_Español_](README.es-ES.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+## Estrutura de Dados
 
 Uma estrutura de dados é uma maneira particular de organizar e armazenar dados em um computador para que ele possa
-ser acessado e modificado de forma eficiente. Mais precisamente, uma estrutura de dados é uma coleção de dados
-valores, as relações entre eles e as funções ou operações que podem ser aplicadas a
-os dados.
+ser acessado e modificado de forma eficiente. Mais precisamente, uma estrutura de dados é uma coleção de valores de dados, as relações entre eles e as funções ou operações que podem ser aplicadas aos dados.
 
 `B` - Iniciante, `A` - Avançado
 
-* `B` [Linked List](src/data-structures/linked-list)
-* `B` [Doubly Linked List](src/data-structures/doubly-linked-list)
-* `B` [Queue](src/data-structures/queue)
-* `B` [Stack](src/data-structures/stack)
-* `B` [Hash Table](src/data-structures/hash-table)
-* `B` [Heap](src/data-structures/heap)
-* `B` [Priority Queue](src/data-structures/priority-queue)
-* `A` [Trie](src/data-structures/trie)
-* `A` [Tree](src/data-structures/tree)
-  * `A` [Binary Search Tree](src/data-structures/tree/binary-search-tree)
-  * `A` [AVL Tree](src/data-structures/tree/avl-tree)
-  * `A` [Red-Black Tree](src/data-structures/tree/red-black-tree)
-  * `A` [Segment Tree](src/data-structures/tree/segment-tree) - com exemplos de consultas min / max / sum range
-  * `A` [Fenwick Tree](src/data-structures/tree/fenwick-tree) (Árvore indexada binária)
-* `A` [Graph](src/data-structures/graph) (ambos dirigidos e não direcionados)
-* `A` [Disjoint Set](src/data-structures/disjoint-set)
-* `A` [Bloom Filter](src/data-structures/bloom-filter)
+* `B` [Lista Encadeada (Linked List)](src/data-structures/linked-list/README.pt-BR.md)
+* `B` [Lista Duplamente Ligada (Doubly Linked List)](src/data-structures/doubly-linked-list/README.pt-BR.md)
+* `B` [Fila (Queue)](src/data-structures/queue/README.pt-BR.md)
+* `B` [Pilha (Stack)](src/data-structures/stack/README.pt-BR.md)
+* `B` [Tabela de Hash (Hash Table)](src/data-structures/hash-table/README.pt-BR.md)
+* `B` [Heap](src/data-structures/heap/README.pt-BR.md) - versões de heap máximo e mínimo
+* `B` [Fila de Prioridade (Priority Queue)](src/data-structures/priority-queue/README.pt-BR.md)
+* `A` [Árvore de Prefixos (Trie)](src/data-structures/trie/README.pt-BR.md)
+* `A` [Árvore (Tree)](src/data-structures/tree/README.pt-BR.md)
+  * `A` [Árvore de Pesquisa Binária (Binary Search Tree)](src/data-structures/tree/binary-search-tree/README.pt-BR.md)
+  * `A` [Árvore AVL (AVL Tree)](src/data-structures/tree/avl-tree/README.pt-BR.md)
+  * `A` [Árvore Rubro-Negra (Red-Black Tree)](src/data-structures/tree/red-black-tree/README.pt-BR.md)
+  * `A` [Árvore de Segmento (Segment Tree)](src/data-structures/tree/segment-tree/README.pt-BR.md) - com exemplos de consultas min / max / sum range
+  * `A` [Árvore Fenwick (Fenwick Tree)](src/data-structures/tree/fenwick-tree/README.pt-BR.md) (Árvore indexada binária)
+* `A` [Grafo (Graph)](src/data-structures/graph/README.pt-BR.md) (ambos dirigidos e não direcionados)
+* `A` [Conjunto Disjunto (Disjoint Set)](src/data-structures/disjoint-set/README.pt-BR.md)
+* `A` [Filtro Bloom (Bloom Filter)](src/data-structures/bloom-filter/README.pt-BR.md)
 
 ## Algoritmos
 
@@ -61,81 +66,82 @@ um conjunto de regras que define precisamente uma sequência de operações.
 
 * **Matemática**
   * `B` [Manipulação Bit](src/algorithms/math/bits) - set/get/update/clear bits, multiplicação / divisão por dois, tornar negativo etc.
-  * `B` [Fatorial](src/algorithms/math/factorial) 
+  * `B` [Fatorial](src/algorithms/math/factorial)
   * `B` [Número de Fibonacci](src/algorithms/math/fibonacci)
   * `B` [Teste de Primalidade](src/algorithms/math/primality-test) (método de divisão experimental)
-  * `B` [Algoritmo Euclidiano](src/algorithms/math/euclidean-algorithm) - calcular o maior divisor comum (GCD)
-  * `B` [Mínimo múltiplo comum](src/algorithms/math/least-common-multiple) (LCM)
-  * `B` [Peneira de Eratóstenes](src/algorithms/math/sieve-of-eratosthenes) - encontrar todos os números primos até um determinado limite
-  * `B` [Potência de dois](src/algorithms/math/is-power-of-two) - verifique se o número é a potência de dois (algoritmos ingênuos e bit a bit)
+  * `B` [Algoritmo Euclidiano](src/algorithms/math/euclidean-algorithm) - Calcular o Máximo Divisor Comum (MDC)
+  * `B` [Mínimo Múltiplo Comum](src/algorithms/math/least-common-multiple) Calcular o Mínimo Múltiplo Comum (MMC)
+  * `B` [Peneira de Eratóstenes](src/algorithms/math/sieve-of-eratosthenes) - Encontrar todos os números primos até um determinado limite
+  * `B` [Potência de Dois](src/algorithms/math/is-power-of-two) - Verifique se o número é a potência de dois (algoritmos ingênuos e bit a bit)
   * `B` [Triângulo de Pascal](src/algorithms/math/pascal-triangle)
-  * `B` [Número complexo](src/algorithms/math/complex-number) - números complexos e operações básicas com eles
-  * `A` [Partição inteira](src/algorithms/math/integer-partition)
-  * `A` [Algoritmo Liu Hui π](src/algorithms/math/liu-hui) - cálculos aproximados de π baseados em N-gons
+  * `B` [Número Complexo](src/algorithms/math/complex-number) - Números complexos e operações básicas com eles
+  * `A` [Partição Inteira](src/algorithms/math/integer-partition)
+  * `A` [Algoritmo Liu Hui π](src/algorithms/math/liu-hui) - Cálculos aproximados de π baseados em N-gons
 * **Conjuntos**
-  * `B` [Produto cartesiano](src/algorithms/sets/cartesian-product) - produto de vários conjuntos
-  * `B` [Permutações de Fisher–Yates](src/algorithms/sets/fisher-yates) - permutação aleatória de uma sequência finita
-  * `A` [Potência e Conjunto](src/algorithms/sets/power-set) - todos os subconjuntos de um conjunto
+  * `B` [Produto Cartesiano](src/algorithms/sets/cartesian-product) - Produto de vários conjuntos
+  * `B` [Permutações de Fisher–Yates](src/algorithms/sets/fisher-yates) - Permutação aleatória de uma sequência finita
+  * `A` [Potência e Conjunto](src/algorithms/sets/power-set) - Todos os subconjuntos de um conjunto
   * `A` [Permutações](src/algorithms/sets/permutations) (com e sem repetições)
   * `A` [Combinações](src/algorithms/sets/combinations) (com e sem repetições)
-  * `A` [Mais longa subsequência comum](src/algorithms/sets/longest-common-subsequence) (LCS)
-  * `A` [Maior subsequência crescente](src/algorithms/sets/longest-increasing-subsequence)
-  * `A` [Supersequência Comum mais curta](src/algorithms/sets/shortest-common-supersequence) (SCS)
-  * `A` [Problema da mochila](src/algorithms/sets/knapsack-problem) - "0/1" e "Não consolidado"
-  * `A` [Máximo Subarray](src/algorithms/sets/maximum-subarray) - "Força bruta" e " Programação Dinâmica" versões (Kadane's)
-  * `A` [Soma de Combinação](src/algorithms/sets/combination-sum) - encontre todas as combinações que formam uma soma específica
+  * `A` [Mais Longa Subsequência Comum](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Maior Subsequência Crescente](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Supersequência Comum Mais Curta](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [Problema da Mochila](src/algorithms/sets/knapsack-problem) - "0/1" e "Não consolidado"
+  * `A` [Subarray Máximo](src/algorithms/sets/maximum-subarray) - "Força bruta" e "Programação Dinâmica", versões de Kadane
+  * `A` [Soma de Combinação](src/algorithms/sets/combination-sum) - Encontre todas as combinações que formam uma soma específica
 * **Cadeia de Caracteres**
-  * `B` [Hamming Distance](src/algorithms/string/hamming-distance) - número de posições em que os símbolos são diferentes
-  * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - distância mínima de edição entre duas sequências
-  * `A` [Knuth–Morris–Pratt Algorithm](src/algorithms/string/knuth-morris-pratt) (Algoritmo KMP) - pesquisa de substring (correspondência de padrão)
-  * `A` [Z Algorithm](src/algorithms/string/z-algorithm) - pesquisa de substring (correspondência de padrão)
-  * `A` [Rabin Karp Algorithm](src/algorithms/string/rabin-karp) - pesquisa de substring
-  * `A` [Longest Common Substring](src/algorithms/string/longest-common-substring)
-  * `A` [Regular Expression Matching](src/algorithms/string/regular-expression-matching)
+  * `B` [Distância de Hamming](src/algorithms/string/hamming-distance) - Número de posições em que os símbolos são diferentes
+  * `B` [Palíndromos](src/algorithms/string/palindrome) - Verifique se a cadeia de caracteres (string) é a mesma ao contrário
+  * `A` [Distância Levenshtein](src/algorithms/string/levenshtein-distance) - Distância mínima de edição entre duas sequências
+  * `A` [Algoritmo Knuth–Morris–Pratt](src/algorithms/string/knuth-morris-pratt) (Algoritmo KMP) - Pesquisa de substring (correspondência de padrão)
+  * `A` [Z Algorithm](src/algorithms/string/z-algorithm) - Pesquisa de substring (correspondência de padrão)
+  * `A` [Algoritmo de Rabin Karp](src/algorithms/string/rabin-karp) - Pesquisa de substring
+  * `A` [Substring Comum Mais Longa](src/algorithms/string/longest-common-substring)
+  * `A` [Expressões Regulares Correspondentes](src/algorithms/string/regular-expression-matching)
 * **Buscas**
-  * `B` [Linear Search](src/algorithms/search/linear-search)
-  * `B` [Jump Search](src/algorithms/search/jump-search) (ou Bloquear pesquisa) - pesquisar na matriz ordenada
-  * `B` [Binary Search](src/algorithms/search/binary-search) - pesquisar na matriz ordenada
-  * `B` [Interpolation Search](src/algorithms/search/interpolation-search) - pesquisar em matriz classificada uniformemente distribuída
+  * `B` [Busca Linear (Linear Search)](src/algorithms/search/linear-search)
+  * `B` [Busca por Saltos (Jump Search)](src/algorithms/search/jump-search) - Pesquisa em matriz ordenada
+  * `B` [Busca Binária (Binary Search)](src/algorithms/search/binary-search) - Pesquisa em matriz ordenada
+  * `B` [Busca por Interpolação (Interpolation Search)](src/algorithms/search/interpolation-search) - Pesquisa em matriz classificada uniformemente distribuída
 * **Classificação**
   * `B` [Bubble Sort](src/algorithms/sorting/bubble-sort)
   * `B` [Selection Sort](src/algorithms/sorting/selection-sort)
   * `B` [Insertion Sort](src/algorithms/sorting/insertion-sort)
   * `B` [Heap Sort](src/algorithms/sorting/heap-sort)
   * `B` [Merge Sort](src/algorithms/sorting/merge-sort)
-  * `B` [Quicksort](src/algorithms/sorting/quick-sort) - implementações local e não local
+  * `B` [Quicksort](src/algorithms/sorting/quick-sort) - Implementações local e não local
   * `B` [Shellsort](src/algorithms/sorting/shell-sort)
   * `B` [Counting Sort](src/algorithms/sorting/counting-sort)
   * `B` [Radix Sort](src/algorithms/sorting/radix-sort)
-* **Arvóres**
-  * `B` [Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
-  * `B` [Breadth-First Search](src/algorithms/tree/breadth-first-search) (BFS)
-* **Gráficos**
-  * `B` [Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
-  * `B` [Breadth-First Search](src/algorithms/graph/breadth-first-search) (BFS)
-  * `B` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - encontrando Árvore Mínima de Abrangência (MST) para grafo não direcionado ponderado
-  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - encontrar caminhos mais curtos para todos os vértices do grafo a partir de um único vértice
-  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - encontrar caminhos mais curtos para todos os vértices do grafo a partir de um único vértice
-  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - encontrar caminhos mais curtos entre todos os pares de vértices
-  * `A` [Detect Cycle](src/algorithms/graph/detect-cycle) - para gráficos direcionados e não direcionados (versões baseadas em DFS e Conjunto Disjuntivo)
-  * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - encontrando Árvore Mínima de Abrangência (MST) para grafo não direcionado ponderado
-  * `A` [Topological Sorting](src/algorithms/graph/topological-sorting) - Métodos DFS 
-  * `A` [Articulation Points](src/algorithms/graph/articulation-points) -O algoritmo de Tarjan (baseado em DFS)
-  * `A` [Bridges](src/algorithms/graph/bridges) - Algoritmo baseado em DFS
-  * `A` [Eulerian Path and Eulerian Circuit](src/algorithms/graph/eulerian-path) - Algoritmo de Fleury - Visite todas as bordas exatamente uma vez
-  * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Visite todas as bordas exatamente uma vez
-  * `A` [Strongly Connected Components](src/algorithms/graph/strongly-connected-components) - Algoritmo de Kosaraju's
-  * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - rota mais curta possível que visita cada cidade e retorna à cidade de origem
-* **criptografia**
-  * `B` [Polynomial Hash](src/algorithms/cryptography/polynomial-hash) - função de hash de rolagem baseada em polinômio
+* **Árvores**
+  * `B` [Busca em Profundidade (Depth-First Search)](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Busca em Largura (Breadth-First Search)](src/algorithms/tree/breadth-first-search) (BFS)
+* **Grafos**
+  * `B` [Busca em Profundidade (Depth-First Search)](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Busca em Largura (Breadth-First Search)](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [Algoritmo de Kruskal](src/algorithms/graph/kruskal) - Encontrando Árvore Mínima de Abrangência (MST) para grafo conexo com pesos
+  * `A` [Algoritmo de Dijkstra](src/algorithms/graph/dijkstra) - Encontrar caminhos mais curtos para todos os vértices do grafo a partir de um único vértice
+  * `A` [Algoritmo de Bellman-Ford](src/algorithms/graph/bellman-ford) - Encontrar caminhos mais curtos para todos os vértices do grafo a partir de um único vértice
+  * `A` [Algoritmo de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - Encontrar caminhos mais curtos entre todos os pares de vértices
+  * `A` [Detectar Ciclo](src/algorithms/graph/detect-cycle) - Para grafos direcionados e não direcionados (versões baseadas em DFS e Conjunto Disjuntivo)
+  * `A` [Algoritmo de Prim](src/algorithms/graph/prim) - Encontrando Árvore Mínima de Abrangência (MST) para grafo não direcionado ponderado
+  * `A` [Ordenação Topológica](src/algorithms/graph/topological-sorting) - Métodos DFS
+  * `A` [Pontos de Articulação](src/algorithms/graph/articulation-points) - O algoritmo de Tarjan (baseado em DFS)
+  * `A` [Pontes](src/algorithms/graph/bridges) - Algoritmo baseado em DFS
+  * `A` [Caminho e Circuito Euleriano](src/algorithms/graph/eulerian-path) - Algoritmo de Fleury - Visite todas as bordas exatamente uma vez
+  * `A` [Ciclo Hamiltoniano](src/algorithms/graph/hamiltonian-cycle) - Visite todas as bordas exatamente uma vez
+  * `A` [Componentes Fortemente Conectados](src/algorithms/graph/strongly-connected-components) - Algoritmo de Kosaraju
+  * `A` [Problema do Caixeiro Viajante](src/algorithms/graph/travelling-salesman) - Rota mais curta possível que visita cada cidade e retorna à cidade de origem
+* **Criptografia**
+  * `B` [Hash Polinomial](src/algorithms/cryptography/polynomial-hash) - Função de hash de rolagem baseada em polinômio
 * **Sem categoria**
-  * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
-  * `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - algoritmo no local
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, programação dinâmica (top-down + bottom-up) e exemplos gananciosos
-  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths) - backtracking, programação dinâmica e exemplos baseados no triângulo de Pascal
-  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping problema da água da chuva (programação dinâmica e versões de força bruta)
-  * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
-  * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
+  * `B` [Torre de Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Rotação de Matriz Quadrada](src/algorithms/uncategorized/square-matrix-rotation) - Algoritmo no local
+  * `B` [Jogo do Salto](src/algorithms/uncategorized/jump-game) - Backtracking, programação dinâmica (top-down + bottom-up) e exemplos gananciosos
+  * `B` [Caminhos Únicos](src/algorithms/uncategorized/unique-paths) - Backtracking, programação dinâmica e exemplos baseados no triângulo de Pascal
+  * `B` [Terraços de Chuva](src/algorithms/uncategorized/rain-terraces) - Problema de retenção da água da chuva (programação dinâmica e versões de força bruta)
+  * `A` [Problema das N-Rainhas](src/algorithms/uncategorized/n-queens)
+  * `A` [Passeio do Cavaleiro](src/algorithms/uncategorized/knight-tour)
 
 ### Algoritmos por Paradigma
 
@@ -143,55 +149,53 @@ Um paradigma algorítmico é um método ou abordagem genérica subjacente ao des
 de algoritmos. É uma abstração maior do que a noção de um algoritmo, assim como
 algoritmo é uma abstração maior que um programa de computador.
 
-* **Força bruta** - look at all the possibilities and selects the best solution
-  * `B` [Linear Search](src/algorithms/search/linear-search)
-  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping problema da água da chuva
-  * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
-  * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - rota mais curta possível que visita cada cidade e retorna à cidade de origem
-* **Greedy** - choose the best option at the current time, without any consideration for the future
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `A` [Unbound Knapsack Problem](src/algorithms/sets/knapsack-problem)
-  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
-  * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - encontrando Árvore Mínima de Abrangência (MST) para grafo não direcionado ponderado
-  * `A` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - encontrando Árvore Mínima de Abrangência (MST) para grafo não direcionado ponderado
-* **Divide and Conquer** - dividir o problema em partes menores e depois resolver essas partes
-  * `B` [Binary Search](src/algorithms/search/binary-search)
-  * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
-  * `B` [Pascal's Triangle](src/algorithms/math/pascal-triangle)
-  * `B` [Euclidean Algorithm](src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
+* **Força bruta** - Pense em todas as possibilidades e escolha a melhor solução
+  * `B` [Busca Linear (Linear Search)](src/algorithms/search/linear-search)
+  * `B` [Terraços de Chuva](src/algorithms/uncategorized/rain-terraces) - Problema de retenção de água da chuva (programação dinâmica e versões de força bruta)
+  * `A` [Subarray Máximo](src/algorithms/sets/maximum-subarray)
+  * `A` [Problema do Caixeiro Viajante](src/algorithms/graph/travelling-salesman) - Rota mais curta possível que visita cada cidade e retorna à cidade de origem
+* **Ganância** - Escolha a melhor opção no momento, sem qualquer consideração pelo futuro
+  * `B` [Jogo do Salto](src/algorithms/uncategorized/jump-game)
+  * `A` [Problema da Mochila](src/algorithms/sets/knapsack-problem)
+  * `A` [Algoritmo de Dijkstra](src/algorithms/graph/dijkstra) - Encontrar caminhos mais curtos para todos os vértices do grafo a partir de um único vértice
+  * `A` [Algoritmo de Prim](src/algorithms/graph/prim) - Encontrando Árvore Mínima de Abrangência (MST) para grafo não direcionado ponderado
+  * `A` [Algoritmo de Kruskal](src/algorithms/graph/kruskal) - Encontrando Árvore Mínima de Abrangência (MST) para grafo conexo com pesos
+* **Dividir e Conquistar** - Dividir o problema em partes menores e então resolver essas partes
+  * `B` [Busca Binária (Binary Search)](src/algorithms/search/binary-search)
+  * `B` [Torre de Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Triângulo de Pascal](src/algorithms/math/pascal-triangle)
+  * `B` [Algoritmo Euclidiano](src/algorithms/math/euclidean-algorithm) - Calcular o Máximo Divisor Comum (MDC)
   * `B` [Merge Sort](src/algorithms/sorting/merge-sort)
   * `B` [Quicksort](src/algorithms/sorting/quick-sort)
-  * `B` [Tree Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
-  * `B` [Graph Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `A` [Permutations](src/algorithms/sets/permutations) (com e sem repetições)
-  * `A` [Combinations](src/algorithms/sets/combinations) (com e sem repetições)
-* **Dynamic Programming** - criar uma solução usando sub-soluções encontradas anteriormente
-  * `B` [Fibonacci Number](src/algorithms/math/fibonacci)
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
-  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping problema da água da chuva
-  * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - distância mínima de edição entre duas sequências
-  * `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS)
-  * `A` [Longest Common Substring](src/algorithms/string/longest-common-substring)
-  * `A` [Longest Increasing Subsequence](src/algorithms/sets/longest-increasing-subsequence)
-  * `A` [Shortest Common Supersequence](src/algorithms/sets/shortest-common-supersequence)
-  * `A` [0/1 Knapsack Problem](src/algorithms/sets/knapsack-problem)
-  * `A` [Integer Partition](src/algorithms/math/integer-partition)
-  * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
-  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - encontrando o caminho mais curto para todos os vértices do gráfico
-  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - encontrar caminhos mais curtos entre todos os pares de vértices
-  * `A` [Regular Expression Matching](src/algorithms/string/regular-expression-matching)
-* **Backtracking** - da mesma forma que a força bruta, tente gerar todas as soluções possíveis, mas cada vez que você gerar a próxima solução, você testará
-se satisfizer todas as condições, e só então continuar gerando soluções subseqüentes. Caso contrário, volte atrás e siga um caminho diferente para encontrar uma solução. Normalmente, a passagem DFS do espaço de estados está sendo usada.
-  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
-  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
-  * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Visite todos os vértices exatamente uma vez
-  * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
-  * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
-  * `A` [Combination Sum](src/algorithms/sets/combination-sum) - encontre todas as combinações que formam uma soma específica
-* **Branch & Bound** - lembre-se da solução de menor custo encontrada em cada etapa do retrocesso
-pesquisar e usar o custo da solução de menor custo encontrada até o limite inferior do custo de
+  * `B` [Busca em Profundidade (Depth-First Search)](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Busca em Largura (Breadth-First Search)](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [Jogo do Salto](src/algorithms/uncategorized/jump-game)
+  * `A` [Permutações](src/algorithms/sets/permutations) (com e sem repetições)
+  * `A` [Combinações](src/algorithms/sets/combinations) (com e sem repetições)
+* **Programação Dinâmica** - Criar uma solução usando sub-soluções encontradas anteriormente
+  * `B` [Número de Fibonacci](src/algorithms/math/fibonacci)
+  * `B` [Jogo do Salto](src/algorithms/uncategorized/jump-game)
+  * `B` [Caminhos Únicos](src/algorithms/uncategorized/unique-paths)
+  * `B` [Terraços de Chuva](src/algorithms/uncategorized/rain-terraces) - Trapping problema da água da chuva
+  * `A` [Distância Levenshtein](src/algorithms/string/levenshtein-distance) - Distância mínima de edição entre duas sequências
+  * `A` [Mais Longa Subsequência Comum](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Substring Comum Mais Longa](src/algorithms/string/longest-common-substring)
+  * `A` [Maior Subsequência Crescente](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Supersequência Comum Mais Curta](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Problema da Mochila](src/algorithms/sets/knapsack-problem)
+  * `A` [Partição Inteira](src/algorithms/math/integer-partition)
+  * `A` [Subarray Máximo](src/algorithms/sets/maximum-subarray)
+  * `A` [Algoritmo de Bellman-Ford](src/algorithms/graph/bellman-ford) - Encontrar caminhos mais curtos para todos os vértices do grafo a partir de um único vértice
+  * `A` [Algoritmo de Floyd-Warshall](src/algorithms/graph/floyd-warshall) - Encontrar caminhos mais curtos entre todos os pares de vértices
+  * `A` [Expressões Regulares Correspondentes](src/algorithms/string/regular-expression-matching)
+* **Backtracking** - Da mesma forma que a força bruta, tente gerar todas as soluções possíveis, mas, cada vez que você gerar a próxima solução será necessário testar se a mesma satisfaz todas as condições, e só então continuará a gerar as soluções subsequentes. Caso contrário, volte atrás e siga um caminho diferente para encontrar uma solução. Normalmente, a passagem DFS do espaço de estados está sendo usada.
+  * `B` [Jogo do Salto](src/algorithms/uncategorized/jump-game)
+  * `B` [Caminhos Únicos](src/algorithms/uncategorized/unique-paths)
+  * `A` [Ciclo Hamiltoniano](src/algorithms/graph/hamiltonian-cycle) - Visite todos os vértices exatamente uma vez
+  * `A` [Problema das N-Rainhas](src/algorithms/uncategorized/n-queens)
+  * `A` [Passeio do Cavaleiro](src/algorithms/uncategorized/knight-tour)
+  * `A` [Soma de Combinação](src/algorithms/sets/combination-sum) - Encontre todas as combinações que formam uma soma específica
+* **Branch & Bound** - Lembre-se da solução de menor custo encontrada em cada etapa do retrocesso, pesquisar e usar o custo da solução de menor custo encontrada até o limite inferior do custo de
 solução de menor custo para o problema, a fim de descartar soluções parciais com custos maiores que o
 solução de menor custo encontrada até o momento. Normalmente, a travessia BFS em combinação com a passagem DFS do espaço de estados
 árvore está sendo usada
@@ -220,10 +224,19 @@ npm test
 ```
 npm test -- 'LinkedList'
 ```
+**Solução de problemas**
 
-**Parque infantil**
+Caso o linting ou o teste estejam falhando, tente excluir a pasta node_modules e reinstalar os pacotes npm:
+```
+rm -rf ./node_modules
+npm i
+```
+
+Verifique também se você está usando uma versão correta do Node (>=14.16.0). Se você estiver usando [nvm](https://github.com/nvm-sh/nvm) para gerenciamento de versão do Node, você pode executar `nvm use` a partir da pasta raiz do projeto e a versão correta será escolhida.
 
-Você pode brincar com estruturas de dados e algoritmos em `./src/playground/playground.js` arquivar e escrever
+**Playground**
+
+Você pode brincar com estruturas de dados e algoritmos no arquivo `./src/playground/playground.js` e escrever
 testes para isso em `./src/playground/__test__/playground.test.js`.
 
 Em seguida, basta executar o seguinte comando para testar se o código do seu playground funciona conforme o esperado:
@@ -236,15 +249,16 @@ npm test -- 'playground'
 
 ### Referências
 
-[▶ Estruturas de dados e algoritmos no YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [▶ Estruturas de Dados e Algoritmos no YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [✍🏻 Esboços de Estruturas de Dados](https://okso.app/showcase/data-structures)
 
 ### Notação Big O
 
-Ordem de crescimento dos algoritmos especificados em notação Big O.
+A notação Big O é usada para classificar algoritmos de acordo com a forma como seu tempo de execução ou requisitos de espaço crescem à medida que o tamanho da entrada aumenta. No gráfico abaixo você pode encontrar as ordens mais comuns de crescimento de algoritmos especificados na notação Big O.
 
 ![Notação Big-O](./assets/big-o-graph.png)
 
-Fonte: [Notação Big-O dicas](http://bigocheatsheet.com/).
+Fonte: [Notação Big-O Dicas](http://bigocheatsheet.com/).
 
 Abaixo está a lista de algumas das notações Big O mais usadas e suas comparações de desempenho em relação aos diferentes tamanhos dos dados de entrada.
 
@@ -260,29 +274,31 @@ Abaixo está a lista de algumas das notações Big O mais usadas e suas compara
 
 ### Complexidade de operações de estrutura de dados
 
-| estrutura de dados      | Acesso    | Busca     | Inserção  | Eliminação | comentários |
+| Estrutura de dados      | Acesso    | Busca     | Inserção  | Eliminação | Comentários |
 | ----------------------- | :-------: | :-------: | :-------: | :-------:  | :--------   |
 | **Array**               | 1         | n         | n         | n          |             |
 | **Stack**               | n         | n         | 1         | 1          |             |
 | **Queue**               | n         | n         | 1         | 1          |             |
 | **Linked List**         | n         | n         | 1         | 1          |             |
-| **Hash Table**          | -         | n         | n         | n          | Em caso de uma função hash perfeita, os custos seriam O (1) |
-| **Binary Search Tree**  | n         | n         | n         | n          | No caso de custos de árvore equilibrados seria O (log (n))
+| **Hash Table**          | -         | n         | n         | n          | Em caso de uma função hash perfeita, os custos seriam O(1) |
+| **Binary Search Tree**  | n         | n         | n         | n          | No caso de custos de árvore equilibrados seria O(log(n))
 | **B-Tree**              | log(n)    | log(n)    | log(n)    | log(n)     |             |
 | **Red-Black Tree**      | log(n)    | log(n)    | log(n)    | log(n)     |             |
 | **AVL Tree**            | log(n)    | log(n)    | log(n)    | log(n)     |             |
 | **Bloom Filter**        | -         | 1         | 1         | -          | Falsos positivos são possíveis durante a pesquisa |
 
-### Array Sorting Algorithms Complexity
+### Complexidade dos Algoritmos de Ordenação de Matrizes
 
-| Nome                  | Melhor          | Média               | Pior                | Mémoria   | Estável   | comentários |
+| Nome                  | Melhor          | Média               | Pior                | Mémoria   | Estável   | Comentários |
 | --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :--------   |
 | **Bubble sort**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Sim       |             |
 | **Insertion sort**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Sim       |             |
 | **Selection sort**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Não       |             |
 | **Heap sort**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | Não       |             |
 | **Merge sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Sim       |             |
-| **Quick sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | Não       | O Quicksort geralmente é feito no local com o espaço de pilha O  O(log(n)) stack space |
+| **Quick sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | Não       | O Quicksort geralmente é feito no local com espaço de pilha O(log(n)) |
 | **Shell sort**        | n&nbsp;log(n)   | depende da sequência de lacunas | n&nbsp;(log(n))<sup>2</sup>     | 1      | Não    |                   |
 | **Counting sort**     | n + r           | n + r               | n + r               | n + r     | Sim       | r - maior número na matriz          |
 | **Radix sort**        | n * k           | n * k               | n * k               | n + k     | Sim       | k - comprimento da chave mais longa |
+
+> ℹ️ Outros [projetos](https://trekhleb.dev/projects/) e [artigos](https://trekhleb.dev/blog/) sobre JavaScript e algoritmos em [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.ru-RU.md b/README.ru-RU.md
new file mode 100644
index 0000000000..939ed46700
--- /dev/null
+++ b/README.ru-RU.md
@@ -0,0 +1,322 @@
+# Алгоритмы и структуры данных на JavaScript
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+В этом репозитории содержатся базовые JavaScript-примеры многих популярных алгоритмов и структур данных.
+
+Для каждого алгоритма и структуры данных есть свой файл README с соответствующими пояснениями и ссылками на материалы для дальнейшего изучения (в том числе и ссылки на видеоролики в YouTube).
+
+_Читать на других языках:_
+[_English_](https://github.com/trekhleb/javascript-algorithms/),
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+*☝ Замечание: этот репозиторий предназначен для учебно-исследовательских целей (**не** для использования в продакшн-системах).*
+
+## Структуры данных
+
+Структура данных (англ. data structure) — программная единица, позволяющая хранить и обрабатывать множество однотипных и/или логически связанных данных в вычислительной технике. Для добавления, поиска, изменения и удаления данных структура данных предоставляет некоторый набор функций, составляющих её интерфейс.
+
+`B` - Базовый уровень, `A` - Продвинутый уровень
+
+* `B` [Связный список](src/data-structures/linked-list)
+* `B` [Двунаправленный связный список](src/data-structures/doubly-linked-list)
+* `B` [Очередь](src/data-structures/queue)
+* `B` [Стек](src/data-structures/stack)
+* `B` [Хеш-таблица](src/data-structures/hash-table)
+* `B` [Куча](src/data-structures/heap) — максимальная и минимальная версии
+* `B` [Очередь с приоритетом](src/data-structures/priority-queue)
+* `A` [Префиксное дерево](src/data-structures/trie)
+* `A` [Деревья](src/data-structures/tree)
+  * `A` [Двоичное дерево поиска](src/data-structures/tree/binary-search-tree)
+  * `A` [АВЛ-дерево](src/data-structures/tree/avl-tree)
+  * `A` [Красно-чёрное дерево](src/data-structures/tree/red-black-tree)
+  * `A` [Дерево отрезков](src/data-structures/tree/segment-tree) — для минимума, максимума и суммы отрезков
+  * `A` [Дерево Фенвика](src/data-structures/tree/fenwick-tree) (двоичное индексированное дерево)
+* `A` [Граф](src/data-structures/graph) (ориентированный и неориентированный)
+* `A` [Система непересекающихся множеств](src/data-structures/disjoint-set)
+* `A` [Фильтр Блума](src/data-structures/bloom-filter)
+
+## Алгоритмы
+
+Алгоритм — конечная совокупность точно заданных правил решения некоторого класса задач или набор инструкций, описывающих порядок действий исполнителя для решения некоторой задачи.
+
+`B` - Базовый уровень, `A` - Продвинутый уровень
+
+### Алгоритмы по тематике
+
+* **Математика**
+  * `B` [Битовые манипуляции](src/algorithms/math/bits) — получение/запись/сброс/обновление битов, умножение/деление на 2, сделать отрицательным и т.п.
+  * `B` [Двоичное число с плавающей запятой](src/algorithms/math/binary-floating-point) - двоичное представление чисел с плавающей запятой
+  * `B` [Факториал](src/algorithms/math/factorial)
+  * `B` [Числа Фибоначчи](src/algorithms/math/fibonacci) — классическое решение, решение в замкнутой форме
+  * `B` [Простые множители](src/algorithms/math/prime-factors) - нахождение простых множителей и их подсчёт с использованием теоремы Харди-Рамануджана
+  * `B` [Тест простоты](src/algorithms/math/primality-test) (метод пробного деления)
+  * `B` [Алгоритм Евклида](src/algorithms/math/euclidean-algorithm) — нахождение наибольшего общего делителя (НОД)
+  * `B` [Наименьшее общее кратное](src/algorithms/math/least-common-multiple) (НОК)
+  * `B` [Решето Эратосфена](src/algorithms/math/sieve-of-eratosthenes) — нахождение всех простых чисел до некоторого целого числа n
+  * `B` [Степень двойки](src/algorithms/math/is-power-of-two) — является ли число степенью двойки (простое и побитовое решения)
+  * `B` [Треугольник Паскаля](src/algorithms/math/pascal-triangle)
+  * `B` [Комплексные числа](src/algorithms/math/complex-number) — комплексные числа, базовые операции над ними
+  * `B` [Радианы и градусы](src/algorithms/math/radian) — конвертирование радианов в градусы и наоборот
+  * `B` [Быстрое возведение в степень](src/algorithms/math/fast-powering)
+  * `B` [Схема Горнера](src/algorithms/math/horner-method) - оценка полиномов
+  * `B` [Матрицы](src/algorithms/math/matrix) - матрицы и основные операции с матрицами (умножение, транспонирование и т.д.)
+  * `B` [Евклидово расстояние](src/algorithms/math/euclidean-distance) - расстояние между двумя точками/векторами/матрицами
+  * `A` [Разбиение числа](src/algorithms/math/integer-partition)
+  * `A` [Квадратный корень](src/algorithms/math/square-root) — метод Ньютона
+  * `A` [Алгоритм Лю Хуэя](src/algorithms/math/liu-hui) — расчёт числа π с заданной точностью методом вписанных правильных многоугольников
+  * `A` [Дискретное преобразование Фурье](src/algorithms/math/fourier-transform) — разложение временной функции (сигнала) на частотные составляющие
+* **Множества**
+  * `B` [Декартово произведение](src/algorithms/sets/cartesian-product) — результат перемножения множеств
+  * `B` [Тасование Фишера — Йетса](src/algorithms/sets/fisher-yates) — создание случайных перестановок конечного множества
+  * `A` [Булеан](src/algorithms/sets/power-set) — все подмножества заданного множества (побитовый поиск и поиск с возвратом)
+  * `A` [Перестановки](src/algorithms/sets/permutations) (с повторениями и без повторений)
+  * `A` [Сочетания](src/algorithms/sets/combinations) (с повторениями и без повторений)
+  * `A` [Наибольшая общая подпоследовательность](src/algorithms/sets/longest-common-subsequence)
+  * `A` [Наибольшая увеличивающаяся подпоследовательность](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Наименьшая общая супер-последовательность](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Задача о рюкзаке](src/algorithms/sets/knapsack-problem) — "0/1" и "неограниченный" рюкзаки
+  * `A` [Максимальный под-массив](src/algorithms/sets/maximum-subarray) — метод полного перебора и алгоритм Кадане
+  * `A` [Комбинации сумм](src/algorithms/sets/combination-sum) — нахождение всех комбинаций, сумма каждой из которых равна заданному числу
+* **Алгоритмы работы со строками**
+  * `B` [Расстояние Хэмминга](src/algorithms/string/hamming-distance) — число позиций, в которых соответствующие символы различны
+  * `A` [Расстояние Левенштейна](src/algorithms/string/levenshtein-distance) — метрика, измеряющая разность между двумя последовательностями
+  * `A` [Алгоритм Кнута — Морриса — Пратта](src/algorithms/string/knuth-morris-pratt) — поиск подстроки (сопоставление с шаблоном)
+  * `A` [Z-функция](src/algorithms/string/z-algorithm) — поиск подстроки (сопоставление с шаблоном)
+  * `A` [Алгоритм Рабина — Карпа](src/algorithms/string/rabin-karp) — поиск подстроки
+  * `A` [Наибольшая общая подстрока](src/algorithms/string/longest-common-substring)
+  * `A` [Разборщик регулярных выражений](src/algorithms/string/regular-expression-matching)
+* **Алгоритмы поиска**
+  * `B` [Линейный поиск](src/algorithms/search/linear-search)
+  * `B` [Поиск с перескоком](src/algorithms/search/jump-search) (поиск блоков) — поиск в упорядоченном массиве
+  * `B` [Двоичный поиск](src/algorithms/search/binary-search) — поиск в упорядоченном массиве
+  * `B` [Интерполяционный поиск](src/algorithms/search/interpolation-search) — поиск в равномерно распределённом упорядоченном массиве.
+* **Алгоритмы сортировки**
+  * `B` [Сортировка пузырьком](src/algorithms/sorting/bubble-sort)
+  * `B` [Сортировка выбором](src/algorithms/sorting/selection-sort)
+  * `B` [Сортировка вставками](src/algorithms/sorting/insertion-sort)
+  * `B` [Пирамидальная сортировка (сортировка кучей)](src/algorithms/sorting/heap-sort)
+  * `B` [Сортировка слиянием](src/algorithms/sorting/merge-sort)
+  * `B` [Быстрая сортировка](src/algorithms/sorting/quick-sort) — с использованием дополнительной памяти и без её использования
+  * `B` [Сортировка Шелла](src/algorithms/sorting/shell-sort)
+  * `B` [Сортировка подсчётом](src/algorithms/sorting/counting-sort)
+  * `B` [Поразрядная сортировка](src/algorithms/sorting/radix-sort)
+* **Связный список**
+  * `B` [Прямой обход](src/algorithms/linked-list/traversal)
+  * `B` [Обратный обход](src/algorithms/linked-list/reverse-traversal)
+* **Деревья**
+  * `B` [Поиск в глубину](src/algorithms/tree/depth-first-search)
+  * `B` [Поиск в ширину](src/algorithms/tree/breadth-first-search)
+* **Графы**
+  * `B` [Поиск в глубину](src/algorithms/graph/depth-first-search)
+  * `B` [Поиск в ширину](src/algorithms/graph/breadth-first-search)
+  * `B` [Алгоритм Краскала](src/algorithms/graph/kruskal) — нахождение минимального остовного дерева для взвешенного неориентированного графа
+  * `A` [Алгоритм Дейкстры](src/algorithms/graph/dijkstra) — нахождение кратчайших путей от одной из вершин графа до всех остальных
+  * `A` [Алгоритм Беллмана — Форда](src/algorithms/graph/bellman-ford) — нахождение кратчайших путей от одной из вершин графа до всех остальных
+  * `A` [Алгоритм Флойда — Уоршелла](src/algorithms/graph/floyd-warshall) — нахождение кратчайших расстояний между всеми вершинами графа
+  * `A` [Задача нахождения цикла](src/algorithms/graph/detect-cycle) — для ориентированных и неориентированных графов (на основе поиска в глубину и системы непересекающихся множеств)
+  * `A` [Алгоритм Прима](src/algorithms/graph/prim) — нахождение минимального остовного дерева для взвешенного неориентированного графа
+  * `A` [Топологическая сортировка](src/algorithms/graph/topological-sorting) — на основе поиска в глубину
+  * `A` [Шарниры (разделяющие вершины)](src/algorithms/graph/articulation-points) — алгоритм Тарьяна (на основе поиска в глубину)
+  * `A` [Мосты](src/algorithms/graph/bridges) — на основе поиска в глубину
+  * `A` [Эйлеров путь и Эйлеров цикл](src/algorithms/graph/eulerian-path) — алгоритм Флёри (однократное посещение каждой вершины)
+  * `A` [Гамильтонов цикл](src/algorithms/graph/hamiltonian-cycle) — проходит через каждую вершину графа ровно один раз
+  * `A` [Компоненты сильной связности](src/algorithms/graph/strongly-connected-components) — алгоритм Косарайю
+  * `A` [Задача коммивояжёра](src/algorithms/graph/travelling-salesman) — кратчайший маршрут, проходящий через указанные города с последующим возвратом в исходный город
+* **Криптография**
+  * `B` [Полиноминальный хэш](src/algorithms/cryptography/polynomial-hash) — функция кольцевого хэша, основанная на полиноме
+  * `B` [Шифр ​​ограждения рельсов](src/algorithms/cryptography/rail-fence-cipher) - алгоритм транспозиционного шифра для кодирования сообщений
+  * `B` [Шифр Цезаря](src/algorithms/cryptography/caesar-cipher) - простой подстановочный шифр
+  * `B` [Шифр Хилла](src/algorithms/cryptography/hill-cipher) - подстановочный шифр на основе линейной алгебры
+* **Машинное обучение**
+  * `B` [Нано-нейрон](https://github.com/trekhleb/nano-neuron) — 7 простых JavaScript функций, отображающих способности машины к обучению (прямое и обратное распространение)
+  * `B` [k-NN](src/algorithms/ml/knn) - алгоритм классификации k-ближайших соседей
+  * `B` [k-Means](src/algorithms/ml/k-means) - алгоритм кластеризации по методу k-средних
+* **Обработка изображений**
+  * `B` [Резьба по шву](src/algorithms/image-processing/seam-carving) - алгоритм изменения размера изображения с учетом содержания
+* **Статистика**
+  * `B` [Взвешенная случайность](src/algorithms/statistics/weighted-random) - выбор случайного элемента из списка на основе веса элементов
+* **Эволюционные алгоритмы**
+  * `A` [Генетический алгоритм](https://github.com/trekhleb/self-parking-car-evolution) - пример применения генетического алгоритма для обучения самопаркующихся автомобилей
+* **Прочие алгоритмы**
+  * `B` [Ханойская башня](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Поворот квадратной матрицы](src/algorithms/uncategorized/square-matrix-rotation) — используется дополнительная память
+  * `B` [Прыжки](src/algorithms/uncategorized/jump-game) — на основе бэктрекинга, динамического программирования (сверху-вниз + снизу-вверх) и жадных алгоритмов
+  * `B` [Поиск уникальных путей](src/algorithms/uncategorized/unique-paths) — на основе бэктрекинга, динамического программирования и треугольника Паскаля
+  * `B` [Подсчёт дождевой воды](src/algorithms/uncategorized/rain-terraces) — на основе перебора и динамического программирования
+  * `B` [Задача о рекурсивной лестнице](src/algorithms/uncategorized/recursive-staircase) — подсчёт количества путей, по которым можно достичь верха лестницы (4 способа)
+  * `B` [Лучшее время для покупки и продажи акций](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - примеры "разделяй и властвуй" и в один проход
+  * `A` [Задача об N ферзях](src/algorithms/uncategorized/n-queens)
+  * `A` [Маршрут коня](src/algorithms/uncategorized/knight-tour)
+
+### Алгоритмы по парадигме программирования
+
+Парадигма программирования — общий метод или подход, лежащий в основе целого класса алгоритмов. Понятие "парадигма программирования" является более абстрактным по отношению к понятию "алгоритм", которое в свою очередь является более абстрактным по отношению к понятию "компьютерная программа".
+
+* **Алгоритмы полного перебора** — поиск лучшего решения исчерпыванием всевозможных вариантов
+  * `B` [Линейный поиск](src/algorithms/search/linear-search)
+  * `B` [Подсчёт дождевой воды](src/algorithms/uncategorized/rain-terraces)
+  * `B` [Задача о рекурсивной лестнице](src/algorithms/uncategorized/recursive-staircase) — подсчёт количества путей, по которым можно достичь верха лестницы
+  * `A` [Максимальный подмассив](src/algorithms/sets/maximum-subarray)
+  * `A` [Задача коммивояжёра](src/algorithms/graph/travelling-salesman) — кратчайший маршрут, проходящий через указанные города с последующим возвратом в исходный город
+  * `A` [Дискретное преобразование Фурье](src/algorithms/math/fourier-transform) — разложение временной функции (сигнала) на частотные составляющие
+* **Жадные алгоритмы** — принятие локально оптимальных решений с учётом допущения об оптимальности конечного решения
+  * `B` [Прыжки](src/algorithms/uncategorized/jump-game)
+  * `A` [Задача о неограниченном рюкзаке](src/algorithms/sets/knapsack-problem)
+  * `A` [Алгоритм Дейкстры](src/algorithms/graph/dijkstra) — нахождение кратчайших путей от одной из вершин графа до всех остальных
+  * `A` [Алгоритм Прима](src/algorithms/graph/prim) — нахождение минимального остовного дерева для взвешенного неориентированного графа
+  * `A` [Алгоритм Краскала](src/algorithms/graph/kruskal) — нахождение минимального остовного дерева для взвешенного неориентированного графа
+* **Разделяй и властвуй** — рекурсивное разбиение решаемой задачи на более мелкие
+  * `B` [Двоичный поиск](src/algorithms/search/binary-search)
+  * `B` [Ханойская башня](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Треугольник Паскаля](src/algorithms/math/pascal-triangle)
+  * `B` [Алгоритм Евклида](src/algorithms/math/euclidean-algorithm) — нахождение наибольшего общего делителя (НОД)
+  * `B` [Сортировка слиянием](src/algorithms/sorting/merge-sort)
+  * `B` [Быстрая сортировка](src/algorithms/sorting/quick-sort)
+  * `B` [Поиск в глубину (дерево)](src/algorithms/tree/depth-first-search)
+  * `B` [Поиск в глубину (граф)](src/algorithms/graph/depth-first-search)
+  * `B` [Матрицы](src/algorithms/math/matrix) - генерирование и обход матриц различной формы
+  * `B` [Прыжки](src/algorithms/uncategorized/jump-game)
+  * `B` [Быстрое возведение в степень](src/algorithms/math/fast-powering)
+  * `B` [Лучшее время для покупки и продажи акций](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - примеры "разделяй и властвуй" и в один проход
+  * `A` [Перестановки](src/algorithms/sets/permutations) (с повторениями и без повторений)
+  * `A` [Сочетания](src/algorithms/sets/combinations) (с повторениями и без повторений)
+* **Динамическое программирование** — решение общей задачи конструируется на основе ранее найденных решений подзадач
+  * `B` [Числа Фибоначчи](src/algorithms/math/fibonacci)
+  * `B` [Прыжки](src/algorithms/uncategorized/jump-game)
+  * `B` [Поиск уникальных путей](src/algorithms/uncategorized/unique-paths)
+  * `B` [Подсчёт дождевой воды](src/algorithms/uncategorized/rain-terraces)
+  * `B` [Задача о рекурсивной лестнице](src/algorithms/uncategorized/recursive-staircase) — подсчёт количества путей, по которым можно достичь верха лестницы
+  * `B` [Резьба по шву](src/algorithms/image-processing/seam-carving) - алгоритм изменения размера изображения с учетом содержания
+  * `A` [Расстояние Левенштейна](src/algorithms/string/levenshtein-distance) — метрика, измеряющая разность между двумя последовательностями
+  * `A` [Наибольшая общая подпоследовательность](src/algorithms/sets/longest-common-subsequence)
+  * `A` [Наибольшая общая подстрока](src/algorithms/string/longest-common-substring)
+  * `A` [Наибольшая увеличивающаяся подпоследовательность](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Наименьшая общая суперпоследовательность](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Рюкзак 0-1](src/algorithms/sets/knapsack-problem)
+  * `A` [Разбиение числа](src/algorithms/math/integer-partition)
+  * `A` [Максимальный подмассив](src/algorithms/sets/maximum-subarray)
+  * `A` [Алгоритм Беллмана — Форда](src/algorithms/graph/bellman-ford) — поиск кратчайшего пути во взвешенном графе
+  * `A` [Алгоритм Флойда — Уоршелла](src/algorithms/graph/floyd-warshall) — нахождение кратчайших путей от одной из вершин графа до всех остальных
+  * `A` [Разборщик регулярных выражений](src/algorithms/string/regular-expression-matching)
+* **Поиск с возвратом (бэктрекинг)** — при поиске решения многократно делается попытка расширить текущее частичное решение. Если расширение невозможно, то происходит возврат к предыдущему более короткому частичному решению, и делается попытка его расширить другим возможным способом. Обычно используется обход пространства состояний в глубину.
+  * `B` [Прыжки](src/algorithms/uncategorized/jump-game)
+  * `B` [Поиск уникальных путей](src/algorithms/uncategorized/unique-paths)
+  * `B` [Булеан](src/algorithms/sets/power-set) — все подмножества заданного множества
+  * `A` [Гамильтонов цикл](src/algorithms/graph/hamiltonian-cycle) — проходит через каждую вершину графа ровно один раз
+  * `A` [Задача об N ферзях](src/algorithms/uncategorized/n-queens)
+  * `A` [Маршрут коня](src/algorithms/uncategorized/knight-tour)
+  * `A` [Комбинации сумм](src/algorithms/sets/combination-sum) — нахождение всех комбинаций, сумма каждой из которых равна заданному числу
+* **Метод ветвей и границ** — основан на упорядоченном переборе решений и рассмотрении только тех из них, которые являются перспективными (по тем или иным признакам) и отбрасывании бесперспективных множеств решений. Обычно используется обход в ширину в совокупности с обходом дерева пространства состояний в глубину.
+
+## Как использовать этот репозиторий
+
+**Установка всех зависимостей**
+```
+npm install
+```
+
+**Запуск ESLint**
+
+Эта команда может потребоваться вам для проверки качества кода.
+
+```
+npm run lint
+```
+
+**Запуск всех тестов**
+
+```
+npm test
+```
+
+**Запуск определённого теста**
+
+```
+npm test -- 'LinkedList'
+```
+
+**Песочница**
+
+Вы можете экспериментировать с алгоритмами и структурами данных в файле `./src/playground/playground.js`
+(файл `./src/playground/__test__/playground.test.js` предназначен для написания тестов).
+
+Для проверки работоспособности вашего кода используйте команду:
+
+```
+npm test -- 'playground'
+```
+
+## Полезная информация
+
+### Ссылки
+
+[▶ О структурах данных и алгоритмах](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### Нотация «О» большое
+
+*Нотация «О» большое* используется для классификации алгоритмов в соответствии с ростом времени выполнения и затрачиваемой памяти при увеличении размера входных данных. На диаграмме ниже представлены общие порядки роста алгоритмов в соответствии с нотацией «О» большое.
+
+![Big O graphs](./assets/big-o-graph.png)
+
+Источник: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+Ниже представлены часто используемые обозначения в нотации «О» большое, а также сравнение их производительностей на различных размерах входных данных.
+
+| Нотация «О» большое | 10 элементов | 100 элементов | 1000 элементов |
+| ------------------- | ------------ | ------------- | -------------- |
+| **O(1)**            | 1            | 1             | 1              |
+| **O(log N)**        | 3            | 6             | 9              |
+| **O(N)**            | 10           | 100           | 1000           |
+| **O(N log N)**      | 30           | 600           | 9000           |
+| **O(N^2)**          | 100          | 10000         | 1000000        |
+| **O(2^N)**          | 1024         | 1.26e+29      | 1.07e+301      |
+| **O(N!)**           | 3628800      | 9.3e+157      | 4.02e+2567     |
+
+### Сложности операций в структурах данных
+
+| Структура данных           | Получение | Поиск     | Вставка   | Удаление  | Комментарии |
+| -------------------------- | :-------: | :-------: | :-------: | :-------: | :---------- |
+| **Массив**                 | 1         | n         | n         | n         |             |
+| **Стек**                   | n         | n         | 1         | 1         |             |
+| **Очередь**                | n         | n         | 1         | 1         |             |
+| **Связный список**         | n         | n         | 1         | n         |             |
+| **Хеш-таблица**            | -         | n         | n         | n         | Для идеальной хеш-функции — O(1) |
+| **Двоичное дерево поиска** | n         | n         | n         | n         | В сбалансированном дереве — O(log(n)) |
+| **B-дерево**               | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Красно-чёрное дерево**   | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **АВЛ-дерево**             | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Фильтр Блума**           | -         | 1         | 1         | -         | Возможно получение ложно-положительного срабатывания |
+
+### Сложности алгоритмов сортировки
+
+| Наименование               | Лучший случай | Средний случай | Худший случай | Память | Устойчивость | Комментарии |
+| -------------------------- | :-----------: | :------------: | :-----------: | :----: | :----------: | :---------- |
+| **Сортировка пузырьком**   | n             | n<sup>2</sup>  | n<sup>2</sup> | 1      | Да           |             |
+| **Сортировка вставками**   | n             | n<sup>2</sup>  | n<sup>2</sup> | 1      | Да           |             |
+| **Сортировка выбором**     | n<sup>2</sup> | n<sup>2</sup>  | n<sup>2</sup> | 1      | Нет          |             |
+| **Сортировка кучей**       | n&nbsp;log(n) | n&nbsp;log(n)  | n&nbsp;log(n) | 1      | Нет          |             |
+| **Сортировка слиянием**    | n&nbsp;log(n) | n&nbsp;log(n)  | n&nbsp;log(n) | n      | Да           |             |
+| **Быстрая сортировка**     | n&nbsp;log(n) | n&nbsp;log(n)  | n<sup>2</sup> | log(n) | Нет          | Быстрая сортировка обычно выполняется с использованием O(log(n)) дополнительной памяти |
+| **Сортировка Шелла**       | n&nbsp;log(n) | зависит от выбранных шагов | n&nbsp;(log(n))<sup>2</sup>  | 1      | Нет          |           |
+| **Сортировка подсчётом**   | n + r         | n + r          | n + r         | n + r  | Да           | r — наибольшее число в массиве |
+| **Поразрядная сортировка** | n * k         | n * k          | n * k         | n + k  | Да           | k — длина самого длинного ключа |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.tr-TR.md b/README.tr-TR.md
new file mode 100644
index 0000000000..53600480db
--- /dev/null
+++ b/README.tr-TR.md
@@ -0,0 +1,325 @@
+# JavaScript Algoritmalar ve Veri Yapıları
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+Bu repository popüler algoritmaların ve veri yapılarının birçoğunun Javascript tabanlı örneklerini bulundurur.
+
+Her bir algoritma ve veri yapısı kendine
+ait açıklama ve videoya sahip README dosyası içerir.
+
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+*☝ Not, bu proje araştırma ve öğrenme amacı ile yapılmış
+olup üretim için **yapılmamıştır**.*
+
+## Veri Yapıları
+
+Bir veri yapısı, verileri bir bilgisayarda organize etmenin ve depolamanın belirli bir yoludur, böylece
+verimli bir şekilde erişilebilir ve değiştirilebilir. Daha iyi ifadeyle, bir veri yapısı bir veri koleksiyonudur,
+aralarındaki ilişkiler, ve işlevler veya işlemler
+veriye uygulanabilir.
+
+`B` - Başlangıç, `A` - İleri Seviye
+
+* `B` [Bağlantılı Veri Yapısı](src/data-structures/linked-list)
+* `B` [Çift Yönlü Bağlı Liste](src/data-structures/doubly-linked-list)
+* `B` [Kuyruk](src/data-structures/queue)
+* `B` [Yığın](src/data-structures/stack)
+* `B` [Hash Table](src/data-structures/hash-table)
+* `B` [Heap](src/data-structures/heap) - max and min heap versions
+* `B` [Öncelikli Kuyruk](src/data-structures/priority-queue)
+* `A` [Trie](src/data-structures/trie)
+* `A` [Ağaç](src/data-structures/tree)
+  * `A` [İkili Arama Ağaçları](src/data-structures/tree/binary-search-tree)
+  * `A` [AVL Tree](src/data-structures/tree/avl-tree)
+  * `A` [Red-Black Tree](src/data-structures/tree/red-black-tree)
+  * `A` [Segment Tree](src/data-structures/tree/segment-tree) - with min/max/sum range queries examples
+  * `A` [Fenwick Tree](src/data-structures/tree/fenwick-tree) (Binary Indexed Tree)
+* `A` [Graph](src/data-structures/graph) (both directed and undirected)
+* `A` [Disjoint Set](src/data-structures/disjoint-set)
+* `A` [Bloom Filter](src/data-structures/bloom-filter)
+
+## Algoritmalar
+
+Bir algoritma, bir problem sınıfının nasıl çözüleceğine dair kesin bir tanımlamadır.
+Bir işlem dizisini açık olarak tanımlayan kurallar dizisidir.
+
+
+`B` - Başlangıç, `A` - İleri Seviye
+
+### Konusuna göre Algoritma
+
+* **Matematik**
+  * `B` [Bit Manipülasyonu](src/algorithms/math/bits) - set/get/update/clear bits, multiplication/division by two, make negative etc.
+  * `B` [İkili Kayan Nokta](src/algorithms/math/binary-floating-point) - kayan noktalı sayıların ikilik sistemde gösterimi.
+  * `B` [Faktöriyel](src/algorithms/math/factorial)
+  * `B` [Fibonacci Sayısı](src/algorithms/math/fibonacci) - klasik ve kapalı-form versiyonları
+  * `B` [Asallık Testi](src/algorithms/math/primality-test) (deneyerek bölüm metodu)
+  * `B` [Öklid Algoritması](src/algorithms/math/euclidean-algorithm) - En büyük ortak bölen hesaplama (EBOB)
+  * `B` [En küçük Ortak Kat](src/algorithms/math/least-common-multiple) (EKOK)
+  * `B` [Eratosten Kalburu](src/algorithms/math/sieve-of-eratosthenes) - belirli bir sayıya kadarki asal sayıları bulma
+  * `B` [Is Power of Two](src/algorithms/math/is-power-of-two) - sayı ikinin katı mı sorgusu (naive ve bitwise algoritmaları)
+  * `B` [Paskal Üçgeni](src/algorithms/math/pascal-triangle)
+  * `B` [Karmaşık Sayılar](src/algorithms/math/complex-number) - karmaşık sayılar ve karmaşık sayılar ile temel işlemler
+  * `B` [Radyan & Derece](src/algorithms/math/radian) - radyandan dereceye çeviri ve tersine çeviri
+  * `B` [Fast Powering](src/algorithms/math/fast-powering)
+  * `B` [Horner's method](src/algorithms/math/horner-method) - polinomal ifadelerin değerlendirilmesi
+  * `B` [Matrices](src/algorithms/math/matrix) - matrisler ve basit matris operasyonları (çarpım, tersçapraz, vb.)
+  * `B` [Euclidean Distance](src/algorithms/math/euclidean-distance) - iki nokta/vektör/matris arasındaki mesafe
+  * `A` [Tamsayı Bölümü](src/algorithms/math/integer-partition)
+  * `A` [Karekök](src/algorithms/math/square-root) - Newton yöntemi
+  * `A` [Liu Hui π Algoritması](src/algorithms/math/liu-hui) - N-gons'a göre yaklaşık π hesabı
+  * `A` [Ayrık Fourier Dönüşümü](src/algorithms/math/fourier-transform) - bir zaman fonksiyonunu (sinyal) içerdiği frekanslara ayırın
+* **Setler**
+  * `B` [Kartezyen Ürün](src/algorithms/sets/cartesian-product) - birden fazla kümenin çarpımı
+  * `B` [Fisher–Yates Shuffle](src/algorithms/sets/fisher-yates) - sonlu bir dizinin rastgele permütasyonu
+  * `A` [Power Set](src/algorithms/sets/power-set) - all subsets of a set (bit düzeyinde ve geri izleme yöntemleri)
+  * `A` [Permütasyonlar](src/algorithms/sets/permutations)(tekrarlı ve tekrarsız)
+  * `A` [Kombinasyonlar](src/algorithms/sets/combinations) (tekrarlı ve tekrarsız)
+  * `A` [En Uzun Ortak Altdizi](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [En Uzun Artan Altdizi](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [En Kısa Ortak Üst Sıra](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [Knapsack Problem](src/algorithms/sets/knapsack-problem) - "0-1 sırt çantası problemi" ve "Sınırsız sırt çantası problemi"
+  * `A` [Maksimum Altdizi](src/algorithms/sets/maximum-subarray) - "Kaba Kuvvet" ve "Dinamik Programlara" (Kadane'nin) versiyonu
+  * `A` [Kombinasyon Toplamı](src/algorithms/sets/combination-sum) - belirli toplamı oluşturan tüm kombinasyonları bulun
+* **Metin**
+  * `B` [Hamming Mesafesi](src/algorithms/string/hamming-distance) - sembollerin farklı olduğu konumların sayısı
+  * `A` [Levenshtein Mesafesi](src/algorithms/string/levenshtein-distance) - iki sekans arasındaki minimum düzenleme mesafesi
+  * `A` [Knuth–Morris–Pratt Algoritması](src/algorithms/string/knuth-morris-pratt) (KMP Algorithm) - altmetin araması (örüntü eşleme)
+  * `A` [Z Algoritması](src/algorithms/string/z-algorithm) - altmetin araması (desen eşleştirme)
+  * `A` [Rabin Karp Algoritması](src/algorithms/string/rabin-karp) - altmetin araması
+  * `A` [En Uzun Ortak Alt Metin](src/algorithms/string/longest-common-substring)
+  * `A` [Regular Expression Eşleme](src/algorithms/string/regular-expression-matching)
+* **Aramalar**
+  * `B` [Doğrusal Arama](src/algorithms/search/linear-search)
+  * `B` [Jump Search](src/algorithms/search/jump-search) (ya da Block Search) - sıralı dizide arama
+  * `B` [İkili Arama](src/algorithms/search/binary-search) - sıralı dizide arama
+  * `B` [Interpolation Search](src/algorithms/search/interpolation-search) - tekdüze dağıtılmış sıralı dizide arama
+* **Sıralama**
+  * `B` [Bubble Sort](src/algorithms/sorting/bubble-sort)
+  * `B` [Selection Sort](src/algorithms/sorting/selection-sort)
+  * `B` [Insertion Sort](src/algorithms/sorting/insertion-sort)
+  * `B` [Heap Sort](src/algorithms/sorting/heap-sort)
+  * `B` [Merge Sort](src/algorithms/sorting/merge-sort)
+  * `B` [Quicksort](src/algorithms/sorting/quick-sort) - in-place and non-in-place implementations
+  * `B` [Shellsort](src/algorithms/sorting/shell-sort)
+  * `B` [Counting Sort](src/algorithms/sorting/counting-sort)
+  * `B` [Radix Sort](src/algorithms/sorting/radix-sort)
+* **Bağlantılı Liste**
+  * `B` [Straight Traversal](src/algorithms/linked-list/traversal)
+  * `B` [Reverse Traversal](src/algorithms/linked-list/reverse-traversal)
+* **Ağaçlar**
+  * `B` [Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Breadth-First Search](src/algorithms/tree/breadth-first-search) (BFS)
+* **Graphs**
+  * `B` [Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Breadth-First Search](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - ağırlıklı yönlendirilmemiş grafik için Minimum Yayılma Ağacı'nı (MST) bulma
+  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - tek tepe noktasından tüm grafik köşelerine en kısa yolları bulmak
+  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - tek tepe noktasından tüm grafik köşelerine en kısa yolları bulmak
+  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - tüm köşe çiftleri arasındaki en kısa yolları bulun
+  * `A` [Detect Cycle](src/algorithms/graph/detect-cycle) - hem yönlendirilmiş hem de yönlendirilmemiş grafikler için (DFS ve Ayrık Küme tabanlı sürümler)
+  * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - ağırlıklı yönlendirilmemiş grafik için Minimum Yayılma Ağacı'nı (MST) bulma
+  * `A` [Topological Sorting](src/algorithms/graph/topological-sorting) - DFS metodu
+  * `A` [Articulation Points](src/algorithms/graph/articulation-points) - Tarjan's algoritması (DFS based)
+  * `A` [Bridges](src/algorithms/graph/bridges) - DFS yöntemi ile algoritma
+  * `A` [Eulerian Path and Eulerian Circuit](src/algorithms/graph/eulerian-path) - Fleury'nin algoritması - Her kenara tam olarak bir kez ulaş
+  * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Her köşeyi tam olarak bir kez ziyaret et
+  * `A` [Strongly Connected Components](src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
+  * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - her şehri ziyaret eden ve başlangıç ​​şehrine geri dönen mümkün olan en kısa rota
+* **Kriptografi**
+  * `B` [Polynomial Hash](src/algorithms/cryptography/polynomial-hash) - polinom temelinde dönen hash işlevi
+  * `B` [Caesar Cipher](src/algorithms/cryptography/caesar-cipher) - simple substitution cipher
+* **Makine Öğrenmesi**
+  * `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 simple JS functions that illustrate how machines can actually learn (forward/backward propagation)
+* **Kategoriye Ayrılmayanlar**
+  * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - in-place algorithm
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, dynamic programming (top-down + bottom-up) and greedy examples
+  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths) - backtracking, dynamic programming and Pascal's Triangle based examples
+  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem (dynamic programming and brute force versions)
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - tepeye ulaşmanın yollarını sayma (4 çözüm)
+  * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
+  * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
+
+### Algoritmik Paradigma
+
+Algoritmik paradigma, bir sınıfın tasarımının altında yatan genel bir yöntem veya yaklaşımdır.
+Algoritma dizayn tekniği olarak düşünülebilir. Her bir altproblemi (subproblem) asıl problemle
+benzerlik gösteren problemlere uygulanabilir.
+
+* **Brute Force** - mümkün olan tüm çözümleri tara ve en iyisini seç
+  * `B` [Doğrusal Arama](src/algorithms/search/linear-search)
+  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - tepeye çıkmanın yollarını hesapla
+  * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
+  * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - her şehri ziyaret eden ve başlangıç şehrine geri dönen mümkün olan en kısa rota
+  * `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - bir zaman fonksiyonunu (bir sinyal) onu oluşturan frekanslara ayırır
+* **Açgözlü** - geleceği düşünmeden şu an için en iyi seçeneği seçin
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `A` [Unbound Knapsack Problem](src/algorithms/sets/knapsack-problem)
+  * `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - tüm grafik köşelerine giden en kısa yolu bulmak
+  * `A` [Prim’s Algorithm](src/algorithms/graph/prim) - ağırlıklı yönlendirilmemiş grafik için Minimum Yayılma Ağacı'nı (MST) bulma
+  * `A` [Kruskal’s Algorithm](src/algorithms/graph/kruskal) - ağırlıklı yönlendirilmemiş grafik için Minimum Yayılma Ağacı'nı (MST) bulma
+* **Böl ve Fethet** - sorunu daha küçük parçalara bölün ve sonra bu parçaları çözün
+  * `B` [Binary Search](src/algorithms/search/binary-search)
+  * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Pascal's Triangle](src/algorithms/math/pascal-triangle)
+  * `B` [Euclidean Algorithm](src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
+  * `B` [Merge Sort](src/algorithms/sorting/merge-sort)
+  * `B` [Quicksort](src/algorithms/sorting/quick-sort)
+  * `B` [Tree Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Graph Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `B` [Fast Powering](src/algorithms/math/fast-powering)
+  * `A` [Permutations](src/algorithms/sets/permutations) (tekrarlı ve tekrarsız)
+  * `A` [Combinations](src/algorithms/sets/combinations) (tekrarlı ve tekrarsız)
+* **Dinamik Programlama** - önceden bulunan alt çözümleri kullanarak bir çözüm oluşturmak
+  * `B` [Fibonacci Sayısı](src/algorithms/math/fibonacci)
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `B` [Eşsiz Yol](src/algorithms/uncategorized/unique-paths)
+  * `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces) - trapping rain water problem
+  * `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - zirveye ulaşmanın yollarının sayısını sayın
+  * `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - iki sekans arasındaki minimum düzenleme mesafesi
+  * `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Longest Common Substring](src/algorithms/string/longest-common-substring)
+  * `A` [Longest Increasing Subsequence](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Shortest Common Supersequence](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [0/1 Knapsack Problem](src/algorithms/sets/knapsack-problem)
+  * `A` [Integer Partition](src/algorithms/math/integer-partition)
+  * `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
+  * `A` [Bellman-Ford Algorithm](src/algorithms/graph/bellman-ford) - tüm grafik köşelerine giden en kısa yolu bulmak
+  * `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall) - tüm köşe çiftleri arasındaki en kısa yolları bulun
+  * `A` [Regular Expression Matching](src/algorithms/string/regular-expression-matching)
+* **Backtracking** - brute forceye benzer, mümkün tüm sonuçları tara, ancak bir sonraki çözümü her ürettiğinizde test edersiniz
+tüm koşulları karşılıyorsa ve ancak o zaman sonraki çözümleri üretmeye devam edin. Aksi takdirde, geri dönün ve farklı bir çözüm arayın(?).
+Normally the DFS traversal of state-space is being used.
+  * `B` [Jump Game](src/algorithms/uncategorized/jump-game)
+  * `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
+  * `B` [Power Set](src/algorithms/sets/power-set) - all subsets of a set
+  * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Her köşeyi tam olarak bir kez ziyaret edin
+  * `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
+  * `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
+  * `A` [Combination Sum](src/algorithms/sets/combination-sum) - belirli toplamı oluşturan tüm kombinasyonları bulun
+* **Branch & Bound** - remember the lowest-cost solution found at each stage of the backtracking
+search, and use the cost of the lowest-cost solution found so far as a lower bound on the cost of
+a least-cost solution to the problem, in order to discard partial solutions with costs larger than the
+lowest-cost solution found so far. Normally BFS traversal in combination with DFS traversal of state-space
+tree is being used.
+
+## Repository'in Kullanımı
+
+**Bütün dependencyleri kurun**
+```
+npm install
+```
+
+**ESLint'i başlatın**
+
+Bunu kodun kalitesini kontrol etmek amacı ile çalıştırabilirsin.
+
+```
+npm run lint
+```
+
+**Bütün testleri çalıştır**
+```
+npm test
+```
+
+**Testleri ismine göre çalıştır**
+```
+npm test -- 'LinkedList'
+```
+
+**Deneme Alanı**
+
+data-structures ve algorithms içerisinde `./src/playground/playground.js`
+yazarak `./src/playground/__test__/playground.test.js` için test edebilirsin.
+
+
+Ardından basitçe alttaki komutu girerek kodunun beklendiği gibi çalışıp çalışmadığını test edebilirsin:
+
+```
+npm test -- 'playground'
+```
+
+## Yararlı Bilgiler
+
+### Referanslar
+
+[▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### Big O Notation
+
+* Big O notation *, algoritmaları, girdi boyutu büyüdükçe çalışma süresi veya alan gereksinimlerinin nasıl arttığına göre sınıflandırmak için kullanılır.
+Aşağıdaki grafikte, Big O gösteriminde belirtilen algoritmaların en yaygın büyüme sıralarını bulabilirsiniz.
+
+![Big O graphs](./assets/big-o-graph.png)
+
+Kaynak: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+Altta Big O notations ve farklı input boyutlarına karşın yapılmış performans karşılaştırması listelenmektedir.
+
+| Big O Notation | 10 Element için hesaplama | 100 Element için hesaplama | 1000 Element için hesaplama  |
+| -------------- | ---------------------------- | ----------------------------- | ------------------------------- |
+| **O(1)**       | 1                            | 1                             | 1                               |
+| **O(log N)**   | 3                            | 6                             | 9                               |
+| **O(N)**       | 10                           | 100                           | 1000                            |
+| **O(N log N)** | 30                           | 600                           | 9000                            |
+| **O(N^2)**     | 100                          | 10000                         | 1000000                         |
+| **O(2^N)**     | 1024                         | 1.26e+29                      | 1.07e+301                       |
+| **O(N!)**      | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+
+### Veri Yapısı İşlem Karmaşıklığı
+
+| Veri Yapısı          | Access    | Search    | Insertion | Deletion  | Comments  |
+| ----------------------- | :-------: | :-------: | :-------: | :-------: | :-------- |
+| **Dizi**               | 1         | n         | n         | n         |           |
+| **Yığın**               | n         | n         | 1         | 1         |           |
+| **Sıralı**               | n         | n         | 1         | 1         |           |
+| **Bağlantılı Liste**         | n         | n         | 1         | n         |           |
+| **Yığın Tablo**          | -         | n         | n         | n         | Kusursuz hash fonksiyonu durumunda sonuç O(1) |
+| **İkili Arama Ağacı**  | n         | n         | n         | n         | In case of balanced tree costs would be O(log(n)) |
+| **B-Tree**              | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Red-Black Tree**      | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **AVL Tree**            | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Bloom Filter**        | -         | 1         | 1         | -         | Arama esnasında yanlış sonuçlar çıkabilir |
+
+### Dizi Sıralama Algoritmaları Karmaşıklığı
+
+| İsim                  | En İyi            | Ortalama             | En Kötü               | Hafıza    | Kararlı    | Yorumlar  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Bubble sort**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Evet       |           |
+| **Insertion sort**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Evet       |           |
+| **Selection sort**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Hayır        |           |
+| **Heap sort**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | Hayır        |           |
+| **Merge sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Evet       |           |
+| **Quick sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | Hayır        | Hızlı sıralama genellikle O(log(n)) yığın alanıyla yapılır |
+| **Shell sort**        | n&nbsp;log(n)   | depends on gap sequence   | n&nbsp;(log(n))<sup>2</sup>  | 1         | Hayır         |           |
+| **Counting sort**     | n + r           | n + r               | n + r               | n + r     | Evet       | r - dizideki en büyük sayı |
+| **Radix sort**        | n * k           | n * k               | n * k               | n + k     | Evet       | k - en uzun key'in uzunluğu |
+
+## Projeyi Destekleme
+
+Bu projeyi buradan destekleyebilirsiniz ❤️️ [GitHub](https://github.com/sponsors/trekhleb) veya ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.uk-UA.md b/README.uk-UA.md
new file mode 100644
index 0000000000..5ee5f7cbcc
--- /dev/null
+++ b/README.uk-UA.md
@@ -0,0 +1,312 @@
+# Алгоритми JavaScript та структури даних
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+Даний репозиторій приклади багатьох популярних алгоритмів та структур даних на основі JavaScript.
+
+Кожен алгоритм та структура даних має свій окремий README-файл із відповідними поясненнями та посиланнями для подальшого вивчення (включаючи посилання на відео на YouTube).
+
+_Вивчення матеріалу на інших мовах:_
+[_English_](README.md),
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+*☝ Зверніть увагу! Даний проект призначений лише для навчальних та дослідницьких цілей, і він **не** призначений для виробництва (продакшн).*
+
+## Структури даних
+
+Структура даних (в програмуванні) - це спосіб організації даних в комп'ютерах. Часто разом зі структурою даних пов'язується і специфічний перелік операцій, що можуть бути виконаними над даними, організованими в таку структуру.
+Точніше, структура даних - це сукупність даних цінності, взаємозв'язки між ними та функції або операції, до яких можна застосувати дані.
+
+`B` - Початківець, `A` - Просунутий рівень
+
+* `B` [Зв'язаний список](src/data-structures/linked-list)
+* `B` [Двобічно зв'язаний список](src/data-structures/doubly-linked-list)
+* `B` [Черга](src/data-structures/queue)
+* `B` [Стек](src/data-structures/stack)
+* `B` [Геш-таблиця](src/data-structures/hash-table)
+* `B` [Купа, стіс або піраміда](src/data-structures/heap) - max and min heap versions
+* `B` [Черга з пріоритетом](src/data-structures/priority-queue)
+* `A` [Префіксне дерево](src/data-structures/trie)
+* `A` [Дерево](src/data-structures/tree)
+  * `A` [Двійкове дерево пошуку](src/data-structures/tree/binary-search-tree)
+  * `A` [АВЛ-дерево](src/data-structures/tree/avl-tree)
+  * `A` [Червоно-чорне дерево](src/data-structures/tree/red-black-tree)
+  * `A` [Дерево відрізків](src/data-structures/tree/segment-tree) - with min/max/sum range queries examples
+  * `A` [Дерево Фенвіка](src/data-structures/tree/fenwick-tree) (Binary Indexed Tree)
+* `A` [Граф (абстрактний тип даних)](src/data-structures/graph) (both directed and undirected)
+* `A` [Система неперетинних множин](src/data-structures/disjoint-set)
+* `A` [Фільтр Блума](src/data-structures/bloom-filter)
+
+
+## Алгоритми
+
+Алгоритм - це однозначна специфікація способу вирішення класу задач. Це набір правил, які точно визначають послідовність операцій.
+
+`B` - Початківець, `A` - Просунутий рівень
+
+### Алгоритми за тематикою
+
+* **Математика**
+  * `B` [Бітова маніпуляція](src/algorithms/math/bits) - встановити / отримати / оновити / очистити біти, множення / ділення на два, робити від’ємними тощо
+  * `B` [Факторіал](src/algorithms/math/factorial)
+  * `B` [Послідовність Фібоначчі](src/algorithms/math/fibonacci) - класична та закриті версії
+  * `B` [Основні фактори](src/algorithms/math/prime-factors) - пошук простих множників і підрахунок їх за допомогою теореми Харді-Рамануджана
+  * `B` [Тест простоти](src/algorithms/math/primality-test) (метод пробного поділу)
+  * `B` [Алгоритм Евкліда](src/algorithms/math/euclidean-algorithm) - метод обчислення найбільшого спільного дільника (НСД)
+  * `B` [Найменше спільне кратне](src/algorithms/math/least-common-multiple) (НСК)
+  * `B` [Решето Ератосфена](src/algorithms/math/sieve-of-eratosthenes) - алгоритм знаходження всіх простих чисел менших деякого цілого числа *n*
+  * `B` [Піднесення до степеня](src/algorithms/math/is-power-of-two) - перевірити, чи є число ступенем двох (просте та побітове рішення)
+  * `B` [Трикутник Паскаля](src/algorithms/math/pascal-triangle)
+  * `B` [Комплексне число](src/algorithms/math/complex-number) - комплексні числа та основні операції з ними
+  * `B` [Радіани & Градуси](src/algorithms/math/radian) - перетворення радіанів у градуси та навпаки
+  * `B` [Швидке піднесення до степеня](src/algorithms/math/fast-powering)
+  * `B` [Схема Горнера](src/algorithms/math/horner-method) - поліноміальна оцінка
+  * `A` [Розбиття числа](src/algorithms/math/integer-partition)
+  * `A` [Метод дотичних (метод Ньютона)](src/algorithms/math/square-root) - метод наближеного знаходження кореня дійсного рівняння
+  * `A` [Алгоритм Лю Хуея](src/algorithms/math/liu-hui) - розрахунок числа π з заданою точністю методом вписаних правильних багатокутників
+  * `A` [Дискретне перетворення Фур'є](src/algorithms/math/fourier-transform) - розкладання тимчасової функції (сигналу) на частотні складові
+* **Множина**
+  * `B` [Декартів добуток множин](src/algorithms/sets/cartesian-product) - множина усіх можливих впорядкованих пар
+  * `B` [Тасування Фішера - Єйтса](src/algorithms/sets/fisher-yates) - створення випадкових перестановок кінцевого безлічі
+  * `A` [Булеан](src/algorithms/sets/power-set) - множина всіх підмножин даної множини (бітові та зворотні рішення)
+  * `A` [Перестановка](src/algorithms/sets/permutations) (з повтореннями та без)
+  * `A` [Комбінації](src/algorithms/sets/combinations) (з повтореннями та без)
+  * `A` [Пошук найдовшої спільної підпослідовності](src/algorithms/sets/longest-common-subsequence)
+  * `A` [Завдання пошуку найбільшою збільшується підпослідовності](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Найменша загальна супер-послідовність](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Задача пакування рюкзака](src/algorithms/sets/knapsack-problem) - приклади "0/1" та "Необмежений"
+  * `A` [Максимальний підмасив](src/algorithms/sets/maximum-subarray) - метод «Грубої сили» та алгоритм Кадана
+  * `A` [Комбінована сума](src/algorithms/sets/combination-sum) - знайти всі комбінації, що утворюють конкретну суму
+* **Алгоритми роботи з рядками**
+  * `B` [Відстань Геммінга](src/algorithms/string/hamming-distance) - число позицій, у яких відповідні цифри двох двійкових слів однакової довжини різні
+  * `A` [Відстань Левенштейна](src/algorithms/string/levenshtein-distance) - міра відмінності двох послідовностей символів (рядків)
+  * `A` [Алгоритм Кнута — Морріса — Пратта](src/algorithms/string/knuth-morris-pratt) пошук підрядків (узгодження шаблонів)
+  * `A` [Z-функція](src/algorithms/string/z-algorithm) - пошук підрядків (зіставлення зразків)
+  * `A` [Алгоритм Рабіна — Карпа](src/algorithms/string/rabin-karp) - алгоритм пошуку рядка
+  * `A` [Найбільший загальний підрядок](src/algorithms/string/longest-common-substring)
+  * `A` [Підбирання регулярного виразу](src/algorithms/string/regular-expression-matching)
+* **Алгоритми пошуку**
+  * `B` [Лінійний пошук](src/algorithms/search/linear-search)
+  * `B` [Пошук блоків](src/algorithms/search/jump-search) - пошук у відсортованому масиві
+  * `B` [Двійковий пошук](src/algorithms/search/binary-search) - знаходження заданого значення у впорядкованому масиві
+  * `B` [Інтерполяційний алгоритм пошуку](src/algorithms/search/interpolation-search) - алгоритм для пошуку за заданим ключем в індексованому масиві, який впорядкований за значенням ключів
+* **Алгоритми сортування**
+  * `B` [Сортування бульбашкою](src/algorithms/sorting/bubble-sort)
+  * `B` [Сортування вибором](src/algorithms/sorting/selection-sort)
+  * `B` [Сортування включенням](src/algorithms/sorting/insertion-sort)
+  * `B` [Пірамідальне сортування](src/algorithms/sorting/heap-sort)
+  * `B` [Сортування злиттям](src/algorithms/sorting/merge-sort)
+  * `B` [Швидке сортування](src/algorithms/sorting/quick-sort)
+  * `B` [Сортування Шелла](src/algorithms/sorting/shell-sort)
+  * `B` [Сортування підрахунком](src/algorithms/sorting/counting-sort)
+  * `B` [Сортування за розрядами](src/algorithms/sorting/radix-sort)
+* **Зв’язані списки**
+  * `B` [Прямий обхід](src/algorithms/linked-list/traversal)
+  * `B` [Зворотний обхід](src/algorithms/linked-list/reverse-traversal)
+* **Дерева**
+  * `B` [Пошук у глибину](src/algorithms/tree/depth-first-search)
+  * `B` [Пошук у ширину](src/algorithms/tree/breadth-first-search)
+* **Графи**
+  * `B` [Пошук у глибину](src/algorithms/graph/depth-first-search)
+  * `B` [Пошук у ширину](src/algorithms/graph/breadth-first-search)
+  * `B` [Алгоритм Крускала](src/algorithms/graph/kruskal) - алгоритм побудови мінімального кістякового дерева зваженого неорієнтовного графа
+  * `A` [Алгоритм Дейкстри](src/algorithms/graph/dijkstra) - знаходження найкоротшого шляху від однієї вершини графа до всіх інших вершин
+  * `A` [Алгоритм Беллмана — Форда](src/algorithms/graph/bellman-ford) -  алгоритм пошуку найкоротшого шляху в зваженому графі
+  * `A` [Алгоритм Флойда — Воршелла](src/algorithms/graph/floyd-warshall) -  знаходження найкоротшого шляху в зваженому графі з додатними або від'ємними вагами ребер (але без від'ємнозначних циклів)
+  * `A` [Циклічний граф](src/algorithms/graph/detect-cycle) - граф, що складається з єдиного циклу, або, іншими словами, деякого числа вершин, з'єднаних замкнутим ланцюгом.
+  * `A` [Алгоритм Прима](src/algorithms/graph/prim) - жадібний алгоритм побудови мінімального кістякового дерева зваженого зв'язного неорієнтованого графа
+  * `A` [Топологічне сортування](src/algorithms/graph/topological-sorting) - впорядковування вершин безконтурного орієнтованого графа згідно з частковим порядком, визначеним ребрами цього графу на множині його вершин
+  * `A` [Алгоритм Тар'яна](src/algorithms/graph/articulation-points) - алгоритм пошуку компонент сильної зв'язності в орієнтованому графі, що працює за лінійний час
+  * `A` [Міст (теорія графів)](src/algorithms/graph/bridges)
+  * `A` [Ейлерів ланцюг](src/algorithms/graph/eulerian-path) - ланцюг у графі, який проходить кожне ребро рівно один раз
+  * `A` [Гамільтонів граф](src/algorithms/graph/hamiltonian-cycle) - шлях, що містить кожну вершину графа рівно один раз
+  * `A` [Компонента сильної зв'язності графа](src/algorithms/graph/strongly-connected-components) - Алгоритм Косараджу - алгоритм для знаходження компонент сильної зв’язності орієнтованого графу
+  * `A` [Задача комівояжера](src/algorithms/graph/travelling-salesman) - знаходження найвигіднішого маршруту, що проходить через вказані міста хоча б по одному разу
+* **Криптографія**
+  * `B` [Хеш-функція](src/algorithms/cryptography/polynomial-hash) - функція, що перетворює вхідні дані будь-якого (як правило великого) розміру в дані фіксованого розміру.
+  * `B` [Шифр Цезаря (шифр зсуву)](src/algorithms/cryptography/caesar-cipher) - симетричний моноалфавітний алгоритм шифрування, в якому кожна буква відкритого тексту заміняється на ту, що віддалена від неї в алфавіті на сталу кількість позицій
+  * `B` [Шифр Гілла](src/algorithms/cryptography/hill-cipher) - поліграмний шифр підстановки, заснований на лінійній алгебрі
+* **Машинне навчання**
+  * `B` [Нано-нейрон](https://github.com/trekhleb/nano-neuron) - 7 простих функцій JS, які ілюструють, як машини насправді можуть навчатися (пряме та зворотнє поширення)
+  * `B` [Метод k-найближчих сусідів](src/algorithms/ml/knn) - простий непараметричний класифікаційний метод, де для класифікації об'єктів у рамках простору властивостей використовуються відстані (зазвичай евклідові), пораховані до усіх інших об'єктів
+  * `B` [Кластеризація методом к–середніх](src/algorithms/ml/knn) - популярний метод кластеризації, — впорядкування множини об'єктів в порівняно однорідні групи.
+* **Без категорії**
+  * `B` [Ханойська вежа](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Поворот квадратної матриці](src/algorithms/uncategorized/square-matrix-rotation)
+  * `B` [Гра стрибків](src/algorithms/uncategorized/jump-game) - зворотне відстеження, динамічне програмування (зверху вниз + знизу вгору) та жадібні приклади
+  * `B` [Проблема унікальних шляхів](src/algorithms/uncategorized/unique-paths) - зворотне відстеження, динамічне програмування та приклади на основі Трикутника Паскаля
+  * `B` [Дощові тераси](src/algorithms/uncategorized/rain-terraces) - проблема захоплення дощової води (динамічне програмування та версії грубої сили)
+  * `B` [Завдання про рекурсивні сходи](src/algorithms/uncategorized/recursive-staircase) - підрахунок кількості способів досягти вершини (4 рішення)
+  * `A` [Задача про вісім ферзів](src/algorithms/uncategorized/n-queens)
+  * `A` [Задача про хід коня](src/algorithms/uncategorized/knight-tour)
+
+### Парадигма програмування
+
+Парадиигма програмува́ння — це система ідей і понять, які визначають стиль написання комп'ютерних програм, а також спосіб мислення програміста. Це спосіб концептуалізації, що визначає організацію обчислень і структурування роботи, яку виконує комп'ютер.
+
+* **Метод «грубої сили» або повний перебір** - метод рішення криптографічної задачі шляхом перебору всіх можливих варіантів ключа
+  * `B` [Лінійний пошук](src/algorithms/search/linear-search)
+  * `B` [Дощові тераси](src/algorithms/uncategorized/rain-terraces) - задача про дощові тераси
+  * `B` [Завдання про рекурсивні сходи](src/algorithms/uncategorized/recursive-staircase) - підрахунок кількості способів досягти вершини
+  * `A` [Максимальний підмасив](src/algorithms/sets/maximum-subarray)
+  * `A` [Задача комівояжера](src/algorithms/graph/travelling-salesman) - знаходження найвигіднішого маршруту, що проходить через вказані міста хоча б по одному разу
+  * `A` [Дискретне перетворення Фур'є](src/algorithms/math/fourier-transform) - розкладання тимчасової функції (сигналу) на частотні складові
+* **"Жадібні" алгоритми** - простий і прямолінійний евристичний алгоритм, який приймає найкраще рішення, виходячи з наявних на кожному етапі даних, не зважаючи на можливі наслідки, сподіваючись урешті-решт отримати оптимальний розв'язок
+  * `B` [Гра стрибків](src/algorithms/uncategorized/jump-game) - зворотне відстеження, динамічне програмування (зверху вниз + знизу вгору) та жадібні приклади
+  * `A` [Задача пакування рюкзака](src/algorithms/sets/knapsack-problem) - приклади "0/1" та "Необмежений"
+  * `A` [Алгоритм Дейкстри](src/algorithms/graph/dijkstra) - знаходження найкоротшого шляху від однієї вершини графа до всіх інших вершин
+  * `A` [Алгоритм Прима](src/algorithms/graph/prim) - жадібний алгоритм побудови мінімального кістякового дерева зваженого зв'язного неорієнтованого графа
+  * `A` [Алгоритм Крускала](src/algorithms/graph/kruskal) - алгоритм побудови мінімального кістякового дерева зваженого неорієнтовного графа
+* **Розділяй і володарюй** - важлива парадигма розробки алгоритмів, що полягає в рекурсивному розбитті розв'язуваної задачі на дві або більше підзадачі того ж типу, але меншого розміру, і комбінуванні їх розв'язків для отримання відповіді до вихідного завдання. Розбиття виконуються доти, поки всі підзавдання не стануть елементарними.
+  * `B` [Двійковий пошук](src/algorithms/search/binary-search) - знаходження заданого значення у впорядкованому масиві
+  * `B` [Ханойська вежа](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Трикутник Паскаля](src/algorithms/math/pascal-triangle)
+  * `B` [Алгоритм Евкліда](src/algorithms/math/euclidean-algorithm) - метод обчислення найбільшого спільного дільника (НСД)
+  * `B` [Сортування злиттям](src/algorithms/sorting/merge-sort)
+  * `B` [Швидке сортування](src/algorithms/sorting/quick-sort)
+  * `B` [Пошук у глибину](src/algorithms/tree/depth-first-search)
+  * `B` [Пошук у ширину](src/algorithms/tree/breadth-first-search)
+  * `B` [Гра стрибків](src/algorithms/uncategorized/jump-game) - зворотне відстеження, динамічне програмування (зверху вниз + знизу вгору) та жадібні приклади
+  * `B` [Швидке піднесення до степеня](src/algorithms/math/fast-powering)
+  * `A` [Перестановка](src/algorithms/sets/permutations) (з повтореннями та без)
+  * `A` [Комбінації](src/algorithms/sets/combinations) (з повтореннями та без)
+* **Динамічне програмування** - розділ математики, який присвячено теорії і методам розв'язання багатокрокових задач оптимального управління
+  * `B` [Послідовність Фібоначчі](src/algorithms/math/fibonacci) - класична та закриті версії
+  * `B` [Гра стрибків](src/algorithms/uncategorized/jump-game) - зворотне відстеження, динамічне програмування (зверху вниз + знизу вгору) та жадібні приклади
+  * `B` [Проблема унікальних шляхів](src/algorithms/uncategorized/unique-paths) - зворотне відстеження, динамічне програмування та приклади на основі Трикутника Паскаля
+  * `B` [Дощові тераси](src/algorithms/uncategorized/rain-terraces) - проблема захоплення дощової води (динамічне програмування та версії грубої сили)
+  * `B` [Завдання про рекурсивні сходи](src/algorithms/uncategorized/recursive-staircase) - підрахунок кількості способів досягти вершини (4 рішення)
+  * `A` [Відстань Левенштейна](src/algorithms/string/levenshtein-distance) - міра відмінності двох послідовностей символів (рядків)
+  * `A` [Пошук найдовшої спільної підпослідовності](src/algorithms/sets/longest-common-subsequence)
+  * `A` [Найбільший загальний підрядок](src/algorithms/string/longest-common-substring)
+  * `A` [Завдання пошуку найбільшою збільшується підпослідовності](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Найменша загальна супер-послідовність](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Задача пакування рюкзака](src/algorithms/sets/knapsack-problem) - приклади "0/1" та "Необмежений"
+  * `A` [Розбиття числа](src/algorithms/math/integer-partition)
+  * `A` [Максимальний підмасив](src/algorithms/sets/maximum-subarray)
+  * `A` [Алгоритм Беллмана — Форда](src/algorithms/graph/bellman-ford) -  алгоритм пошуку найкоротшого шляху в зваженому графі
+  * `A` [Алгоритм Флойда — Воршелла](src/algorithms/graph/floyd-warshall) -  знаходження найкоротшого шляху в зваженому графі з додатними або від'ємними вагами ребер (але без від'ємнозначних циклів)
+  * `A` [Підбирання регулярного виразу](src/algorithms/string/regular-expression-matching)
+* **Пошук із зворотом** - подібно до грубої сили, намагайтеся генерувати всі можливі рішення, але кожного разу, коли ви створюєте наступне рішення, тестуєте чи він задовольняє всім умовам, і лише потім продовжуєте генерувати наступні рішення. В іншому випадку поверніться назад і рухайтесь далі іншим шляхом пошуку рішення.
+  * `B` [Гра стрибків](src/algorithms/uncategorized/jump-game) - зворотне відстеження, динамічне програмування (зверху вниз + знизу вгору) та жадібні приклади
+  * `B` [Проблема унікальних шляхів](src/algorithms/uncategorized/unique-paths) - зворотне відстеження, динамічне програмування та приклади на основі Трикутника Паскаля
+  * `B` [Булеан](src/algorithms/sets/power-set) - множина всіх підмножин даної множини (бітові та зворотні рішення)
+  * `A` [Гамільтонів граф](src/algorithms/graph/hamiltonian-cycle) - шлях, що містить кожну вершину графа рівно один раз
+  * `A` [Задача про вісім ферзів](src/algorithms/uncategorized/n-queens)
+  * `A` [Задача про хід коня](src/algorithms/uncategorized/knight-tour)
+  * `A` [Комбінована сума](src/algorithms/sets/combination-sum) - знайти всі комбінації, що утворюють конкретну суму
+* **Метод гілок і меж** - один з поширених методів дискретної оптимізації. Метод працює на дереві рішень та визначає принципи роботи конкретних алгоритмів пошуку розв'язків, тобто, є мета-алгоритмом. Для різних задач комбінаторної оптимізації створюють спеціалізовані алгоритми гілок та меж.
+
+## Як користуватися цим репозиторієм
+
+**Встановіть усі залежності**
+```
+npm install
+```
+
+**Запустіть ESLint**
+
+Запустіть для перевірки якості коду
+
+```
+npm run lint
+```
+
+**Запустіть усі тести**
+```
+npm test
+```
+
+**Запустіть тести за назвою**
+```
+npm test -- 'LinkedList'
+```
+
+**Ігрище**
+
+Ви можете побавитись зі структурами даних та алгоритмами в файлі `./src/playground/playground.js` та писати тести до них в даному файлі `./src/playground/__test__/playground.test.js`.
+
+Для перевірки, чи працює ваш код належним чином запустіть команду:
+
+```
+npm test -- 'playground'
+```
+
+## Корисна інформація
+
+### Список літератури
+
+[▶ Структури даних та алгоритми на YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### Асимптотична нотація великого О (нотація Ландау)
+
+*Асимптотична нотація великого О (нотація Ландау)* розповсюджена математична нотація для формального запису асимптотичної поведінки функцій. Широко вживається в теорії складності обчислень, інформатиці та математиці.
+![Асимптотична нотація великого О](./assets/big-o-graph.png)
+
+Джерело: [Асимптотична нотація великого О](http://bigocheatsheet.com/).
+
+Нижче наведено список деяких найбільш часто використовуваних позначень нотації Ландаута їх порівняння продуктивності з різними розмірами вхідних даних.
+
+| Нотація Ландау | Обчислення для 10 елементів | Обчислення для 100 елементів | Обчислення для 1000 елементів  |
+| -------------- | ---------------------------- | ----------------------------- | ------------------------------- |
+| **O(1)**       | 1                            | 1                             | 1                               |
+| **O(log N)**   | 3                            | 6                             | 9                               |
+| **O(N)**       | 10                           | 100                           | 1000                            |
+| **O(N log N)** | 30                           | 600                           | 9000                            |
+| **O(N^2)**     | 100                          | 10000                         | 1000000                         |
+| **O(2^N)**     | 1024                         | 1.26e+29                      | 1.07e+301                       |
+| **O(N!)**      | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+
+### Складність операцій в структурі даних
+
+| Структура даних        | Доступ    | Пошук    | Вставка | Видалення  | Коментарі  |
+| ----------------------- | :-------: | :-------: | :-------: | :-------: | :-------- |
+| **Масив**               | 1         | n         | n         | n         |           |
+| **Купа**               | n         | n         | 1         | 1         |           |
+| **Черга**               | n         | n         | 1         | 1         |           |
+| **Зв’язаний список**         | n         | n         | 1         | n         |           |
+| **Хеш-таблиця**          | -         | n         | n         | n         | У разі ідеальної хеш-функції - O(1) |
+| **Бінарне дерево пошуку**  | n         | n         | n         | n         | У разі збалансованого дерева витрати становитимуть O (log (n)) |
+| **Б-дерево**              | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Червоно-чорне дерево**      | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **АВЛ-дерево**            | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Фільтр Блума**        | -         | 1         | 1         | -         | Під час пошуку можливі помилкові спрацьовування |
+
+### Складність алгоритмів сортування масивів
+
+| Назва                  | Найкращий            | Середній             | Найгірший               | Пам'ять    | Стабільність    | Коментарі  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Сортування бульбашкою**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Так       |           |
+| **Сортування включенням**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Так       |           |
+| **Сортування вибором**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Ні        |           |
+| **Пірамідальне сортування**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | Ні        |           |
+| **Сортування злиттям**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Так       |           |
+| **Швидке сортування**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | Ні        | Швидке сортування зазвичай виконується на місці з використанням O (log (n)) додаткової пам'яті |
+| **Сортування Шелла**        | n&nbsp;log(n)   | залежить від послідовності проміжків   | n&nbsp;(log(n))<sup>2</sup>  | 1         | Ні         |           |
+| **Сортування підрахунком**     | n + r           | n + r               | n + r               | n + r     | Так       | Де r - найбільше число в масиві |
+| **Сортування за розрядами**        | n * k           | n * k               | n * k               | n + k     | Так       | Де k - довжина найдовшого ключа |
+
+## Патронати проекту
+
+> Ви можете підтримати цей проект через ❤️️ [GitHub](https://github.com/sponsors/trekhleb) або ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+[Люди, які підтримують цей проект](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 1`
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.uz-UZ.md b/README.uz-UZ.md
new file mode 100644
index 0000000000..114a4de9cc
--- /dev/null
+++ b/README.uz-UZ.md
@@ -0,0 +1,359 @@
+# JavaScript algoritmlari va ma'lumotlar tuzilmalari
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+![repo size](https://img.shields.io/github/repo-size/trekhleb/javascript-algorithms.svg)
+
+Bu repozitoriyada JavaScript-ga asoslangan ko'plab mashhur algoritmlar
+va ma'lumotlar tuzilmalarining namunalari mavjud.
+
+Har bir algoritm va ma'lumotlar tuzilmasining alohida README fayli
+bo'lib, unda tegishli tushuntirishlar va qo'shimcha o'qish uchun
+havolalar (shu jumladan YouTube videolariga ham havolalar) mavjud.
+
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türkçe_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+Yodda tuting, bu loyiha faqat o'quv va tadqiqot maqsadida ishlatilishi
+uchun mo'ljallangan va ishlab chiqarishda ishlatilishi **mumkin emas**.
+
+## Ma'lumotlar tuzilmalari
+
+Ma'lumotlar tuzilmasi - bu kompyuterda ma'lumotlarni samarali tarzda
+olish va o'zgartirish uchun ularni tashkil etish va saqlashning ma'lum
+bir usuli. Ayniqsa, ma'lumotlar tuzilmasi ma'lumot qiymatlarining
+to'plami, ular orasidagi munosabatlar va ma'lumotlarga qo'llanilishi
+mumkin bo'lgan funksiyalar yoki operatsiyalardir.
+
+`B` - Boshlang'ich, `A` - Ilg'or
+
+- `B` [Bog'langan ro'yxat](src/data-structures/linked-list)
+- `B` [Ikki marta bog'langan ro'yxat](src/data-structures/doubly-linked-list)
+- `B` [Navbat](src/data-structures/queue)
+- `B` [Stek](src/data-structures/stack)
+- `B` [Hash jadvali](src/data-structures/hash-table)
+- `B` [Heap](src/data-structures/heap) - maksimal va minimal heap versiyalari
+- `B` [Ustuvor navbat](src/data-structures/priority-queue)
+- `A` [Trie](src/data-structures/trie)
+- `A` [Daraxt](src/data-structures/tree)
+  - `A` [Ikkilik qidiruv daraxt](src/data-structures/tree/binary-search-tree)
+  - `A` [AVL daraxt](src/data-structures/tree/avl-tree)
+  - `A` [Qizil-qora daraxt](src/data-structures/tree/red-black-tree)
+  - `A` [Segment daraxt](src/data-structures/tree/segment-tree) - min/max/sum diapazon so'rovlari bilan misollar
+  - `A` [Fenwick daraxt](src/data-structures/tree/fenwick-tree) (ikkilik indeksli daraxt)
+- `A` [Graf](src/data-structures/graph) (yo'naltirilgan hamda yo'naltirilmagan)
+- `A` [Ajratilgan to'plam](src/data-structures/disjoint-set) - union-find ma'lumotlar strukturasi yoki merge-find to'plami
+- `A` [Bloom filtri](src/data-structures/bloom-filter)
+- `A` [LRU keshi](src/data-structures/lru-cache/) - Eng kam ishlatilgan (LRU) keshi
+
+## Algoritmlar
+
+Algoritm muammolar sinfini qanday hal qilishning aniq spetsifikatsiyasi. Bu operatsiyalar ketma-ketligini aniqlaydigan qoidalar to'plami.
+
+`B` - Boshlang'ich, `A` - Ilg'or
+
+### Mavzu bo'yicha algoritmlar
+
+- **Matematika**
+  - `B` [Bit manipulatsiyasi](src/algorithms/math/bits) - bitlarni qo'yish/olish/yangilash/tozalash, ikkilikka ko'paytirish/bo'lish, manfiy qilish va hokazo.
+  - `B` [Ikkilik suzuvchi nuqta](src/algorithms/math/binary-floating-point) - suzuvchi nuqtali sonlarning ikkilik tasviri.
+  - `B` [Faktorial](src/algorithms/math/factorial)
+  - `B` [Fibonachchi raqam](src/algorithms/math/fibonacci) - klassik va yopiq shakldagi versiyalar
+  - `B` [Asosiy omillar](src/algorithms/math/prime-factors) - tub omillarni topish va ularni Xardi-Ramanujan teoremasi yordamida sanash
+  - `B` [Birlamchilik testi](src/algorithms/math/primality-test) (sinov bo'linish usuli)
+  - `B` [Evklid algoritmi](src/algorithms/math/euclidean-algorithm) - eng katta umumiy bo'luvchini (EKUB) hisoblash
+  - `B` [Eng kichik umumiy karrali](src/algorithms/math/least-common-multiple) (EKUK)
+  - `B` [Eratosfen elagi](src/algorithms/math/sieve-of-eratosthenes) - berilgan chegaragacha barcha tub sonlarni topish
+  - `B` [Ikkining darajasimi](src/algorithms/math/is-power-of-two) - raqamning ikkining darajasi ekanligini tekshirish (sodda va bitli algoritmlar)
+  - `B` [Paskal uchburchagi](src/algorithms/math/pascal-triangle)
+  - `B` [Kompleks sonlar](src/algorithms/math/complex-number) - kompleks sonlar va ular bilan asosiy amallar
+  - `B` [Radian & Daraja](src/algorithms/math/radian) - radianlarni darajaga va orqaga aylantirish
+  - `B` [Tez ko'tarish](src/algorithms/math/fast-powering)
+  - `B` [Horner metodi](src/algorithms/math/horner-method) - polinomlarni baholash
+  - `B` [Matritsalar](src/algorithms/math/matrix) - matritsalar va asosiy matritsa operatsiyalari (ko'paytirish, transpozitsiya va boshqalar).
+  - `B` [Evklid masofasi](src/algorithms/math/euclidean-distance) - ikki nuqta/vektor/matritsa orasidagi masofa
+  - `A` [Butun sonlarni bo'lish](src/algorithms/math/integer-partition)
+  - `A` [Kvadrat ildiz](src/algorithms/math/square-root) - Nyuton metodi
+  - `A` [Liu Hui π algoritmi](src/algorithms/math/liu-hui) - N-gonlarga asoslangan π ning taxminiy hisoblari
+  - `A` [Diskret Furye transformatsiyasi](src/algorithms/math/fourier-transform) - vaqt funksiyasini (signalni) uni tashkil etuvchi chastotalarga ajratish
+- **Sets**
+  - `B` [Karteziya maxsuloti](src/algorithms/sets/cartesian-product) - bir nechta to'plamlarning ko'paytmasi
+  - `B` [Fisher–Yates Shuffle](src/algorithms/sets/fisher-yates) - chekli ketma-ketlikni tasodifiy almashtirish
+  - `A` [Power Set](src/algorithms/sets/power-set) - to'plamning barcha kichik to'plamlari (bitwise, backtracking va kaskadli echimlar)
+  - `A` [Permutatsiyalar](src/algorithms/sets/permutations) (takroriyalash bilan va takroriyalashsiz)
+  - `A` [Kombinatsiyalar](src/algorithms/sets/combinations) (takroriyalash bilan va takroriyalashsiz)
+  - `A` [Eng uzun umumiy ketma-ketlik](src/algorithms/sets/longest-common-subsequence) (LCS)
+  - `A` [Eng uzun ortib boruvchi ketma-ketlik](src/algorithms/sets/longest-increasing-subsequence)
+  - `A` [Eng qisqa umumiy ketma-ketlik](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  - `A` [Knapsack muammosi](src/algorithms/sets/knapsack-problem) - "0/1" va "Bir-biriga bog'lanmagan"
+  - `A` [Maksimal kichik massiv](src/algorithms/sets/maximum-subarray) - Toʻliq kuch va dinamik dasturlash (Kadane usuli) versiyalari
+  - `A` [Kombinatsiya yig'indisi](src/algorithms/sets/combination-sum) - ma'lum summani tashkil etuvchi barcha kombinatsiyalarni topish
+- **Stringlar**
+  - `B` [Hamming masofasi](src/algorithms/string/hamming-distance) - belgilarning bir-biridan farq qiladigan pozitsiyalar soni
+  - `B` [Palindrom](src/algorithms/string/palindrome) - satrning teskari tomoni ham bir xil ekanligini tekshirish
+  - `A` [Levenshtein masofasi](src/algorithms/string/levenshtein-distance) - ikki ketma-ketlik o'rtasidagi minimal tahrirlash masofasi
+  - `A` [Knuth–Morris–Pratt Algoritmi](src/algorithms/string/knuth-morris-pratt) (KMP Algoritmi) - kichik qatorlarni qidirish (mosh keluvchi naqshni qidirish)
+  - `A` [Z Algoritmi](src/algorithms/string/z-algorithm) - kichik qatorlarni qidirish (mosh keluvchi naqshni qidirish)
+  - `A` [Rabin Karp Algoritmi](src/algorithms/string/rabin-karp) - kichik qatorlarni qidirish
+  - `A` [Eng uzun umumiy kichik matn](src/algorithms/string/longest-common-substring)
+  - `A` [Regulyar ifoda moslashuvi](src/algorithms/string/regular-expression-matching) (RegEx)
+- **Qidiruvlar**
+  - `B` [Linear qidirish](src/algorithms/search/linear-search)
+  - `B` [Jump qidirish](src/algorithms/search/jump-search) (yoki Blok qidirish) - saralangan qatorda qidirish
+  - `B` [Ikkilik qidirish](src/algorithms/search/binary-search) - saralangan qatorda qidirish
+  - `B` [Interpolatsiya qidirish](src/algorithms/search/interpolation-search) - bir tekis taqsimlangan saralangan qatorda qidirish
+- **Tartiblash**
+  - `B` [Pufakcha tartiblash](src/algorithms/sorting/bubble-sort)
+  - `B` [Tanlash tartibi](src/algorithms/sorting/selection-sort)
+  - `B` [Kiritish tartibi](src/algorithms/sorting/insertion-sort)
+  - `B` [Heap tartibi](src/algorithms/sorting/heap-sort)
+  - `B` [Birlashtirish tartibi](src/algorithms/sorting/merge-sort)
+  - `B` [Tezkor saralash](src/algorithms/sorting/quick-sort) - joyida va joyida bo'lmagan amalga oshirish
+  - `B` [Shell tartiblash](src/algorithms/sorting/shell-sort)
+  - `B` [Sanash tartibi](src/algorithms/sorting/counting-sort)
+  - `B` [Radiksli tartiblash](src/algorithms/sorting/radix-sort)
+  - `B` [Bucket tartiblash](src/algorithms/sorting/bucket-sort)
+- **Bog'langan ro'yhatlar**
+  - `B` [To'g'ri traversal](src/algorithms/linked-list/traversal)
+  - `B` [Teskari traversal](src/algorithms/linked-list/reverse-traversal)
+- **Daraxtlar**
+  - `B` [Birinchi-pastga qarab qidirish](src/algorithms/tree/depth-first-search) (Depth-First Search)
+  - `B` [Birinchi-yonga qarab qidirish](src/algorithms/tree/breadth-first-search) (Breadth-First Search)
+- **Grafiklar**
+  - `B` [Birinchi-pastga qarab qidirish](src/algorithms/graph/depth-first-search) (Depth-First Search)
+  - `B` [Birinchi-yonga qarab qidirish](src/algorithms/graph/breadth-first-search) (Breadth-First Search)
+  - `B` [Kruskal Algoritmi](src/algorithms/graph/kruskal) - og'irlikdagi yo'naltirilmagan grafik uchun Minimal kengayuvchi daraxtni (MST) topish
+  - `A` [Dijkstra Algoritmi](src/algorithms/graph/dijkstra) - grafikning bir cho'qqisidan qolgan barcha nuqtalarga eng qisqa yo'llarni topish
+  - `A` [Bellman-Ford Algoritmi](src/algorithms/graph/bellman-ford) - grafikning bir cho'qqisidan qolgan barcha nuqtalarga eng qisqa yo'llarni topish
+  - `A` [Floyd-Warshall Algoritmi](src/algorithms/graph/floyd-warshall) - grafikning barcha uchlari orasidagi eng qisqa masofalarni topish
+  - `A` [Siklni aniqlash](src/algorithms/graph/detect-cycle) - yo'naltirilgan va yo'naltirilmagan grafiklar uchun (DFS va Disjoint Set-ga asoslangan versiyalar)
+  - `A` [Prim Algoritmi](src/algorithms/graph/prim) - og'irlikdagi yo'naltirilmagan grafik uchun Minimal kengayuvchi daraxtni (MST) topish
+  - `A` [Topologik saralash](src/algorithms/graph/topological-sorting) - DFS metodi
+  - `A` [Artikulyatsiya nuqtalari](src/algorithms/graph/articulation-points) - Tarjan algoritmi (DFS asosida)
+  - `A` [Ko'priklar](src/algorithms/graph/bridges) - DFS asosidagi algoritm
+  - `A` [Eyler yo'li va Eyler sxemasi](src/algorithms/graph/eulerian-path) - Fleury algoritmi - Har bir chekkaga bir marta tashrif buyurish
+  - `A` [Gamilton sikli](src/algorithms/graph/hamiltonian-cycle) - Har bir cho'qqiga bir marta tashrif buyurish
+  - `A` [Kuchli bog'langan komponentlar](src/algorithms/graph/strongly-connected-components) - Kosaraju algoritmi
+  - `A` [Sayohatchi sotuvchi muammosi](src/algorithms/graph/travelling-salesman) - har bir shaharga tashrif buyuradigan va kelib chiqqan shaharga qaytib keladigan eng qisqa yo'l
+- **Kriptografiya**
+  - `B` [Polynomial Hash](src/algorithms/cryptography/polynomial-hash) - polinomga asoslangan hash funktsiyasi
+  - `B` [Rail Fence Cipher](src/algorithms/cryptography/rail-fence-cipher) - xabarlarni kodlash uchun transpozitsiya shifrlash algoritmi
+  - `B` [Caesar Cipher](src/algorithms/cryptography/caesar-cipher) - oddiy almashtirish shifridir
+  - `B` [Hill Cipher](src/algorithms/cryptography/hill-cipher) - chiziqli algebraga asoslangan almashtirish shifri
+- **Machine Learning**
+  - `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - Mashinalar aslida qanday o'rganishi mumkinligini ko'rsatadigan 7 ta oddiy JS funksiyasi (forward/backward tarqalish)
+  - `B` [k-NN](src/algorithms/ml/knn) - eng yaqin qo'shnilarni tasniflash algoritmi
+  - `B` [k-Means](src/algorithms/ml/k-means) - k-Means kalsterlash algoritmi
+- **Tasvirga ishlov berish**
+  - `B` [Seam Carving](src/algorithms/image-processing/seam-carving) - kontentga moslashuvchan rasm o'lchamini o'zgartirish algoritmi
+- **Statistikalar**
+  - `B` [Weighted Random](src/algorithms/statistics/weighted-random) - elementlarning og'irligi asosida ro'yxatdan tasodifiy elementni tanlash
+- **Evolyutsion algoritmlar**
+  - `A` [Genetik algoritm](https://github.com/trekhleb/self-parking-car-evolution) - avtoturargohni o'rgatish uchun genetik algoritm qanday qo'llanilishiga misol.
+- **Kategoriyasiz**
+  - `B` [Xanoy minorasi](src/algorithms/uncategorized/hanoi-tower)
+  - `B` [Kvadrat matritsaning aylanishi](src/algorithms/uncategorized/square-matrix-rotation) - joyidagi algoritm
+  - `B` [Sakrash o'yini](src/algorithms/uncategorized/jump-game) - orqaga qaytish, dinamik dasturlash (yuqoridan pastga + pastdan yuqoriga) va ochko'z misollar
+  - `B` [Noyob yo'llar](src/algorithms/uncategorized/unique-paths) - orqaga qaytish, dinamik dasturlash va Paskal uchburchagiga asoslangan misolla
+  - `B` [Yomg'ir teraslari](src/algorithms/uncategorized/rain-terraces) - yomg'ir suvini ushlab turish muammosi (dinamik dasturlash va qo'pol kuch versiyalari)
+  - `B` [Rekursiv zinapoya](src/algorithms/uncategorized/recursive-staircase) - yuqoriga chiqish yo'llari sonini hisoblash (4 ta echim)
+  - `B` [Aksiyalarni sotib olish va sotish uchun eng yaxshi vaqt](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - bo'linib-zabt etish va bir marta o'tish misollari
+  - `A` [N-Queens Muommosi](src/algorithms/uncategorized/n-queens)
+  - `A` [Ritsar sayohati](src/algorithms/uncategorized/knight-tour)
+
+### Paradigma bo'yicha algoritmlar
+
+Algorithmic paradigm - bu algoritmlar sinfini loyihalashtirishga asos bo'lib xizmat qiladigan umumiy usul yoki yondashuv. Bu algoritm tushunchasidan yuqori darajadagi abstraktsiya bo'lib, algoritm kompyuter dasturi tushunchasidan yuqori darajadagi abstraktsiya bo'lgani kabi.
+
+- **Brute Force** - barcha imkoniyatlarni ko'rib chiqib va eng yaxshi echimni tanlash
+  - `B` [Chiziqli qidirish](src/algorithms/search/linear-search)
+  - `B` [Yomg'irli teraslar](src/algorithms/uncategorized/rain-terraces) - yomg'ir suvini to'plash muammosi
+  - `B` [Rekursiv zinapoya](src/algorithms/uncategorized/recursive-staircase) - cho'qqiga chiqish yo'llari sonini hisoblash
+  - `A` [Maksimal kichik massiv](src/algorithms/sets/maximum-subarray)
+  - `A` [Sayohatchi sotuvchi muammosi](src/algorithms/graph/travelling-salesman) - har bir shaharga tashrif buyuradigan va kelib chiqqan shaharga qaytib keladigan eng qisqa yo'l
+  - `A` [Diskret Furye transformatsiyasi](src/algorithms/math/fourier-transform) - vaqt funksiyasini (signalni) uni tashkil etuvchi chastotalarga ajratish
+- **Greedy** - kelajakni o'ylamasdan, hozirgi vaqtda eng yaxshi variantni tanlash
+  - `B` [Sakrash o'yini](src/algorithms/uncategorized/jump-game)
+  - `A` [Bog'lanmagan yukxalta muammosi](src/algorithms/sets/knapsack-problem)
+  - `A` [Dijkstra Algoritmi](src/algorithms/graph/dijkstra) - grafikning bir cho'qqisidan qolgan barcha nuqtalarga eng qisqa yo'llarni topish
+  - `A` [Prim Algoritmi](src/algorithms/graph/prim) - og'irlikdagi yo'naltirilmagan grafik uchun Minimal kengayuvchi daraxtni (MST) topish
+  - `A` [Kruskal Algoritmi](src/algorithms/graph/kruskal) - og'irlikdagi yo'naltirilmagan grafik uchun Minimal kengayuvchi daraxtni (MST) topish
+- **Divide and Conquer** - muammoni kichikroq qismlarga bo'lib va keyin bu qismlarni hal qilish
+
+  - `B` [Ikkilik qidiruv](src/algorithms/search/binary-search)
+  - `B` [Xanoy minorasi](src/algorithms/uncategorized/hanoi-tower)
+  - `B` [Paskal uchburchagi](src/algorithms/math/pascal-triangle)
+  - `B` [Evklid Algoritmi](src/algorithms/math/euclidean-algorithm) - eng katta umumiy bo'luvchini (EKUB) hisoblash
+  - `B` [Birlashtirish tartibi](src/algorithms/sorting/merge-sort)
+  - `B` [Tezkor saralash](src/algorithms/sorting/quick-sort)
+  - `B` [Birinchi-pastga qarab qidirish daraxti](src/algorithms/tree/depth-first-search) (DFS)
+  - `B` [Birinchi-pastga qarab qidirish grafigi](src/algorithms/graph/depth-first-search) (DFS)
+  - `B` [Matritsalar](src/algorithms/math/matrix) - turli shakldagi matritsalarni hosil qilish va kesib o'tish
+  - `B` [Sakrash o'yini](src/algorithms/uncategorized/jump-game)
+  - `B` [Tez ko'tarish](src/algorithms/math/fast-powering)
+  - `B` [Aksiyalarni sotib olish va sotish uchun eng yaxshi vaqt](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - bo'linib-zabt etish va bir marta o'tish misollari
+  - `A` [Permutatsiyalar](src/algorithms/sets/permutations) (takroriyalash bilan va takroriyalashsiz)
+  - `A` [Kombinatsiyalar](src/algorithms/sets/combinations) (takroriyalash bilan va takroriyalashsiz)
+  - `A` [Maksimal kichik massiv](src/algorithms/sets/maximum-subarray)
+
+- **Dinamik dasturlash** - ilgari topilgan kichik yechimlar yordamida yechim yaratish
+  - `B` [Fibonachchi raqam](src/algorithms/math/fibonacci)
+  - `B` [Sakrash o'yini](src/algorithms/uncategorized/jump-game)
+  - `B` [Noyob yo'llar](src/algorithms/uncategorized/unique-paths)
+  - `B` [Yomg'ir teraslari](src/algorithms/uncategorized/rain-terraces) - yomg'ir suvini to'plash muammosi
+  - `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase) - count the number of ways to reach to the top
+  - `B` [Seam Carving](src/algorithms/image-processing/seam-carving) - kontentga moslashuvchan rasm o'lchamini o'zgartirish algoritmi
+  - `A` [Levenshtein masofasi](src/algorithms/string/levenshtein-distance) - ikki ketma-ketlik o'rtasidagi minimal tahrirlash masofasi
+  - `A` [Eng uzun umumiy ketma-ketlik](src/algorithms/sets/longest-common-subsequence) (LCS)
+  - `A` [Eng uzun umumiy kichik matn](src/algorithms/string/longest-common-substring)
+  - `A` [Eng uzun ortib boruvchi ketma-ketlik](src/algorithms/sets/longest-increasing-subsequence)
+  - `A` [Eng qisqa umumiy ketma-ketlik](src/algorithms/sets/shortest-common-supersequence)
+  - `A` [0/1 Knapsak muommosi](src/algorithms/sets/knapsack-problem)
+  - `A` [Butun sonlarni bo'lish](src/algorithms/math/integer-partition)
+  - `A` [Maksimal kichik massiv](src/algorithms/sets/maximum-subarray)
+  - `A` [Bellman-Ford Algoritmi](src/algorithms/graph/bellman-ford) - grafikning bir cho'qqisidan qolgan barcha nuqtalarga eng qisqa yo'llarni topish
+  - `A` [Floyd-Warshall Algoritmi](src/algorithms/graph/floyd-warshall) -grafikning barcha uchlari orasidagi eng qisqa masofalarni topish
+  - `A` [Regulyar ifoda moslashuvi](src/algorithms/string/regular-expression-matching)
+- **Backtracking** - brute forcega o'xshab, barcha mumkin bo'lgan yechimlarni generatsiya qilishga harakat qiladi, lekin har safar keyingi yechimni yaratganingizda, yechim barcha shartlarga javob beradimi yoki yo'qligini tekshirasiz va shundan keyingina keyingi yechimlarni ishlab chiqarishni davom ettirasiz. Aks holda, orqaga qaytib, yechim topishning boshqa yo'liga o'tasiz. Odatda state-space ning DFS-qidiruvi ishlatiladi.
+  - `B` [Sakrash o'yini](src/algorithms/uncategorized/jump-game)
+  - `B` [Noyob yo'llar](src/algorithms/uncategorized/unique-paths)
+  - `B` [Power Set](src/algorithms/sets/power-set) - to'plamning barcha kichik to'plamlari
+  - `A` [Gamilton sikli](src/algorithms/graph/hamiltonian-cycle) - Har bir cho'qqiga bir marta tashrif buyurish
+  - `A` [N-Queens muommosi](src/algorithms/uncategorized/n-queens)
+  - `A` [Ritsar sayohati](src/algorithms/uncategorized/knight-tour)
+  - `A` [Kombinatsiya yig'indisi](src/algorithms/sets/combination-sum) - ma'lum summani tashkil etuvchi barcha kombinatsiyalarni topish
+- **Branch & Bound** - shu paytgacha topilgan eng arzon echimdan kattaroq xarajatlarga ega qisman echimlarni bekor qilish uchun, backtracking qidiruvining har bir bosqichida topilgan eng arzon echimni eslab qoling va shu paytgacha topilgan eng arzon yechim narxidan muammoni eng kam xarajatli yechim narxining past chegarasi sifatida foydalaning. Odatda state-space daraxtining DFS o'tishi bilan birgalikda BFS traversal qo'llaniladi.
+
+## Ushbu repozitoriyadan qanday foydalanish kerak
+
+**Barcha dependensiylarni o'rnating**
+
+```
+npm install
+```
+
+**ESLint ni ishga tushiring**
+
+Kod sifatini tekshirish uchun ESLint ni ishga tushirishingiz mumkin.
+
+```
+npm run lint
+```
+
+**Barcha testlarni ishga tushuring**
+
+```
+npm test
+```
+
+**Testlarni nom bo'yicha ishga tushirish**
+
+```
+npm test -- 'LinkedList'
+```
+
+**Muammolarni bartaraf qilish (Troubleshooting)**
+
+Agar linting yoki sinov muvaffaqiyatsiz bo'lsa, `node_modules` papkasini o'chirib, npm paketlarini qayta o'rnatishga harakat qiling:
+
+```
+rm -rf ./node_modules
+npm i
+```
+
+Shuningdek, to'g'ri Node versiyasidan foydalanayotganingizga ishonch hosil qiling (`>=16`). Agar Node versiyasini boshqarish uchun [nvm](https://github.com/nvm-sh/nvm) dan foydalanayotgan bo'lsangiz, loyihaning ildiz papkasidan `nvm use` ni ishga tushiring va to'g'ri versiya tanlanadi.
+
+**O'yin maydoni (Playground)**
+
+`./src/playground/playground.js` faylida ma'lumotlar strukturalari va algoritmlar bilan o'ynashingiz, `./src/playground/test/playground.test.js` faylida esa ular uchun testlar yozishingiz mumkin.
+
+Shundan so'ng, playground kodingiz kutilgandek ishlashini tekshirish uchun quyidagi buyruqni ishga tushirishingiz kifoya:
+
+```
+npm test -- 'playground'
+```
+
+## Foydali ma'lumotlar
+
+### Manbalar
+
+- [▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [✍🏻 Data Structure Sketches](https://okso.app/showcase/data-structures)
+
+### Big O Notation
+
+_Big O notation_ algoritmlarni kirish hajmi oshgani sayin ularning ishlash vaqti yoki bo'sh joy talablari qanday o'sishiga qarab tasniflash uchun ishlatiladi. Quyidagi jadvalda siz Big O notatsiyasida ko'rsatilgan algoritmlarning o'sishining eng keng tarqalgan tartiblarini topishingiz mumkin.
+
+![Big O grafiklar](./assets/big-o-graph.png)
+
+Manba: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+Quyida eng ko'p qo'llaniladigan Big O notatsiyalarining ro'yxati va ularning kirish ma'lumotlarining turli o'lchamlariga nisbatan ishlashini taqqoslash keltirilgan.
+
+| Big O Notatsiya | Turi         | 10 ta element uchun hisob-kitoblar | 100 ta element uchun hisob-kitoblar | 1000 ta element uchun hisob-kitoblar |
+| --------------- | ------------ | ---------------------------------- | ----------------------------------- | ------------------------------------ |
+| **O(1)**        | O'zgarmas    | 1                                  | 1                                   | 1                                    |
+| **O(log N)**    | Logarifmik   | 3                                  | 6                                   | 9                                    |
+| **O(N)**        | Chiziqli     | 10                                 | 100                                 | 1000                                 |
+| **O(N log N)**  | n log(n)     | 30                                 | 600                                 | 9000                                 |
+| **O(N^2)**      | Kvadrat      | 100                                | 10000                               | 1000000                              |
+| **O(2^N)**      | Eksponensial | 1024                               | 1.26e+29                            | 1.07e+301                            |
+| **O(N!)**       | Faktorial    | 3628800                            | 9.3e+157                            | 4.02e+2567                           |
+
+### Ma'lumotlar tuzilmalarining operatsiyalari murakkabligi
+
+| Ma'lumotlar tuzilmalari     | Kirish | Qidirish | Kiritish | O'chirish | Izohlar                                                    |
+| --------------------------- | :----: | :------: | :------: | :-------: | :--------------------------------------------------------- |
+| **Massiv**                  |   1    |    n     |    n     |     n     |                                                            |
+| **Stak**                    |   n    |    n     |    1     |     1     |                                                            |
+| **Navbat**                  |   n    |    n     |    1     |     1     |                                                            |
+| **Bog'langan ro'yhat**      |   n    |    n     |    1     |     n     |                                                            |
+| **Hash jadval**             |   -    |    n     |    n     |     n     | Mukammal xash funksiyasi bo'lsa, xarajatlar O (1) bo'ladi. |
+| **Ikkilik qidiruv daraxti** |   n    |    n     |    n     |     n     | Balanslangan daraxt narxida O(log(n)) bo'ladi.             |
+| **B-daraxti**               | log(n) |  log(n)  |  log(n)  |  log(n)   |                                                            |
+| **Qizil-qora daraxt**       | log(n) |  log(n)  |  log(n)  |  log(n)   |                                                            |
+| **AVL Daraxt**              | log(n) |  log(n)  |  log(n)  |  log(n)   |                                                            |
+| **Bloom filtri**            |   -    |    1     |    1     |     -     | Qidiruv paytida noto'g'ri pozitivlar bo'lishi mumkin       |
+
+### Massivlarni saralash algoritmlarining murakkabligi
+
+| Nomi                      |  Eng yaxshi   |          O'rta          |          Eng yomon          | Xotira | Barqaror | Izohlar                                                                      |
+| ------------------------- | :-----------: | :---------------------: | :-------------------------: | :----: | :------: | :--------------------------------------------------------------------------- |
+| **Pufakcha tartiblash**   |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |   1    |    Ha    |                                                                              |
+| **Kiritish tartibi**      |       n       |      n<sup>2</sup>      |        n<sup>2</sup>        |   1    |    Ha    |                                                                              |
+| **Tanlash tartibi**       | n<sup>2</sup> |      n<sup>2</sup>      |        n<sup>2</sup>        |   1    |   Yo'q   |                                                                              |
+| **Heap tartibi**          | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |   1    |   Yo'q   |                                                                              |
+| **Birlashtirish tartibi** | n&nbsp;log(n) |      n&nbsp;log(n)      |        n&nbsp;log(n)        |   n    |    Ha    |                                                                              |
+| **Tezkor saralash**       | n&nbsp;log(n) |      n&nbsp;log(n)      |        n<sup>2</sup>        | log(n) |   Yo'q   | Tezkor saralash odatda O(log(n)) stek maydoni bilan joyida amalga oshiriladi |
+| **Shell tartiblash**      | n&nbsp;log(n) | depends on gap sequence | n&nbsp;(log(n))<sup>2</sup> |   1    |   Yo'q   |                                                                              |
+| **Sanash tartibi**        |     n + r     |          n + r          |            n + r            | n + r  |    Ha    | r - massivdagi eng katta raqam                                               |
+| **Radiksli tartiblash**   |    n \* k     |         n \* k          |           n \* k            | n + k  |    Ha    | k - eng uzun kalitning uzunligi                                              |
+
+## Loyihani qo'llab-quvvatlovchilar
+
+> Siz ushbu loyihani ❤️️ [GitHub](https://github.com/sponsors/trekhleb) yoki ❤️️ [Patreon](https://www.patreon.com/trekhleb) orqali qo'llab-quvvatlashingiz mumkin.
+
+[Ushbu loyihani qo'llab-quvvatlagan odamlar](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 1`
+
+## Muallif
+
+[@trekhleb](https://trekhleb.dev)
+
+A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.vi-VN.md b/README.vi-VN.md
new file mode 100644
index 0000000000..4c35f467f8
--- /dev/null
+++ b/README.vi-VN.md
@@ -0,0 +1,329 @@
+# JavaScript Thuật Toán và Cấu Trúc Dữ Liệu
+
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
+[![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
+
+Repository này bao gồm nhiều ví dụ thuật toán và cấu trúc dữ liệu phổ biến
+dựa trên Javascript.
+
+Mối thuật toán và cấu trúc dữ liệu có README riêng với những lý giải và links
+liên quan để đọc thêm (bao gồm cả những videos trên Youtube).
+
+_Đọc bằng ngôn ngữ khác:_
+[_English_](README.md),
+[_简体中文_](README.zh-CN.md),
+[_繁體中文_](README.zh-TW.md),
+[_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
+[_Polski_](README.pl-PL.md),
+[_Français_](README.fr-FR.md),
+[_Español_](README.es-ES.md),
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md)
+[_עברית_](README.he-IL.md)
+
+*☝ Dự án này chỉ được sử dụng cho mục đích học tập và nghiên cứu, **không** được dùng
+cho mục đích thương mại.*
+
+## Cấu Trúc Dữ Liệu
+
+Cấu trúc dữ liệu là một cách cụ thể để tổ chức và lưu trữ dữ liệu trong máy tính để nó có thể
+được truy cập và sửa đổi một cách hiệu quả. Chính xác hơn, cấu trúc dữ liệu là một tập hợp
+các giá trị dữ liệu, các mối quan hệ giữa chúng và các hàm hoặc phép toán có thể được áp dụng
+cho dữ liệu.
+
+`B` - Cơ bản, `A` - Nâng cao
+
+* `B` [Danh sách liên kết](src/data-structures/linked-list)
+* `B` [Danh sách liên kết đôi](src/data-structures/doubly-linked-list)
+* `B` [Hàng đợi](src/data-structures/queue)
+* `B` [Ngăn xếp](src/data-structures/stack)
+* `B` [Bảng băm](src/data-structures/hash-table)
+* `B` [Đống](src/data-structures/heap) - max và min heap
+* `B` [Hàng đợi ưu tiên](src/data-structures/priority-queue)
+* `A` [Cây tiền tố](src/data-structures/trie)
+* `A` [Cây](src/data-structures/tree)
+  * `A` [Cây tìm kiếm nhị phân](src/data-structures/tree/binary-search-tree)
+  * `A` [Cây AVL](src/data-structures/tree/avl-tree)
+  * `A` [Cây đỏ đen](src/data-structures/tree/red-black-tree)
+  * `A` [Cây phân đoạn](src/data-structures/tree/segment-tree) - với các ví dụ truy vấn phạm vi nhỏ nhất/lớn nhất/tổng
+  * `A` [CÂy Fenwick](src/data-structures/tree/fenwick-tree) (Cây chỉ mục nhị phân)
+* `A` [Đồ thị](src/data-structures/graph) (có hướng và vô hướng)
+* `A` [Tập hợp không giao nhau](src/data-structures/disjoint-set)
+* `A` [Bộ lọc Bloom](src/data-structures/bloom-filter)
+
+## Thuật Toán
+
+Thuật toán là một đặc tả rõ ràng về cách giải quyết một lớp vấn đề. Nó là một tập hợp các
+quy tắc xác định chính xác một chuỗi phép toán.
+
+`B` - Cơ bản, `A` - Nâng cao
+
+### Thuật toán theo chủ đề
+
+* **Toán**
+  * `B` [Thao tác bit](src/algorithms/math/bits) - đặt/lấy/cập nhật/xóa bit, nhân/chia 2, đổi dấu âm,...
+  * `B` [Giai thừa](src/algorithms/math/factorial)
+  * `B` [Số Fibonacci](src/algorithms/math/fibonacci) - cổ điển và dạng đóng
+  * `B` [Thừa số nguyên tố](src/algorithms/math/prime-factors) - tìm và đếm thừa số nguyên tố sử dụng định luật Hardy-Ramanujan's
+  * `B` [Kiểm tra tính nguyên tố](src/algorithms/math/primality-test) (phân chia thử nghiệm)
+  * `B` [Thuật toán Euclid](src/algorithms/math/euclidean-algorithm) - tính ước số chung lớn nhất (GCD)
+  * `B` [Bội số chung nhỏ nhất](src/algorithms/math/least-common-multiple) (LCM)
+  * `B` [Sàng số nguyên tố](src/algorithms/math/sieve-of-eratosthenes) - tìm tất cả các số nguyên tố trong bất kỳ phạm vi nhất định nào
+  * `B` [Xác định lũy thừa của 2](src/algorithms/math/is-power-of-two) - kiểm tra xem số có phải là lũy thừa của 2 hay không (thuật toán nguyên bản và theo bit)
+  * `B` [Tam giác Pascal](src/algorithms/math/pascal-triangle)
+  * `B` [Số phức](src/algorithms/math/complex-number) - số phức và các phép toán cơ bản với số phức
+  * `B` [Radian & độ](src/algorithms/math/radian) - chuyển đổi giữa đơn vị radian và độ
+  * `B` [Tính nhanh lũy thừa](src/algorithms/math/fast-powering)
+  * `B` [Phương pháp Horner's](src/algorithms/math/horner-method) - tính giá trị đa thức
+  * `B` [Ma trận](src/algorithms/math/matrix) - ma trận và các phép toán cơ bản (phép nhân, phép chuyển vị,...)
+  * `B` [Khoảng cách Euclid](src/algorithms/math/euclidean-distance) - khoảng cách giữa hai điểm/véc-tơ/ma trận
+  * `A` [Phân hoạch](src/algorithms/math/integer-partition)
+  * `A` [Căn bậc hai](src/algorithms/math/square-root) - phương pháp Newton
+  * `A` [Thuật cắt đường tròn - Lưu Huy](src/algorithms/math/liu-hui) - phép tính gần đúng số π dựa vào đa giác
+  * `A` [Biến đổi Fourier rời rạc](src/algorithms/math/fourier-transform) - phân giải tín hiệu thời gian thành các tần số tạo nên tín hiệu đó
+* **Tập hợp**
+  * `B` [Tích Đề-các](src/algorithms/sets/cartesian-product) - tích của nhiều tập hợp
+  * `B` [Thuật toán xáo trộn](src/algorithms/sets/fisher-yates) - dãy hữu hạn hoán vị ngẫu nhiên
+  * `A` [Tập lũy thừa](src/algorithms/sets/power-set) - tập hợp chứa tất cả các tập con (theo bit và quay lui)
+  * `A` [Hoán vị](src/algorithms/sets/permutations) (lặp và không lặp)
+  * `A` [Tổ hợp](src/algorithms/sets/combinations) (lặp và không lặp)
+  * `A` [Dãy con chung dài nhất](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Dãy con chung tăng dần dài nhất](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Dãy con chung ngắn nhất](src/algorithms/sets/shortest-common-supersequence) (SCS)
+  * `A` [Bài toán xếp ba lô](src/algorithms/sets/knapsack-problem) - dạng 0-1 và không bị chặn
+  * `A` [Mảng con lớn nhất](src/algorithms/sets/maximum-subarray) - phiên bản vét cạn và quy hoạch động (Kadane)
+  * `A` [Tổ hợp của tổng](src/algorithms/sets/combination-sum) - tìm tất cả các tổ hợp tạo thành tổng cụ thể
+* **Chuỗi**
+  * `B` [Khoảng cách Hamming](src/algorithms/string/hamming-distance) - số các vị trí các ký hiệu khác nhau
+  * `A` [Khoảng cách Levenshtein](src/algorithms/string/levenshtein-distance) - khoảng cách thay đổi nhỏ nhất giữa hai chuỗi ký tự
+  * `A` [Thuật toán Knuth–Morris–Pratt](src/algorithms/string/knuth-morris-pratt) (thuật toán KMP) - tìm chuỗi con (đối sánh mẫu)
+  * `A` [Thuật toán Z](src/algorithms/string/z-algorithm) - tìm chuỗi con (đối sánh mẫu)
+  * `A` [Thuật toán Rabin Karp](src/algorithms/string/rabin-karp) - tìm chuỗi con
+  * `A` [Xâu con chung dài nhất](src/algorithms/string/longest-common-substring)
+  * `A` [Phối biểu thức chính quy](src/algorithms/string/regular-expression-matching)
+* **Tìm kiếm**
+  * `B` [Tìm kiếm tuyến tính](src/algorithms/search/linear-search)
+  * `B` [Tìm kiếm nhảy](src/algorithms/search/jump-search) (tìm khối) - tìm kiếm trong mảng đã sắp xếp
+  * `B` [Tìm kiếm nhị phân](src/algorithms/search/binary-search) - tìm kiếm trong mảng đã sắp xếp
+  * `B` [Tìm kiếm nội suy ](src/algorithms/search/interpolation-search) - Tìm kiếm strong mảng có thứ tự được phân phối đồng nhất
+* **Sắp xếp**
+  * `B` [Sắp xếp nổi bọt](src/algorithms/sorting/bubble-sort)
+  * `B` [Sắp xếp chọn](src/algorithms/sorting/selection-sort)
+  * `B` [Sắp xếp chèn](src/algorithms/sorting/insertion-sort)
+  * `B` [Sắp xếp vun đống](src/algorithms/sorting/heap-sort)
+  * `B` [Sắp xếp trộn](src/algorithms/sorting/merge-sort)
+  * `B` [Sắp xếp nhanh](src/algorithms/sorting/quick-sort) - Tại chỗ và không tại chỗ
+  * `B` [Shellsort](src/algorithms/sorting/shell-sort)
+  * `B` [Sắp xếp đếm](src/algorithms/sorting/counting-sort)
+  * `B` [Sắp xếp theo cơ số](src/algorithms/sorting/radix-sort)
+* **Danh sách liên kết**
+  * `B` [Di chuyển chính hướng](src/algorithms/linked-list/traversal)
+  * `B` [Di chuyển ngược hướng](src/algorithms/linked-list/reverse-traversal)
+* **Cây**
+  * `B` [Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Breadth-First Search](src/algorithms/tree/breadth-first-search) (BFS)
+* **Đồ thị**
+  * `B` [Tìm kiếm theo chiều sâu](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Tìm kiếm theo chiều rộng](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [Thuật toán Kruskal](src/algorithms/graph/kruskal) - tìm cây bao trùm nhỏ nhất (MST) cho đồ thị vô hướng có trọng số
+  * `A` [Thuật toán Dijkstra Algorithm](src/algorithms/graph/dijkstra) - tìm những đường ngắn nhất từ một định tới tất cả các đỉnh
+  * `A` [Thuật toán Bellman-Ford](src/algorithms/graph/bellman-ford) - tìm những đường ngắn nhất từ một đỉnh tới tất cả các đỉnh của đồ thị
+  * `A` [Thuật toán Floyd-Warshall](src/algorithms/graph/floyd-warshall) - tìm những đường ngắn nhất giữa tất cả các cặp đỉnh
+  * `A` [Phát hiện vòng](src/algorithms/graph/detect-cycle) - cho cả đồ thị có hướng và vô hướng (dựa trên DFS và tập không giao)
+  * `A` [Thuật toán Prim](src/algorithms/graph/prim) - tìm cây bao trùm nhỏ nhất (MST) cho đồ thị vô hướng có trọng số
+  * `A` [Sắp xếp tô pô](src/algorithms/graph/topological-sorting) - phương pháp DFS
+  * `A` [Điểm khớp](src/algorithms/graph/articulation-points) - Thuật toán Tarjan (dựa trên DFS)
+  * `A` [Cầu nối](src/algorithms/graph/bridges) - dựa trên DFS
+  * `A` [Đường đi Euler và Chu trình Euler](src/algorithms/graph/eulerian-path) - thuật toán Fleury - đi qua các cạnh chỉ một lần duy nhất
+  * `A` [Chu trình Hamilton](src/algorithms/graph/hamiltonian-cycle) - đi qua các đỉnh chỉ một lần duy nhất
+  * `A` [Các thành phần kết nối chặt](src/algorithms/graph/strongly-connected-components) - Thuật toán Kosaraju
+  * `A` [Bài toán người bán hàng](src/algorithms/graph/travelling-salesman) - tuyến đường ngắn nhất có thể đến thăm từng thành phố và trở về thành phố gốc
+* **Mật mã học**
+  * `B` [Băm đa thức](src/algorithms/cryptography/polynomial-hash) - lăn hàm băm dựa trên đa thức
+  * `B` [Mật mã hàng rào đường sắt](src/algorithms/cryptography/rail-fence-cipher) - một thuật toán mật mã chuyển vị để mã hóa thông điệp
+  * `B` [Mật mã Caesar](src/algorithms/cryptography/caesar-cipher) - mật mã chuyển vị đơn giản
+  * `B` [Mật mã Hill](src/algorithms/cryptography/hill-cipher) - mật mã chuyển vị đơn giản dựa trên đại số tuyến tính
+* **Học máy**
+  * `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 hàm JS đơn giản minh họa cách máy tính thực sự có thể học (truyền thuận / truyền ngược)
+  * `B` [k-NN](src/algorithms/ml/knn) - thuật toán phân loại k láng giềng gần nhất
+  * `B` [k-Means](src/algorithms/ml/k-means) - thuật toán phân cụm k-Means
+* **Khác**
+  * `B` [Tháp Hà Nội](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Xoay ma trận vuông](src/algorithms/uncategorized/square-matrix-rotation) - thuật toán tại chỗ
+  * `B` [Trò chơi nhảy](src/algorithms/uncategorized/jump-game) - ví dụ quay lui, quy hoạch động (từ trên xuống + từ dưới lên), dynamic programming (top-down + bottom-up) và tham lam
+  * `B` [Các đường đi đặc trưng duy nhất](src/algorithms/uncategorized/unique-paths) - ví dụ quay lui, quy hoạch động và tam giác Pascal
+  * `B` [Thu thập nước mưa](src/algorithms/uncategorized/rain-terraces) - bài toán bẫy nước mưa (phiên bản quy hoạch động và vét cạn)
+  * `B` [Cầu thang đệ quy](src/algorithms/uncategorized/recursive-staircase) - đếm số cách lên đến đỉnh (4 lời giải)
+  * `B` [Thời điểm tốt nhất để mua bán cổ phiếu ](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - ví dụ chia để trị và một đường chuyền
+  * `A` [Bài toán n quân hậu](src/algorithms/uncategorized/n-queens)
+  * `A` [Mã đi tuần](src/algorithms/uncategorized/knight-tour)
+
+### Thuật toán theo mẫu hình
+
+Mẫu hình thuật toán là một phương pháp hoặc cách tiếp cận chung làm cơ sở cho việc thiết kế một
+lớp thuật toán. Nó là một sự trừu tượng cao hơn khái niệm về một thuật toán, cũng giống như
+một thuật toán là một sự trừu tượng cao hơn một chương trình máy tính.
+
+* **Vét cạn** - xem xét tất cả các khả năng và chọn giải pháp tốt nhất
+  * `B` [Tìm kiếm tuyến tính](src/algorithms/search/linear-search)
+  * `B` [Thu thập nước mưa](src/algorithms/uncategorized/rain-terraces) - bài toán bẫy nước mưa
+  * `B` [Cầu thang đệ quy](src/algorithms/uncategorized/recursive-staircase) - đếm số cách lên đến đỉnh
+  * `A` [Mảng con lớn nhất](src/algorithms/sets/maximum-subarray)
+  * `A` [Bài toán người bán hàng](src/algorithms/graph/travelling-salesman) - tuyến đường ngắn nhất có thể đến thăm từng thành phố và trở về thành phố gốc
+  * `A` [Biến đổi Fourier rời rạc](src/algorithms/math/fourier-transform) - phân giải tín hiệu thời gian thành các tần số tạo nên tín hiệu đó
+* **Tham lam** - chọn phương án tốt nhất vào thời điểm hiện tại mà không cần cân nhắc đến tương lai
+  * `B` [Trò chơi nhảy](src/algorithms/uncategorized/jump-game)
+  * `A` [Bài xếp ba lô không bị chặn](src/algorithms/sets/knapsack-problem)
+  * `A` [Thuật toán Dijkstra](src/algorithms/graph/dijkstra) - tìm những đường ngắn nhất từ một định tới tất cả các đỉnh
+  * `A` [Thuật toán Prim](src/algorithms/graph/prim) - tìm cây bao trùm nhỏ nhất (MST) cho đồ thị vô hướng có trọng số
+  * `A` [Thuật toán Kruskal](src/algorithms/graph/kruskal) - tìm cây bao trùm nhỏ nhất (MST) cho đồ thị vô hướng có trọng số
+* **Chia để trị** - chia vấn đề thành các phần nhỏ hơn rồi giải quyết các phần đó
+  * `B` [Tìm kiếm nhị phân](src/algorithms/search/binary-search)
+  * `B` [Tháp Hà Nội](src/algorithms/uncategorized/hanoi-tower)
+  * `B` [Tam giác Pascal](src/algorithms/math/pascal-triangle)
+  * `B` [Thuật toán Euclid](src/algorithms/math/euclidean-algorithm) - tính ước số chung lớn nhất
+  * `B` [Sắp xếp trộn](src/algorithms/sorting/merge-sort)
+  * `B` [Sắp xếp nhanh](src/algorithms/sorting/quick-sort)
+  * `B` [Cây tìm kiếm theo chiều sâu](src/algorithms/tree/depth-first-search) (DFS)
+  * `B` [Đồ thị tìm kiếm theo chiều sâu](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [Ma trận](src/algorithms/math/matrix) - tạo và duyệt các ma trận có kích thước khác nhau
+  * `B` [Trò chơi nhảy](src/algorithms/uncategorized/jump-game)
+  * `B` [Tính nhanh lũy thừa](src/algorithms/math/fast-powering)
+  * `B` [Thời điểm tốt nhất để mua bán cổ phiếu](src/algorithms/uncategorized/best-time-to-buy-sell-stocks) - ví dụ chia để trị và một đường chuyền
+  * `A` [Hoán vị](src/algorithms/sets/permutations) (lặp và không lặp)
+  * `A` [Tổ hợp](src/algorithms/sets/combinations) (lặp và không lặp)
+* **Quy hoạch động** - xây dựng một giải pháp bằng cách sử dụng các giải pháp phụ đã tìm thấy trước đây
+  * `B` [Số Fibonacci](src/algorithms/math/fibonacci)
+  * `B` [Trò chơi nhảy](src/algorithms/uncategorized/jump-game)
+  * `B` [Đường đi độc nhất](src/algorithms/uncategorized/unique-paths)
+  * `B` [Thu thập nước mưa](src/algorithms/uncategorized/rain-terraces) - bài toán bẫy nước mưa
+  * `B` [Cầu thang đệ quy](src/algorithms/uncategorized/recursive-staircase) - đếm số cách lên đến đỉnh
+  * `A` [Khoảng cách Levenshtein](src/algorithms/string/levenshtein-distance) - khoảng cách thay đổi nhỏ nhất giữa hai chuỗi ký tự
+  * `A` [Dãy con chung dài nhất](src/algorithms/sets/longest-common-subsequence) (LCS)
+  * `A` [Xâu con chung dài nhất](src/algorithms/string/longest-common-substring)
+  * `A` [Dãy con chung tăng dần dài nhất](src/algorithms/sets/longest-increasing-subsequence)
+  * `A` [Dãy con chung ngắn nhất](src/algorithms/sets/shortest-common-supersequence)
+  * `A` [Bài xếp ba lô dạng 0-1](src/algorithms/sets/knapsack-problem)
+  * `A` [Integer Partition](src/algorithms/math/integer-partition)
+  * `A` [Mảng con lớn nhất](src/algorithms/sets/maximum-subarray)
+  * `A` [Thuật toán Bellman-Ford](src/algorithms/graph/bellman-ford) - tìm những đường ngắn nhất từ một đỉnh tới tất cả các đỉnh của đồ thị
+  * `A` [Thuật toán Floyd-Warshall](src/algorithms/graph/floyd-warshall) - tìm những đường ngắn nhất giữa tất cả các cặp đỉnh
+  * `A` [Phối biểu thức chính quy](src/algorithms/string/regular-expression-matching)
+* **Quay lui** - tương tự như vét cạn, cố tạo ra tất cả các giải pháp có thể, nhưng mỗi lần bạn tạo ra giải pháp tiếp theo,
+bạn sẽ kiểm tra xem nó có thỏa mãn tất cả các điều kiện hay không và chỉ khi thỏa mãn mới tiếp tục tạo ra các giải pháp tiếp theo.
+Nếu không, hãy quay lại và đi trên một con đường khác để tìm ra giải pháp. Thông thường, truyền DFS của không gian trạng thái được sử dụng.
+  * `B` [Trò chơi nhảy](src/algorithms/uncategorized/jump-game)
+  * `B` [Đường đi độc nhất](src/algorithms/uncategorized/unique-paths)
+  * `B` [Tập lũy thừa](src/algorithms/sets/power-set) - tập hợp chứa tất cả các tập con
+  * `A` [Chu trình Hamilton](src/algorithms/graph/hamiltonian-cycle) - đi qua các đỉnh một lần duy nhất
+  * `A` [Bài toán n quân hậu](src/algorithms/uncategorized/n-queens)
+  * `A` [Mã đi tuần](src/algorithms/uncategorized/knight-tour)
+  * `A` [Tổ hợp của tổng](src/algorithms/sets/combination-sum) - tìm tất cả các tổ hợp tạo thành tổng cụ thể
+* **Branch & Bound** - ghi nhớ giải pháp chi với phí thấp nhất được tìm thấy ở mỗi giai đoạn của quá trình tìm kiếm quay lui,
+sử dụng chi phí của giải pháp có chi phí thấp nhất được tìm thấy cho đến nay như một giới hạn dưới về chi phí của
+một giải pháp ít chi phí nhân cho bài toán, để loại bỏ các giải pháp từng phần với chi phí lớn hơn giải pháp chi phí thấp nhất được tìm thấy cho đến nay.
+Thông thường BFS duyệt kết hợp với duyệt DFS của cây không gian trạng thái đang được sử dụng.
+
+## Hướng dẫn sử dụng repository
+
+**Cài đặt tất cả các phụ thuộc**
+```
+npm install
+```
+
+**Chạy ESLint**
+
+Bạn có thể muốn chạy nó để kiểm tra chất lượng code.
+
+```
+npm run lint
+```
+
+**Chạy tất cả các kiểm thử**
+```
+npm test
+```
+
+**Chạy kiểm thử theo tên**
+```
+npm test -- 'LinkedList'
+```
+
+**Sân chơi**
+
+Bạn có thể chơi với các cấu trúc dữ liệu và thuật toán trong tệp `./src/playground/playground.js`
+và viết các bài kiểm thử cho nó ở `./src/playground/__test__/playground.test.js`.
+
+Sau đó, chỉ cần chạy lệnh sau để kiểm tra xem sân chơi của bạn có hoạt động như mong đợi hay không:
+
+```
+npm test -- 'playground'
+```
+
+## Thông tin hữu ích
+
+### Tham khảo
+
+[▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
+### Kí hiệu O lớn
+
+*Kí hiệu O lớn* được dùng để phân loại thuật toán theo thời gian chạy hoặc yêu cầu không gian gia tăng khi kích thước đầu vào gia tăng.
+Trên biểu đồ bên dưới, bạn có thể tìm thấy hầu hết các thứ tự tăng trưởng phổ biến của các thuật toán được chỉ định trong ký hiệu O lớn.
+
+![Đồ thị O lớn](./assets/big-o-graph.png)
+
+Nguồn: [Big O Cheat Sheet](http://bigocheatsheet.com/).
+
+Dưới đây là danh sách một số ký hiệu O lớn thông dụng và so sánh với các kích thước khác nhau của dữ liệu đầu vào.
+
+| Kí hiệu O lớn | Tính toán cho 10 phần tử | Tính toán cho 100 phần tử  | Tính toán cho 1000 phần tử   |
+| -------------- | ---------------------------- | ----------------------------- | ------------------------------- |
+| **O(1)**       | 1                            | 1                             | 1                               |
+| **O(log N)**   | 3                            | 6                             | 9                               |
+| **O(N)**       | 10                           | 100                           | 1000                            |
+| **O(N log N)** | 30                           | 600                           | 9000                            |
+| **O(N^2)**     | 100                          | 10000                         | 1000000                         |
+| **O(2^N)**     | 1024                         | 1.26e+29                      | 1.07e+301                       |
+| **O(N!)**      | 3628800                      | 9.3e+157                      | 4.02e+2567                      |
+
+### Độ phức tạp của các phép toán cấu trúc dữ liệu
+
+| Cấu trúc dữ liệu        | Truy cập   | Tìm kiếm    | Chèn | Xóa  | Bình luận  |
+| ----------------------- | :-------: | :-------: | :-------: | :-------: | :-------- |
+| **Mảng**               | 1         | n         | n         | n         |           |
+| **Ngăn xếp**               | n         | n         | 1         | 1         |           |
+| **Hàng đợi**               | n         | n         | 1         | 1         |           |
+| **Danh sách liên kết**         | n         | n         | 1         | n         |           |
+| **Bảng băm**          | -         | n         | n         | n         | Trong trường hợp hàm băm hoàn hảo, chi phí sẽ là O(1) |
+| **Cây tìm kiếm nhị phân**  | n         | n         | n         | n         | Trong trường hợp cây cân bằng, chi phí sẽ là  O(log(n)) |
+| **Cây B**              | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Cây đỏ đen**      | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Cây AVL**            | log(n)    | log(n)    | log(n)    | log(n)    |           |
+| **Bộ lọc Bloom**        | -         | 1         | 1         | -         | Có thể có kết quả dương tính giả trong khi tìm kiếm  |
+
+### Độ phức tạp của các thuật toán sắp xếp mảng
+
+| Tên                  | Tốt nhất            | Trung bình             | Tệ nhất              | Bộ nhớ    | Ổn định    | Bình luận  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Sắp xếp nổi bọt**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Có       |           |
+| **Sắp xếp chèn**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Có       |           |
+| **Sắp xếp chọn**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Không        |           |
+| **Sắp xếp vun đống**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | Không        |           |
+| **Sắp xếp trộn**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Có       |           |
+| **Sắp xếp nhanh**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | Không        | Sắp xếp nhanh thường được thực hiện tại chỗ với không gian ngăn xếp O (log (n))  |
+| **Shell sort**        | n&nbsp;log(n)   | phụ thuộc vào khoảng cách dãy   | n&nbsp;(log(n))<sup>2</sup>  | 1         | Không         |           |
+| **Sắp xếp đếm**     | n + r           | n + r               | n + r               | n + r     | Có       | r - số lớn nhất trong mảng |
+| **Sắp xếp theo cơ số**        | n * k           | n * k               | n * k               | n + k     | Có       | k - độ dài của khóa dài nhất |
+
+## Project Backers
+
+> Bạn có thể hỗ trợ dự án này qua ❤️️ [GitHub](https://github.com/sponsors/trekhleb) hoặc ❤️️ [Patreon](https://www.patreon.com/trekhleb).
+
+[Những người đang ủng hộ dự án này](https://github.com/trekhleb/javascript-algorithms/blob/master/BACKERS.md) `∑ = 0`
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 438fad2986..14e0f0e36c 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,163 +1,189 @@
 # JavaScript 算法与数据结构
 
-[![build status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
 
 本仓库包含了多种基于 JavaScript 的算法与数据结构。
 
-每种算法和数据结构都有自己的 README 并提供相关说明以及进一步阅读和 YouTube 视频。
+每种算法和数据结构都有自己的 README，包含相关说明和链接，以便进一步阅读 (还有 YouTube 视频) 。
 
 _Read this in other languages:_
 [_English_](https://github.com/trekhleb/javascript-algorithms/),
 [_繁體中文_](README.zh-TW.md),
 [_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
 [_Polski_](README.pl-PL.md),
 [_Français_](README.fr-FR.md),
 [_Español_](README.es-ES.md),
-[_Português_](README.pt-BR.md)
-
-> We’re writing a book that will clearly explain, in detail, the main algorithms. 
-If you’d like to be notified when the “JavaScript Algorithms” book 
-launches, [click here](https://upscri.be/402324/).
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
+
+*注意：这个项目仅用于学习和研究，**不是**用于生产环境。*
 
 ## 数据结构
 
-数据结构是在计算机中 组织和存储数 据的一种特殊方式, 它可以高效地 访问和修改 数据。更确切地说, 数据结构是数据值的集合, 它们之间的关系、函数或操作可以应用于数据。
+数据结构是在计算机中组织和存储数据的一种特殊方式，使得数据可以高效地被访问和修改。更确切地说，数据结构是数据值的集合，表示数据之间的关系，也包括了作用在数据上的函数或操作。
 
-`B` - 初学者, `A` - 进阶
+`B` - 初学者， `A` - 进阶
 
-* `B` [链表](src/data-structures/linked-list)
-* `B` [双向链表](src/data-structures/doubly-linked-list)
-* `B` [队列](src/data-structures/queue)
-* `B` [栈](src/data-structures/stack)
-* `B` [哈希表](src/data-structures/hash-table)
-* `B` [堆](src/data-structures/heap)
-* `B` [优先队列](src/data-structures/priority-queue)
-* `A` [字典树](src/data-structures/trie)
-* `A` [树](src/data-structures/tree)
+* `B` [链表](src/data-structures/linked-list/README.zh-CN.md)
+* `B` [双向链表](src/data-structures/doubly-linked-list/README.zh-CN.md)
+* `B` [队列](src/data-structures/queue/README.zh-CN.md)
+* `B` [栈](src/data-structures/stack/README.zh-CN.md)
+* `B` [哈希表(散列)](src/data-structures/hash-table/README.zh-CN.md)
+* `B` [堆](src/data-structures/heap/README.zh-CN.md) - 最大堆 & 最小堆
+* `B` [优先队列](src/data-structures/priority-queue/README.zh-CN.md)
+* `A` [字典树](src/data-structures/trie/README.zh-CN.md)
+* `A` [树](src/data-structures/tree/README.zh-CN.md)
   * `A` [二叉查找树](src/data-structures/tree/binary-search-tree)
   * `A` [AVL 树](src/data-structures/tree/avl-tree)
   * `A` [红黑树](src/data-structures/tree/red-black-tree)
-  * `A` [线段树](src/data-structures/tree/segment-tree) - 使用 最小/最大/总和 范围查询示例
+  * `A` [线段树](src/data-structures/tree/segment-tree) - 使用 `最小/最大/总和` 范围查询示例
   * `A` [树状数组](src/data-structures/tree/fenwick-tree) (二叉索引树)
-* `A` [图](src/data-structures/graph) (有向图与无向图)
+* `A` [图](src/data-structures/graph/README.zh-CN.md) (有向图与无向图)
 * `A` [并查集](src/data-structures/disjoint-set)
 * `A` [布隆过滤器](src/data-structures/bloom-filter)
 
 ## 算法
 
-算法是如何解决一类问题的明确规范。 算法是一组精确定义操作序列的规则。
+算法是如何解决一类问题的明确规范。算法是一组精确定义操作序列的规则。
+
+`B` - 初学者， `A` - 进阶
 
 ### 算法主题
 
-*  **数学**
-  * `B` [Bit 操控](src/algorithms/math/bits) - set/get/update/clear 位, 乘以/除以 二进制位, 变负 等.
-  * `B` [阶乘](src/algorithms/math/factorial)
-  * `B` [斐波那契数](src/algorithms/math/fibonacci)
+* **数学**
+  * `B` [位运算](src/algorithms/math/bits) - set/get/update/clear 位、乘以/除以二进制位 、变负等
+  * `B` [阶乘](src/algorithms/math/factorial/README.zh-CN.md)
+  * `B` [斐波那契数](src/algorithms/math/fibonacci) - `经典` 和 `闭式` 版本
   * `B` [素数检测](src/algorithms/math/primality-test) (排除法)
-  * `B` [欧几里得算法](src/algorithms/math/euclidean-algorithm) - 计算最大公约数 (GCD) 
+  * `B` [欧几里得算法](src/algorithms/math/euclidean-algorithm) - 计算最大公约数 (GCD)
   * `B` [最小公倍数](src/algorithms/math/least-common-multiple) (LCM)
-  * `B` [素数筛](src/algorithms/math/sieve-of-eratosthenes) - 查找所有素数达到任何给定限制
-  * `B` [判断2次方数](src/algorithms/math/is-power-of-two) - 检查数字是否为2的幂 (原生和按位算法) 
+  * `B` [素数筛](src/algorithms/math/sieve-of-eratosthenes) - 查找任意给定范围内的所有素数
+  * `B` [判断 2 次方数](src/algorithms/math/is-power-of-two) - 检查数字是否为 2 的幂 (原生和按位算法)
   * `B` [杨辉三角形](src/algorithms/math/pascal-triangle)
+  * `B` [复数](src/algorithms/math/complex-number) - 复数及其基本运算
+  * `B` [弧度和角](src/algorithms/math/radian) - 弧度与角的相互转换
+  * `B` [快速算次方](src/algorithms/math/fast-powering)
   * `A` [整数拆分](src/algorithms/math/integer-partition)
-  * `A` [割圆术](src/algorithms/math/liu-hui) - 基于N-gons的近似π计算
+  * `A` [割圆术](src/algorithms/math/liu-hui) - 基于 N-gons 的近似 π 计算
+  * `A` [离散傅里叶变换](src/algorithms/math/fourier-transform) - 把时间信号解析成构成它的频率
 * **集合**
   * `B` [笛卡尔积](src/algorithms/sets/cartesian-product) - 多集合结果
+  * `A` [洗牌算法](src/algorithms/sets/fisher-yates) - 随机置换有限序列
   * `A` [幂集](src/algorithms/sets/power-set) - 该集合的所有子集
   * `A` [排列](src/algorithms/sets/permutations) (有/无重复)
   * `A` [组合](src/algorithms/sets/combinations) (有/无重复)
-  * `A` [洗牌算法](src/algorithms/sets/fisher-yates) - 随机置换有限序列
   * `A` [最长公共子序列](src/algorithms/sets/longest-common-subsequence) (LCS)
   * `A` [最长递增子序列](src/algorithms/sets/longest-increasing-subsequence)
   * `A` [最短公共父序列](src/algorithms/sets/shortest-common-supersequence) (SCS)
-  * `A` [背包问题](src/algorithms/sets/knapsack-problem) - "0/1" and "Unbound" ones
-  * `A` [最大子数列问题](src/algorithms/sets/maximum-subarray) - BF算法 与 动态规划
+  * `A` [背包问题](src/algorithms/sets/knapsack-problem) - `0/1` 和 `无边界` 问题
+  * `A` [最大子数列问题](src/algorithms/sets/maximum-subarray) - `BF 算法` 和 `动态规划`
   * `A` [组合求和](src/algorithms/sets/combination-sum) - 查找形成特定总和的所有组合
 * **字符串**
-  * `A` [莱温斯坦距离](src/algorithms/string/levenshtein-distance) - 两个序列之间的最小编辑距离
   * `B` [汉明距离](src/algorithms/string/hamming-distance) - 符号不同的位置数
-  * `A` [克努斯-莫里斯-普拉特算法](src/algorithms/string/knuth-morris-pratt) - 子串搜索
-  * `A` [字符串快速查找](src/algorithms/string/rabin-karp) - 子串搜索
+  * `A` [莱温斯坦距离](src/algorithms/string/levenshtein-distance) - 两个序列之间的最小编辑距离
+  * `A` [Knuth–Morris–Pratt 算法](src/algorithms/string/knuth-morris-pratt) KMP 算法 - 子串搜索 (模式匹配)
+  * `A` [字符串快速查找](src/algorithms/string/z-algorithm) - 子串搜索 (模式匹配)
+  * `A` [Rabin Karp 算法](src/algorithms/string/rabin-karp) - 子串搜索
   * `A` [最长公共子串](src/algorithms/string/longest-common-substring)
-  *  `A` [正则表达式匹配](src/algorithms/string/regular-expression-matching)
+  * `A` [正则表达式匹配](src/algorithms/string/regular-expression-matching)
 * **搜索**
   * `B` [线性搜索](src/algorithms/search/linear-search)
-  * `B` [跳转搜索](src/algorithms/search/jump-search)  (或块搜索)  - 搜索排序数组
-  * `B` [二分查找](src/algorithms/search/binary-search)
-  * `B` [插值搜索](src/algorithms/search/interpolation-search) - 搜索均匀分布的排序数组
+  * `B` [跳转搜索/块搜索](src/algorithms/search/jump-search) - 搜索有序数组
+  * `B` [二分查找](src/algorithms/search/binary-search) - 搜索有序数组
+  * `B` [插值搜索](src/algorithms/search/interpolation-search) - 搜索均匀分布的有序数组
 * **排序**
   * `B` [冒泡排序](src/algorithms/sorting/bubble-sort)
   * `B` [选择排序](src/algorithms/sorting/selection-sort)
   * `B` [插入排序](src/algorithms/sorting/insertion-sort)
   * `B` [堆排序](src/algorithms/sorting/heap-sort)
   * `B` [归并排序](src/algorithms/sorting/merge-sort)
-  * `B` [快速排序](src/algorithms/sorting/quick-sort)
+  * `B` [快速排序](src/algorithms/sorting/quick-sort) - in-place (原地) 和 non-in-place 版本
   * `B` [希尔排序](src/algorithms/sorting/shell-sort)
   * `B` [计数排序](src/algorithms/sorting/counting-sort)
   * `B` [基数排序](src/algorithms/sorting/radix-sort)
+* **链表**
+  - `B` [正向遍历](src/algorithms/linked-list/traversal)
+  - `B` [反向遍历](src/algorithms/linked-list/reverse-traversal)
 * **树**
   * `B` [深度优先搜索](src/algorithms/tree/depth-first-search) (DFS)
   * `B` [广度优先搜索](src/algorithms/tree/breadth-first-search) (BFS)
 * **图**
   * `B` [深度优先搜索](src/algorithms/graph/depth-first-search) (DFS)
   * `B` [广度优先搜索](src/algorithms/graph/breadth-first-search) (BFS)
+  * `B` [克鲁斯克尔演算法](src/algorithms/graph/kruskal) - 寻找加权无向图的最小生成树 (MST)
   * `A` [戴克斯特拉算法](src/algorithms/graph/dijkstra) - 找到图中所有顶点的最短路径
   * `A` [贝尔曼-福特算法](src/algorithms/graph/bellman-ford) - 找到图中所有顶点的最短路径
   * `A` [弗洛伊德算法](src/algorithms/graph/floyd-warshall) - 找到所有顶点对 之间的最短路径
-  * `A` [判圈算法](src/algorithms/graph/detect-cycle) - 对于有向图和无向图 (基于DFS和不相交集的版本) 
-  * `A` [普林演算法](src/algorithms/graph/prim) - 寻找加权无向图的最小生成树 (MST) 
-  * `B` [克鲁斯克尔演算法](src/algorithms/graph/kruskal) - 寻找加权无向图的最小生成树 (MST) 
+  * `A` [判圈算法](src/algorithms/graph/detect-cycle) - 对于有向图和无向图 (基于 DFS 和不相交集的版本)
+  * `A` [普林演算法](src/algorithms/graph/prim) - 寻找加权无向图的最小生成树 (MST)
   * `A` [拓扑排序](src/algorithms/graph/topological-sorting) - DFS 方法
-  * `A` [关节点](src/algorithms/graph/articulation-points) - Tarjan算法 (基于DFS) 
-  * `A` [桥](src/algorithms/graph/bridges) - 基于DFS的算法
-  * `A` [欧拉回径与一笔画问题](src/algorithms/graph/eulerian-path) - Fleury的算法 - 一次访问每个边
+  * `A` [关节点](src/algorithms/graph/articulation-points) - Tarjan 算法 (基于 DFS)
+  * `A` [桥](src/algorithms/graph/bridges) - 基于 DFS 的算法
+  * `A` [欧拉回径与一笔画问题](src/algorithms/graph/eulerian-path) - Fleury 的算法 - 一次访问每个边
   * `A` [哈密顿图](src/algorithms/graph/hamiltonian-cycle) - 恰好访问每个顶点一次
-  * `A` [强连通分量](src/algorithms/graph/strongly-connected-components) - Kosaraju算法
+  * `A` [强连通分量](src/algorithms/graph/strongly-connected-components) - Kosaraju 算法
   * `A` [旅行推销员问题](src/algorithms/graph/travelling-salesman) - 尽可能以最短的路线访问每个城市并返回原始城市
+* **加密**
+  * `B` [多项式 hash](src/algorithms/cryptography/polynomial-hash) - 基于多项式的 rolling hash 函数
+* **机器学习**
+  * `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) -7个简单的JS函数，说明机器如何实际学习（向前/向后传播）
 * **未分类**
   * `B` [汉诺塔](src/algorithms/uncategorized/hanoi-tower)
   * `B` [旋转矩阵](src/algorithms/uncategorized/square-matrix-rotation) - 原地算法
-  * `B` [跳跃 游戏](src/algorithms/uncategorized/jump-game) - 回溯, 动态编程 (自上而下+自下而上) 和贪婪的例子
-  * `B` [独特(唯一) 路径](src/algorithms/uncategorized/unique-paths) - 回溯, 动态编程和基于Pascal三角形的例子
-  * `B` [雨水收集](src/algorithms/uncategorized/rain-terraces) - 诱捕雨水问题 (动态编程和暴力版本) 
+  * `B` [跳跃游戏](src/algorithms/uncategorized/jump-game) - 回溯,、动态编程 (自上而下+自下而上) 和贪婪的例子
+  * `B` [独特(唯一) 路径](src/algorithms/uncategorized/unique-paths) - 回溯、动态编程和基于 Pascal 三角形的例子
+  * `B` [雨水收集](src/algorithms/uncategorized/rain-terraces) - 诱捕雨水问题 (动态编程和暴力版本)
+  * `B` [递归楼梯](src/algorithms/uncategorized/recursive-staircase) - 计算有共有多少种方法可以到达顶层 (4 种解题方案)
   * `A` [八皇后问题](src/algorithms/uncategorized/n-queens)
   * `A` [骑士巡逻](src/algorithms/uncategorized/knight-tour)
 
 ### 算法范式
 
-算法范式是基于类的设计的通用方法或方法的算法。 这是一个比算法概念更高的抽象, 就像一个
-算法是比计算机程序更高的抽象。
+算法范式是一种通用方法，基于一类算法的设计。这是比算法更高的抽象，就像算法是比计算机程序更高的抽象。
 
-* **BF算法** - 查找/搜索 所有可能性并选择最佳解决方案
+* **BF 算法** - `查找/搜索` 所有可能性并选择最佳解决方案
   * `B` [线性搜索](src/algorithms/search/linear-search)
   * `B` [雨水收集](src/algorithms/uncategorized/rain-terraces) - 诱导雨水问题
+  * `B` [递归楼梯](src/algorithms/uncategorized/recursive-staircase) - 计算有共有多少种方法可以到达顶层 (4 种解题方案)
   * `A` [最大子数列](src/algorithms/sets/maximum-subarray)
   * `A` [旅行推销员问题](src/algorithms/graph/travelling-salesman) - 尽可能以最短的路线访问每个城市并返回原始城市
-
-* **贪心法** - 在当前选择最佳选项, 不考虑以后情况
+  * `A` [离散傅里叶变换](src/algorithms/math/fourier-transform) - 把时间信号解析成构成它的频率
+* **贪心法** - 在当前选择最佳选项，不考虑以后情况
   * `B` [跳跃游戏](src/algorithms/uncategorized/jump-game)
   * `A` [背包问题](src/algorithms/sets/knapsack-problem)
   * `A` [戴克斯特拉算法](src/algorithms/graph/dijkstra) - 找到所有图顶点的最短路径
   * `A` [普里姆算法](src/algorithms/graph/prim) - 寻找加权无向图的最小生成树 (MST)
   * `A` [克鲁斯卡尔算法](src/algorithms/graph/kruskal) - 寻找加权无向图的最小生成树 (MST)
-* **分治法** - 将问题分成较小的部分, 然后解决这些部分
+* **分治法** - 将问题分成较小的部分，然后解决这些部分
   * `B` [二分查找](src/algorithms/search/binary-search)
   * `B` [汉诺塔](src/algorithms/uncategorized/hanoi-tower)
   * `B` [杨辉三角形](src/algorithms/math/pascal-triangle)
   * `B` [欧几里得算法](src/algorithms/math/euclidean-algorithm) - 计算最大公约数 (GCD)
-  * `B` [跳跃游戏](src/algorithms/uncategorized/jump-game)
   * `B` [归并排序](src/algorithms/sorting/merge-sort)
   * `B` [快速排序](src/algorithms/sorting/quick-sort)
   * `B` [树深度优先搜索](src/algorithms/tree/depth-first-search) (DFS)
   * `B` [图深度优先搜索](src/algorithms/graph/depth-first-search) (DFS)
+  * `B` [跳跃游戏](src/algorithms/uncategorized/jump-game)
+  * `B` [快速算次方](src/algorithms/math/fast-powering)
   * `A` [排列](src/algorithms/sets/permutations) (有/无重复)
   * `A` [组合](src/algorithms/sets/combinations) (有/无重复)
-*  **动态编程** - 使用以前找到的子解决方案构建解决方案
+* **动态规划(Dynamic programming)** - 使用以前找到的子解决方案构建解决方案
   * `B` [斐波那契数](src/algorithms/math/fibonacci)
   * `B` [跳跃游戏](src/algorithms/uncategorized/jump-game)
   * `B` [独特路径](src/algorithms/uncategorized/unique-paths)
   * `B` [雨水收集](src/algorithms/uncategorized/rain-terraces) - 疏导雨水问题
+  * `B` [递归楼梯](src/algorithms/uncategorized/recursive-staircase) - 计算有共有多少种方法可以到达顶层 (4 种解题方案)
   * `A` [莱温斯坦距离](src/algorithms/string/levenshtein-distance) - 两个序列之间的最小编辑距离
   * `A` [最长公共子序列](src/algorithms/sets/longest-common-subsequence) (LCS)
   * `A` [最长公共子串](src/algorithms/string/longest-common-substring)
@@ -166,16 +192,18 @@ launches, [click here](https://upscri.be/402324/).
   * `A` [0-1背包问题](src/algorithms/sets/knapsack-problem)
   * `A` [整数拆分](src/algorithms/math/integer-partition)
   * `A` [最大子数列](src/algorithms/sets/maximum-subarray)
-  * `A` [弗洛伊德算法](src/algorithms/graph/floyd-warshall) - 找到所有顶点对之间的最短路径
   * `A` [贝尔曼-福特算法](src/algorithms/graph/bellman-ford) - 找到所有图顶点的最短路径
-* **回溯法** - 类似于 BF算法 试图产生所有可能的解决方案, 但每次生成解决方案测试如果它满足所有条件, 那么只有继续生成后续解决方案。 否则回溯并继续寻找不同路径的解决方案。
+  * `A` [弗洛伊德算法](src/algorithms/graph/floyd-warshall) - 找到所有顶点对之间的最短路径
+  * `A` [正则表达式匹配](src/algorithms/string/regular-expression-matching)
+* **回溯法** - 类似于 `BF 算法` 试图产生所有可能的解决方案，但每次生成解决方案测试如果它满足所有条件，那么只有继续生成后续解决方案。否则回溯并继续寻找不同路径的解决方案。
   * `B` [跳跃游戏](src/algorithms/uncategorized/jump-game)
   * `B` [独特路径](src/algorithms/uncategorized/unique-paths)
+  * `A` [幂集](src/algorithms/sets/power-set) - 该集合的所有子集
   * `A` [哈密顿图](src/algorithms/graph/hamiltonian-cycle) - 恰好访问每个顶点一次
   * `A` [八皇后问题](src/algorithms/uncategorized/n-queens)
   * `A` [骑士巡逻](src/algorithms/uncategorized/knight-tour)
   * `A` [组合求和](src/algorithms/sets/combination-sum) - 从规定的总和中找出所有的组合
-* **Branch & Bound**
+* **Branch & Bound** - 记住在回溯搜索的每个阶段找到的成本最低的解决方案，并使用到目前为止找到的成本最小值作为下限。以便丢弃成本大于最小值的解决方案。通常，使用 BFS 遍历以及状态空间树的 DFS 遍历。
 
 ## 如何使用本仓库
 
@@ -184,7 +212,16 @@ launches, [click here](https://upscri.be/402324/).
 npm install
 ```
 
+**运行 ESLint**
+
+检查代码质量
+
+```
+npm run lint
+```
+
 **执行测试**
+
 ```
 npm test
 ```
@@ -196,9 +233,9 @@ npm test -- 'LinkedList'
 
 **Playground**
 
-你可以在`./src/playground/playground.js`文件中操作数据结构与算法, 并在`./src/playground/__test__/playground.test.js`中编写测试。
+你可以在 `./src/playground/playground.js` 文件中操作数据结构与算法，并在 `./src/playground/__test__/playground.test.js` 中编写测试。
 
-然后, 只需运行以下命令来测试你的 Playground 是否按无误:
+然后，只需运行以下命令来测试你的 Playground 是否无误:
 
 ```
 npm test -- 'playground'
@@ -232,26 +269,31 @@ npm test -- 'playground'
 
 ### 数据结构操作的复杂性
 
-| 数据结构                | 连接      | 查找      | 插入      | 删除      |
-| ----------------------- | :-------: | :-------: | :-------: | :-------: |
-| **数组**                | 1         | n         | n         | n         |
-| **栈**                  | n         | n         | 1         | 1         |
-| **队列**                | n         | n         | 1         | 1         |
-| **链表**                | n         | n         | 1         | 1         |
-| **哈希表**              | -         | n         | n         | n         |
-| **二分查找树**          | n         | n         | n         | n         |
-| **B树**                 | log(n)    | log(n)    | log(n)    | log(n)    |
-| **红黑树**              | log(n)    | log(n)    | log(n)    | log(n)    |
-| **AVL树**               | log(n)    | log(n)    | log(n)    | log(n)    |
+| 数据结构       |  连接  |  查找  |  插入  |  删除  | 备注 |
+| -------------- | :----: | :----: | :----: | :----: | ---- |
+| **数组**       |   1    |   n    |   n    |   n    |      |
+| **栈**         |   n    |   n    |   1    |   1    |      |
+| **队列**       |   n    |   n    |   1    |   1    |      |
+| **链表**       |   n    |   n    |   1    |   1    |      |
+| **哈希表**     |   -    |   n    |   n    |   n    | 在完全哈希函数情况下，复杂度是 O(1） |
+| **二分查找树** |   n    |   n    |   n    |   n    | 在平衡树情况下，复杂度是 O(log(n)) |
+| **B 树**       | log(n) | log(n) | log(n) | log(n) |      |
+| **红黑树**     | log(n) | log(n) | log(n) | log(n) |      |
+| **AVL 树**     | log(n) | log(n) | log(n) | log(n) |      |
+| **布隆过滤器** |   -    |   1    |   1    | - | 存在一定概率的判断错误（误判成存在） |
 
 ### 数组排序算法的复杂性
 
-| 名称                  | 最优      | 平均      | 最坏          | 内存      | 稳定      |
-| --------------------- | :-------: | :-------: | :-----------: | :-------: | :-------: |
-| **冒泡排序**          | n         | n^2       | n^2           | 1         | Yes       |
-| **插入排序**          | n         | n^2       | n^2           | 1         | Yes       |
-| **选择排序**          | n^2       | n^2       | n^2           | 1         | No        |
-| **堆排序**            | n log(n)  | n log(n)  | n log(n)      | 1         | No        |
-| **归并排序**          | n log(n)  | n log(n)  | n log(n)      | n         | Yes       |
-| **快速排序**          | n log(n)  | n log(n)  | n^2           | log(n)    | No        |
-| **希尔排序**          | n log(n)  | 取决于差距序列   | n (log(n))^2  | 1         | No        |
+| 名称                  | 最优      | 平均      | 最坏          | 内存      | 稳定      | 备注                  |
+| --------------------- | :-------: | :-------: | :-----------: | :-------: | :-------: | --------------------- |
+| **冒泡排序**          | n         | n^2       | n^2           | 1         | Yes       |                       |
+| **插入排序**          | n         | n^2       | n^2           | 1         | Yes       |                       |
+| **选择排序**          | n^2       | n^2       | n^2           | 1         | No        |                       |
+| **堆排序**            | n log(n)  | n log(n)  | n log(n)      | 1         | No        |                       |
+| **归并排序**          | n log(n)  | n log(n)  | n log(n)      | n         | Yes       |                       |
+| **快速排序**          | n log(n)  | n log(n)  | n^2           | log(n)    | No        | 在 in-place 版本下，内存复杂度通常是 O(log(n)) |
+| **希尔排序**          | n log(n)  | 取决于差距序列   | n (log(n))^2  | 1         | No        |  |
+| **计数排序**          | n + r     | n + r     | n + r         | n + r     | Yes       | r - 数组里最大的数    |
+| **基数排序**          | n * k     | n * k     | n * k         | n + k     | Yes       | k - 最长 key 的升序   |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/README.zh-TW.md b/README.zh-TW.md
index 2199f19e69..aa48b40bd8 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -1,6 +1,6 @@
 # JavaScript 演算法與資料結構
 
-[![build status](https://travis-ci.org/trekhleb/javascript-algorithms.svg?branch=master)](https://travis-ci.org/trekhleb/javascript-algorithms)
+[![CI](https://github.com/trekhleb/javascript-algorithms/workflows/CI/badge.svg)](https://github.com/trekhleb/javascript-algorithms/actions?query=workflow%3ACI+branch%3Amaster)
 [![codecov](https://codecov.io/gh/trekhleb/javascript-algorithms/branch/master/graph/badge.svg)](https://codecov.io/gh/trekhleb/javascript-algorithms)
 
 這個知識庫包含許多 JavaScript 的資料結構與演算法的基礎範例。
@@ -10,14 +10,21 @@ _Read this in other languages:_
 [_English_](https://github.com/trekhleb/javascript-algorithms/),
 [_简体中文_](README.zh-CN.md),
 [_한국어_](README.ko-KR.md),
+[_日本語_](README.ja-JP.md),
 [_Polski_](README.pl-PL.md),
 [_Français_](README.fr-FR.md),
 [_Español_](README.es-ES.md),
-[_Português_](README.pt-BR.md)
-
-> We’re writing a book that will clearly explain, in detail, the main algorithms. 
-If you’d like to be notified when the “JavaScript Algorithms” book 
-launches, [click here](https://upscri.be/402324/).
+[_Português_](README.pt-BR.md),
+[_Русский_](README.ru-RU.md),
+[_Türk_](README.tr-TR.md),
+[_Italiana_](README.it-IT.md),
+[_Bahasa Indonesia_](README.id-ID.md),
+[_Українська_](README.uk-UA.md),
+[_Arabic_](README.ar-AR.md),
+[_Tiếng Việt_](README.vi-VN.md),
+[_Deutsch_](README.de-DE.md),
+[_Uzbek_](README.uz-UZ.md)
+[_עברית_](README.he-IL.md)
 
 ## 資料結構
 
@@ -211,10 +218,12 @@ npm test -- 'playground'
 
 | 名稱                   | 最佳      | 平均      | 最差          | 記憶體    | 穩定      |
 | ---------------------- | :-------: | :-------: | :-----------: | :-------: | :-------: |
-| **氣派排序**           | n         | n^2       | n^2           | 1         | Yes       |
+| **氣泡排序**           | n         | n^2       | n^2           | 1         | Yes       |
 | **插入排序**           | n         | n^2       | n^2           | 1         | Yes       |
 | **選擇排序**           | n^2       | n^2       | n^2           | 1         | No        |
 | **Heap 排序**          | n log(n)  | n log(n)  | n log(n)      | 1         | No        |
 | **合併排序**           | n log(n)  | n log(n)  | n log(n)      | n         | Yes       |
 | **快速排序**           | n log(n)  | n log(n)  | n^2           | log(n)    | No        |
 | **希爾排序**           | n log(n)  | 由gap sequence決定   | n (log(n))^2  | 1         | No        |
+
+> ℹ️ A few more [projects](https://trekhleb.dev/projects/) and [articles](https://trekhleb.dev/blog/) about JavaScript and algorithms on [trekhleb.dev](https://trekhleb.dev)
diff --git a/jest.config.js b/jest.config.js
index 06e765266f..78568a1e94 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -24,5 +24,17 @@ module.exports = {
   // This option sets the URL for the jsdom environment.
   // It is reflected in properties such as location.href.
   // @see: https://github.com/facebook/jest/issues/6769
-  testURL: 'http://localhost/',
+  testEnvironmentOptions: {
+    url: 'http://localhost/',
+  },
+
+  // @see: https://jestjs.io/docs/en/configuration#coveragethreshold-object
+  coverageThreshold: {
+    global: {
+      statements: 100,
+      branches: 95,
+      functions: 100,
+      lines: 100,
+    },
+  },
 };
diff --git a/package-lock.json b/package-lock.json
index 7fd3e2e127..b61d279d87 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9386 +1,15788 @@
 {
   "name": "javascript-algorithms-and-data-structures",
   "version": "0.0.4",
-  "lockfileVersion": 1,
+  "lockfileVersion": 2,
   "requires": true,
-  "dependencies": {
-    "@babel/code-frame": {
-      "version": "7.0.0-beta.51",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.51.tgz",
-      "integrity": "sha1-vXHZsZKvl435FYKdOdQJRFZDmgw=",
-      "dev": true,
-      "requires": {
-        "@babel/highlight": "7.0.0-beta.51"
+  "packages": {
+    "": {
+      "name": "javascript-algorithms-and-data-structures",
+      "version": "0.0.4",
+      "license": "MIT",
+      "devDependencies": {
+        "@babel/cli": "7.20.7",
+        "@babel/preset-env": "7.20.2",
+        "@types/jest": "29.4.0",
+        "eslint": "8.33.0",
+        "eslint-config-airbnb": "19.0.4",
+        "eslint-plugin-import": "2.27.5",
+        "eslint-plugin-jest": "27.2.1",
+        "eslint-plugin-jsx-a11y": "6.7.1",
+        "husky": "8.0.3",
+        "jest": "29.4.1",
+        "pngjs": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=16.15.0",
+        "npm": ">=8.5.5"
       }
     },
-    "@babel/highlight": {
-      "version": "7.0.0-beta.51",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.51.tgz",
-      "integrity": "sha1-6IRK4loVlcz9QriWI7Q3bKBtIl0=",
+    "node_modules/@ampproject/remapping": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+      "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
       "dev": true,
-      "requires": {
-        "chalk": "^2.0.0",
-        "esutils": "^2.0.2",
-        "js-tokens": "^3.0.0"
-      },
       "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "@jridgewell/gen-mapping": "^0.3.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      },
+      "engines": {
+        "node": ">=6.0.0"
       }
     },
-    "@types/jest": {
-      "version": "23.1.4",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.1.4.tgz",
-      "integrity": "sha512-w2KaQFm8zaZMsvPGYWoCxvpDli+dXhee2QZCk35sCpG595KKinyxfpKlfvxZWH776K2kPk53AgO5zv+U4JIrdg==",
-      "dev": true
-    },
-    "abab": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
-      "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=",
-      "dev": true
-    },
-    "acorn": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
-      "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==",
-      "dev": true
-    },
-    "acorn-globals": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz",
-      "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==",
+    "node_modules/@babel/cli": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.20.7.tgz",
+      "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==",
       "dev": true,
-      "requires": {
-        "acorn": "^5.0.0"
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.8",
+        "commander": "^4.0.1",
+        "convert-source-map": "^1.1.0",
+        "fs-readdir-recursive": "^1.1.0",
+        "glob": "^7.2.0",
+        "make-dir": "^2.1.0",
+        "slash": "^2.0.0"
+      },
+      "bin": {
+        "babel": "bin/babel.js",
+        "babel-external-helpers": "bin/babel-external-helpers.js"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "optionalDependencies": {
+        "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
+        "chokidar": "^3.4.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "acorn-jsx": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
-      "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+    "node_modules/@babel/code-frame": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
+      "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
       "dev": true,
-      "requires": {
-        "acorn": "^3.0.4"
-      },
       "dependencies": {
-        "acorn": {
-          "version": "3.3.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
-          "dev": true
-        }
+        "@babel/highlight": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "ajv": {
-      "version": "5.5.2",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+    "node_modules/@babel/compat-data": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz",
+      "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==",
       "dev": true,
-      "requires": {
-        "co": "^4.6.0",
-        "fast-deep-equal": "^1.0.0",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.3.0"
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "ajv-keywords": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
-      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
-      "dev": true
-    },
-    "align-text": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
-      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+    "node_modules/@babel/core": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
+      "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
       "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2",
-        "longest": "^1.0.1",
-        "repeat-string": "^1.5.2"
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.21.4",
+        "@babel/generator": "^7.21.4",
+        "@babel/helper-compilation-targets": "^7.21.4",
+        "@babel/helper-module-transforms": "^7.21.2",
+        "@babel/helpers": "^7.21.0",
+        "@babel/parser": "^7.21.4",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.4",
+        "@babel/types": "^7.21.4",
+        "convert-source-map": "^1.7.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.2",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
       }
     },
-    "amdefine": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
-      "dev": true
-    },
-    "ansi-escapes": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
-      "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
-      "dev": true
-    },
-    "ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-      "dev": true
-    },
-    "ansi-styles": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-      "dev": true
-    },
-    "anymatch": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
-      "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
+    "node_modules/@babel/generator": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
+      "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "micromatch": "^2.1.5",
-        "normalize-path": "^2.0.0"
+      "dependencies": {
+        "@babel/types": "^7.21.4",
+        "@jridgewell/gen-mapping": "^0.3.2",
+        "@jridgewell/trace-mapping": "^0.3.17",
+        "jsesc": "^2.5.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "append-transform": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
-      "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
+    "node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
+      "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
       "dev": true,
-      "requires": {
-        "default-require-extensions": "^2.0.0"
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+    "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz",
+      "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==",
       "dev": true,
-      "requires": {
-        "sprintf-js": "~1.0.2"
+      "dependencies": {
+        "@babel/helper-explode-assignable-expression": "^7.18.6",
+        "@babel/types": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "argv": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz",
-      "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=",
-      "dev": true
-    },
-    "aria-query": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
-      "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz",
+      "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==",
       "dev": true,
-      "requires": {
-        "ast-types-flow": "0.0.7",
-        "commander": "^2.11.0"
+      "dependencies": {
+        "@babel/compat-data": "^7.21.4",
+        "@babel/helper-validator-option": "^7.21.0",
+        "browserslist": "^4.21.3",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
       }
     },
-    "arr-diff": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
-      "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+    "node_modules/@babel/helper-create-class-features-plugin": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz",
+      "integrity": "sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "arr-flatten": "^1.0.1"
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-function-name": "^7.21.0",
+        "@babel/helper-member-expression-to-functions": "^7.21.0",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.20.7",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+        "@babel/helper-split-export-declaration": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
       }
     },
-    "arr-flatten": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
-      "dev": true
-    },
-    "arr-union": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
-      "dev": true
-    },
-    "array-equal": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
-      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
-      "dev": true
-    },
-    "array-includes": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
-      "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
+    "node_modules/@babel/helper-create-regexp-features-plugin": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz",
+      "integrity": "sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==",
       "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "es-abstract": "^1.7.0"
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "regexpu-core": "^5.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
       }
     },
-    "array-union": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
-      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+    "node_modules/@babel/helper-define-polyfill-provider": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz",
+      "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==",
       "dev": true,
-      "requires": {
-        "array-uniq": "^1.0.1"
+      "dependencies": {
+        "@babel/helper-compilation-targets": "^7.17.7",
+        "@babel/helper-plugin-utils": "^7.16.7",
+        "debug": "^4.1.1",
+        "lodash.debounce": "^4.0.8",
+        "resolve": "^1.14.2",
+        "semver": "^6.1.2"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.4.0-0"
       }
     },
-    "array-uniq": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
-      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
-      "dev": true
-    },
-    "array-unique": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
-      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+    "node_modules/@babel/helper-environment-visitor": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+      "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
       "dev": true,
-      "optional": true
-    },
-    "arrify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
-      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
-      "dev": true
-    },
-    "asn1": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
-      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
-      "dev": true
-    },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-      "dev": true
-    },
-    "assign-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
-      "dev": true
-    },
-    "ast-types-flow": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
-      "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
-      "dev": true
-    },
-    "astral-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
-      "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
-      "dev": true
+      "engines": {
+        "node": ">=6.9.0"
+      }
     },
-    "async": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
-      "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+    "node_modules/@babel/helper-explode-assignable-expression": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz",
+      "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==",
       "dev": true,
-      "requires": {
-        "lodash": "^4.17.10"
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "async-each": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
-      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
+    "node_modules/@babel/helper-function-name": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
+      "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
       "dev": true,
-      "optional": true
-    },
-    "async-limiter": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
-      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
-      "dev": true
-    },
-    "asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
-    },
-    "atob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz",
-      "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=",
-      "dev": true
-    },
-    "aws-sign2": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
-      "dev": true
-    },
-    "aws4": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
-      "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==",
-      "dev": true
+      "dependencies": {
+        "@babel/template": "^7.20.7",
+        "@babel/types": "^7.21.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
     },
-    "axobject-query": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.1.tgz",
-      "integrity": "sha1-Bd+nBa2orZ25k/polvItOVsLCgc=",
+    "node_modules/@babel/helper-hoist-variables": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+      "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
       "dev": true,
-      "requires": {
-        "ast-types-flow": "0.0.7"
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-cli": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz",
-      "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=",
+    "node_modules/@babel/helper-member-expression-to-functions": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz",
+      "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==",
       "dev": true,
-      "requires": {
-        "babel-core": "^6.26.0",
-        "babel-polyfill": "^6.26.0",
-        "babel-register": "^6.26.0",
-        "babel-runtime": "^6.26.0",
-        "chokidar": "^1.6.1",
-        "commander": "^2.11.0",
-        "convert-source-map": "^1.5.0",
-        "fs-readdir-recursive": "^1.0.0",
-        "glob": "^7.1.2",
-        "lodash": "^4.17.4",
-        "output-file-sync": "^1.1.2",
-        "path-is-absolute": "^1.0.1",
-        "slash": "^1.0.0",
-        "source-map": "^0.5.6",
-        "v8flags": "^2.1.1"
+      "dependencies": {
+        "@babel/types": "^7.21.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-code-frame": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
+      "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
       "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "esutils": "^2.0.2",
-        "js-tokens": "^3.0.2"
+      "dependencies": {
+        "@babel/types": "^7.21.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-core": {
-      "version": "6.26.3",
-      "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
-      "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.21.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
+      "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
       "dev": true,
-      "requires": {
-        "babel-code-frame": "^6.26.0",
-        "babel-generator": "^6.26.0",
-        "babel-helpers": "^6.24.1",
-        "babel-messages": "^6.23.0",
-        "babel-register": "^6.26.0",
-        "babel-runtime": "^6.26.0",
-        "babel-template": "^6.26.0",
-        "babel-traverse": "^6.26.0",
-        "babel-types": "^6.26.0",
-        "babylon": "^6.18.0",
-        "convert-source-map": "^1.5.1",
-        "debug": "^2.6.9",
-        "json5": "^0.5.1",
-        "lodash": "^4.17.4",
-        "minimatch": "^3.0.4",
-        "path-is-absolute": "^1.0.1",
-        "private": "^0.1.8",
-        "slash": "^1.0.0",
-        "source-map": "^0.5.7"
+      "dependencies": {
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-simple-access": "^7.20.2",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/helper-validator-identifier": "^7.19.1",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.2",
+        "@babel/types": "^7.21.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-generator": {
-      "version": "6.26.1",
-      "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
-      "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+    "node_modules/@babel/helper-optimise-call-expression": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
+      "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
       "dev": true,
-      "requires": {
-        "babel-messages": "^6.23.0",
-        "babel-runtime": "^6.26.0",
-        "babel-types": "^6.26.0",
-        "detect-indent": "^4.0.0",
-        "jsesc": "^1.3.0",
-        "lodash": "^4.17.4",
-        "source-map": "^0.5.7",
-        "trim-right": "^1.0.1"
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-builder-binary-assignment-operator-visitor": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
-      "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
+    "node_modules/@babel/helper-plugin-utils": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+      "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
       "dev": true,
-      "requires": {
-        "babel-helper-explode-assignable-expression": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-call-delegate": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
-      "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
+    "node_modules/@babel/helper-remap-async-to-generator": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz",
+      "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==",
       "dev": true,
-      "requires": {
-        "babel-helper-hoist-variables": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-traverse": "^6.24.1",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-wrap-function": "^7.18.9",
+        "@babel/types": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
       }
     },
-    "babel-helper-define-map": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
-      "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
+    "node_modules/@babel/helper-replace-supers": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz",
+      "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==",
       "dev": true,
-      "requires": {
-        "babel-helper-function-name": "^6.24.1",
-        "babel-runtime": "^6.26.0",
-        "babel-types": "^6.26.0",
-        "lodash": "^4.17.4"
+      "dependencies": {
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-member-expression-to-functions": "^7.20.7",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.20.7",
+        "@babel/types": "^7.20.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-explode-assignable-expression": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
-      "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
+    "node_modules/@babel/helper-simple-access": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+      "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-traverse": "^6.24.1",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/types": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-function-name": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
-      "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
+    "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz",
+      "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==",
       "dev": true,
-      "requires": {
-        "babel-helper-get-function-arity": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1",
-        "babel-traverse": "^6.24.1",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/types": "^7.20.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-get-function-arity": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
-      "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
+    "node_modules/@babel/helper-split-export-declaration": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+      "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/types": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-hoist-variables": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
-      "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-optimise-call-expression": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
-      "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.19.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-regex": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
-      "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=",
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
+      "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.26.0",
-        "babel-types": "^6.26.0",
-        "lodash": "^4.17.4"
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-remap-async-to-generator": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
-      "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
+    "node_modules/@babel/helper-wrap-function": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz",
+      "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==",
       "dev": true,
-      "requires": {
-        "babel-helper-function-name": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1",
-        "babel-traverse": "^6.24.1",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-function-name": "^7.19.0",
+        "@babel/template": "^7.18.10",
+        "@babel/traverse": "^7.20.5",
+        "@babel/types": "^7.20.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helper-replace-supers": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
-      "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
+    "node_modules/@babel/helpers": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
+      "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
       "dev": true,
-      "requires": {
-        "babel-helper-optimise-call-expression": "^6.24.1",
-        "babel-messages": "^6.23.0",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1",
-        "babel-traverse": "^6.24.1",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.0",
+        "@babel/types": "^7.21.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-helpers": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
-      "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
+    "node_modules/@babel/highlight": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+      "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.18.6",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "babel-jest": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-23.2.0.tgz",
-      "integrity": "sha1-FKnWo/QSLf6mBp03CFrfJqU6Tbo=",
+    "node_modules/@babel/parser": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+      "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
       "dev": true,
-      "requires": {
-        "babel-plugin-istanbul": "^4.1.6",
-        "babel-preset-jest": "^23.2.0"
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
       }
     },
-    "babel-messages": {
-      "version": "6.23.0",
-      "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
-      "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+    "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz",
+      "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
       }
     },
-    "babel-plugin-check-es2015-constants": {
-      "version": "6.22.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
-      "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
+    "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz",
+      "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+        "@babel/plugin-proposal-optional-chaining": "^7.20.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.13.0"
       }
     },
-    "babel-plugin-istanbul": {
-      "version": "4.1.6",
-      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
-      "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==",
+    "node_modules/@babel/plugin-proposal-async-generator-functions": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz",
+      "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==",
       "dev": true,
-      "requires": {
-        "babel-plugin-syntax-object-rest-spread": "^6.13.0",
-        "find-up": "^2.1.0",
-        "istanbul-lib-instrument": "^1.10.1",
-        "test-exclude": "^4.2.1"
+      "dependencies": {
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-remap-async-to-generator": "^7.18.9",
+        "@babel/plugin-syntax-async-generators": "^7.8.4"
       },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-proposal-class-properties": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
+      "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
+      "dev": true,
       "dependencies": {
-        "find-up": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
-          "dev": true,
-          "requires": {
-            "locate-path": "^2.0.0"
-          }
-        }
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-jest-hoist": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz",
-      "integrity": "sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=",
-      "dev": true
-    },
-    "babel-plugin-syntax-async-functions": {
-      "version": "6.13.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
-      "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=",
-      "dev": true
-    },
-    "babel-plugin-syntax-exponentiation-operator": {
-      "version": "6.13.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
-      "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=",
-      "dev": true
-    },
-    "babel-plugin-syntax-object-rest-spread": {
-      "version": "6.13.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
-      "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
-      "dev": true
-    },
-    "babel-plugin-syntax-trailing-function-commas": {
-      "version": "6.22.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
-      "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=",
-      "dev": true
-    },
-    "babel-plugin-transform-async-to-generator": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
-      "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
+    "node_modules/@babel/plugin-proposal-class-static-block": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz",
+      "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==",
       "dev": true,
-      "requires": {
-        "babel-helper-remap-async-to-generator": "^6.24.1",
-        "babel-plugin-syntax-async-functions": "^6.8.0",
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-create-class-features-plugin": "^7.21.0",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-class-static-block": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.12.0"
       }
     },
-    "babel-plugin-transform-es2015-arrow-functions": {
-      "version": "6.22.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
-      "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
+    "node_modules/@babel/plugin-proposal-dynamic-import": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz",
+      "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-dynamic-import": "^7.8.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-block-scoped-functions": {
-      "version": "6.22.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
-      "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
+    "node_modules/@babel/plugin-proposal-export-namespace-from": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz",
+      "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.9",
+        "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-block-scoping": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
-      "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
+    "node_modules/@babel/plugin-proposal-json-strings": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz",
+      "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.26.0",
-        "babel-template": "^6.26.0",
-        "babel-traverse": "^6.26.0",
-        "babel-types": "^6.26.0",
-        "lodash": "^4.17.4"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-json-strings": "^7.8.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-classes": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
-      "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
+    "node_modules/@babel/plugin-proposal-logical-assignment-operators": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz",
+      "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==",
       "dev": true,
-      "requires": {
-        "babel-helper-define-map": "^6.24.1",
-        "babel-helper-function-name": "^6.24.1",
-        "babel-helper-optimise-call-expression": "^6.24.1",
-        "babel-helper-replace-supers": "^6.24.1",
-        "babel-messages": "^6.23.0",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1",
-        "babel-traverse": "^6.24.1",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-computed-properties": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
-      "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
+    "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
+      "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-destructuring": {
-      "version": "6.23.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
-      "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
+    "node_modules/@babel/plugin-proposal-numeric-separator": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz",
+      "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-duplicate-keys": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
-      "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
+    "node_modules/@babel/plugin-proposal-object-rest-spread": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
+      "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/compat-data": "^7.20.5",
+        "@babel/helper-compilation-targets": "^7.20.7",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-transform-parameters": "^7.20.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-for-of": {
-      "version": "6.23.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
-      "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
+    "node_modules/@babel/plugin-proposal-optional-catch-binding": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz",
+      "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-function-name": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
-      "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
+    "node_modules/@babel/plugin-proposal-optional-chaining": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
+      "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
       "dev": true,
-      "requires": {
-        "babel-helper-function-name": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-literals": {
-      "version": "6.22.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
-      "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
+    "node_modules/@babel/plugin-proposal-private-methods": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
+      "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-modules-amd": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
-      "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
+    "node_modules/@babel/plugin-proposal-private-property-in-object": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz",
+      "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==",
       "dev": true,
-      "requires": {
-        "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-create-class-features-plugin": "^7.21.0",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-modules-commonjs": {
-      "version": "6.26.2",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz",
-      "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==",
+    "node_modules/@babel/plugin-proposal-unicode-property-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
+      "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
       "dev": true,
-      "requires": {
-        "babel-plugin-transform-strict-mode": "^6.24.1",
-        "babel-runtime": "^6.26.0",
-        "babel-template": "^6.26.0",
-        "babel-types": "^6.26.0"
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-modules-systemjs": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
-      "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
+    "node_modules/@babel/plugin-syntax-async-generators": {
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+      "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
       "dev": true,
-      "requires": {
-        "babel-helper-hoist-variables": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-modules-umd": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
-      "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
+    "node_modules/@babel/plugin-syntax-bigint": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+      "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
       "dev": true,
-      "requires": {
-        "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-object-super": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
-      "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
+    "node_modules/@babel/plugin-syntax-class-properties": {
+      "version": "7.12.13",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+      "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
       "dev": true,
-      "requires": {
-        "babel-helper-replace-supers": "^6.24.1",
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.12.13"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-parameters": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
-      "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
+    "node_modules/@babel/plugin-syntax-class-static-block": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+      "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
       "dev": true,
-      "requires": {
-        "babel-helper-call-delegate": "^6.24.1",
-        "babel-helper-get-function-arity": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-template": "^6.24.1",
-        "babel-traverse": "^6.24.1",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-shorthand-properties": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
-      "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
+    "node_modules/@babel/plugin-syntax-dynamic-import": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+      "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-spread": {
-      "version": "6.22.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
-      "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
+    "node_modules/@babel/plugin-syntax-export-namespace-from": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+      "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.3"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-sticky-regex": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
-      "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
+    "node_modules/@babel/plugin-syntax-import-assertions": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz",
+      "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==",
       "dev": true,
-      "requires": {
-        "babel-helper-regex": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.19.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-template-literals": {
-      "version": "6.22.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
-      "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
+    "node_modules/@babel/plugin-syntax-import-meta": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+      "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-typeof-symbol": {
-      "version": "6.23.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
-      "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
+    "node_modules/@babel/plugin-syntax-json-strings": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+      "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-es2015-unicode-regex": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
-      "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
+    "node_modules/@babel/plugin-syntax-jsx": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz",
+      "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==",
       "dev": true,
-      "requires": {
-        "babel-helper-regex": "^6.24.1",
-        "babel-runtime": "^6.22.0",
-        "regexpu-core": "^2.0.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-exponentiation-operator": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
-      "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
+    "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+      "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
       "dev": true,
-      "requires": {
-        "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1",
-        "babel-plugin-syntax-exponentiation-operator": "^6.8.0",
-        "babel-runtime": "^6.22.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-regenerator": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
-      "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
+    "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+      "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
       "dev": true,
-      "requires": {
-        "regenerator-transform": "^0.10.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-plugin-transform-strict-mode": {
-      "version": "6.24.1",
-      "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
-      "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+    "node_modules/@babel/plugin-syntax-numeric-separator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+      "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.22.0",
-        "babel-types": "^6.24.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-polyfill": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
-      "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
+    "node_modules/@babel/plugin-syntax-object-rest-spread": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+      "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.26.0",
-        "core-js": "^2.5.0",
-        "regenerator-runtime": "^0.10.5"
-      },
       "dependencies": {
-        "regenerator-runtime": {
-          "version": "0.10.5",
-          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
-          "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
-          "dev": true
-        }
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-preset-env": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz",
-      "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==",
-      "dev": true,
-      "requires": {
-        "babel-plugin-check-es2015-constants": "^6.22.0",
-        "babel-plugin-syntax-trailing-function-commas": "^6.22.0",
-        "babel-plugin-transform-async-to-generator": "^6.22.0",
-        "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
-        "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
-        "babel-plugin-transform-es2015-block-scoping": "^6.23.0",
-        "babel-plugin-transform-es2015-classes": "^6.23.0",
-        "babel-plugin-transform-es2015-computed-properties": "^6.22.0",
-        "babel-plugin-transform-es2015-destructuring": "^6.23.0",
-        "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0",
-        "babel-plugin-transform-es2015-for-of": "^6.23.0",
-        "babel-plugin-transform-es2015-function-name": "^6.22.0",
-        "babel-plugin-transform-es2015-literals": "^6.22.0",
-        "babel-plugin-transform-es2015-modules-amd": "^6.22.0",
-        "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
-        "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0",
-        "babel-plugin-transform-es2015-modules-umd": "^6.23.0",
-        "babel-plugin-transform-es2015-object-super": "^6.22.0",
-        "babel-plugin-transform-es2015-parameters": "^6.23.0",
-        "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0",
-        "babel-plugin-transform-es2015-spread": "^6.22.0",
-        "babel-plugin-transform-es2015-sticky-regex": "^6.22.0",
-        "babel-plugin-transform-es2015-template-literals": "^6.22.0",
-        "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0",
-        "babel-plugin-transform-es2015-unicode-regex": "^6.22.0",
-        "babel-plugin-transform-exponentiation-operator": "^6.22.0",
-        "babel-plugin-transform-regenerator": "^6.22.0",
-        "browserslist": "^3.2.6",
-        "invariant": "^2.2.2",
-        "semver": "^5.3.0"
+    "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+      "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-preset-jest": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz",
-      "integrity": "sha1-jsegOhOPABoaj7HoETZSvxpV2kY=",
+    "node_modules/@babel/plugin-syntax-optional-chaining": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+      "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
       "dev": true,
-      "requires": {
-        "babel-plugin-jest-hoist": "^23.2.0",
-        "babel-plugin-syntax-object-rest-spread": "^6.13.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-register": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
-      "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
+    "node_modules/@babel/plugin-syntax-private-property-in-object": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+      "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
       "dev": true,
-      "requires": {
-        "babel-core": "^6.26.0",
-        "babel-runtime": "^6.26.0",
-        "core-js": "^2.5.0",
-        "home-or-tmp": "^2.0.0",
-        "lodash": "^4.17.4",
-        "mkdirp": "^0.5.1",
-        "source-map-support": "^0.4.15"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-runtime": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
-      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+    "node_modules/@babel/plugin-syntax-top-level-await": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+      "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
       "dev": true,
-      "requires": {
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.11.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-template": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
-      "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+    "node_modules/@babel/plugin-syntax-typescript": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz",
+      "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.26.0",
-        "babel-traverse": "^6.26.0",
-        "babel-types": "^6.26.0",
-        "babylon": "^6.18.0",
-        "lodash": "^4.17.4"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-traverse": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
-      "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+    "node_modules/@babel/plugin-transform-arrow-functions": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz",
+      "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==",
       "dev": true,
-      "requires": {
-        "babel-code-frame": "^6.26.0",
-        "babel-messages": "^6.23.0",
-        "babel-runtime": "^6.26.0",
-        "babel-types": "^6.26.0",
-        "babylon": "^6.18.0",
-        "debug": "^2.6.8",
-        "globals": "^9.18.0",
-        "invariant": "^2.2.2",
-        "lodash": "^4.17.4"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babel-types": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
-      "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+    "node_modules/@babel/plugin-transform-async-to-generator": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz",
+      "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==",
       "dev": true,
-      "requires": {
-        "babel-runtime": "^6.26.0",
-        "esutils": "^2.0.2",
-        "lodash": "^4.17.4",
-        "to-fast-properties": "^1.0.3"
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-remap-async-to-generator": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "babylon": {
-      "version": "6.18.0",
-      "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
-      "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
-      "dev": true
-    },
-    "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
-    },
-    "base": {
-      "version": "0.11.2",
-      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+    "node_modules/@babel/plugin-transform-block-scoped-functions": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz",
+      "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==",
       "dev": true,
-      "requires": {
-        "cache-base": "^1.0.1",
-        "class-utils": "^0.3.5",
-        "component-emitter": "^1.2.1",
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.1",
-        "mixin-deep": "^1.2.0",
-        "pascalcase": "^0.1.1"
-      },
       "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "bcrypt-pbkdf": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
-      "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+    "node_modules/@babel/plugin-transform-block-scoping": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz",
+      "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "tweetnacl": "^0.14.3"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "binary-extensions": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
-      "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
+    "node_modules/@babel/plugin-transform-classes": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz",
+      "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==",
       "dev": true,
-      "optional": true
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-compilation-targets": "^7.20.7",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-function-name": "^7.21.0",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-replace-supers": "^7.20.7",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "globals": "^11.1.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
     },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+    "node_modules/@babel/plugin-transform-computed-properties": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz",
+      "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==",
       "dev": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/template": "^7.20.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "braces": {
-      "version": "1.8.5",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
-      "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+    "node_modules/@babel/plugin-transform-destructuring": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz",
+      "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "expand-range": "^1.8.1",
-        "preserve": "^0.2.0",
-        "repeat-element": "^1.1.2"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "browser-process-hrtime": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
-      "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=",
-      "dev": true
-    },
-    "browser-resolve": {
-      "version": "1.11.3",
-      "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
-      "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+    "node_modules/@babel/plugin-transform-dotall-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz",
+      "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==",
       "dev": true,
-      "requires": {
-        "resolve": "1.1.7"
-      },
       "dependencies": {
-        "resolve": {
-          "version": "1.1.7",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
-          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
-          "dev": true
-        }
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "browserslist": {
-      "version": "3.2.8",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz",
-      "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==",
+    "node_modules/@babel/plugin-transform-duplicate-keys": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz",
+      "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==",
       "dev": true,
-      "requires": {
-        "caniuse-lite": "^1.0.30000844",
-        "electron-to-chromium": "^1.3.47"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "bser": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz",
-      "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=",
+    "node_modules/@babel/plugin-transform-exponentiation-operator": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz",
+      "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==",
       "dev": true,
-      "requires": {
-        "node-int64": "^0.4.0"
+      "dependencies": {
+        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "buffer-from": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
-      "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
-      "dev": true
-    },
-    "builtin-modules": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
-      "dev": true
-    },
-    "cache-base": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+    "node_modules/@babel/plugin-transform-for-of": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz",
+      "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==",
       "dev": true,
-      "requires": {
-        "collection-visit": "^1.0.0",
-        "component-emitter": "^1.2.1",
-        "get-value": "^2.0.6",
-        "has-value": "^1.0.0",
-        "isobject": "^3.0.1",
-        "set-value": "^2.0.0",
-        "to-object-path": "^0.3.0",
-        "union-value": "^1.0.0",
-        "unset-value": "^1.0.0"
-      },
       "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "caller-path": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
-      "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+    "node_modules/@babel/plugin-transform-function-name": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz",
+      "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==",
       "dev": true,
-      "requires": {
-        "callsites": "^0.2.0"
+      "dependencies": {
+        "@babel/helper-compilation-targets": "^7.18.9",
+        "@babel/helper-function-name": "^7.18.9",
+        "@babel/helper-plugin-utils": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "callsites": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
-      "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
-      "dev": true
-    },
-    "camelcase": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
-      "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+    "node_modules/@babel/plugin-transform-literals": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz",
+      "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==",
       "dev": true,
-      "optional": true
-    },
-    "caniuse-lite": {
-      "version": "1.0.30000846",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000846.tgz",
-      "integrity": "sha512-qxUOHr5mTaadWH1ap0ueivHd8x42Bnemcn+JutVr7GWmm2bU4zoBhjuv5QdXgALQnnT626lOQros7cCDf8PwCg==",
-      "dev": true
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
     },
-    "capture-exit": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz",
-      "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=",
+    "node_modules/@babel/plugin-transform-member-expression-literals": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz",
+      "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==",
       "dev": true,
-      "requires": {
-        "rsvp": "^3.3.3"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "caseless": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
-      "dev": true
-    },
-    "center-align": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
-      "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+    "node_modules/@babel/plugin-transform-modules-amd": {
+      "version": "7.20.11",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz",
+      "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "align-text": "^0.1.3",
-        "lazy-cache": "^1.0.3"
+      "dependencies": {
+        "@babel/helper-module-transforms": "^7.20.11",
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "chalk": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+    "node_modules/@babel/plugin-transform-modules-commonjs": {
+      "version": "7.21.2",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz",
+      "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==",
       "dev": true,
-      "requires": {
-        "ansi-styles": "^2.2.1",
-        "escape-string-regexp": "^1.0.2",
-        "has-ansi": "^2.0.0",
-        "strip-ansi": "^3.0.0",
-        "supports-color": "^2.0.0"
+      "dependencies": {
+        "@babel/helper-module-transforms": "^7.21.2",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-simple-access": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "chardet": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
-      "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
-      "dev": true
-    },
-    "chokidar": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
-      "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
+    "node_modules/@babel/plugin-transform-modules-systemjs": {
+      "version": "7.20.11",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz",
+      "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "anymatch": "^1.3.0",
-        "async-each": "^1.0.0",
-        "fsevents": "^1.0.0",
-        "glob-parent": "^2.0.0",
-        "inherits": "^2.0.1",
-        "is-binary-path": "^1.0.0",
-        "is-glob": "^2.0.0",
-        "path-is-absolute": "^1.0.0",
-        "readdirp": "^2.0.0"
+      "dependencies": {
+        "@babel/helper-hoist-variables": "^7.18.6",
+        "@babel/helper-module-transforms": "^7.20.11",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-validator-identifier": "^7.19.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "ci-info": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz",
-      "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==",
-      "dev": true
-    },
-    "circular-json": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
-      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
-      "dev": true
-    },
-    "class-utils": {
-      "version": "0.3.6",
-      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+    "node_modules/@babel/plugin-transform-modules-umd": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz",
+      "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==",
       "dev": true,
-      "requires": {
-        "arr-union": "^3.1.0",
-        "define-property": "^0.2.5",
-        "isobject": "^3.0.0",
-        "static-extend": "^0.1.1"
-      },
       "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
+        "@babel/helper-module-transforms": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "cli-cursor": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
-      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+    "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz",
+      "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==",
       "dev": true,
-      "requires": {
-        "restore-cursor": "^2.0.0"
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.20.5",
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
       }
     },
-    "cli-width": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
-      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
-      "dev": true
-    },
-    "cliui": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
-      "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+    "node_modules/@babel/plugin-transform-new-target": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz",
+      "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "center-align": "^0.1.1",
-        "right-align": "^0.1.1",
-        "wordwrap": "0.0.2"
-      },
       "dependencies": {
-        "wordwrap": {
-          "version": "0.0.2",
-          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
-          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
-          "dev": true,
-          "optional": true
-        }
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "clorox": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/clorox/-/clorox-1.0.3.tgz",
-      "integrity": "sha512-w3gKAUKMJYmmaJyc+p+iDrDtLvsFasrx/y6/zWo2U1TZfsz3y4Vl4T9PHCZrOwk1eMTOSRI6xHdpDR4PhTdy8Q==",
-      "dev": true
-    },
-    "co": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
-      "dev": true
-    },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
-    },
-    "codecov": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.0.2.tgz",
-      "integrity": "sha512-9ljtIROIjPIUmMRqO+XuDITDoV8xRrZmA0jcEq6p2hg2+wY9wGmLfreAZGIL72IzUfdEDZaU8+Vjidg1fBQ8GQ==",
+    "node_modules/@babel/plugin-transform-object-super": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz",
+      "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==",
       "dev": true,
-      "requires": {
-        "argv": "0.0.2",
-        "request": "^2.81.0",
-        "urlgrey": "0.4.4"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "collection-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+    "node_modules/@babel/plugin-transform-parameters": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz",
+      "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==",
       "dev": true,
-      "requires": {
-        "map-visit": "^1.0.0",
-        "object-visit": "^1.0.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "color-convert": {
-      "version": "1.9.2",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz",
-      "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==",
+    "node_modules/@babel/plugin-transform-property-literals": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz",
+      "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==",
       "dev": true,
-      "requires": {
-        "color-name": "1.1.1"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "color-name": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
-      "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=",
-      "dev": true
-    },
-    "combined-stream": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
-      "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+    "node_modules/@babel/plugin-transform-regenerator": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz",
+      "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==",
       "dev": true,
-      "requires": {
-        "delayed-stream": "~1.0.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "regenerator-transform": "^0.15.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "commander": {
-      "version": "2.15.1",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
-      "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
-      "dev": true
-    },
-    "compare-versions": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.3.0.tgz",
-      "integrity": "sha512-MAAAIOdi2s4Gl6rZ76PNcUa9IOYB+5ICdT41o5uMRf09aEu/F9RK+qhe8RjXNPwcTjGV7KU7h2P/fljThFVqyQ==",
-      "dev": true
-    },
-    "component-emitter": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
-    },
-    "concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+    "node_modules/@babel/plugin-transform-reserved-words": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz",
+      "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==",
       "dev": true,
-      "requires": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.2.2",
-        "typedarray": "^0.0.6"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "contains-path": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
-      "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
-      "dev": true
-    },
-    "convert-source-map": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
-      "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=",
-      "dev": true
-    },
-    "copy-descriptor": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
-      "dev": true
-    },
-    "core-js": {
-      "version": "2.5.5",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz",
-      "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=",
-      "dev": true
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "dev": true
-    },
-    "cross-spawn": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-      "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+    "node_modules/@babel/plugin-transform-shorthand-properties": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz",
+      "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==",
       "dev": true,
-      "requires": {
-        "lru-cache": "^4.0.1",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "cssom": {
-      "version": "0.3.4",
-      "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz",
-      "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==",
-      "dev": true
-    },
-    "cssstyle": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz",
-      "integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==",
+    "node_modules/@babel/plugin-transform-spread": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz",
+      "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==",
       "dev": true,
-      "requires": {
-        "cssom": "0.3.x"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "damerau-levenshtein": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
-      "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=",
-      "dev": true
-    },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+    "node_modules/@babel/plugin-transform-sticky-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz",
+      "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==",
       "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "data-urls": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.0.tgz",
-      "integrity": "sha512-ai40PPQR0Fn1lD2PPie79CibnlMN2AYiDhwFX/rZHVsxbs5kNJSjegqXIprhouGXlRdEnfybva7kqRGnB6mypA==",
+    "node_modules/@babel/plugin-transform-template-literals": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz",
+      "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==",
       "dev": true,
-      "requires": {
-        "abab": "^1.0.4",
-        "whatwg-mimetype": "^2.0.0",
-        "whatwg-url": "^6.4.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+    "node_modules/@babel/plugin-transform-typeof-symbol": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz",
+      "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==",
       "dev": true,
-      "requires": {
-        "ms": "2.0.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
-      "dev": true
-    },
-    "decode-uri-component": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
-      "dev": true
-    },
-    "deep-is": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
-      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
-      "dev": true
-    },
-    "default-require-extensions": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
-      "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
+    "node_modules/@babel/plugin-transform-unicode-escapes": {
+      "version": "7.18.10",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz",
+      "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==",
       "dev": true,
-      "requires": {
-        "strip-bom": "^3.0.0"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "define-properties": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
-      "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+    "node_modules/@babel/plugin-transform-unicode-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz",
+      "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==",
       "dev": true,
-      "requires": {
-        "foreach": "^2.0.5",
-        "object-keys": "^1.0.8"
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "define-property": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+    "node_modules/@babel/preset-env": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz",
+      "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==",
       "dev": true,
-      "requires": {
-        "is-descriptor": "^1.0.2",
-        "isobject": "^3.0.1"
-      },
       "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
+        "@babel/compat-data": "^7.20.1",
+        "@babel/helper-compilation-targets": "^7.20.0",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-validator-option": "^7.18.6",
+        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
+        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9",
+        "@babel/plugin-proposal-async-generator-functions": "^7.20.1",
+        "@babel/plugin-proposal-class-properties": "^7.18.6",
+        "@babel/plugin-proposal-class-static-block": "^7.18.6",
+        "@babel/plugin-proposal-dynamic-import": "^7.18.6",
+        "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
+        "@babel/plugin-proposal-json-strings": "^7.18.6",
+        "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9",
+        "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
+        "@babel/plugin-proposal-numeric-separator": "^7.18.6",
+        "@babel/plugin-proposal-object-rest-spread": "^7.20.2",
+        "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
+        "@babel/plugin-proposal-optional-chaining": "^7.18.9",
+        "@babel/plugin-proposal-private-methods": "^7.18.6",
+        "@babel/plugin-proposal-private-property-in-object": "^7.18.6",
+        "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-class-properties": "^7.12.13",
+        "@babel/plugin-syntax-class-static-block": "^7.14.5",
+        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+        "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+        "@babel/plugin-syntax-import-assertions": "^7.20.0",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+        "@babel/plugin-syntax-top-level-await": "^7.14.5",
+        "@babel/plugin-transform-arrow-functions": "^7.18.6",
+        "@babel/plugin-transform-async-to-generator": "^7.18.6",
+        "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
+        "@babel/plugin-transform-block-scoping": "^7.20.2",
+        "@babel/plugin-transform-classes": "^7.20.2",
+        "@babel/plugin-transform-computed-properties": "^7.18.9",
+        "@babel/plugin-transform-destructuring": "^7.20.2",
+        "@babel/plugin-transform-dotall-regex": "^7.18.6",
+        "@babel/plugin-transform-duplicate-keys": "^7.18.9",
+        "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
+        "@babel/plugin-transform-for-of": "^7.18.8",
+        "@babel/plugin-transform-function-name": "^7.18.9",
+        "@babel/plugin-transform-literals": "^7.18.9",
+        "@babel/plugin-transform-member-expression-literals": "^7.18.6",
+        "@babel/plugin-transform-modules-amd": "^7.19.6",
+        "@babel/plugin-transform-modules-commonjs": "^7.19.6",
+        "@babel/plugin-transform-modules-systemjs": "^7.19.6",
+        "@babel/plugin-transform-modules-umd": "^7.18.6",
+        "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1",
+        "@babel/plugin-transform-new-target": "^7.18.6",
+        "@babel/plugin-transform-object-super": "^7.18.6",
+        "@babel/plugin-transform-parameters": "^7.20.1",
+        "@babel/plugin-transform-property-literals": "^7.18.6",
+        "@babel/plugin-transform-regenerator": "^7.18.6",
+        "@babel/plugin-transform-reserved-words": "^7.18.6",
+        "@babel/plugin-transform-shorthand-properties": "^7.18.6",
+        "@babel/plugin-transform-spread": "^7.19.0",
+        "@babel/plugin-transform-sticky-regex": "^7.18.6",
+        "@babel/plugin-transform-template-literals": "^7.18.9",
+        "@babel/plugin-transform-typeof-symbol": "^7.18.9",
+        "@babel/plugin-transform-unicode-escapes": "^7.18.10",
+        "@babel/plugin-transform-unicode-regex": "^7.18.6",
+        "@babel/preset-modules": "^0.1.5",
+        "@babel/types": "^7.20.2",
+        "babel-plugin-polyfill-corejs2": "^0.3.3",
+        "babel-plugin-polyfill-corejs3": "^0.6.0",
+        "babel-plugin-polyfill-regenerator": "^0.4.1",
+        "core-js-compat": "^3.25.1",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "del": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
-      "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+    "node_modules/@babel/preset-modules": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz",
+      "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==",
       "dev": true,
-      "requires": {
-        "globby": "^5.0.0",
-        "is-path-cwd": "^1.0.0",
-        "is-path-in-cwd": "^1.0.0",
-        "object-assign": "^4.0.1",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0",
-        "rimraf": "^2.2.8"
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+        "@babel/plugin-transform-dotall-regex": "^7.4.4",
+        "@babel/types": "^7.4.4",
+        "esutils": "^2.0.2"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
       }
     },
-    "delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+    "node_modules/@babel/regjsgen": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
+      "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==",
       "dev": true
     },
-    "detect-indent": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
-      "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+    "node_modules/@babel/runtime": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+      "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
       "dev": true,
-      "requires": {
-        "repeating": "^2.0.0"
+      "dependencies": {
+        "regenerator-runtime": "^0.13.11"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "detect-newline": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
-      "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
-      "dev": true
-    },
-    "diff": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
-      "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
-      "dev": true
-    },
-    "doctrine": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+    "node_modules/@babel/template": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+      "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
       "dev": true,
-      "requires": {
-        "esutils": "^2.0.2"
+      "dependencies": {
+        "@babel/code-frame": "^7.18.6",
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "domexception": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
-      "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+    "node_modules/@babel/traverse": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+      "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
       "dev": true,
-      "requires": {
-        "webidl-conversions": "^4.0.2"
+      "dependencies": {
+        "@babel/code-frame": "^7.21.4",
+        "@babel/generator": "^7.21.4",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-function-name": "^7.21.0",
+        "@babel/helper-hoist-variables": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/parser": "^7.21.4",
+        "@babel/types": "^7.21.4",
+        "debug": "^4.1.0",
+        "globals": "^11.1.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "ecc-jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
-      "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+    "node_modules/@babel/types": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz",
+      "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "jsbn": "~0.1.0"
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.19.4",
+        "@babel/helper-validator-identifier": "^7.19.1",
+        "to-fast-properties": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
       }
     },
-    "electron-to-chromium": {
-      "version": "1.3.48",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz",
-      "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=",
+    "node_modules/@bcoe/v8-coverage": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+      "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
       "dev": true
     },
-    "emoji-regex": {
-      "version": "6.5.1",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
-      "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==",
-      "dev": true
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
     },
-    "error-ex": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
-      "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+    "node_modules/@eslint/eslintrc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
+      "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
       "dev": true,
-      "requires": {
-        "is-arrayish": "^0.2.1"
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.4.0",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "es-abstract": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz",
-      "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==",
+    "node_modules/@eslint/eslintrc/node_modules/globals": {
+      "version": "13.20.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+      "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
       "dev": true,
-      "requires": {
-        "es-to-primitive": "^1.1.1",
-        "function-bind": "^1.1.1",
-        "has": "^1.0.1",
-        "is-callable": "^1.1.3",
-        "is-regex": "^1.0.4"
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "es-to-primitive": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
-      "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
+    "node_modules/@eslint/eslintrc/node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
       "dev": true,
-      "requires": {
-        "is-callable": "^1.1.1",
-        "is-date-object": "^1.0.1",
-        "is-symbol": "^1.0.1"
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true
-    },
-    "escodegen": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.10.0.tgz",
-      "integrity": "sha512-fjUOf8johsv23WuIKdNQU4P9t9jhQ4Qzx6pC2uW890OloK3Zs1ZAoCNpg/2larNF501jLl3UNy0kIRcF6VI22g==",
+    "node_modules/@humanwhocodes/config-array": {
+      "version": "0.11.8",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+      "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
       "dev": true,
-      "requires": {
-        "esprima": "^3.1.3",
-        "estraverse": "^4.2.0",
-        "esutils": "^2.0.2",
-        "optionator": "^0.8.1",
-        "source-map": "~0.6.1"
-      },
       "dependencies": {
-        "esprima": {
-          "version": "3.1.3",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
-          "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true,
-          "optional": true
-        }
+        "@humanwhocodes/object-schema": "^1.2.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      },
+      "engines": {
+        "node": ">=10.10.0"
       }
     },
-    "eslint": {
-      "version": "4.19.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
-      "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
       "dev": true,
-      "requires": {
-        "ajv": "^5.3.0",
-        "babel-code-frame": "^6.22.0",
-        "chalk": "^2.1.0",
-        "concat-stream": "^1.6.0",
-        "cross-spawn": "^5.1.0",
-        "debug": "^3.1.0",
-        "doctrine": "^2.1.0",
-        "eslint-scope": "^3.7.1",
-        "eslint-visitor-keys": "^1.0.0",
-        "espree": "^3.5.4",
-        "esquery": "^1.0.0",
-        "esutils": "^2.0.2",
-        "file-entry-cache": "^2.0.0",
-        "functional-red-black-tree": "^1.0.1",
-        "glob": "^7.1.2",
-        "globals": "^11.0.1",
-        "ignore": "^3.3.3",
-        "imurmurhash": "^0.1.4",
-        "inquirer": "^3.0.6",
-        "is-resolvable": "^1.0.0",
-        "js-yaml": "^3.9.1",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.3.0",
-        "lodash": "^4.17.4",
-        "minimatch": "^3.0.2",
-        "mkdirp": "^0.5.1",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.8.2",
-        "path-is-inside": "^1.0.2",
-        "pluralize": "^7.0.0",
-        "progress": "^2.0.0",
-        "regexpp": "^1.0.1",
-        "require-uncached": "^1.0.3",
-        "semver": "^5.3.0",
-        "strip-ansi": "^4.0.0",
-        "strip-json-comments": "~2.0.1",
-        "table": "4.0.2",
-        "text-table": "~0.2.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "globals": {
-          "version": "11.7.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz",
-          "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
       }
     },
-    "eslint-config-airbnb": {
-      "version": "17.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-17.0.0.tgz",
-      "integrity": "sha512-FLaAYKpu9gLw/ca0qXQRT150Nbfvwc8eaCITaJCU25H9x+5CHsBPW1pFB6EF3r3T2PZTnXrHKisLh+GT43ZY5Q==",
+    "node_modules/@humanwhocodes/object-schema": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+      "dev": true
+    },
+    "node_modules/@istanbuljs/load-nyc-config": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
       "dev": true,
-      "requires": {
-        "eslint-config-airbnb-base": "^13.0.0",
-        "object.assign": "^4.1.0",
-        "object.entries": "^1.0.4"
+      "dependencies": {
+        "camelcase": "^5.3.1",
+        "find-up": "^4.1.0",
+        "get-package-type": "^0.1.0",
+        "js-yaml": "^3.13.1",
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "eslint-config-airbnb-base": {
-      "version": "13.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.0.0.tgz",
-      "integrity": "sha512-hUFXRlE6AY84z0qYh4wKdtSF4EqDnyT8sxrvTpcXCV4ENSLF8li5yNA1yDM26iinH8Ierbpc4lv8Rp62uX6VSQ==",
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
       "dev": true,
-      "requires": {
-        "eslint-restricted-globals": "^0.1.1",
-        "object.assign": "^4.1.0",
-        "object.entries": "^1.0.4"
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
       }
     },
-    "eslint-import-resolver-node": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
-      "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
       "dev": true,
-      "requires": {
-        "debug": "^2.6.9",
-        "resolve": "^1.5.0"
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "eslint-module-utils": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz",
-      "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=",
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
       "dev": true,
-      "requires": {
-        "debug": "^2.6.8",
-        "pkg-dir": "^1.0.0"
+      "dependencies": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
       }
     },
-    "eslint-plugin-import": {
-      "version": "2.13.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz",
-      "integrity": "sha512-t6hGKQDMIt9N8R7vLepsYXgDfeuhp6ZJSgtrLEDxonpSubyxUZHjhm6LsAaZX8q6GYVxkbT3kTsV9G5mBCFR6A==",
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
       "dev": true,
-      "requires": {
-        "contains-path": "^0.1.0",
-        "debug": "^2.6.8",
-        "doctrine": "1.5.0",
-        "eslint-import-resolver-node": "^0.3.1",
-        "eslint-module-utils": "^2.2.0",
-        "has": "^1.0.1",
-        "lodash": "^4.17.4",
-        "minimatch": "^3.0.3",
-        "read-pkg-up": "^2.0.0",
-        "resolve": "^1.6.0"
-      },
       "dependencies": {
-        "doctrine": {
-          "version": "1.5.0",
-          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
-          "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
-          "dev": true,
-          "requires": {
-            "esutils": "^2.0.2",
-            "isarray": "^1.0.0"
-          }
-        }
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "eslint-plugin-jest": {
-      "version": "21.17.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-21.17.0.tgz",
-      "integrity": "sha512-kB0gaMLy4RA1bAltYSnnoW33hzX0bUrALGaIqaLoB41Fif38/uAv6oNUFbrzp7aFrwegxKUgFcE/8Z0DZEa0SQ==",
-      "dev": true
-    },
-    "eslint-plugin-jsx-a11y": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.1.0.tgz",
-      "integrity": "sha512-hnhf28u7Z9zlh7Y56tETrwnPeBvXgcqlP7ntHvZsWQs/n/p/vPnfNMNFWTqJAFcbd8PrDEifX1NRGHsjnUmqMw==",
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
       "dev": true,
-      "requires": {
-        "aria-query": "^3.0.0",
-        "array-includes": "^3.0.3",
-        "ast-types-flow": "^0.0.7",
-        "axobject-query": "^2.0.1",
-        "damerau-levenshtein": "^1.0.4",
-        "emoji-regex": "^6.5.1",
-        "has": "^1.0.3",
-        "jsx-ast-utils": "^2.0.1"
-      },
       "dependencies": {
-        "has": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-          "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-          "dev": true,
-          "requires": {
-            "function-bind": "^1.1.1"
-          }
-        }
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "eslint-plugin-react": {
-      "version": "7.10.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.10.0.tgz",
-      "integrity": "sha512-18rzWn4AtbSUxFKKM7aCVcj5LXOhOKdwBino3KKWy4psxfPW0YtIbE8WNRDUdyHFL50BeLb6qFd4vpvNYyp7hw==",
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
       "dev": true,
-      "requires": {
-        "doctrine": "^2.1.0",
-        "has": "^1.0.3",
-        "jsx-ast-utils": "^2.0.1",
-        "prop-types": "^15.6.2"
-      },
       "dependencies": {
-        "has": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-          "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-          "dev": true,
-          "requires": {
-            "function-bind": "^1.1.1"
-          }
-        }
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "eslint-restricted-globals": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
-      "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
-      "dev": true
-    },
-    "eslint-scope": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
-      "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
       "dev": true,
-      "requires": {
-        "esrecurse": "^4.1.0",
-        "estraverse": "^4.1.1"
+      "engines": {
+        "node": ">=8"
       }
     },
-    "eslint-visitor-keys": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
-      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
-      "dev": true
-    },
-    "espree": {
-      "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
-      "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
+    "node_modules/@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
       "dev": true,
-      "requires": {
-        "acorn": "^5.5.0",
-        "acorn-jsx": "^3.0.0"
+      "engines": {
+        "node": ">=8"
       }
     },
-    "esprima": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
-      "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
-      "dev": true
-    },
-    "esquery": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
-      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+    "node_modules/@jest/console": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz",
+      "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==",
       "dev": true,
-      "requires": {
-        "estraverse": "^4.0.0"
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "esrecurse": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
-      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+    "node_modules/@jest/console/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
       "dev": true,
-      "requires": {
-        "estraverse": "^4.1.0"
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "estraverse": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
-      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
-      "dev": true
-    },
-    "esutils": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
-      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
-      "dev": true
-    },
-    "exec-sh": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz",
-      "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==",
+    "node_modules/@jest/console/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
       "dev": true,
-      "requires": {
-        "merge": "^1.2.0"
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
-    "execa": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
-      "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+    "node_modules/@jest/console/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
       "dev": true,
-      "requires": {
-        "cross-spawn": "^5.0.1",
-        "get-stream": "^3.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
       }
     },
-    "exit": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
-      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+    "node_modules/@jest/console/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "dev": true
     },
-    "expand-brackets": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
-      "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+    "node_modules/@jest/console/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "is-posix-bracket": "^0.1.0"
+      "engines": {
+        "node": ">=8"
       }
     },
-    "expand-range": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
-      "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
+    "node_modules/@jest/console/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "fill-range": "^2.1.0"
+      "engines": {
+        "node": ">=8"
       }
     },
-    "expect": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/expect/-/expect-23.3.0.tgz",
-      "integrity": "sha1-7LBRrcvcQKxNtXbBYGfxL9sTzGE=",
+    "node_modules/@jest/console/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
       "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.0",
-        "jest-diff": "^23.2.0",
-        "jest-get-type": "^22.1.0",
-        "jest-matcher-utils": "^23.2.0",
-        "jest-message-util": "^23.3.0",
-        "jest-regex-util": "^23.3.0"
-      },
       "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        }
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "extend": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
-      "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
-      "dev": true
-    },
-    "extend-shallow": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+    "node_modules/@jest/core": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz",
+      "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==",
       "dev": true,
-      "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
-      },
       "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
+        "@jest/console": "^29.5.0",
+        "@jest/reporters": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.9",
+        "jest-changed-files": "^29.5.0",
+        "jest-config": "^29.5.0",
+        "jest-haste-map": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-resolve-dependencies": "^29.5.0",
+        "jest-runner": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "jest-watcher": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
         }
       }
     },
-    "external-editor": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
-      "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
+    "node_modules/@jest/core/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
       "dev": true,
-      "requires": {
-        "chardet": "^0.4.0",
-        "iconv-lite": "^0.4.17",
-        "tmp": "^0.0.33"
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "extglob": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
-      "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+    "node_modules/@jest/core/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "is-extglob": "^1.0.0"
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
-      "dev": true
-    },
-    "fast-deep-equal": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
-      "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
-      "dev": true
+    "node_modules/@jest/core/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
     },
-    "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+    "node_modules/@jest/core/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "dev": true
     },
-    "fast-levenshtein": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
-      "dev": true
+    "node_modules/@jest/core/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
     },
-    "fb-watchman": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz",
-      "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=",
+    "node_modules/@jest/core/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
       "dev": true,
-      "requires": {
-        "bser": "^2.0.0"
+      "engines": {
+        "node": ">=8"
       }
     },
-    "figures": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
-      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+    "node_modules/@jest/core/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
       "dev": true,
-      "requires": {
-        "escape-string-regexp": "^1.0.5"
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "file-entry-cache": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
-      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+    "node_modules/@jest/environment": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz",
+      "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==",
       "dev": true,
-      "requires": {
-        "flat-cache": "^1.2.1",
-        "object-assign": "^4.0.1"
+      "dependencies": {
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-mock": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "filename-regex": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
-      "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
+    "node_modules/@jest/expect": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz",
+      "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==",
       "dev": true,
-      "optional": true
+      "dependencies": {
+        "expect": "^29.5.0",
+        "jest-snapshot": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
     },
-    "fileset": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
-      "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
+    "node_modules/@jest/expect-utils": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz",
+      "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==",
       "dev": true,
-      "requires": {
-        "glob": "^7.0.3",
-        "minimatch": "^3.0.3"
+      "dependencies": {
+        "jest-get-type": "^29.4.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "fill-range": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
-      "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=",
+    "node_modules/@jest/fake-timers": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz",
+      "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "is-number": "^2.1.0",
-        "isobject": "^2.0.0",
-        "randomatic": "^1.1.3",
-        "repeat-element": "^1.1.2",
-        "repeat-string": "^1.5.2"
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@sinonjs/fake-timers": "^10.0.2",
+        "@types/node": "*",
+        "jest-message-util": "^29.5.0",
+        "jest-mock": "^29.5.0",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
-    "find-up": {
+    "node_modules/@jest/globals": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz",
+      "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/expect": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "jest-mock": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/reporters": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz",
+      "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==",
+      "dev": true,
+      "dependencies": {
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@jest/console": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "exit": "^0.1.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-instrument": "^5.1.0",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^4.0.0",
+        "istanbul-reports": "^3.1.3",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "slash": "^3.0.0",
+        "string-length": "^4.0.1",
+        "strip-ansi": "^6.0.0",
+        "v8-to-istanbul": "^9.0.1"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@jest/reporters/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@jest/reporters/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@jest/reporters/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/@jest/reporters/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/@jest/reporters/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/reporters/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/reporters/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/schemas": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
+      "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
+      "dev": true,
+      "dependencies": {
+        "@sinclair/typebox": "^0.25.16"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/source-map": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz",
+      "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "callsites": "^3.0.0",
+        "graceful-fs": "^4.2.9"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/test-result": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz",
+      "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/console": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "collect-v8-coverage": "^1.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/test-sequencer": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz",
+      "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/test-result": "^29.5.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/test-sequencer/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/transform": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz",
+      "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.11.6",
+        "@jest/types": "^29.5.0",
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "babel-plugin-istanbul": "^6.1.1",
+        "chalk": "^4.0.0",
+        "convert-source-map": "^2.0.0",
+        "fast-json-stable-stringify": "^2.1.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "pirates": "^4.0.4",
+        "slash": "^3.0.0",
+        "write-file-atomic": "^4.0.2"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/transform/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@jest/transform/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@jest/transform/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/@jest/transform/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/@jest/transform/node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true
+    },
+    "node_modules/@jest/transform/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/transform/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/transform/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/types": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
+      "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
+      "dev": true,
+      "dependencies": {
+        "@jest/schemas": "^29.4.3",
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "@types/istanbul-reports": "^3.0.0",
+        "@types/node": "*",
+        "@types/yargs": "^17.0.8",
+        "chalk": "^4.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@jest/types/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@jest/types/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@jest/types/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/@jest/types/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/@jest/types/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/types/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/set-array": {
       "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
       "dev": true,
-      "requires": {
-        "path-exists": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
+      "engines": {
+        "node": ">=6.0.0"
       }
     },
-    "flat-cache": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
-      "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.15",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+      "dev": true
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.18",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+      "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
       "dev": true,
-      "requires": {
-        "circular-json": "^0.3.1",
-        "del": "^2.0.2",
-        "graceful-fs": "^4.1.2",
-        "write": "^0.2.1"
+      "dependencies": {
+        "@jridgewell/resolve-uri": "3.1.0",
+        "@jridgewell/sourcemap-codec": "1.4.14"
       }
     },
-    "for-in": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+    "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
       "dev": true
     },
-    "for-own": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
-      "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+    "node_modules/@nicolo-ribaudo/chokidar-2": {
+      "version": "2.1.8-no-fsevents.3",
+      "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
+      "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "for-in": "^1.0.1"
+      "optional": true
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
       }
     },
-    "foreach": {
+    "node_modules/@nodelib/fs.stat": {
       "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
-      "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@sinclair/typebox": {
+      "version": "0.25.24",
+      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
+      "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==",
       "dev": true
     },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+    "node_modules/@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+      "dev": true,
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/@sinonjs/fake-timers": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
+      "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
+      "dev": true,
+      "dependencies": {
+        "@sinonjs/commons": "^2.0.0"
+      }
+    },
+    "node_modules/@types/babel__core": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
+      "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7",
+        "@types/babel__generator": "*",
+        "@types/babel__template": "*",
+        "@types/babel__traverse": "*"
+      }
+    },
+    "node_modules/@types/babel__generator": {
+      "version": "7.6.4",
+      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
+      "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__template": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
+      "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__traverse": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz",
+      "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.3.0"
+      }
+    },
+    "node_modules/@types/graceful-fs": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
+      "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/istanbul-lib-coverage": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+      "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
       "dev": true
     },
-    "form-data": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
-      "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+    "node_modules/@types/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
       "dev": true,
-      "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "1.0.6",
-        "mime-types": "^2.1.12"
+      "dependencies": {
+        "@types/istanbul-lib-coverage": "*"
       }
     },
-    "fragment-cache": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+    "node_modules/@types/istanbul-reports": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+      "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
       "dev": true,
-      "requires": {
-        "map-cache": "^0.2.2"
+      "dependencies": {
+        "@types/istanbul-lib-report": "*"
       }
     },
-    "fs-readdir-recursive": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
-      "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+    "node_modules/@types/jest": {
+      "version": "29.4.0",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz",
+      "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==",
+      "dev": true,
+      "dependencies": {
+        "expect": "^29.0.0",
+        "pretty-format": "^29.0.0"
+      }
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.11",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+      "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+    "node_modules/@types/json5": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
       "dev": true
     },
-    "fsevents": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.2.tgz",
-      "integrity": "sha512-iownA+hC4uHFp+7gwP/y5SzaiUo7m2vpa0dhpzw8YuKtiZsz7cIXsFbXpLEeBM6WuCQyw1MH4RRe6XI8GFUctQ==",
+    "node_modules/@types/node": {
+      "version": "18.15.11",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
+      "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
+      "dev": true
+    },
+    "node_modules/@types/prettier": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
+      "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==",
+      "dev": true
+    },
+    "node_modules/@types/semver": {
+      "version": "7.3.13",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+      "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+      "dev": true
+    },
+    "node_modules/@types/stack-utils": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+      "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+      "dev": true
+    },
+    "node_modules/@types/yargs": {
+      "version": "17.0.24",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
+      "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "nan": "^2.9.2",
-        "node-pre-gyp": "^0.9.0"
-      },
       "dependencies": {
-        "abbrev": {
-          "version": "1.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "ansi-regex": {
-          "version": "2.1.1",
-          "bundled": true,
-          "dev": true
-        },
-        "aproba": {
-          "version": "1.2.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "are-we-there-yet": {
-          "version": "1.1.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "delegates": "^1.0.0",
-            "readable-stream": "^2.0.6"
-          }
-        },
-        "balanced-match": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true
-        },
-        "brace-expansion": {
-          "version": "1.1.11",
-          "bundled": true,
-          "dev": true,
-          "requires": {
-            "balanced-match": "^1.0.0",
-            "concat-map": "0.0.1"
-          }
-        },
-        "chownr": {
-          "version": "1.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "code-point-at": {
-          "version": "1.1.0",
-          "bundled": true,
-          "dev": true
-        },
-        "concat-map": {
-          "version": "0.0.1",
-          "bundled": true,
-          "dev": true
-        },
-        "console-control-strings": {
-          "version": "1.1.0",
-          "bundled": true,
-          "dev": true
-        },
-        "core-util-is": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "debug": {
-          "version": "2.6.9",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "deep-extend": {
-          "version": "0.4.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "delegates": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "detect-libc": {
-          "version": "1.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "fs-minipass": {
-          "version": "1.2.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minipass": "^2.2.1"
-          }
-        },
-        "fs.realpath": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "gauge": {
-          "version": "2.7.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "aproba": "^1.0.3",
-            "console-control-strings": "^1.0.0",
-            "has-unicode": "^2.0.0",
-            "object-assign": "^4.1.0",
-            "signal-exit": "^3.0.0",
-            "string-width": "^1.0.1",
-            "strip-ansi": "^3.0.1",
-            "wide-align": "^1.1.0"
-          }
-        },
-        "glob": {
-          "version": "7.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "has-unicode": {
-          "version": "2.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "iconv-lite": {
-          "version": "0.4.21",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safer-buffer": "^2.1.0"
-          }
-        },
-        "ignore-walk": {
-          "version": "3.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minimatch": "^3.0.4"
-          }
-        },
-        "inflight": {
-          "version": "1.0.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "once": "^1.3.0",
-            "wrappy": "1"
-          }
-        },
-        "inherits": {
-          "version": "2.0.3",
-          "bundled": true,
-          "dev": true
-        },
-        "ini": {
-          "version": "1.3.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "requires": {
-            "number-is-nan": "^1.0.0"
-          }
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
+        "@types/yargs-parser": "*"
+      }
+    },
+    "node_modules/@types/yargs-parser": {
+      "version": "21.0.0",
+      "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
+      "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
+      "dev": true
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.57.1.tgz",
+      "integrity": "sha512-N/RrBwEUKMIYxSKl0oDK5sFVHd6VI7p9K5MyUlVYAY6dyNb/wHUqndkTd3XhpGlXgnQsBkRZuu4f9kAHghvgPw==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "5.57.1",
+        "@typescript-eslint/visitor-keys": "5.57.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.57.1.tgz",
+      "integrity": "sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.1.tgz",
+      "integrity": "sha512-A2MZqD8gNT0qHKbk2wRspg7cHbCDCk2tcqt6ScCFLr5Ru8cn+TCfM786DjPhqwseiS+PrYwcXht5ztpEQ6TFTw==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "5.57.1",
+        "@typescript-eslint/visitor-keys": "5.57.1",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
           "optional": true
-        },
-        "minimatch": {
-          "version": "3.0.4",
-          "bundled": true,
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+      "version": "7.3.8",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+      "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.57.1.tgz",
+      "integrity": "sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@types/json-schema": "^7.0.9",
+        "@types/semver": "^7.3.12",
+        "@typescript-eslint/scope-manager": "5.57.1",
+        "@typescript-eslint/types": "5.57.1",
+        "@typescript-eslint/typescript-estree": "5.57.1",
+        "eslint-scope": "^5.1.1",
+        "semver": "^7.3.7"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/utils/node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/@typescript-eslint/utils/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@typescript-eslint/utils/node_modules/semver": {
+      "version": "7.3.8",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+      "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@typescript-eslint/utils/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.1.tgz",
+      "integrity": "sha512-RjQrAniDU0CEk5r7iphkm731zKlFiUjvcBS2yHAg8WWqFMCaCrD0rKEVOMUyMMcbGPZ0bPp56srkGWrgfZqLRA==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "5.57.1",
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.8.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+      "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.21.3"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
+    "node_modules/aria-query": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+      "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+      "dev": true,
+      "dependencies": {
+        "deep-equal": "^2.0.5"
+      }
+    },
+    "node_modules/array-buffer-byte-length": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
+      "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "is-array-buffer": "^3.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array-includes": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
+      "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "get-intrinsic": "^1.1.3",
+        "is-string": "^1.0.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/array.prototype.flat": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
+      "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.flatmap": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz",
+      "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.tosorted": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz",
+      "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0",
+        "get-intrinsic": "^1.1.3"
+      }
+    },
+    "node_modules/ast-types-flow": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+      "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
+      "dev": true
+    },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/axe-core": {
+      "version": "4.6.3",
+      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz",
+      "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/axobject-query": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
+      "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==",
+      "dev": true,
+      "dependencies": {
+        "deep-equal": "^2.0.5"
+      }
+    },
+    "node_modules/babel-jest": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz",
+      "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==",
+      "dev": true,
+      "dependencies": {
+        "@jest/transform": "^29.5.0",
+        "@types/babel__core": "^7.1.14",
+        "babel-plugin-istanbul": "^6.1.1",
+        "babel-preset-jest": "^29.5.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.8.0"
+      }
+    },
+    "node_modules/babel-jest/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/babel-jest/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/babel-jest/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/babel-jest/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/babel-jest/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/babel-jest/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/babel-jest/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/babel-plugin-istanbul": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+      "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@istanbuljs/load-nyc-config": "^1.0.0",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-instrument": "^5.0.4",
+        "test-exclude": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/babel-plugin-jest-hoist": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz",
+      "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.3.3",
+        "@babel/types": "^7.3.3",
+        "@types/babel__core": "^7.1.14",
+        "@types/babel__traverse": "^7.0.6"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/babel-plugin-polyfill-corejs2": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
+      "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/compat-data": "^7.17.7",
+        "@babel/helper-define-polyfill-provider": "^0.3.3",
+        "semver": "^6.1.1"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/babel-plugin-polyfill-corejs3": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz",
+      "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-define-polyfill-provider": "^0.3.3",
+        "core-js-compat": "^3.25.1"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/babel-plugin-polyfill-regenerator": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz",
+      "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-define-polyfill-provider": "^0.3.3"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/babel-preset-current-node-syntax": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
+      "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-bigint": "^7.8.3",
+        "@babel/plugin-syntax-class-properties": "^7.8.3",
+        "@babel/plugin-syntax-import-meta": "^7.8.3",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-top-level-await": "^7.8.3"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/babel-preset-jest": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz",
+      "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==",
+      "dev": true,
+      "dependencies": {
+        "babel-plugin-jest-hoist": "^29.5.0",
+        "babel-preset-current-node-syntax": "^1.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true,
+      "optional": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.21.5",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
+      "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        }
+      ],
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001449",
+        "electron-to-chromium": "^1.4.284",
+        "node-releases": "^2.0.8",
+        "update-browserslist-db": "^1.0.10"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/bser": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+      "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+      "dev": true,
+      "dependencies": {
+        "node-int64": "^0.4.0"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001476",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001476.tgz",
+      "integrity": "sha512-JmpktFppVSvyUN4gsLS0bShY2L9ZUslHLE72vgemBkS43JD2fOvKTKs+GtRwuxrtRGnwJFW0ye7kWRRlLJS9vQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ]
+    },
+    "node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/char-regex": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+      "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "optional": true,
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/ci-info": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+      "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cjs-module-lexer": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
+      "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+      "dev": true
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+      "dev": true,
+      "engines": {
+        "iojs": ">= 1.0.0",
+        "node": ">= 0.12.0"
+      }
+    },
+    "node_modules/collect-v8-coverage": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
+      "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
+      "dev": true
+    },
+    "node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "dev": true
+    },
+    "node_modules/commander": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
+    },
+    "node_modules/confusing-browser-globals": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+      "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+      "dev": true
+    },
+    "node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true
+    },
+    "node_modules/core-js-compat": {
+      "version": "3.30.0",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.0.tgz",
+      "integrity": "sha512-P5A2h/9mRYZFIAP+5Ab8ns6083IyVpSclU74UNvbGVQ8VM7n3n3/g2yF3AkKQ9NXz2O+ioxLbEWKnDtgsFamhg==",
+      "dev": true,
+      "dependencies": {
+        "browserslist": "^4.21.5"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/damerau-levenshtein": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+      "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+      "dev": true
+    },
+    "node_modules/debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/dedent": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+      "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
+      "dev": true
+    },
+    "node_modules/deep-equal": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz",
+      "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "es-get-iterator": "^1.1.2",
+        "get-intrinsic": "^1.1.3",
+        "is-arguments": "^1.1.1",
+        "is-array-buffer": "^3.0.1",
+        "is-date-object": "^1.0.5",
+        "is-regex": "^1.1.4",
+        "is-shared-array-buffer": "^1.0.2",
+        "isarray": "^2.0.5",
+        "object-is": "^1.1.5",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.4",
+        "regexp.prototype.flags": "^1.4.3",
+        "side-channel": "^1.0.4",
+        "which-boxed-primitive": "^1.0.2",
+        "which-collection": "^1.0.1",
+        "which-typed-array": "^1.1.9"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
+    },
+    "node_modules/deepmerge": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/define-properties": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
+      "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
+      "dev": true,
+      "dependencies": {
+        "has-property-descriptors": "^1.0.0",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/detect-newline": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/diff-sequences": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
+      "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
+      "dev": true,
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "dependencies": {
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.4.356",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.356.tgz",
+      "integrity": "sha512-nEftV1dRX3omlxAj42FwqRZT0i4xd2dIg39sog/CnCJeCcL1TRd2Uh0i9Oebgv8Ou0vzTPw++xc+Z20jzS2B6A==",
+      "dev": true
+    },
+    "node_modules/emittery": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
+      "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/es-abstract": {
+      "version": "1.21.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
+      "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==",
+      "dev": true,
+      "dependencies": {
+        "array-buffer-byte-length": "^1.0.0",
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "es-set-tostringtag": "^2.0.1",
+        "es-to-primitive": "^1.2.1",
+        "function.prototype.name": "^1.1.5",
+        "get-intrinsic": "^1.2.0",
+        "get-symbol-description": "^1.0.0",
+        "globalthis": "^1.0.3",
+        "gopd": "^1.0.1",
+        "has": "^1.0.3",
+        "has-property-descriptors": "^1.0.0",
+        "has-proto": "^1.0.1",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.5",
+        "is-array-buffer": "^3.0.2",
+        "is-callable": "^1.2.7",
+        "is-negative-zero": "^2.0.2",
+        "is-regex": "^1.1.4",
+        "is-shared-array-buffer": "^1.0.2",
+        "is-string": "^1.0.7",
+        "is-typed-array": "^1.1.10",
+        "is-weakref": "^1.0.2",
+        "object-inspect": "^1.12.3",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.4",
+        "regexp.prototype.flags": "^1.4.3",
+        "safe-regex-test": "^1.0.0",
+        "string.prototype.trim": "^1.2.7",
+        "string.prototype.trimend": "^1.0.6",
+        "string.prototype.trimstart": "^1.0.6",
+        "typed-array-length": "^1.0.4",
+        "unbox-primitive": "^1.0.2",
+        "which-typed-array": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/es-get-iterator": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+      "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.3",
+        "has-symbols": "^1.0.3",
+        "is-arguments": "^1.1.1",
+        "is-map": "^2.0.2",
+        "is-set": "^2.0.2",
+        "is-string": "^1.0.7",
+        "isarray": "^2.0.5",
+        "stop-iteration-iterator": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+      "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.1.3",
+        "has": "^1.0.3",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-shim-unscopables": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+      "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+      "dev": true,
+      "dependencies": {
+        "has": "^1.0.3"
+      }
+    },
+    "node_modules/es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "dev": true,
+      "dependencies": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "8.33.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
+      "integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
+      "dev": true,
+      "dependencies": {
+        "@eslint/eslintrc": "^1.4.1",
+        "@humanwhocodes/config-array": "^0.11.8",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.1.1",
+        "eslint-utils": "^3.0.0",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.4.0",
+        "esquery": "^1.4.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "grapheme-splitter": "^1.0.4",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-sdsl": "^4.1.4",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "regexpp": "^3.2.0",
+        "strip-ansi": "^6.0.1",
+        "strip-json-comments": "^3.1.0",
+        "text-table": "^0.2.0"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-config-airbnb": {
+      "version": "19.0.4",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
+      "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
+      "dev": true,
+      "dependencies": {
+        "eslint-config-airbnb-base": "^15.0.0",
+        "object.assign": "^4.1.2",
+        "object.entries": "^1.1.5"
+      },
+      "engines": {
+        "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^7.32.0 || ^8.2.0",
+        "eslint-plugin-import": "^2.25.3",
+        "eslint-plugin-jsx-a11y": "^6.5.1",
+        "eslint-plugin-react": "^7.28.0",
+        "eslint-plugin-react-hooks": "^4.3.0"
+      }
+    },
+    "node_modules/eslint-config-airbnb-base": {
+      "version": "15.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+      "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+      "dev": true,
+      "dependencies": {
+        "confusing-browser-globals": "^1.0.10",
+        "object.assign": "^4.1.2",
+        "object.entries": "^1.1.5",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^7.32.0 || ^8.2.0",
+        "eslint-plugin-import": "^2.25.2"
+      }
+    },
+    "node_modules/eslint-import-resolver-node": {
+      "version": "0.3.7",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz",
+      "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^3.2.7",
+        "is-core-module": "^2.11.0",
+        "resolve": "^1.22.1"
+      }
+    },
+    "node_modules/eslint-import-resolver-node/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/eslint-module-utils": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+      "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^3.2.7"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-module-utils/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/eslint-plugin-import": {
+      "version": "2.27.5",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
+      "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
+      "dev": true,
+      "dependencies": {
+        "array-includes": "^3.1.6",
+        "array.prototype.flat": "^1.3.1",
+        "array.prototype.flatmap": "^1.3.1",
+        "debug": "^3.2.7",
+        "doctrine": "^2.1.0",
+        "eslint-import-resolver-node": "^0.3.7",
+        "eslint-module-utils": "^2.7.4",
+        "has": "^1.0.3",
+        "is-core-module": "^2.11.0",
+        "is-glob": "^4.0.3",
+        "minimatch": "^3.1.2",
+        "object.values": "^1.1.6",
+        "resolve": "^1.22.1",
+        "semver": "^6.3.0",
+        "tsconfig-paths": "^3.14.1"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependencies": {
+        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/eslint-plugin-import/node_modules/doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/eslint-plugin-jest": {
+      "version": "27.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.1.tgz",
+      "integrity": "sha512-l067Uxx7ZT8cO9NJuf+eJHvt6bqJyz2Z29wykyEdz/OtmcELQl2MQGQLX8J94O1cSJWAwUSEvCjwjA7KEK3Hmg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/utils": "^5.10.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/eslint-plugin": "^5.0.0",
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@typescript-eslint/eslint-plugin": {
+          "optional": true
+        },
+        "jest": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-plugin-jsx-a11y": {
+      "version": "6.7.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz",
+      "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/runtime": "^7.20.7",
+        "aria-query": "^5.1.3",
+        "array-includes": "^3.1.6",
+        "array.prototype.flatmap": "^1.3.1",
+        "ast-types-flow": "^0.0.7",
+        "axe-core": "^4.6.2",
+        "axobject-query": "^3.1.1",
+        "damerau-levenshtein": "^1.0.8",
+        "emoji-regex": "^9.2.2",
+        "has": "^1.0.3",
+        "jsx-ast-utils": "^3.3.3",
+        "language-tags": "=1.0.5",
+        "minimatch": "^3.1.2",
+        "object.entries": "^1.1.6",
+        "object.fromentries": "^2.0.6",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependencies": {
+        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+      }
+    },
+    "node_modules/eslint-plugin-react": {
+      "version": "7.32.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz",
+      "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "array-includes": "^3.1.6",
+        "array.prototype.flatmap": "^1.3.1",
+        "array.prototype.tosorted": "^1.1.1",
+        "doctrine": "^2.1.0",
+        "estraverse": "^5.3.0",
+        "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+        "minimatch": "^3.1.2",
+        "object.entries": "^1.1.6",
+        "object.fromentries": "^2.0.6",
+        "object.hasown": "^1.1.2",
+        "object.values": "^1.1.6",
+        "prop-types": "^15.8.1",
+        "resolve": "^2.0.0-next.4",
+        "semver": "^6.3.0",
+        "string.prototype.matchall": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=4"
+      },
+      "peerDependencies": {
+        "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+      }
+    },
+    "node_modules/eslint-plugin-react-hooks": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
+      "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+      }
+    },
+    "node_modules/eslint-plugin-react/node_modules/doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/eslint-plugin-react/node_modules/resolve": {
+      "version": "2.0.0-next.4",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
+      "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "is-core-module": "^2.9.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+      "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+      "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint-utils": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+      "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^2.0.0"
+      },
+      "engines": {
+        "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      },
+      "peerDependencies": {
+        "eslint": ">=5"
+      }
+    },
+    "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
+      "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/eslint/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/eslint/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/eslint/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/eslint/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint/node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/eslint/node_modules/globals": {
+      "version": "13.20.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+      "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/eslint/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/eslint/node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/espree": {
+      "version": "9.5.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
+      "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.8.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true,
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/expect": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz",
+      "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==",
+      "dev": true,
+      "dependencies": {
+        "@jest/expect-utils": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "node_modules/fast-glob": {
+      "version": "3.2.12",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+      "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true
+    },
+    "node_modules/fastq": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+      "dev": true,
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fb-watchman": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+      "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+      "dev": true,
+      "dependencies": {
+        "bser": "2.1.1"
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "dependencies": {
+        "flat-cache": "^3.0.4"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
+      "dependencies": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+      "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+      "dev": true
+    },
+    "node_modules/for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+      "dev": true,
+      "dependencies": {
+        "is-callable": "^1.1.3"
+      }
+    },
+    "node_modules/fs-readdir-recursive": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+      "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+      "dev": true
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "node_modules/function.prototype.name": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+      "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.0",
+        "functions-have-names": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/functions-have-names": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
+      "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-package-type": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/get-symbol-description": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/globalthis": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+      "dev": true,
+      "dependencies": {
+        "define-properties": "^1.1.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "dependencies": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globby/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.1.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "node_modules/grapheme-splitter": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+      "dev": true
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-bigints": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/has-property-descriptors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+      "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.1.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+      "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "dev": true,
+      "dependencies": {
+        "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true
+    },
+    "node_modules/human-signals": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10.17.0"
+      }
+    },
+    "node_modules/husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true,
+      "bin": {
+        "husky": "lib/bin.js"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/typicode"
+      }
+    },
+    "node_modules/ignore": {
+      "version": "5.2.4",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+      "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/import-local": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+      "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+      "dev": true,
+      "dependencies": {
+        "pkg-dir": "^4.2.0",
+        "resolve-cwd": "^3.0.0"
+      },
+      "bin": {
+        "import-local-fixture": "fixtures/cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "node_modules/internal-slot": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+      "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.2.0",
+        "has": "^1.0.3",
+        "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-array-buffer": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
+      "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.2.0",
+        "is-typed-array": "^1.1.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true
+    },
+    "node_modules/is-bigint": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+      "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+      "dev": true,
+      "dependencies": {
+        "has-bigints": "^1.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-boolean-object": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+      "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+      "dev": true,
+      "dependencies": {
+        "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-date-object": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-generator-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+      "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
+      "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-negative-zero": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-number-object": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+      "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-regex": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-set": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
+      "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-shared-array-buffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+      "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-string": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+      "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-symbol": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+      "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+      "dev": true,
+      "dependencies": {
+        "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-typed-array": {
+      "version": "1.1.10",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
+      "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
+      "dev": true,
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-weakmap": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
+      "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-weakref": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+      "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-weakset": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
+      "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "dev": true
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true
+    },
+    "node_modules/istanbul-lib-coverage": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-instrument": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+      "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.12.3",
+        "@babel/parser": "^7.14.7",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+      "dev": true,
+      "dependencies": {
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^3.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-report/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-report/node_modules/make-dir": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+      "dev": true,
+      "dependencies": {
+        "semver": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/istanbul-lib-report/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-source-maps": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^3.0.0",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-reports": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+      "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+      "dev": true,
+      "dependencies": {
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest": {
+      "version": "29.4.1",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.1.tgz",
+      "integrity": "sha512-cknimw7gAXPDOmj0QqztlxVtBVCw2lYY9CeIE5N6kD+kET1H4H79HSNISJmijb1HF+qk+G+ploJgiDi5k/fRlg==",
+      "dev": true,
+      "dependencies": {
+        "@jest/core": "^29.4.1",
+        "@jest/types": "^29.4.1",
+        "import-local": "^3.0.2",
+        "jest-cli": "^29.4.1"
+      },
+      "bin": {
+        "jest": "bin/jest.js"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-changed-files": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz",
+      "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==",
+      "dev": true,
+      "dependencies": {
+        "execa": "^5.0.0",
+        "p-limit": "^3.1.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-circus": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz",
+      "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/expect": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "co": "^4.6.0",
+        "dedent": "^0.7.0",
+        "is-generator-fn": "^2.0.0",
+        "jest-each": "^29.5.0",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "p-limit": "^3.1.0",
+        "pretty-format": "^29.5.0",
+        "pure-rand": "^6.0.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-circus/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-circus/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-circus/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-circus/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-circus/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-circus/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-circus/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-cli": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz",
+      "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/core": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "chalk": "^4.0.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.9",
+        "import-local": "^3.0.2",
+        "jest-config": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "prompts": "^2.0.1",
+        "yargs": "^17.3.1"
+      },
+      "bin": {
+        "jest": "bin/jest.js"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-cli/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-cli/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-cli/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-cli/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-cli/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-cli/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-config": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz",
+      "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.11.6",
+        "@jest/test-sequencer": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "babel-jest": "^29.5.0",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "deepmerge": "^4.2.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "jest-circus": "^29.5.0",
+        "jest-environment-node": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-runner": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "parse-json": "^5.2.0",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@types/node": "*",
+        "ts-node": ">=9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "ts-node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-config/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-config/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-config/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-config/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-config/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-config/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-config/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-diff": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
+      "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "diff-sequences": "^29.4.3",
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-diff/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-diff/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-diff/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-diff/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-diff/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-diff/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-docblock": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz",
+      "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==",
+      "dev": true,
+      "dependencies": {
+        "detect-newline": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-each": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz",
+      "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "chalk": "^4.0.0",
+        "jest-get-type": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-each/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-each/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-each/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-each/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-each/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-each/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-environment-node": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz",
+      "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-mock": "^29.5.0",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-get-type": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
+      "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
+      "dev": true,
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-haste-map": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz",
+      "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/graceful-fs": "^4.1.3",
+        "@types/node": "*",
+        "anymatch": "^3.0.3",
+        "fb-watchman": "^2.0.0",
+        "graceful-fs": "^4.2.9",
+        "jest-regex-util": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "walker": "^1.0.8"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "^2.3.2"
+      }
+    },
+    "node_modules/jest-leak-detector": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz",
+      "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==",
+      "dev": true,
+      "dependencies": {
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-matcher-utils": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz",
+      "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "jest-diff": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-matcher-utils/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-matcher-utils/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-matcher-utils/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-matcher-utils/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-matcher-utils/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-matcher-utils/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-message-util": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz",
+      "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.12.13",
+        "@jest/types": "^29.5.0",
+        "@types/stack-utils": "^2.0.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "micromatch": "^4.0.4",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-message-util/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-message-util/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-message-util/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-message-util/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-message-util/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-message-util/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-message-util/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-mock": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz",
+      "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-util": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-pnp-resolver": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+      "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      },
+      "peerDependencies": {
+        "jest-resolve": "*"
+      },
+      "peerDependenciesMeta": {
+        "jest-resolve": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-regex-util": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz",
+      "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==",
+      "dev": true,
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-resolve": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz",
+      "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-pnp-resolver": "^1.2.2",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "resolve": "^1.20.0",
+        "resolve.exports": "^2.0.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-resolve-dependencies": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz",
+      "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==",
+      "dev": true,
+      "dependencies": {
+        "jest-regex-util": "^29.4.3",
+        "jest-snapshot": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-resolve/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-resolve/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-resolve/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-resolve/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-resolve/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-resolve/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-resolve/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-runner": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz",
+      "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/console": "^29.5.0",
+        "@jest/environment": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "emittery": "^0.13.1",
+        "graceful-fs": "^4.2.9",
+        "jest-docblock": "^29.4.3",
+        "jest-environment-node": "^29.5.0",
+        "jest-haste-map": "^29.5.0",
+        "jest-leak-detector": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-resolve": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-watcher": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "p-limit": "^3.1.0",
+        "source-map-support": "0.5.13"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-runner/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-runner/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-runner/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-runner/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-runner/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-runner/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-runtime": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz",
+      "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.5.0",
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/globals": "^29.5.0",
+        "@jest/source-map": "^29.4.3",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "cjs-module-lexer": "^1.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-mock": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-bom": "^4.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-runtime/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-runtime/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-runtime/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-runtime/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-runtime/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-runtime/node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-runtime/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-snapshot": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz",
+      "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.11.6",
+        "@babel/generator": "^7.7.2",
+        "@babel/plugin-syntax-jsx": "^7.7.2",
+        "@babel/plugin-syntax-typescript": "^7.7.2",
+        "@babel/traverse": "^7.7.2",
+        "@babel/types": "^7.3.3",
+        "@jest/expect-utils": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/babel__traverse": "^7.0.6",
+        "@types/prettier": "^2.1.5",
+        "babel-preset-current-node-syntax": "^1.0.0",
+        "chalk": "^4.0.0",
+        "expect": "^29.5.0",
+        "graceful-fs": "^4.2.9",
+        "jest-diff": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "natural-compare": "^1.4.0",
+        "pretty-format": "^29.5.0",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-snapshot/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/semver": {
+      "version": "7.3.8",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+      "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-snapshot/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "node_modules/jest-util": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz",
+      "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "graceful-fs": "^4.2.9",
+        "picomatch": "^2.2.3"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-util/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-util/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-util/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-util/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-util/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-util/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-validate": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz",
+      "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.5.0",
+        "camelcase": "^6.2.0",
+        "chalk": "^4.0.0",
+        "jest-get-type": "^29.4.3",
+        "leven": "^3.1.0",
+        "pretty-format": "^29.5.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-validate/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-validate/node_modules/camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/jest-validate/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-validate/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-validate/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-validate/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-validate/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-watcher": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz",
+      "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==",
+      "dev": true,
+      "dependencies": {
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.0.0",
+        "emittery": "^0.13.1",
+        "jest-util": "^29.5.0",
+        "string-length": "^4.0.1"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-watcher/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/jest-watcher/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/jest-watcher/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/jest-watcher/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/jest-watcher/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-watcher/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-worker": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz",
+      "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "jest-util": "^29.5.0",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/jest-worker/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-worker/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/js-sdsl": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
+      "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==",
+      "dev": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/js-sdsl"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+      "dev": true,
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsx-ast-utils": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz",
+      "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==",
+      "dev": true,
+      "dependencies": {
+        "array-includes": "^3.1.5",
+        "object.assign": "^4.1.3"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/kleur": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+      "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/language-subtag-registry": {
+      "version": "0.3.22",
+      "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+      "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+      "dev": true
+    },
+    "node_modules/language-tags": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
+      "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
+      "dev": true,
+      "dependencies": {
+        "language-subtag-registry": "~0.3.2"
+      }
+    },
+    "node_modules/leven": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+      "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+      "dev": true
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "dev": true,
+      "dependencies": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/make-dir/node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/makeerror": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+      "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+      "dev": true,
+      "dependencies": {
+        "tmpl": "1.0.5"
+      }
+    },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dev": true,
+      "dependencies": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true
+    },
+    "node_modules/node-int64": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+      "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+      "dev": true
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
+      "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+      "dev": true
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.12.3",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+      "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object-is": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+      "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.assign": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+      "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "has-symbols": "^1.0.3",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.entries": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz",
+      "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.fromentries": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
+      "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.hasown": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz",
+      "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.values": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
+      "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+      "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+      "dev": true,
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "dev": true
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pirates": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
+      "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/pkg-dir": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+      "dev": true,
+      "dependencies": {
+        "find-up": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/pkg-dir/node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pngjs": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
+      "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.19.0"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/pretty-format": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+      "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
+      "dev": true,
+      "dependencies": {
+        "@jest/schemas": "^29.4.3",
+        "ansi-styles": "^5.0.0",
+        "react-is": "^18.0.0"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/pretty-format/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/prompts": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+      "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+      "dev": true,
+      "dependencies": {
+        "kleur": "^3.0.3",
+        "sisteransi": "^1.0.5"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/prop-types": {
+      "version": "15.8.1",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "loose-envify": "^1.4.0",
+        "object-assign": "^4.1.1",
+        "react-is": "^16.13.1"
+      }
+    },
+    "node_modules/prop-types/node_modules/react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/punycode": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+      "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pure-rand": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz",
+      "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/dubzzz"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fast-check"
+        }
+      ]
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/react-is": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+      "dev": true
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/regenerate": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+      "dev": true
+    },
+    "node_modules/regenerate-unicode-properties": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz",
+      "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==",
+      "dev": true,
+      "dependencies": {
+        "regenerate": "^1.4.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+      "dev": true
+    },
+    "node_modules/regenerator-transform": {
+      "version": "0.15.1",
+      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz",
+      "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/runtime": "^7.8.4"
+      }
+    },
+    "node_modules/regexp.prototype.flags": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "functions-have-names": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/regexpp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      }
+    },
+    "node_modules/regexpu-core": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz",
+      "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/regjsgen": "^0.8.0",
+        "regenerate": "^1.4.2",
+        "regenerate-unicode-properties": "^10.1.0",
+        "regjsparser": "^0.9.1",
+        "unicode-match-property-ecmascript": "^2.0.0",
+        "unicode-match-property-value-ecmascript": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/regjsparser": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
+      "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
+      "dev": true,
+      "dependencies": {
+        "jsesc": "~0.5.0"
+      },
+      "bin": {
+        "regjsparser": "bin/parser"
+      }
+    },
+    "node_modules/regjsparser/node_modules/jsesc": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+      "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
+      "dev": true,
+      "bin": {
+        "jsesc": "bin/jsesc"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.2",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
+      "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
+      "dev": true,
+      "dependencies": {
+        "is-core-module": "^2.11.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-cwd": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+      "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+      "dev": true,
+      "dependencies": {
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/resolve-cwd/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/resolve.exports": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
+      "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true,
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/safe-regex-test": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+      "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.3",
+        "is-regex": "^1.1.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true
+    },
+    "node_modules/sisteransi": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+      "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+      "dev": true
+    },
+    "node_modules/slash": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+      "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.13",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+      "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+      "dev": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+      "dev": true
+    },
+    "node_modules/stack-utils": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+      "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+      "dev": true,
+      "dependencies": {
+        "escape-string-regexp": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/stack-utils/node_modules/escape-string-regexp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+      "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/stop-iteration-iterator": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
+      "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
+      "dev": true,
+      "dependencies": {
+        "internal-slot": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/string-length": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+      "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+      "dev": true,
+      "dependencies": {
+        "char-regex": "^1.0.2",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "node_modules/string.prototype.matchall": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
+      "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "get-intrinsic": "^1.1.3",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.3",
+        "regexp.prototype.flags": "^1.4.3",
+        "side-channel": "^1.0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/string.prototype.trim": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
+      "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/string.prototype.trimend": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
+      "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/string.prototype.trimstart": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
+      "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-bom": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+      "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/test-exclude": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+      "dev": true,
+      "dependencies": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^7.1.4",
+        "minimatch": "^3.0.4"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true
+    },
+    "node_modules/tmpl": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+      "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+      "dev": true
+    },
+    "node_modules/to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/tsconfig-paths": {
+      "version": "3.14.2",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
+      "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
+      "dev": true,
+      "dependencies": {
+        "@types/json5": "^0.0.29",
+        "json5": "^1.0.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "node_modules/tsconfig-paths/node_modules/json5": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+      "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+      "dev": true,
+      "dependencies": {
+        "minimist": "^1.2.0"
+      },
+      "bin": {
+        "json5": "lib/cli.js"
+      }
+    },
+    "node_modules/tsconfig-paths/node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "dev": true
+    },
+    "node_modules/tsutils": {
+      "version": "3.21.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+      "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^1.8.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      },
+      "peerDependencies": {
+        "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/typed-array-length": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+      "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "is-typed-array": "^1.1.9"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
+      "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
+      "dev": true,
+      "peer": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=12.20"
+      }
+    },
+    "node_modules/unbox-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+      "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-bigints": "^1.0.2",
+        "has-symbols": "^1.0.3",
+        "which-boxed-primitive": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/unicode-canonical-property-names-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/unicode-match-property-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+      "dev": true,
+      "dependencies": {
+        "unicode-canonical-property-names-ecmascript": "^2.0.0",
+        "unicode-property-aliases-ecmascript": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/unicode-match-property-value-ecmascript": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
+      "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/unicode-property-aliases-ecmascript": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
+      "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+      "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.1.1",
+        "picocolors": "^1.0.0"
+      },
+      "bin": {
+        "browserslist-lint": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/v8-to-istanbul": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+      "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.12",
+        "@types/istanbul-lib-coverage": "^2.0.1",
+        "convert-source-map": "^1.6.0"
+      },
+      "engines": {
+        "node": ">=10.12.0"
+      }
+    },
+    "node_modules/walker": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+      "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+      "dev": true,
+      "dependencies": {
+        "makeerror": "1.0.12"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/which-boxed-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+      "dev": true,
+      "dependencies": {
+        "is-bigint": "^1.0.1",
+        "is-boolean-object": "^1.1.0",
+        "is-number-object": "^1.0.4",
+        "is-string": "^1.0.5",
+        "is-symbol": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/which-collection": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
+      "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
+      "dev": true,
+      "dependencies": {
+        "is-map": "^2.0.1",
+        "is-set": "^2.0.1",
+        "is-weakmap": "^2.0.1",
+        "is-weakset": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/which-typed-array": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
+      "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
+      "dev": true,
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0",
+        "is-typed-array": "^1.1.10"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
+    },
+    "node_modules/write-file-atomic": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+      "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+      "dev": true,
+      "dependencies": {
+        "imurmurhash": "^0.1.4",
+        "signal-exit": "^3.0.7"
+      },
+      "engines": {
+        "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true
+    },
+    "node_modules/yargs": {
+      "version": "17.7.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+      "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    }
+  },
+  "dependencies": {
+    "@ampproject/remapping": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+      "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/gen-mapping": "^0.3.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      }
+    },
+    "@babel/cli": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.20.7.tgz",
+      "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/trace-mapping": "^0.3.8",
+        "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
+        "chokidar": "^3.4.0",
+        "commander": "^4.0.1",
+        "convert-source-map": "^1.1.0",
+        "fs-readdir-recursive": "^1.1.0",
+        "glob": "^7.2.0",
+        "make-dir": "^2.1.0",
+        "slash": "^2.0.0"
+      }
+    },
+    "@babel/code-frame": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
+      "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.18.6"
+      }
+    },
+    "@babel/compat-data": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz",
+      "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==",
+      "dev": true
+    },
+    "@babel/core": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
+      "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
+      "dev": true,
+      "requires": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.21.4",
+        "@babel/generator": "^7.21.4",
+        "@babel/helper-compilation-targets": "^7.21.4",
+        "@babel/helper-module-transforms": "^7.21.2",
+        "@babel/helpers": "^7.21.0",
+        "@babel/parser": "^7.21.4",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.4",
+        "@babel/types": "^7.21.4",
+        "convert-source-map": "^1.7.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.2",
+        "semver": "^6.3.0"
+      }
+    },
+    "@babel/generator": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
+      "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.21.4",
+        "@jridgewell/gen-mapping": "^0.3.2",
+        "@jridgewell/trace-mapping": "^0.3.17",
+        "jsesc": "^2.5.1"
+      }
+    },
+    "@babel/helper-annotate-as-pure": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
+      "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.18.6"
+      }
+    },
+    "@babel/helper-builder-binary-assignment-operator-visitor": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz",
+      "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-explode-assignable-expression": "^7.18.6",
+        "@babel/types": "^7.18.9"
+      }
+    },
+    "@babel/helper-compilation-targets": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz",
+      "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==",
+      "dev": true,
+      "requires": {
+        "@babel/compat-data": "^7.21.4",
+        "@babel/helper-validator-option": "^7.21.0",
+        "browserslist": "^4.21.3",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.0"
+      }
+    },
+    "@babel/helper-create-class-features-plugin": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz",
+      "integrity": "sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-function-name": "^7.21.0",
+        "@babel/helper-member-expression-to-functions": "^7.21.0",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.20.7",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+        "@babel/helper-split-export-declaration": "^7.18.6"
+      }
+    },
+    "@babel/helper-create-regexp-features-plugin": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz",
+      "integrity": "sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "regexpu-core": "^5.3.1"
+      }
+    },
+    "@babel/helper-define-polyfill-provider": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz",
+      "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-compilation-targets": "^7.17.7",
+        "@babel/helper-plugin-utils": "^7.16.7",
+        "debug": "^4.1.1",
+        "lodash.debounce": "^4.0.8",
+        "resolve": "^1.14.2",
+        "semver": "^6.1.2"
+      }
+    },
+    "@babel/helper-environment-visitor": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+      "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+      "dev": true
+    },
+    "@babel/helper-explode-assignable-expression": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz",
+      "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.18.6"
+      }
+    },
+    "@babel/helper-function-name": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
+      "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+      "dev": true,
+      "requires": {
+        "@babel/template": "^7.20.7",
+        "@babel/types": "^7.21.0"
+      }
+    },
+    "@babel/helper-hoist-variables": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+      "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.18.6"
+      }
+    },
+    "@babel/helper-member-expression-to-functions": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz",
+      "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.21.0"
+      }
+    },
+    "@babel/helper-module-imports": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
+      "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.21.4"
+      }
+    },
+    "@babel/helper-module-transforms": {
+      "version": "7.21.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
+      "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-simple-access": "^7.20.2",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/helper-validator-identifier": "^7.19.1",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.2",
+        "@babel/types": "^7.21.2"
+      }
+    },
+    "@babel/helper-optimise-call-expression": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
+      "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.18.6"
+      }
+    },
+    "@babel/helper-plugin-utils": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
+      "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+      "dev": true
+    },
+    "@babel/helper-remap-async-to-generator": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz",
+      "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-wrap-function": "^7.18.9",
+        "@babel/types": "^7.18.9"
+      }
+    },
+    "@babel/helper-replace-supers": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz",
+      "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-member-expression-to-functions": "^7.20.7",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.20.7",
+        "@babel/types": "^7.20.7"
+      }
+    },
+    "@babel/helper-simple-access": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+      "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.20.2"
+      }
+    },
+    "@babel/helper-skip-transparent-expression-wrappers": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz",
+      "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.20.0"
+      }
+    },
+    "@babel/helper-split-export-declaration": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+      "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.18.6"
+      }
+    },
+    "@babel/helper-string-parser": {
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+      "dev": true
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.19.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+      "dev": true
+    },
+    "@babel/helper-validator-option": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
+      "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
+      "dev": true
+    },
+    "@babel/helper-wrap-function": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz",
+      "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-function-name": "^7.19.0",
+        "@babel/template": "^7.18.10",
+        "@babel/traverse": "^7.20.5",
+        "@babel/types": "^7.20.5"
+      }
+    },
+    "@babel/helpers": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
+      "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
+      "dev": true,
+      "requires": {
+        "@babel/template": "^7.20.7",
+        "@babel/traverse": "^7.21.0",
+        "@babel/types": "^7.21.0"
+      }
+    },
+    "@babel/highlight": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+      "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.18.6",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "@babel/parser": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+      "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
+      "dev": true
+    },
+    "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz",
+      "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz",
+      "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+        "@babel/plugin-proposal-optional-chaining": "^7.20.7"
+      }
+    },
+    "@babel/plugin-proposal-async-generator-functions": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz",
+      "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-remap-async-to-generator": "^7.18.9",
+        "@babel/plugin-syntax-async-generators": "^7.8.4"
+      }
+    },
+    "@babel/plugin-proposal-class-properties": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
+      "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-proposal-class-static-block": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz",
+      "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-class-features-plugin": "^7.21.0",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-class-static-block": "^7.14.5"
+      }
+    },
+    "@babel/plugin-proposal-dynamic-import": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz",
+      "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-dynamic-import": "^7.8.3"
+      }
+    },
+    "@babel/plugin-proposal-export-namespace-from": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz",
+      "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.9",
+        "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+      }
+    },
+    "@babel/plugin-proposal-json-strings": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz",
+      "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-json-strings": "^7.8.3"
+      }
+    },
+    "@babel/plugin-proposal-logical-assignment-operators": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz",
+      "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+      }
+    },
+    "@babel/plugin-proposal-nullish-coalescing-operator": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
+      "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
+      }
+    },
+    "@babel/plugin-proposal-numeric-separator": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz",
+      "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+      }
+    },
+    "@babel/plugin-proposal-object-rest-spread": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
+      "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
+      "dev": true,
+      "requires": {
+        "@babel/compat-data": "^7.20.5",
+        "@babel/helper-compilation-targets": "^7.20.7",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-transform-parameters": "^7.20.7"
+      }
+    },
+    "@babel/plugin-proposal-optional-catch-binding": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz",
+      "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
+      }
+    },
+    "@babel/plugin-proposal-optional-chaining": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
+      "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+      }
+    },
+    "@babel/plugin-proposal-private-methods": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
+      "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-proposal-private-property-in-object": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz",
+      "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-create-class-features-plugin": "^7.21.0",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+      }
+    },
+    "@babel/plugin-proposal-unicode-property-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
+      "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-syntax-async-generators": {
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+      "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-bigint": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+      "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-class-properties": {
+      "version": "7.12.13",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+      "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.12.13"
+      }
+    },
+    "@babel/plugin-syntax-class-static-block": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+      "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      }
+    },
+    "@babel/plugin-syntax-dynamic-import": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+      "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-export-namespace-from": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+      "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.3"
+      }
+    },
+    "@babel/plugin-syntax-import-assertions": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz",
+      "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.19.0"
+      }
+    },
+    "@babel/plugin-syntax-import-meta": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+      "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-syntax-json-strings": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+      "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-jsx": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz",
+      "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-syntax-logical-assignment-operators": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+      "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-syntax-nullish-coalescing-operator": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+      "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-numeric-separator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+      "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-syntax-object-rest-spread": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+      "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-optional-catch-binding": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+      "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-optional-chaining": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+      "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-private-property-in-object": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+      "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      }
+    },
+    "@babel/plugin-syntax-top-level-await": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+      "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      }
+    },
+    "@babel/plugin-syntax-typescript": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz",
+      "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-arrow-functions": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz",
+      "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-async-to-generator": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz",
+      "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-remap-async-to-generator": "^7.18.9"
+      }
+    },
+    "@babel/plugin-transform-block-scoped-functions": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz",
+      "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-block-scoping": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz",
+      "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-classes": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz",
+      "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-compilation-targets": "^7.20.7",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-function-name": "^7.21.0",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-replace-supers": "^7.20.7",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "globals": "^11.1.0"
+      }
+    },
+    "@babel/plugin-transform-computed-properties": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz",
+      "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/template": "^7.20.7"
+      }
+    },
+    "@babel/plugin-transform-destructuring": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz",
+      "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-dotall-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz",
+      "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-duplicate-keys": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz",
+      "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      }
+    },
+    "@babel/plugin-transform-exponentiation-operator": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz",
+      "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-for-of": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz",
+      "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-function-name": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz",
+      "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-compilation-targets": "^7.18.9",
+        "@babel/helper-function-name": "^7.18.9",
+        "@babel/helper-plugin-utils": "^7.18.9"
+      }
+    },
+    "@babel/plugin-transform-literals": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz",
+      "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      }
+    },
+    "@babel/plugin-transform-member-expression-literals": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz",
+      "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-modules-amd": {
+      "version": "7.20.11",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz",
+      "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-transforms": "^7.20.11",
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-modules-commonjs": {
+      "version": "7.21.2",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz",
+      "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-transforms": "^7.21.2",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-simple-access": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-modules-systemjs": {
+      "version": "7.20.11",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz",
+      "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-hoist-variables": "^7.18.6",
+        "@babel/helper-module-transforms": "^7.20.11",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-validator-identifier": "^7.19.1"
+      }
+    },
+    "@babel/plugin-transform-modules-umd": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz",
+      "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-transforms": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-named-capturing-groups-regex": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz",
+      "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.20.5",
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-new-target": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz",
+      "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-object-super": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz",
+      "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-parameters": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz",
+      "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2"
+      }
+    },
+    "@babel/plugin-transform-property-literals": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz",
+      "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-regenerator": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz",
+      "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "regenerator-transform": "^0.15.1"
+      }
+    },
+    "@babel/plugin-transform-reserved-words": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz",
+      "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-shorthand-properties": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz",
+      "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-spread": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz",
+      "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0"
+      }
+    },
+    "@babel/plugin-transform-sticky-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz",
+      "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-transform-template-literals": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz",
+      "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      }
+    },
+    "@babel/plugin-transform-typeof-symbol": {
+      "version": "7.18.9",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz",
+      "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      }
+    },
+    "@babel/plugin-transform-unicode-escapes": {
+      "version": "7.18.10",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz",
+      "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.9"
+      }
+    },
+    "@babel/plugin-transform-unicode-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz",
+      "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/preset-env": {
+      "version": "7.20.2",
+      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz",
+      "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==",
+      "dev": true,
+      "requires": {
+        "@babel/compat-data": "^7.20.1",
+        "@babel/helper-compilation-targets": "^7.20.0",
+        "@babel/helper-plugin-utils": "^7.20.2",
+        "@babel/helper-validator-option": "^7.18.6",
+        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
+        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9",
+        "@babel/plugin-proposal-async-generator-functions": "^7.20.1",
+        "@babel/plugin-proposal-class-properties": "^7.18.6",
+        "@babel/plugin-proposal-class-static-block": "^7.18.6",
+        "@babel/plugin-proposal-dynamic-import": "^7.18.6",
+        "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
+        "@babel/plugin-proposal-json-strings": "^7.18.6",
+        "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9",
+        "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
+        "@babel/plugin-proposal-numeric-separator": "^7.18.6",
+        "@babel/plugin-proposal-object-rest-spread": "^7.20.2",
+        "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
+        "@babel/plugin-proposal-optional-chaining": "^7.18.9",
+        "@babel/plugin-proposal-private-methods": "^7.18.6",
+        "@babel/plugin-proposal-private-property-in-object": "^7.18.6",
+        "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-class-properties": "^7.12.13",
+        "@babel/plugin-syntax-class-static-block": "^7.14.5",
+        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+        "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+        "@babel/plugin-syntax-import-assertions": "^7.20.0",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+        "@babel/plugin-syntax-top-level-await": "^7.14.5",
+        "@babel/plugin-transform-arrow-functions": "^7.18.6",
+        "@babel/plugin-transform-async-to-generator": "^7.18.6",
+        "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
+        "@babel/plugin-transform-block-scoping": "^7.20.2",
+        "@babel/plugin-transform-classes": "^7.20.2",
+        "@babel/plugin-transform-computed-properties": "^7.18.9",
+        "@babel/plugin-transform-destructuring": "^7.20.2",
+        "@babel/plugin-transform-dotall-regex": "^7.18.6",
+        "@babel/plugin-transform-duplicate-keys": "^7.18.9",
+        "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
+        "@babel/plugin-transform-for-of": "^7.18.8",
+        "@babel/plugin-transform-function-name": "^7.18.9",
+        "@babel/plugin-transform-literals": "^7.18.9",
+        "@babel/plugin-transform-member-expression-literals": "^7.18.6",
+        "@babel/plugin-transform-modules-amd": "^7.19.6",
+        "@babel/plugin-transform-modules-commonjs": "^7.19.6",
+        "@babel/plugin-transform-modules-systemjs": "^7.19.6",
+        "@babel/plugin-transform-modules-umd": "^7.18.6",
+        "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1",
+        "@babel/plugin-transform-new-target": "^7.18.6",
+        "@babel/plugin-transform-object-super": "^7.18.6",
+        "@babel/plugin-transform-parameters": "^7.20.1",
+        "@babel/plugin-transform-property-literals": "^7.18.6",
+        "@babel/plugin-transform-regenerator": "^7.18.6",
+        "@babel/plugin-transform-reserved-words": "^7.18.6",
+        "@babel/plugin-transform-shorthand-properties": "^7.18.6",
+        "@babel/plugin-transform-spread": "^7.19.0",
+        "@babel/plugin-transform-sticky-regex": "^7.18.6",
+        "@babel/plugin-transform-template-literals": "^7.18.9",
+        "@babel/plugin-transform-typeof-symbol": "^7.18.9",
+        "@babel/plugin-transform-unicode-escapes": "^7.18.10",
+        "@babel/plugin-transform-unicode-regex": "^7.18.6",
+        "@babel/preset-modules": "^0.1.5",
+        "@babel/types": "^7.20.2",
+        "babel-plugin-polyfill-corejs2": "^0.3.3",
+        "babel-plugin-polyfill-corejs3": "^0.6.0",
+        "babel-plugin-polyfill-regenerator": "^0.4.1",
+        "core-js-compat": "^3.25.1",
+        "semver": "^6.3.0"
+      }
+    },
+    "@babel/preset-modules": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz",
+      "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+        "@babel/plugin-transform-dotall-regex": "^7.4.4",
+        "@babel/types": "^7.4.4",
+        "esutils": "^2.0.2"
+      }
+    },
+    "@babel/regjsgen": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
+      "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==",
+      "dev": true
+    },
+    "@babel/runtime": {
+      "version": "7.21.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+      "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
+      "dev": true,
+      "requires": {
+        "regenerator-runtime": "^0.13.11"
+      }
+    },
+    "@babel/template": {
+      "version": "7.20.7",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
+      "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.18.6",
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7"
+      }
+    },
+    "@babel/traverse": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+      "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.21.4",
+        "@babel/generator": "^7.21.4",
+        "@babel/helper-environment-visitor": "^7.18.9",
+        "@babel/helper-function-name": "^7.21.0",
+        "@babel/helper-hoist-variables": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/parser": "^7.21.4",
+        "@babel/types": "^7.21.4",
+        "debug": "^4.1.0",
+        "globals": "^11.1.0"
+      }
+    },
+    "@babel/types": {
+      "version": "7.21.4",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz",
+      "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-string-parser": "^7.19.4",
+        "@babel/helper-validator-identifier": "^7.19.1",
+        "to-fast-properties": "^2.0.0"
+      }
+    },
+    "@bcoe/v8-coverage": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+      "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+      "dev": true
+    },
+    "@eslint-community/eslint-utils": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^3.3.0"
+      }
+    },
+    "@eslint/eslintrc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
+      "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.4.0",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "dependencies": {
+        "globals": {
+          "version": "13.20.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+          "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
           "dev": true,
           "requires": {
-            "brace-expansion": "^1.1.7"
+            "type-fest": "^0.20.2"
           }
         },
-        "minimist": {
-          "version": "0.0.8",
-          "bundled": true,
+        "type-fest": {
+          "version": "0.20.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
           "dev": true
-        },
-        "minipass": {
-          "version": "2.2.4",
-          "bundled": true,
-          "dev": true,
-          "requires": {
-            "safe-buffer": "^5.1.1",
-            "yallist": "^3.0.0"
-          }
-        },
-        "minizlib": {
-          "version": "1.1.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minipass": "^2.2.1"
-          }
-        },
-        "mkdirp": {
-          "version": "0.5.1",
-          "bundled": true,
+        }
+      }
+    },
+    "@humanwhocodes/config-array": {
+      "version": "0.11.8",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+      "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+      "dev": true,
+      "requires": {
+        "@humanwhocodes/object-schema": "^1.2.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      }
+    },
+    "@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true
+    },
+    "@humanwhocodes/object-schema": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+      "dev": true
+    },
+    "@istanbuljs/load-nyc-config": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.3.1",
+        "find-up": "^4.1.0",
+        "get-package-type": "^0.1.0",
+        "js-yaml": "^3.13.1",
+        "resolve-from": "^5.0.0"
+      },
+      "dependencies": {
+        "argparse": {
+          "version": "1.0.10",
+          "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+          "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
           "dev": true,
           "requires": {
-            "minimist": "0.0.8"
+            "sprintf-js": "~1.0.2"
           }
         },
-        "ms": {
-          "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "needle": {
-          "version": "2.2.0",
-          "bundled": true,
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "debug": "^2.1.2",
-            "iconv-lite": "^0.4.4",
-            "sax": "^1.2.4"
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
           }
         },
-        "node-pre-gyp": {
-          "version": "0.9.1",
-          "bundled": true,
+        "js-yaml": {
+          "version": "3.14.1",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+          "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "detect-libc": "^1.0.2",
-            "mkdirp": "^0.5.1",
-            "needle": "^2.2.0",
-            "nopt": "^4.0.1",
-            "npm-packlist": "^1.1.6",
-            "npmlog": "^4.0.2",
-            "rc": "^1.1.7",
-            "rimraf": "^2.6.1",
-            "semver": "^5.3.0",
-            "tar": "^4"
+            "argparse": "^1.0.7",
+            "esprima": "^4.0.0"
           }
         },
-        "nopt": {
-          "version": "4.0.1",
-          "bundled": true,
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "abbrev": "1",
-            "osenv": "^0.1.4"
+            "p-locate": "^4.1.0"
           }
         },
-        "npm-bundled": {
-          "version": "1.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "npm-packlist": {
-          "version": "1.1.10",
-          "bundled": true,
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "ignore-walk": "^3.0.1",
-            "npm-bundled": "^1.0.1"
+            "p-try": "^2.0.0"
           }
         },
-        "npmlog": {
-          "version": "4.1.2",
-          "bundled": true,
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "are-we-there-yet": "~1.1.2",
-            "console-control-strings": "~1.1.0",
-            "gauge": "~2.7.3",
-            "set-blocking": "~2.0.0"
+            "p-limit": "^2.2.0"
           }
         },
-        "number-is-nan": {
-          "version": "1.0.1",
-          "bundled": true,
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
           "dev": true
-        },
-        "object-assign": {
-          "version": "4.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "once": {
-          "version": "1.4.0",
-          "bundled": true,
-          "dev": true,
-          "requires": {
-            "wrappy": "1"
-          }
-        },
-        "os-homedir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "os-tmpdir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "osenv": {
-          "version": "0.1.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "os-homedir": "^1.0.0",
-            "os-tmpdir": "^1.0.0"
-          }
-        },
-        "path-is-absolute": {
-          "version": "1.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "rc": {
-          "version": "1.2.6",
-          "bundled": true,
+        }
+      }
+    },
+    "@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+      "dev": true
+    },
+    "@jest/console": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz",
+      "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "slash": "^3.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "deep-extend": "~0.4.0",
-            "ini": "~1.3.0",
-            "minimist": "^1.2.0",
-            "strip-json-comments": "~2.0.1"
-          },
-          "dependencies": {
-            "minimist": {
-              "version": "1.2.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            }
+            "color-convert": "^2.0.1"
           }
         },
-        "readable-stream": {
-          "version": "2.3.6",
-          "bundled": true,
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
           }
         },
-        "rimraf": {
-          "version": "2.6.2",
-          "bundled": true,
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "glob": "^7.0.5"
+            "color-name": "~1.1.4"
           }
         },
-        "safe-buffer": {
-          "version": "5.1.1",
-          "bundled": true,
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
           "dev": true
         },
-        "safer-buffer": {
-          "version": "2.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "sax": {
-          "version": "1.2.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "semver": {
-          "version": "5.5.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "set-blocking": {
-          "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
         },
-        "signal-exit": {
-          "version": "3.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
         },
-        "string-width": {
-          "version": "1.0.2",
-          "bundled": true,
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "code-point-at": "^1.0.0",
-            "is-fullwidth-code-point": "^1.0.0",
-            "strip-ansi": "^3.0.0"
+            "has-flag": "^4.0.0"
           }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "bundled": true,
+        }
+      }
+    },
+    "@jest/core": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz",
+      "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^29.5.0",
+        "@jest/reporters": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.9",
+        "jest-changed-files": "^29.5.0",
+        "jest-config": "^29.5.0",
+        "jest-haste-map": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-resolve-dependencies": "^29.5.0",
+        "jest-runner": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "jest-watcher": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "safe-buffer": "~5.1.0"
+            "color-convert": "^2.0.1"
           }
         },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "bundled": true,
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
           "dev": true,
           "requires": {
-            "ansi-regex": "^2.0.0"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
           }
         },
-        "strip-json-comments": {
+        "color-convert": {
           "version": "2.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "tar": {
-          "version": "4.4.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "chownr": "^1.0.1",
-            "fs-minipass": "^1.2.5",
-            "minipass": "^2.2.4",
-            "minizlib": "^1.1.0",
-            "mkdirp": "^0.5.0",
-            "safe-buffer": "^5.1.1",
-            "yallist": "^3.0.2"
-          }
-        },
-        "util-deprecate": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "wide-align": {
-          "version": "1.1.2",
-          "bundled": true,
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "string-width": "^1.0.2"
+            "color-name": "~1.1.4"
           }
         },
-        "wrappy": {
-          "version": "1.0.2",
-          "bundled": true,
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
           "dev": true
         },
-        "yallist": {
-          "version": "3.0.2",
-          "bundled": true,
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
-        }
-      }
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "functional-red-black-tree": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
-      "dev": true
-    },
-    "get-caller-file": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
-      "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
-      "dev": true
-    },
-    "get-stream": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
-      "dev": true
-    },
-    "get-value": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
-      "dev": true
-    },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "glob": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
-      "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
-      "dev": true,
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "glob-base": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
-      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "glob-parent": "^2.0.0",
-        "is-glob": "^2.0.0"
-      }
-    },
-    "glob-parent": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
-      "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
-      "dev": true,
-      "requires": {
-        "is-glob": "^2.0.0"
-      }
-    },
-    "globals": {
-      "version": "9.18.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
-      "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
-      "dev": true
-    },
-    "globby": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
-      "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
-      "dev": true,
-      "requires": {
-        "array-union": "^1.0.1",
-        "arrify": "^1.0.0",
-        "glob": "^7.0.3",
-        "object-assign": "^4.0.1",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
-      }
-    },
-    "graceful-fs": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
-      "dev": true
-    },
-    "growly": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
-      "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
-      "dev": true
-    },
-    "handlebars": {
-      "version": "4.0.11",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz",
-      "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
-      "dev": true,
-      "requires": {
-        "async": "^1.4.0",
-        "optimist": "^0.6.1",
-        "source-map": "^0.4.4",
-        "uglify-js": "^2.6"
-      },
-      "dependencies": {
-        "async": {
-          "version": "1.5.2",
-          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
-          "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
           "dev": true
         },
-        "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "amdefine": ">=0.0.4"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "har-schema": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-      "dev": true
-    },
-    "har-validator": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
-      "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+    "@jest/environment": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz",
+      "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==",
       "dev": true,
       "requires": {
-        "ajv": "^5.1.0",
-        "har-schema": "^2.0.0"
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-mock": "^29.5.0"
       }
     },
-    "has": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
-      "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
+    "@jest/expect": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz",
+      "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==",
       "dev": true,
       "requires": {
-        "function-bind": "^1.0.2"
+        "expect": "^29.5.0",
+        "jest-snapshot": "^29.5.0"
       }
     },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+    "@jest/expect-utils": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz",
+      "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==",
       "dev": true,
       "requires": {
-        "ansi-regex": "^2.0.0"
+        "jest-get-type": "^29.4.3"
       }
     },
-    "has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true
-    },
-    "has-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
-      "dev": true
+    "@jest/fake-timers": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz",
+      "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^29.5.0",
+        "@sinonjs/fake-timers": "^10.0.2",
+        "@types/node": "*",
+        "jest-message-util": "^29.5.0",
+        "jest-mock": "^29.5.0",
+        "jest-util": "^29.5.0"
+      }
     },
-    "has-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+    "@jest/globals": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz",
+      "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==",
       "dev": true,
       "requires": {
-        "get-value": "^2.0.6",
-        "has-values": "^1.0.0",
-        "isobject": "^3.0.0"
-      },
-      "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
+        "@jest/environment": "^29.5.0",
+        "@jest/expect": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "jest-mock": "^29.5.0"
       }
     },
-    "has-values": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+    "@jest/reporters": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz",
+      "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==",
       "dev": true,
       "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@jest/console": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "exit": "^0.1.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-instrument": "^5.1.0",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^4.0.0",
+        "istanbul-reports": "^3.1.3",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "slash": "^3.0.0",
+        "string-length": "^4.0.1",
+        "strip-ansi": "^6.0.0",
+        "v8-to-istanbul": "^9.0.1"
       },
       "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
+            "color-name": "~1.1.4"
           }
         },
-        "kind-of": {
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "is-buffer": "^1.1.5"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "home-or-tmp": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
-      "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
+    "@jest/schemas": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
+      "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
       "dev": true,
       "requires": {
-        "os-homedir": "^1.0.0",
-        "os-tmpdir": "^1.0.1"
+        "@sinclair/typebox": "^0.25.16"
       }
     },
-    "hosted-git-info": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
-      "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==",
-      "dev": true
-    },
-    "html-encoding-sniffer": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
-      "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+    "@jest/source-map": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz",
+      "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==",
       "dev": true,
       "requires": {
-        "whatwg-encoding": "^1.0.1"
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "callsites": "^3.0.0",
+        "graceful-fs": "^4.2.9"
       }
     },
-    "http-signature": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+    "@jest/test-result": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz",
+      "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
+        "@jest/console": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "collect-v8-coverage": "^1.0.0"
       }
     },
-    "iconv-lite": {
-      "version": "0.4.23",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+    "@jest/test-sequencer": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz",
+      "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==",
       "dev": true,
       "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
+        "@jest/test-result": "^29.5.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "slash": "^3.0.0"
+      },
+      "dependencies": {
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        }
       }
     },
-    "ignore": {
-      "version": "3.3.10",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
-      "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
-      "dev": true
-    },
-    "import-local": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz",
-      "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==",
-      "dev": true,
-      "requires": {
-        "pkg-dir": "^2.0.0",
-        "resolve-cwd": "^2.0.0"
+    "@jest/transform": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz",
+      "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.11.6",
+        "@jest/types": "^29.5.0",
+        "@jridgewell/trace-mapping": "^0.3.15",
+        "babel-plugin-istanbul": "^6.1.1",
+        "chalk": "^4.0.0",
+        "convert-source-map": "^2.0.0",
+        "fast-json-stable-stringify": "^2.1.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "pirates": "^4.0.4",
+        "slash": "^3.0.0",
+        "write-file-atomic": "^4.0.2"
       },
       "dependencies": {
-        "find-up": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "locate-path": "^2.0.0"
+            "color-name": "~1.1.4"
           }
         },
-        "pkg-dir": {
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "convert-source-map": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
-          "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+          "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+          "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "find-up": "^2.1.0"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
-      "dev": true
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-      "dev": true
-    },
-    "inquirer": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
-      "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
+    "@jest/types": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz",
+      "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==",
       "dev": true,
       "requires": {
-        "ansi-escapes": "^3.0.0",
-        "chalk": "^2.0.0",
-        "cli-cursor": "^2.1.0",
-        "cli-width": "^2.0.0",
-        "external-editor": "^2.0.4",
-        "figures": "^2.0.0",
-        "lodash": "^4.3.0",
-        "mute-stream": "0.0.7",
-        "run-async": "^2.2.0",
-        "rx-lite": "^4.0.8",
-        "rx-lite-aggregates": "^4.0.8",
-        "string-width": "^2.1.0",
-        "strip-ansi": "^4.0.0",
-        "through": "^2.3.6"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
+        "@jest/schemas": "^29.4.3",
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "@types/istanbul-reports": "^3.0.0",
+        "@types/node": "*",
+        "@types/yargs": "^17.0.8",
+        "chalk": "^4.0.0"
+      },
+      "dependencies": {
         "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "color-convert": "^1.9.0"
+            "color-convert": "^2.0.1"
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
           }
         },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "ansi-regex": "^3.0.0"
+            "color-name": "~1.1.4"
           }
         },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
         "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "has-flag": "^3.0.0"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "invariant": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+    "@jridgewell/gen-mapping": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
       "dev": true,
       "requires": {
-        "loose-envify": "^1.0.0"
+        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
       }
     },
-    "invert-kv": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
-      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
+    "@jridgewell/resolve-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+      "dev": true
+    },
+    "@jridgewell/set-array": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+      "dev": true
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.4.15",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
       "dev": true
     },
-    "is-accessor-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+    "@jridgewell/trace-mapping": {
+      "version": "0.3.18",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+      "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "@jridgewell/resolve-uri": "3.1.0",
+        "@jridgewell/sourcemap-codec": "1.4.14"
+      },
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": {
+          "version": "1.4.14",
+          "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+          "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+          "dev": true
+        }
       }
     },
-    "is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
+    "@nicolo-ribaudo/chokidar-2": {
+      "version": "2.1.8-no-fsevents.3",
+      "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
+      "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
+      "dev": true,
+      "optional": true
     },
-    "is-binary-path": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+    "@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "binary-extensions": "^1.0.0"
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
       }
     },
-    "is-buffer": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+    "@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
       "dev": true
     },
-    "is-builtin-module": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
-      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+    "@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
       "dev": true,
       "requires": {
-        "builtin-modules": "^1.0.0"
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
       }
     },
-    "is-callable": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
-      "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
+    "@sinclair/typebox": {
+      "version": "0.25.24",
+      "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
+      "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==",
       "dev": true
     },
-    "is-ci": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz",
-      "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==",
+    "@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
       "dev": true,
       "requires": {
-        "ci-info": "^1.0.0"
+        "type-detect": "4.0.8"
       }
     },
-    "is-data-descriptor": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+    "@sinonjs/fake-timers": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
+      "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "@sinonjs/commons": "^2.0.0"
       }
     },
-    "is-date-object": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
-      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
-      "dev": true
-    },
-    "is-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+    "@types/babel__core": {
+      "version": "7.20.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
+      "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==",
       "dev": true,
       "requires": {
-        "is-accessor-descriptor": "^0.1.6",
-        "is-data-descriptor": "^0.1.4",
-        "kind-of": "^5.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7",
+        "@types/babel__generator": "*",
+        "@types/babel__template": "*",
+        "@types/babel__traverse": "*"
       }
     },
-    "is-dotfile": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
-      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
-      "dev": true,
-      "optional": true
-    },
-    "is-equal-shallow": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
-      "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
+    "@types/babel__generator": {
+      "version": "7.6.4",
+      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
+      "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "is-primitive": "^2.0.0"
+        "@babel/types": "^7.0.0"
       }
     },
-    "is-extendable": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
-      "dev": true
-    },
-    "is-extglob": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-      "dev": true
-    },
-    "is-finite": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
-      "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+    "@types/babel__template": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
+      "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
       "dev": true,
       "requires": {
-        "number-is-nan": "^1.0.0"
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0"
       }
     },
-    "is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-      "dev": true
-    },
-    "is-generator-fn": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz",
-      "integrity": "sha1-lp1J4bszKfa7fwkIm+JleLLd1Go=",
-      "dev": true
-    },
-    "is-glob": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+    "@types/babel__traverse": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz",
+      "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==",
       "dev": true,
       "requires": {
-        "is-extglob": "^1.0.0"
+        "@babel/types": "^7.3.0"
       }
     },
-    "is-number": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
-      "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+    "@types/graceful-fs": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
+      "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "@types/node": "*"
       }
     },
-    "is-path-cwd": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
-      "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+    "@types/istanbul-lib-coverage": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+      "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
       "dev": true
     },
-    "is-path-in-cwd": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
-      "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+    "@types/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
       "dev": true,
       "requires": {
-        "is-path-inside": "^1.0.0"
+        "@types/istanbul-lib-coverage": "*"
       }
     },
-    "is-path-inside": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
-      "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+    "@types/istanbul-reports": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+      "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
       "dev": true,
       "requires": {
-        "path-is-inside": "^1.0.1"
+        "@types/istanbul-lib-report": "*"
       }
     },
-    "is-plain-object": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
-      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+    "@types/jest": {
+      "version": "29.4.0",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz",
+      "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==",
       "dev": true,
       "requires": {
-        "isobject": "^3.0.1"
-      },
-      "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        }
+        "expect": "^29.0.0",
+        "pretty-format": "^29.0.0"
       }
     },
-    "is-posix-bracket": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
-      "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
-      "dev": true,
-      "optional": true
+    "@types/json-schema": {
+      "version": "7.0.11",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+      "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+      "dev": true
     },
-    "is-primitive": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
-      "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
-      "dev": true,
-      "optional": true
+    "@types/json5": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+      "dev": true
     },
-    "is-promise": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
-      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+    "@types/node": {
+      "version": "18.15.11",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
+      "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
       "dev": true
     },
-    "is-regex": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
-      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+    "@types/prettier": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz",
+      "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==",
+      "dev": true
+    },
+    "@types/semver": {
+      "version": "7.3.13",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+      "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+      "dev": true
+    },
+    "@types/stack-utils": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+      "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+      "dev": true
+    },
+    "@types/yargs": {
+      "version": "17.0.24",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
+      "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
       "dev": true,
       "requires": {
-        "has": "^1.0.1"
+        "@types/yargs-parser": "*"
       }
     },
-    "is-resolvable": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
-      "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+    "@types/yargs-parser": {
+      "version": "21.0.0",
+      "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
+      "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
       "dev": true
     },
-    "is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
-      "dev": true
+    "@typescript-eslint/scope-manager": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.57.1.tgz",
+      "integrity": "sha512-N/RrBwEUKMIYxSKl0oDK5sFVHd6VI7p9K5MyUlVYAY6dyNb/wHUqndkTd3XhpGlXgnQsBkRZuu4f9kAHghvgPw==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "5.57.1",
+        "@typescript-eslint/visitor-keys": "5.57.1"
+      }
     },
-    "is-symbol": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
-      "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
+    "@typescript-eslint/types": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.57.1.tgz",
+      "integrity": "sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA==",
       "dev": true
     },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
-      "dev": true
+    "@typescript-eslint/typescript-estree": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.1.tgz",
+      "integrity": "sha512-A2MZqD8gNT0qHKbk2wRspg7cHbCDCk2tcqt6ScCFLr5Ru8cn+TCfM786DjPhqwseiS+PrYwcXht5ztpEQ6TFTw==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "5.57.1",
+        "@typescript-eslint/visitor-keys": "5.57.1",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "semver": {
+          "version": "7.3.8",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+          "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
     },
-    "is-utf8": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
+    "@typescript-eslint/utils": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.57.1.tgz",
+      "integrity": "sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@types/json-schema": "^7.0.9",
+        "@types/semver": "^7.3.12",
+        "@typescript-eslint/scope-manager": "5.57.1",
+        "@typescript-eslint/types": "5.57.1",
+        "@typescript-eslint/typescript-estree": "5.57.1",
+        "eslint-scope": "^5.1.1",
+        "semver": "^7.3.7"
+      },
+      "dependencies": {
+        "eslint-scope": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+          "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.3.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "estraverse": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+          "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+          "dev": true
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "semver": {
+          "version": "7.3.8",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+          "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "@typescript-eslint/visitor-keys": {
+      "version": "5.57.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.1.tgz",
+      "integrity": "sha512-RjQrAniDU0CEk5r7iphkm731zKlFiUjvcBS2yHAg8WWqFMCaCrD0rKEVOMUyMMcbGPZ0bPp56srkGWrgfZqLRA==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "5.57.1",
+        "eslint-visitor-keys": "^3.3.0"
+      }
     },
-    "is-windows": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+    "acorn": {
+      "version": "8.8.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+      "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
       "dev": true
     },
-    "isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "dev": true
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "requires": {}
     },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
     },
-    "isobject": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-      "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+    "ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "isarray": "1.0.0"
+        "type-fest": "^0.21.3"
       }
     },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+    "ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
       "dev": true
     },
-    "istanbul-api": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.1.tgz",
-      "integrity": "sha512-duj6AlLcsWNwUpfyfHt0nWIeRiZpuShnP40YTxOGQgtaN8fd6JYSxsvxUphTDy8V5MfDXo4s/xVCIIvVCO808g==",
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
       "dev": true,
       "requires": {
-        "async": "^2.1.4",
-        "compare-versions": "^3.1.0",
-        "fileset": "^2.0.2",
-        "istanbul-lib-coverage": "^1.2.0",
-        "istanbul-lib-hook": "^1.2.0",
-        "istanbul-lib-instrument": "^1.10.1",
-        "istanbul-lib-report": "^1.1.4",
-        "istanbul-lib-source-maps": "^1.2.4",
-        "istanbul-reports": "^1.3.0",
-        "js-yaml": "^3.7.0",
-        "mkdirp": "^0.5.1",
-        "once": "^1.4.0"
+        "color-convert": "^1.9.0"
       }
     },
-    "istanbul-lib-coverage": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz",
-      "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==",
+    "anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
       "dev": true
     },
-    "istanbul-lib-hook": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz",
-      "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==",
+    "aria-query": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+      "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
       "dev": true,
       "requires": {
-        "append-transform": "^1.0.0"
+        "deep-equal": "^2.0.5"
       }
     },
-    "istanbul-lib-instrument": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz",
-      "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==",
+    "array-buffer-byte-length": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
+      "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
       "dev": true,
       "requires": {
-        "babel-generator": "^6.18.0",
-        "babel-template": "^6.16.0",
-        "babel-traverse": "^6.18.0",
-        "babel-types": "^6.18.0",
-        "babylon": "^6.18.0",
-        "istanbul-lib-coverage": "^1.2.0",
-        "semver": "^5.3.0"
+        "call-bind": "^1.0.2",
+        "is-array-buffer": "^3.0.1"
       }
     },
-    "istanbul-lib-report": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.4.tgz",
-      "integrity": "sha512-Azqvq5tT0U09nrncK3q82e/Zjkxa4tkFZv7E6VcqP0QCPn6oNljDPfrZEC/umNXds2t7b8sRJfs6Kmpzt8m2kA==",
+    "array-includes": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
+      "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
       "dev": true,
       "requires": {
-        "istanbul-lib-coverage": "^1.2.0",
-        "mkdirp": "^0.5.1",
-        "path-parse": "^1.0.5",
-        "supports-color": "^3.1.2"
-      },
-      "dependencies": {
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "get-intrinsic": "^1.1.3",
+        "is-string": "^1.0.7"
       }
     },
-    "istanbul-lib-source-maps": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz",
-      "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==",
+    "array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true
+    },
+    "array.prototype.flat": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
+      "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
       "dev": true,
       "requires": {
-        "debug": "^3.1.0",
-        "istanbul-lib-coverage": "^1.2.0",
-        "mkdirp": "^0.5.1",
-        "rimraf": "^2.6.1",
-        "source-map": "^0.5.3"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
       }
     },
-    "istanbul-reports": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.3.0.tgz",
-      "integrity": "sha512-y2Z2IMqE1gefWUaVjrBm0mSKvUkaBy9Vqz8iwr/r40Y9hBbIteH5wqHG/9DLTfJ9xUnUT2j7A3+VVJ6EaYBllA==",
+    "array.prototype.flatmap": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz",
+      "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==",
       "dev": true,
       "requires": {
-        "handlebars": "^4.0.3"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
       }
     },
-    "jest": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest/-/jest-23.3.0.tgz",
-      "integrity": "sha1-E1XNeS84zyD7pNoC3dt8oU2UhLU=",
+    "array.prototype.tosorted": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz",
+      "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==",
+      "dev": true,
+      "peer": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0",
+        "get-intrinsic": "^1.1.3"
+      }
+    },
+    "ast-types-flow": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+      "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
+      "dev": true
+    },
+    "available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "dev": true
+    },
+    "axe-core": {
+      "version": "4.6.3",
+      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz",
+      "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==",
+      "dev": true
+    },
+    "axobject-query": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
+      "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==",
       "dev": true,
       "requires": {
-        "import-local": "^1.0.0",
-        "jest-cli": "^23.3.0"
+        "deep-equal": "^2.0.5"
+      }
+    },
+    "babel-jest": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz",
+      "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==",
+      "dev": true,
+      "requires": {
+        "@jest/transform": "^29.5.0",
+        "@types/babel__core": "^7.1.14",
+        "babel-plugin-istanbul": "^6.1.1",
+        "babel-preset-jest": "^29.5.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "slash": "^3.0.0"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
         "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
+            "color-convert": "^2.0.1"
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
           }
         },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "debug": "^2.3.3",
-            "define-property": "^0.2.5",
-            "extend-shallow": "^2.0.1",
-            "posix-character-classes": "^0.1.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^0.1.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "^0.1.6",
-                "is-data-descriptor": "^0.1.4",
-                "kind-of": "^5.0.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
+            "color-name": "~1.1.4"
           }
         },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "^0.3.2",
-            "define-property": "^1.0.0",
-            "expand-brackets": "^2.1.4",
-            "extend-shallow": "^2.0.1",
-            "fragment-cache": "^0.2.1",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^1.0.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
         },
-        "fill-range": {
+        "has-flag": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
-        "jest-cli": {
-          "version": "23.3.0",
-          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-23.3.0.tgz",
-          "integrity": "sha1-MH6b53M0Q7eJqCedaUBU0FGp5eI=",
-          "dev": true,
-          "requires": {
-            "ansi-escapes": "^3.0.0",
-            "chalk": "^2.0.1",
-            "exit": "^0.1.2",
-            "glob": "^7.1.2",
-            "graceful-fs": "^4.1.11",
-            "import-local": "^1.0.0",
-            "is-ci": "^1.0.10",
-            "istanbul-api": "^1.3.1",
-            "istanbul-lib-coverage": "^1.2.0",
-            "istanbul-lib-instrument": "^1.10.1",
-            "istanbul-lib-source-maps": "^1.2.4",
-            "jest-changed-files": "^23.2.0",
-            "jest-config": "^23.3.0",
-            "jest-environment-jsdom": "^23.3.0",
-            "jest-get-type": "^22.1.0",
-            "jest-haste-map": "^23.2.0",
-            "jest-message-util": "^23.3.0",
-            "jest-regex-util": "^23.3.0",
-            "jest-resolve-dependencies": "^23.3.0",
-            "jest-runner": "^23.3.0",
-            "jest-runtime": "^23.3.0",
-            "jest-snapshot": "^23.3.0",
-            "jest-util": "^23.3.0",
-            "jest-validate": "^23.3.0",
-            "jest-watcher": "^23.2.0",
-            "jest-worker": "^23.2.0",
-            "micromatch": "^3.1.10",
-            "node-notifier": "^5.2.1",
-            "prompts": "^0.1.9",
-            "realpath-native": "^1.0.0",
-            "rimraf": "^2.5.4",
-            "slash": "^1.0.0",
-            "string-length": "^2.0.0",
-            "strip-ansi": "^4.0.0",
-            "which": "^1.2.12",
-            "yargs": "^11.0.0"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
           "dev": true
         },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-          "dev": true,
-          "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        },
         "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "has-flag": "^3.0.0"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "jest-changed-files": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-23.2.0.tgz",
-      "integrity": "sha1-oUWm5LZtASn8fJnO4TTck3pkPZw=",
+    "babel-plugin-istanbul": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+      "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
       "dev": true,
       "requires": {
-        "throat": "^4.0.0"
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@istanbuljs/load-nyc-config": "^1.0.0",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-instrument": "^5.0.4",
+        "test-exclude": "^6.0.0"
       }
     },
-    "jest-config": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-23.3.0.tgz",
-      "integrity": "sha1-u01Ttw+VAPr933GNImq7U7E7gyM=",
-      "dev": true,
-      "requires": {
-        "babel-core": "^6.0.0",
-        "babel-jest": "^23.2.0",
-        "chalk": "^2.0.1",
-        "glob": "^7.1.1",
-        "jest-environment-jsdom": "^23.3.0",
-        "jest-environment-node": "^23.3.0",
-        "jest-get-type": "^22.1.0",
-        "jest-jasmine2": "^23.3.0",
-        "jest-regex-util": "^23.3.0",
-        "jest-resolve": "^23.2.0",
-        "jest-util": "^23.3.0",
-        "jest-validate": "^23.3.0",
-        "pretty-format": "^23.2.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+    "babel-plugin-jest-hoist": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz",
+      "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==",
+      "dev": true,
+      "requires": {
+        "@babel/template": "^7.3.3",
+        "@babel/types": "^7.3.3",
+        "@types/babel__core": "^7.1.14",
+        "@types/babel__traverse": "^7.0.6"
       }
     },
-    "jest-diff": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-23.2.0.tgz",
-      "integrity": "sha1-nyz0tR4Sx5FVAgCrwWtHEwrxBio=",
+    "babel-plugin-polyfill-corejs2": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
+      "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==",
       "dev": true,
       "requires": {
-        "chalk": "^2.0.1",
-        "diff": "^3.2.0",
-        "jest-get-type": "^22.1.0",
-        "pretty-format": "^23.2.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "@babel/compat-data": "^7.17.7",
+        "@babel/helper-define-polyfill-provider": "^0.3.3",
+        "semver": "^6.1.1"
+      }
+    },
+    "babel-plugin-polyfill-corejs3": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz",
+      "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-define-polyfill-provider": "^0.3.3",
+        "core-js-compat": "^3.25.1"
+      }
+    },
+    "babel-plugin-polyfill-regenerator": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz",
+      "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-define-polyfill-provider": "^0.3.3"
+      }
+    },
+    "babel-preset-current-node-syntax": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
+      "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+      "dev": true,
+      "requires": {
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-bigint": "^7.8.3",
+        "@babel/plugin-syntax-class-properties": "^7.8.3",
+        "@babel/plugin-syntax-import-meta": "^7.8.3",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-top-level-await": "^7.8.3"
+      }
+    },
+    "babel-preset-jest": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz",
+      "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==",
+      "dev": true,
+      "requires": {
+        "babel-plugin-jest-hoist": "^29.5.0",
+        "babel-preset-current-node-syntax": "^1.0.0"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true,
+      "optional": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "browserslist": {
+      "version": "4.21.5",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
+      "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+      "dev": true,
+      "requires": {
+        "caniuse-lite": "^1.0.30001449",
+        "electron-to-chromium": "^1.4.284",
+        "node-releases": "^2.0.8",
+        "update-browserslist-db": "^1.0.10"
+      }
+    },
+    "bser": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+      "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+      "dev": true,
+      "requires": {
+        "node-int64": "^0.4.0"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
       }
     },
-    "jest-docblock": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-23.2.0.tgz",
-      "integrity": "sha1-8IXh8YVI2Z/dabICB+b9VdkTg6c=",
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true
+    },
+    "caniuse-lite": {
+      "version": "1.0.30001476",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001476.tgz",
+      "integrity": "sha512-JmpktFppVSvyUN4gsLS0bShY2L9ZUslHLE72vgemBkS43JD2fOvKTKs+GtRwuxrtRGnwJFW0ye7kWRRlLJS9vQ==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
       "dev": true,
       "requires": {
-        "detect-newline": "^2.1.0"
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
       }
     },
-    "jest-each": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-23.2.0.tgz",
-      "integrity": "sha1-pAD4HIVwg/UMT1M5mxCfEgI/sZ0=",
+    "char-regex": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+      "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+      "dev": true
+    },
+    "chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
       "dev": true,
+      "optional": true,
       "requires": {
-        "chalk": "^2.0.1",
-        "pretty-format": "^23.2.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
       }
     },
-    "jest-environment-jsdom": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-23.3.0.tgz",
-      "integrity": "sha1-GQRX+RyeYVRUxBhgVgZdtu16Tio=",
+    "ci-info": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+      "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
+      "dev": true
+    },
+    "cjs-module-lexer": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
+      "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+      "dev": true
+    },
+    "cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
       "dev": true,
       "requires": {
-        "jest-mock": "^23.2.0",
-        "jest-util": "^23.3.0",
-        "jsdom": "^11.5.1"
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
       }
     },
-    "jest-environment-node": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-23.3.0.tgz",
-      "integrity": "sha1-Ho3yHIR6pdA7dlc/DcFvzeUDTDI=",
+    "co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+      "dev": true
+    },
+    "collect-v8-coverage": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
+      "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
+      "dev": true
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
       "dev": true,
       "requires": {
-        "jest-mock": "^23.2.0",
-        "jest-util": "^23.3.0"
+        "color-name": "1.1.3"
       }
     },
-    "jest-get-type": {
-      "version": "22.4.3",
-      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
-      "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
       "dev": true
     },
-    "jest-haste-map": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-23.2.0.tgz",
-      "integrity": "sha1-0Qy6wAfGlZSMjvGCGisu0tTy1Ng=",
+    "commander": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
+    },
+    "confusing-browser-globals": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+      "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+      "dev": true
+    },
+    "convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true
+    },
+    "core-js-compat": {
+      "version": "3.30.0",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.0.tgz",
+      "integrity": "sha512-P5A2h/9mRYZFIAP+5Ab8ns6083IyVpSclU74UNvbGVQ8VM7n3n3/g2yF3AkKQ9NXz2O+ioxLbEWKnDtgsFamhg==",
       "dev": true,
       "requires": {
-        "fb-watchman": "^2.0.0",
-        "graceful-fs": "^4.1.11",
-        "jest-docblock": "^23.2.0",
-        "jest-serializer": "^23.0.1",
-        "jest-worker": "^23.2.0",
-        "micromatch": "^3.1.10",
-        "sane": "^2.0.0"
-      },
-      "dependencies": {
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-          "dev": true,
-          "requires": {
-            "debug": "^2.3.3",
-            "define-property": "^0.2.5",
-            "extend-shallow": "^2.0.1",
-            "posix-character-classes": "^0.1.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^0.1.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "^0.1.6",
-                "is-data-descriptor": "^0.1.4",
-                "kind-of": "^5.0.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
-          }
-        },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "^0.3.2",
-            "define-property": "^1.0.0",
-            "expand-brackets": "^2.1.4",
-            "extend-shallow": "^2.0.1",
-            "fragment-cache": "^0.2.1",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^1.0.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-          "dev": true,
-          "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
-          }
-        }
+        "browserslist": "^4.21.5"
       }
     },
-    "jest-jasmine2": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-23.3.0.tgz",
-      "integrity": "sha1-qHBrqsI8ihMNWqjvVGSp1JCW0bU=",
+    "cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
       "dev": true,
       "requires": {
-        "chalk": "^2.0.1",
-        "co": "^4.6.0",
-        "expect": "^23.3.0",
-        "is-generator-fn": "^1.0.0",
-        "jest-diff": "^23.2.0",
-        "jest-each": "^23.2.0",
-        "jest-matcher-utils": "^23.2.0",
-        "jest-message-util": "^23.3.0",
-        "jest-snapshot": "^23.3.0",
-        "jest-util": "^23.3.0",
-        "pretty-format": "^23.2.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
       }
     },
-    "jest-leak-detector": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-23.2.0.tgz",
-      "integrity": "sha1-wonZYdxjjxQ1fU75bgQx7MGqN30=",
+    "damerau-levenshtein": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+      "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+      "dev": true
+    },
+    "debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
       "dev": true,
       "requires": {
-        "pretty-format": "^23.2.0"
+        "ms": "2.1.2"
       }
     },
-    "jest-matcher-utils": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.2.0.tgz",
-      "integrity": "sha1-TUmB8jIT6Tnjzt8j3DTHR7WuGRM=",
+    "dedent": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+      "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
+      "dev": true
+    },
+    "deep-equal": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz",
+      "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "es-get-iterator": "^1.1.2",
+        "get-intrinsic": "^1.1.3",
+        "is-arguments": "^1.1.1",
+        "is-array-buffer": "^3.0.1",
+        "is-date-object": "^1.0.5",
+        "is-regex": "^1.1.4",
+        "is-shared-array-buffer": "^1.0.2",
+        "isarray": "^2.0.5",
+        "object-is": "^1.1.5",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.4",
+        "regexp.prototype.flags": "^1.4.3",
+        "side-channel": "^1.0.4",
+        "which-boxed-primitive": "^1.0.2",
+        "which-collection": "^1.0.1",
+        "which-typed-array": "^1.1.9"
+      }
+    },
+    "deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
+    },
+    "deepmerge": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
+      "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
       "dev": true,
       "requires": {
-        "chalk": "^2.0.1",
-        "jest-get-type": "^22.1.0",
-        "pretty-format": "^23.2.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "has-property-descriptors": "^1.0.0",
+        "object-keys": "^1.1.1"
       }
     },
-    "jest-message-util": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.3.0.tgz",
-      "integrity": "sha1-vAexHOxpcftd2d4t+2DrwiFQwWA=",
+    "detect-newline": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+      "dev": true
+    },
+    "diff-sequences": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
+      "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
+      "dev": true
+    },
+    "dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.0.0-beta.35",
-        "chalk": "^2.0.1",
-        "micromatch": "^3.1.10",
-        "slash": "^1.0.0",
-        "stack-utils": "^1.0.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-          "dev": true,
-          "requires": {
-            "debug": "^2.3.3",
-            "define-property": "^0.2.5",
-            "extend-shallow": "^2.0.1",
-            "posix-character-classes": "^0.1.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^0.1.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "^0.1.6",
-                "is-data-descriptor": "^0.1.4",
-                "kind-of": "^5.0.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
-          }
-        },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "^0.3.2",
-            "define-property": "^1.0.0",
-            "expand-brackets": "^2.1.4",
-            "extend-shallow": "^2.0.1",
-            "fragment-cache": "^0.2.1",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^1.0.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-          "dev": true,
-          "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "path-type": "^4.0.0"
+      }
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
       }
     },
-    "jest-mock": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-23.2.0.tgz",
-      "integrity": "sha1-rRxg8p6HGdR8JuETgJi20YsmETQ=",
+    "electron-to-chromium": {
+      "version": "1.4.356",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.356.tgz",
+      "integrity": "sha512-nEftV1dRX3omlxAj42FwqRZT0i4xd2dIg39sog/CnCJeCcL1TRd2Uh0i9Oebgv8Ou0vzTPw++xc+Z20jzS2B6A==",
       "dev": true
     },
-    "jest-regex-util": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-23.3.0.tgz",
-      "integrity": "sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U=",
+    "emittery": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
+      "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
       "dev": true
     },
-    "jest-resolve": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.2.0.tgz",
-      "integrity": "sha1-oHkK1aO5kAKrTb/L+Nni1qabPZk=",
+    "emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
       "dev": true,
       "requires": {
-        "browser-resolve": "^1.11.3",
-        "chalk": "^2.0.1",
-        "realpath-native": "^1.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "is-arrayish": "^0.2.1"
       }
     },
-    "jest-resolve-dependencies": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-23.3.0.tgz",
-      "integrity": "sha1-hETTsLEoi4CGTYgB/1C0Sk1pXR0=",
+    "es-abstract": {
+      "version": "1.21.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
+      "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==",
+      "dev": true,
+      "requires": {
+        "array-buffer-byte-length": "^1.0.0",
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "es-set-tostringtag": "^2.0.1",
+        "es-to-primitive": "^1.2.1",
+        "function.prototype.name": "^1.1.5",
+        "get-intrinsic": "^1.2.0",
+        "get-symbol-description": "^1.0.0",
+        "globalthis": "^1.0.3",
+        "gopd": "^1.0.1",
+        "has": "^1.0.3",
+        "has-property-descriptors": "^1.0.0",
+        "has-proto": "^1.0.1",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.5",
+        "is-array-buffer": "^3.0.2",
+        "is-callable": "^1.2.7",
+        "is-negative-zero": "^2.0.2",
+        "is-regex": "^1.1.4",
+        "is-shared-array-buffer": "^1.0.2",
+        "is-string": "^1.0.7",
+        "is-typed-array": "^1.1.10",
+        "is-weakref": "^1.0.2",
+        "object-inspect": "^1.12.3",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.4",
+        "regexp.prototype.flags": "^1.4.3",
+        "safe-regex-test": "^1.0.0",
+        "string.prototype.trim": "^1.2.7",
+        "string.prototype.trimend": "^1.0.6",
+        "string.prototype.trimstart": "^1.0.6",
+        "typed-array-length": "^1.0.4",
+        "unbox-primitive": "^1.0.2",
+        "which-typed-array": "^1.1.9"
+      }
+    },
+    "es-get-iterator": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+      "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
       "dev": true,
       "requires": {
-        "jest-regex-util": "^23.3.0",
-        "jest-snapshot": "^23.3.0"
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.3",
+        "has-symbols": "^1.0.3",
+        "is-arguments": "^1.1.1",
+        "is-map": "^2.0.2",
+        "is-set": "^2.0.2",
+        "is-string": "^1.0.7",
+        "isarray": "^2.0.5",
+        "stop-iteration-iterator": "^1.0.0"
       }
     },
-    "jest-runner": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-23.3.0.tgz",
-      "integrity": "sha1-BMfkWKYXUBpIddsNf/vg48vUO/s=",
+    "es-set-tostringtag": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+      "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
       "dev": true,
       "requires": {
-        "exit": "^0.1.2",
-        "graceful-fs": "^4.1.11",
-        "jest-config": "^23.3.0",
-        "jest-docblock": "^23.2.0",
-        "jest-haste-map": "^23.2.0",
-        "jest-jasmine2": "^23.3.0",
-        "jest-leak-detector": "^23.2.0",
-        "jest-message-util": "^23.3.0",
-        "jest-runtime": "^23.3.0",
-        "jest-util": "^23.3.0",
-        "jest-worker": "^23.2.0",
-        "source-map-support": "^0.5.6",
-        "throat": "^4.0.0"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "source-map-support": {
-          "version": "0.5.6",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz",
-          "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
-          "dev": true,
-          "requires": {
-            "buffer-from": "^1.0.0",
-            "source-map": "^0.6.0"
-          }
-        }
+        "get-intrinsic": "^1.1.3",
+        "has": "^1.0.3",
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "jest-runtime": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-23.3.0.tgz",
-      "integrity": "sha1-SGWqtM7/gvnOxjNf164UIswd598=",
+    "es-shim-unscopables": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+      "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
       "dev": true,
       "requires": {
-        "babel-core": "^6.0.0",
-        "babel-plugin-istanbul": "^4.1.6",
-        "chalk": "^2.0.1",
-        "convert-source-map": "^1.4.0",
-        "exit": "^0.1.2",
-        "fast-json-stable-stringify": "^2.0.0",
-        "graceful-fs": "^4.1.11",
-        "jest-config": "^23.3.0",
-        "jest-haste-map": "^23.2.0",
-        "jest-message-util": "^23.3.0",
-        "jest-regex-util": "^23.3.0",
-        "jest-resolve": "^23.2.0",
-        "jest-snapshot": "^23.3.0",
-        "jest-util": "^23.3.0",
-        "jest-validate": "^23.3.0",
-        "micromatch": "^3.1.10",
-        "realpath-native": "^1.0.0",
-        "slash": "^1.0.0",
-        "strip-bom": "3.0.0",
-        "write-file-atomic": "^2.1.0",
-        "yargs": "^11.0.0"
+        "has": "^1.0.3"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "dev": true,
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true
+    },
+    "eslint": {
+      "version": "8.33.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
+      "integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
+      "dev": true,
+      "requires": {
+        "@eslint/eslintrc": "^1.4.1",
+        "@humanwhocodes/config-array": "^0.11.8",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.1.1",
+        "eslint-utils": "^3.0.0",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.4.0",
+        "esquery": "^1.4.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "grapheme-splitter": "^1.0.4",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-sdsl": "^4.1.4",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "regexpp": "^3.2.0",
+        "strip-ansi": "^6.0.1",
+        "strip-json-comments": "^3.1.0",
+        "text-table": "^0.2.0"
       },
       "dependencies": {
         "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
+            "color-convert": "^2.0.1"
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
           }
         },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "debug": "^2.3.3",
-            "define-property": "^0.2.5",
-            "extend-shallow": "^2.0.1",
-            "posix-character-classes": "^0.1.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^0.1.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "^0.1.6",
-                "is-data-descriptor": "^0.1.4",
-                "kind-of": "^5.0.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
+            "color-name": "~1.1.4"
           }
         },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "^0.3.2",
-            "define-property": "^1.0.0",
-            "expand-brackets": "^2.1.4",
-            "extend-shallow": "^2.0.1",
-            "fragment-cache": "^0.2.1",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^1.0.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
         },
-        "fill-range": {
+        "escape-string-regexp": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+          "dev": true
         },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+        "glob-parent": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+          "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "is-glob": "^4.0.3"
           }
         },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+        "globals": {
+          "version": "13.20.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+          "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
           "dev": true,
           "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
+            "type-fest": "^0.20.2"
           }
         },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
+            "has-flag": "^4.0.0"
           }
         },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+        "type-fest": {
+          "version": "0.20.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+          "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+          "dev": true
+        }
+      }
+    },
+    "eslint-config-airbnb": {
+      "version": "19.0.4",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
+      "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
+      "dev": true,
+      "requires": {
+        "eslint-config-airbnb-base": "^15.0.0",
+        "object.assign": "^4.1.2",
+        "object.entries": "^1.1.5"
+      }
+    },
+    "eslint-config-airbnb-base": {
+      "version": "15.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+      "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+      "dev": true,
+      "requires": {
+        "confusing-browser-globals": "^1.0.10",
+        "object.assign": "^4.1.2",
+        "object.entries": "^1.1.5",
+        "semver": "^6.3.0"
+      }
+    },
+    "eslint-import-resolver-node": {
+      "version": "0.3.7",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz",
+      "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==",
+      "dev": true,
+      "requires": {
+        "debug": "^3.2.7",
+        "is-core-module": "^2.11.0",
+        "resolve": "^1.22.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
           "dev": true,
           "requires": {
-            "has-flag": "^3.0.0"
+            "ms": "^2.1.1"
           }
         }
       }
     },
-    "jest-serializer": {
-      "version": "23.0.1",
-      "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-23.0.1.tgz",
-      "integrity": "sha1-o3dq6zEekP6D+rnlM+hRAr0WQWU=",
-      "dev": true
-    },
-    "jest-snapshot": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-23.3.0.tgz",
-      "integrity": "sha1-/E6fgeRUMtEFB+J/ULzmD0TYFCQ=",
-      "dev": true,
-      "requires": {
-        "babel-traverse": "^6.0.0",
-        "babel-types": "^6.0.0",
-        "chalk": "^2.0.1",
-        "jest-diff": "^23.2.0",
-        "jest-matcher-utils": "^23.2.0",
-        "jest-message-util": "^23.3.0",
-        "jest-resolve": "^23.2.0",
-        "mkdirp": "^0.5.1",
-        "natural-compare": "^1.4.0",
-        "pretty-format": "^23.2.0",
-        "semver": "^5.5.0"
+    "eslint-module-utils": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+      "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
+      "dev": true,
+      "requires": {
+        "debug": "^3.2.7"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
           "dev": true,
           "requires": {
-            "color-convert": "^1.9.0"
+            "ms": "^2.1.1"
           }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+        }
+      }
+    },
+    "eslint-plugin-import": {
+      "version": "2.27.5",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
+      "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
+      "dev": true,
+      "requires": {
+        "array-includes": "^3.1.6",
+        "array.prototype.flat": "^1.3.1",
+        "array.prototype.flatmap": "^1.3.1",
+        "debug": "^3.2.7",
+        "doctrine": "^2.1.0",
+        "eslint-import-resolver-node": "^0.3.7",
+        "eslint-module-utils": "^2.7.4",
+        "has": "^1.0.3",
+        "is-core-module": "^2.11.0",
+        "is-glob": "^4.0.3",
+        "minimatch": "^3.1.2",
+        "object.values": "^1.1.6",
+        "resolve": "^1.22.1",
+        "semver": "^6.3.0",
+        "tsconfig-paths": "^3.14.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
+            "ms": "^2.1.1"
           }
         },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+        "doctrine": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+          "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
           "dev": true,
           "requires": {
-            "has-flag": "^3.0.0"
+            "esutils": "^2.0.2"
           }
         }
       }
     },
-    "jest-util": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-23.3.0.tgz",
-      "integrity": "sha1-efNbsMMBAO9hHZY+5riPjthzqB0=",
-      "dev": true,
-      "requires": {
-        "callsites": "^2.0.0",
-        "chalk": "^2.0.1",
-        "graceful-fs": "^4.1.11",
-        "is-ci": "^1.0.10",
-        "jest-message-util": "^23.3.0",
-        "mkdirp": "^0.5.1",
-        "slash": "^1.0.0",
-        "source-map": "^0.6.0"
+    "eslint-plugin-jest": {
+      "version": "27.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.1.tgz",
+      "integrity": "sha512-l067Uxx7ZT8cO9NJuf+eJHvt6bqJyz2Z29wykyEdz/OtmcELQl2MQGQLX8J94O1cSJWAwUSEvCjwjA7KEK3Hmg==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/utils": "^5.10.0"
+      }
+    },
+    "eslint-plugin-jsx-a11y": {
+      "version": "6.7.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz",
+      "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.20.7",
+        "aria-query": "^5.1.3",
+        "array-includes": "^3.1.6",
+        "array.prototype.flatmap": "^1.3.1",
+        "ast-types-flow": "^0.0.7",
+        "axe-core": "^4.6.2",
+        "axobject-query": "^3.1.1",
+        "damerau-levenshtein": "^1.0.8",
+        "emoji-regex": "^9.2.2",
+        "has": "^1.0.3",
+        "jsx-ast-utils": "^3.3.3",
+        "language-tags": "=1.0.5",
+        "minimatch": "^3.1.2",
+        "object.entries": "^1.1.6",
+        "object.fromentries": "^2.0.6",
+        "semver": "^6.3.0"
+      }
+    },
+    "eslint-plugin-react": {
+      "version": "7.32.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz",
+      "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==",
+      "dev": true,
+      "peer": true,
+      "requires": {
+        "array-includes": "^3.1.6",
+        "array.prototype.flatmap": "^1.3.1",
+        "array.prototype.tosorted": "^1.1.1",
+        "doctrine": "^2.1.0",
+        "estraverse": "^5.3.0",
+        "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+        "minimatch": "^3.1.2",
+        "object.entries": "^1.1.6",
+        "object.fromentries": "^2.0.6",
+        "object.hasown": "^1.1.2",
+        "object.values": "^1.1.6",
+        "prop-types": "^15.8.1",
+        "resolve": "^2.0.0-next.4",
+        "semver": "^6.3.0",
+        "string.prototype.matchall": "^4.0.8"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+        "doctrine": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+          "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
           "dev": true,
+          "peer": true,
           "requires": {
-            "color-convert": "^1.9.0"
+            "esutils": "^2.0.2"
           }
         },
-        "callsites": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
-          "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+        "resolve": {
+          "version": "2.0.0-next.4",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
+          "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
           "dev": true,
+          "peer": true,
           "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
+            "is-core-module": "^2.9.0",
+            "path-parse": "^1.0.7",
+            "supports-preserve-symlinks-flag": "^1.0.0"
           }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        }
+      }
+    },
+    "eslint-plugin-react-hooks": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
+      "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+      "dev": true,
+      "peer": true,
+      "requires": {}
+    },
+    "eslint-scope": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+      "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      }
+    },
+    "eslint-utils": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+      "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^2.0.0"
+      },
+      "dependencies": {
+        "eslint-visitor-keys": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+          "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
           "dev": true
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
         }
       }
     },
-    "jest-validate": {
-      "version": "23.3.0",
-      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-23.3.0.tgz",
-      "integrity": "sha1-1Jvqaq2YwwrNLLtUJDR5igzBP3Y=",
+    "eslint-visitor-keys": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
+      "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "9.5.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
+      "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+      "dev": true,
+      "requires": {
+        "acorn": "^8.8.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.0"
+      }
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.1.0"
+      }
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      }
+    },
+    "estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true
+    },
+    "execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      }
+    },
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+      "dev": true
+    },
+    "expect": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz",
+      "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==",
+      "dev": true,
+      "requires": {
+        "@jest/expect-utils": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0"
+      }
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-glob": {
+      "version": "3.2.12",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+      "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      }
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true
+    },
+    "fastq": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "fb-watchman": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+      "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
       "dev": true,
       "requires": {
-        "chalk": "^2.0.1",
-        "jest-get-type": "^22.1.0",
-        "leven": "^2.1.0",
-        "pretty-format": "^23.2.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "bser": "2.1.1"
       }
     },
-    "jest-watcher": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-23.2.0.tgz",
-      "integrity": "sha1-Z46FKJbpGenZoOtLi68a4nliDqk=",
+    "file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
       "dev": true,
       "requires": {
-        "ansi-escapes": "^3.0.0",
-        "chalk": "^2.0.1",
-        "string-length": "^2.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "flat-cache": "^3.0.4"
       }
     },
-    "jest-worker": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-23.2.0.tgz",
-      "integrity": "sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=",
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
       "dev": true,
       "requires": {
-        "merge-stream": "^1.0.1"
+        "to-regex-range": "^5.0.1"
       }
     },
-    "js-tokens": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+    "find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      }
+    },
+    "flat-cache": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
+      "requires": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      }
+    },
+    "flatted": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+      "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
       "dev": true
     },
-    "js-yaml": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
-      "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+    "for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
       "dev": true,
       "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
+        "is-callable": "^1.1.3"
       }
     },
-    "jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+    "fs-readdir-recursive": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+      "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+      "dev": true
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
       "dev": true,
       "optional": true
     },
-    "jsdom": {
-      "version": "11.11.0",
-      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.11.0.tgz",
-      "integrity": "sha512-ou1VyfjwsSuWkudGxb03FotDajxAto6USAlmMZjE2lc0jCznt7sBWkhfRBRaWwbnmDqdMSTKTLT5d9sBFkkM7A==",
-      "dev": true,
-      "requires": {
-        "abab": "^1.0.4",
-        "acorn": "^5.3.0",
-        "acorn-globals": "^4.1.0",
-        "array-equal": "^1.0.0",
-        "cssom": ">= 0.3.2 < 0.4.0",
-        "cssstyle": ">= 0.3.1 < 0.4.0",
-        "data-urls": "^1.0.0",
-        "domexception": "^1.0.0",
-        "escodegen": "^1.9.0",
-        "html-encoding-sniffer": "^1.0.2",
-        "left-pad": "^1.2.0",
-        "nwsapi": "^2.0.0",
-        "parse5": "4.0.0",
-        "pn": "^1.1.0",
-        "request": "^2.83.0",
-        "request-promise-native": "^1.0.5",
-        "sax": "^1.2.4",
-        "symbol-tree": "^3.2.2",
-        "tough-cookie": "^2.3.3",
-        "w3c-hr-time": "^1.0.1",
-        "webidl-conversions": "^4.0.2",
-        "whatwg-encoding": "^1.0.3",
-        "whatwg-mimetype": "^2.1.0",
-        "whatwg-url": "^6.4.1",
-        "ws": "^4.0.0",
-        "xml-name-validator": "^3.0.0"
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "function.prototype.name": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+      "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.0",
+        "functions-have-names": "^1.2.2"
       }
     },
-    "jsesc": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
-      "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+    "functions-have-names": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
       "dev": true
     },
-    "json-schema": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+    "gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
       "dev": true
     },
-    "json-schema-traverse": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
       "dev": true
     },
-    "json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+    "get-intrinsic": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
+      "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.3"
+      }
+    },
+    "get-package-type": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
       "dev": true
     },
-    "json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+    "get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
       "dev": true
     },
-    "json5": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
-      "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+    "get-symbol-description": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
+      }
+    },
+    "glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
       "dev": true
     },
-    "jsprim": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+    "globalthis": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
       "dev": true,
       "requires": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.2.3",
-        "verror": "1.10.0"
+        "define-properties": "^1.1.3"
       }
     },
-    "jsx-ast-utils": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
-      "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
+    "globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
       "dev": true,
       "requires": {
-        "array-includes": "^3.0.3"
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "dependencies": {
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        }
       }
     },
-    "kind-of": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+    "gopd": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
       "dev": true,
       "requires": {
-        "is-buffer": "^1.1.5"
+        "get-intrinsic": "^1.1.3"
       }
     },
-    "lazy-cache": {
+    "graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "grapheme-splitter": {
       "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
-      "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+      "dev": true
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-bigints": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true
+    },
+    "has-property-descriptors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+      "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+      "dev": true,
+      "requires": {
+        "get-intrinsic": "^1.1.1"
+      }
+    },
+    "has-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+      "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "dev": true
     },
-    "lcid": {
+    "has-tostringtag": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
-      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
       "dev": true,
       "requires": {
-        "invert-kv": "^1.0.0"
+        "has-symbols": "^1.0.2"
       }
     },
-    "left-pad": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
-      "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==",
+    "html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
       "dev": true
     },
-    "leven": {
+    "human-signals": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
-      "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
       "dev": true
     },
-    "levn": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+    "husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true
+    },
+    "ignore": {
+      "version": "5.2.4",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+      "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
       "dev": true,
       "requires": {
-        "prelude-ls": "~1.1.2",
-        "type-check": "~0.3.2"
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
       }
     },
-    "load-json-file": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
-      "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+    "import-local": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+      "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "parse-json": "^2.2.0",
-        "pify": "^2.0.0",
-        "strip-bom": "^3.0.0"
+        "pkg-dir": "^4.2.0",
+        "resolve-cwd": "^3.0.0"
       }
     },
-    "locate-path": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
-      "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
       "dev": true,
       "requires": {
-        "p-locate": "^2.0.0",
-        "path-exists": "^3.0.0"
-      },
-      "dependencies": {
-        "path-exists": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-          "dev": true
-        }
+        "once": "^1.3.0",
+        "wrappy": "1"
       }
     },
-    "lodash": {
-      "version": "4.17.10",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
-      "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
       "dev": true
     },
-    "lodash.sortby": {
-      "version": "4.7.0",
-      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
-      "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+    "internal-slot": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+      "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+      "dev": true,
+      "requires": {
+        "get-intrinsic": "^1.2.0",
+        "has": "^1.0.3",
+        "side-channel": "^1.0.4"
+      }
+    },
+    "is-arguments": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      }
+    },
+    "is-array-buffer": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
+      "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.2.0",
+        "is-typed-array": "^1.1.10"
+      }
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
       "dev": true
     },
-    "longest": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
-      "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+    "is-bigint": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+      "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+      "dev": true,
+      "requires": {
+        "has-bigints": "^1.0.1"
+      }
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-boolean-object": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+      "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      }
+    },
+    "is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
       "dev": true
     },
-    "loose-envify": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
-      "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+    "is-core-module": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
       "dev": true,
       "requires": {
-        "js-tokens": "^3.0.0"
+        "has": "^1.0.3"
       }
     },
-    "lru-cache": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
-      "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
+    "is-date-object": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
       "dev": true,
       "requires": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "makeerror": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
-      "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=",
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true
+    },
+    "is-generator-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+      "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
       "dev": true,
       "requires": {
-        "tmpl": "1.0.x"
+        "is-extglob": "^2.1.1"
       }
     },
-    "map-cache": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+    "is-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
+      "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
       "dev": true
     },
-    "map-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
-      "dev": true,
-      "requires": {
-        "object-visit": "^1.0.0"
-      }
+    "is-negative-zero": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+      "dev": true
     },
-    "mem": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
-      "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-number-object": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+      "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
       "dev": true,
       "requires": {
-        "mimic-fn": "^1.0.0"
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "merge": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz",
-      "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=",
+    "is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
       "dev": true
     },
-    "merge-stream": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
-      "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=",
+    "is-regex": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
       "dev": true,
       "requires": {
-        "readable-stream": "^2.0.1"
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "micromatch": {
-      "version": "2.3.11",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
-      "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+    "is-set": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
+      "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
+      "dev": true
+    },
+    "is-shared-array-buffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+      "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "arr-diff": "^2.0.0",
-        "array-unique": "^0.2.1",
-        "braces": "^1.8.2",
-        "expand-brackets": "^0.1.4",
-        "extglob": "^0.3.1",
-        "filename-regex": "^2.0.0",
-        "is-extglob": "^1.0.0",
-        "is-glob": "^2.0.1",
-        "kind-of": "^3.0.2",
-        "normalize-path": "^2.0.1",
-        "object.omit": "^2.0.0",
-        "parse-glob": "^3.0.4",
-        "regex-cache": "^0.4.2"
+        "call-bind": "^1.0.2"
       }
     },
-    "mime-db": {
-      "version": "1.33.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
-      "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
+    "is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
       "dev": true
     },
-    "mime-types": {
-      "version": "2.1.18",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
-      "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+    "is-string": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+      "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
       "dev": true,
       "requires": {
-        "mime-db": "~1.33.0"
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "mimic-fn": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
-      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
-      "dev": true
+    "is-symbol": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+      "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+      "dev": true,
+      "requires": {
+        "has-symbols": "^1.0.2"
+      }
     },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+    "is-typed-array": {
+      "version": "1.1.10",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
+      "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
       "dev": true,
       "requires": {
-        "brace-expansion": "^1.1.7"
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "minimist": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+    "is-weakmap": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
+      "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
       "dev": true
     },
-    "mixin-deep": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
-      "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+    "is-weakref": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+      "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
       "dev": true,
       "requires": {
-        "for-in": "^1.0.2",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
+        "call-bind": "^1.0.2"
       }
     },
-    "mkdirp": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+    "is-weakset": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
+      "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
       "dev": true,
       "requires": {
-        "minimist": "0.0.8"
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
       }
     },
-    "ms": {
+    "isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "dev": true
+    },
+    "isexe": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
       "dev": true
     },
-    "mute-stream": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
-      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+    "istanbul-lib-coverage": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
       "dev": true
     },
-    "nan": {
-      "version": "2.10.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
-      "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
+    "istanbul-lib-instrument": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+      "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "@babel/core": "^7.12.3",
+        "@babel/parser": "^7.14.7",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^6.3.0"
+      }
     },
-    "nanomatch": {
-      "version": "1.2.13",
-      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+    "istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
       "dev": true,
       "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "fragment-cache": "^0.2.1",
-        "is-windows": "^1.0.2",
-        "kind-of": "^6.0.2",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^3.0.0",
+        "supports-color": "^7.1.0"
       },
       "dependencies": {
-        "arr-diff": {
+        "has-flag": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
+        "make-dir": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+          "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+          "dev": true,
+          "requires": {
+            "semver": "^6.0.0"
+          }
         },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
-    "natural-compare": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
-      "dev": true
-    },
-    "node-int64": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
-      "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
-      "dev": true
-    },
-    "node-notifier": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.2.1.tgz",
-      "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==",
+    "istanbul-lib-source-maps": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
       "dev": true,
       "requires": {
-        "growly": "^1.3.0",
-        "semver": "^5.4.1",
-        "shellwords": "^0.1.1",
-        "which": "^1.3.0"
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^3.0.0",
+        "source-map": "^0.6.1"
       }
     },
-    "normalize-package-data": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
-      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+    "istanbul-reports": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+      "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
       "dev": true,
       "requires": {
-        "hosted-git-info": "^2.1.4",
-        "is-builtin-module": "^1.0.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
       }
     },
-    "normalize-path": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+    "jest": {
+      "version": "29.4.1",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.1.tgz",
+      "integrity": "sha512-cknimw7gAXPDOmj0QqztlxVtBVCw2lYY9CeIE5N6kD+kET1H4H79HSNISJmijb1HF+qk+G+ploJgiDi5k/fRlg==",
       "dev": true,
       "requires": {
-        "remove-trailing-separator": "^1.0.1"
+        "@jest/core": "^29.4.1",
+        "@jest/types": "^29.4.1",
+        "import-local": "^3.0.2",
+        "jest-cli": "^29.4.1"
       }
     },
-    "npm-run-path": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+    "jest-changed-files": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz",
+      "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==",
       "dev": true,
       "requires": {
-        "path-key": "^2.0.0"
+        "execa": "^5.0.0",
+        "p-limit": "^3.1.0"
       }
     },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
-    },
-    "nwsapi": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.4.tgz",
-      "integrity": "sha512-Zt6HRR6RcJkuj5/N9zeE7FN6YitRW//hK2wTOwX274IBphbY3Zf5+yn5mZ9v/SzAOTMjQNxZf9KkmPLWn0cV4g==",
-      "dev": true
-    },
-    "oauth-sign": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
-      "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
-      "dev": true
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true
-    },
-    "object-copy": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+    "jest-circus": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz",
+      "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==",
       "dev": true,
       "requires": {
-        "copy-descriptor": "^0.1.0",
-        "define-property": "^0.2.5",
-        "kind-of": "^3.0.3"
+        "@jest/environment": "^29.5.0",
+        "@jest/expect": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "co": "^4.6.0",
+        "dedent": "^0.7.0",
+        "is-generator-fn": "^2.0.0",
+        "jest-each": "^29.5.0",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "p-limit": "^3.1.0",
+        "pretty-format": "^29.5.0",
+        "pure-rand": "^6.0.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.3"
       },
       "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "is-descriptor": "^0.1.0"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "object-keys": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
-      "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
-      "dev": true
-    },
-    "object-visit": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+    "jest-cli": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz",
+      "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==",
       "dev": true,
       "requires": {
-        "isobject": "^3.0.0"
+        "@jest/core": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "chalk": "^4.0.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.9",
+        "import-local": "^3.0.2",
+        "jest-config": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "prompts": "^2.0.1",
+        "yargs": "^17.3.1"
       },
       "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
-    "object.assign": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
-      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "function-bind": "^1.1.1",
-        "has-symbols": "^1.0.0",
-        "object-keys": "^1.0.11"
-      }
-    },
-    "object.entries": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz",
-      "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "es-abstract": "^1.6.1",
-        "function-bind": "^1.1.0",
-        "has": "^1.0.1"
-      }
-    },
-    "object.getownpropertydescriptors": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
-      "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "es-abstract": "^1.5.1"
-      }
-    },
-    "object.omit": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
-      "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "for-own": "^0.1.4",
-        "is-extendable": "^0.1.1"
+    "jest-config": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz",
+      "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.11.6",
+        "@jest/test-sequencer": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "babel-jest": "^29.5.0",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "deepmerge": "^4.2.2",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "jest-circus": "^29.5.0",
+        "jest-environment-node": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-runner": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "parse-json": "^5.2.0",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-json-comments": "^3.1.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "object.pick": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+    "jest-diff": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz",
+      "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==",
       "dev": true,
       "requires": {
-        "isobject": "^3.0.1"
+        "chalk": "^4.0.0",
+        "diff-sequences": "^29.4.3",
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
       },
       "dependencies": {
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "onetime": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
-      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+    "jest-docblock": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz",
+      "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==",
       "dev": true,
       "requires": {
-        "mimic-fn": "^1.0.0"
+        "detect-newline": "^3.0.0"
       }
     },
-    "optimist": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
-      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+    "jest-each": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz",
+      "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==",
       "dev": true,
       "requires": {
-        "minimist": "~0.0.1",
-        "wordwrap": "~0.0.2"
+        "@jest/types": "^29.5.0",
+        "chalk": "^4.0.0",
+        "jest-get-type": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "pretty-format": "^29.5.0"
       },
       "dependencies": {
-        "wordwrap": {
-          "version": "0.0.3",
-          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
-          "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
-    "optionator": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
-      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+    "jest-environment-node": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz",
+      "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==",
       "dev": true,
       "requires": {
-        "deep-is": "~0.1.3",
-        "fast-levenshtein": "~2.0.4",
-        "levn": "~0.3.0",
-        "prelude-ls": "~1.1.2",
-        "type-check": "~0.3.2",
-        "wordwrap": "~1.0.0"
+        "@jest/environment": "^29.5.0",
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-mock": "^29.5.0",
+        "jest-util": "^29.5.0"
       }
     },
-    "os-homedir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+    "jest-get-type": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz",
+      "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==",
       "dev": true
     },
-    "os-locale": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
-      "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+    "jest-haste-map": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz",
+      "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==",
       "dev": true,
       "requires": {
-        "execa": "^0.7.0",
-        "lcid": "^1.0.0",
-        "mem": "^1.1.0"
+        "@jest/types": "^29.5.0",
+        "@types/graceful-fs": "^4.1.3",
+        "@types/node": "*",
+        "anymatch": "^3.0.3",
+        "fb-watchman": "^2.0.0",
+        "fsevents": "^2.3.2",
+        "graceful-fs": "^4.2.9",
+        "jest-regex-util": "^29.4.3",
+        "jest-util": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "micromatch": "^4.0.4",
+        "walker": "^1.0.8"
       }
     },
-    "os-shim": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
-      "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=",
-      "dev": true
-    },
-    "os-tmpdir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
-      "dev": true
-    },
-    "output-file-sync": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz",
-      "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=",
+    "jest-leak-detector": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz",
+      "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.4",
-        "mkdirp": "^0.5.1",
-        "object-assign": "^4.1.0"
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
       }
     },
-    "p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
-      "dev": true
-    },
-    "p-limit": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
-      "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
+    "jest-matcher-utils": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz",
+      "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==",
       "dev": true,
       "requires": {
-        "p-try": "^1.0.0"
+        "chalk": "^4.0.0",
+        "jest-diff": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "pretty-format": "^29.5.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "p-locate": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
-      "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
-      "dev": true,
-      "requires": {
-        "p-limit": "^1.1.0"
+    "jest-message-util": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz",
+      "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.12.13",
+        "@jest/types": "^29.5.0",
+        "@types/stack-utils": "^2.0.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "micromatch": "^4.0.4",
+        "pretty-format": "^29.5.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.3"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "p-try": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-      "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
-      "dev": true
-    },
-    "parse-glob": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
-      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
+    "jest-mock": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz",
+      "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "glob-base": "^0.3.0",
-        "is-dotfile": "^1.0.0",
-        "is-extglob": "^1.0.0",
-        "is-glob": "^2.0.0"
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "jest-util": "^29.5.0"
       }
     },
-    "parse-json": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+    "jest-pnp-resolver": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+      "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
       "dev": true,
-      "requires": {
-        "error-ex": "^1.2.0"
-      }
+      "requires": {}
     },
-    "parse5": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
-      "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
+    "jest-regex-util": {
+      "version": "29.4.3",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz",
+      "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==",
       "dev": true
     },
-    "pascalcase": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
-      "dev": true
+    "jest-resolve": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz",
+      "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-pnp-resolver": "^1.2.2",
+        "jest-util": "^29.5.0",
+        "jest-validate": "^29.5.0",
+        "resolve": "^1.20.0",
+        "resolve.exports": "^2.0.0",
+        "slash": "^3.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
     },
-    "path-exists": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+    "jest-resolve-dependencies": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz",
+      "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==",
       "dev": true,
       "requires": {
-        "pinkie-promise": "^2.0.0"
+        "jest-regex-util": "^29.4.3",
+        "jest-snapshot": "^29.5.0"
       }
     },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "path-is-inside": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
-      "dev": true
-    },
-    "path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
-      "dev": true
-    },
-    "path-parse": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
-      "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
-      "dev": true
+    "jest-runner": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz",
+      "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^29.5.0",
+        "@jest/environment": "^29.5.0",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "emittery": "^0.13.1",
+        "graceful-fs": "^4.2.9",
+        "jest-docblock": "^29.4.3",
+        "jest-environment-node": "^29.5.0",
+        "jest-haste-map": "^29.5.0",
+        "jest-leak-detector": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-resolve": "^29.5.0",
+        "jest-runtime": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "jest-watcher": "^29.5.0",
+        "jest-worker": "^29.5.0",
+        "p-limit": "^3.1.0",
+        "source-map-support": "0.5.13"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
     },
-    "path-type": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
-      "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
-      "dev": true,
-      "requires": {
-        "pify": "^2.0.0"
+    "jest-runtime": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz",
+      "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==",
+      "dev": true,
+      "requires": {
+        "@jest/environment": "^29.5.0",
+        "@jest/fake-timers": "^29.5.0",
+        "@jest/globals": "^29.5.0",
+        "@jest/source-map": "^29.4.3",
+        "@jest/test-result": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "cjs-module-lexer": "^1.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.2.9",
+        "jest-haste-map": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-mock": "^29.5.0",
+        "jest-regex-util": "^29.4.3",
+        "jest-resolve": "^29.5.0",
+        "jest-snapshot": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "slash": "^3.0.0",
+        "strip-bom": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "performance-now": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true
-    },
-    "pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-      "dev": true
-    },
-    "pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
-      "dev": true
-    },
-    "pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
-      "dev": true,
-      "requires": {
-        "pinkie": "^2.0.0"
+    "jest-snapshot": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz",
+      "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.11.6",
+        "@babel/generator": "^7.7.2",
+        "@babel/plugin-syntax-jsx": "^7.7.2",
+        "@babel/plugin-syntax-typescript": "^7.7.2",
+        "@babel/traverse": "^7.7.2",
+        "@babel/types": "^7.3.3",
+        "@jest/expect-utils": "^29.5.0",
+        "@jest/transform": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/babel__traverse": "^7.0.6",
+        "@types/prettier": "^2.1.5",
+        "babel-preset-current-node-syntax": "^1.0.0",
+        "chalk": "^4.0.0",
+        "expect": "^29.5.0",
+        "graceful-fs": "^4.2.9",
+        "jest-diff": "^29.5.0",
+        "jest-get-type": "^29.4.3",
+        "jest-matcher-utils": "^29.5.0",
+        "jest-message-util": "^29.5.0",
+        "jest-util": "^29.5.0",
+        "natural-compare": "^1.4.0",
+        "pretty-format": "^29.5.0",
+        "semver": "^7.3.5"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "semver": {
+          "version": "7.3.8",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+          "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
       }
     },
-    "pkg-dir": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
-      "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+    "jest-util": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz",
+      "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==",
       "dev": true,
       "requires": {
-        "find-up": "^1.0.0"
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "ci-info": "^3.2.0",
+        "graceful-fs": "^4.2.9",
+        "picomatch": "^2.2.3"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
-    "pluralize": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
-      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
-      "dev": true
-    },
-    "pn": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
-      "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
-      "dev": true
-    },
-    "posix-character-classes": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
-      "dev": true
-    },
-    "pre-commit": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz",
-      "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=",
+    "jest-validate": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz",
+      "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==",
       "dev": true,
       "requires": {
-        "cross-spawn": "^5.0.1",
-        "spawn-sync": "^1.0.15",
-        "which": "1.2.x"
+        "@jest/types": "^29.5.0",
+        "camelcase": "^6.2.0",
+        "chalk": "^4.0.0",
+        "jest-get-type": "^29.4.3",
+        "leven": "^3.1.0",
+        "pretty-format": "^29.5.0"
       },
       "dependencies": {
-        "which": {
-          "version": "1.2.14",
-          "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
-          "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "camelcase": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+          "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+          "dev": true
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
           "dev": true,
           "requires": {
-            "isexe": "^2.0.0"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "prelude-ls": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
-      "dev": true
-    },
-    "preserve": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
-      "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
-      "dev": true,
-      "optional": true
+    "jest-watcher": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz",
+      "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==",
+      "dev": true,
+      "requires": {
+        "@jest/test-result": "^29.5.0",
+        "@jest/types": "^29.5.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.0.0",
+        "emittery": "^0.13.1",
+        "jest-util": "^29.5.0",
+        "string-length": "^4.0.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
     },
-    "pretty-format": {
-      "version": "23.2.0",
-      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.2.0.tgz",
-      "integrity": "sha1-OwqqY8AYpTWDNzwcs6XZbMXoMBc=",
+    "jest-worker": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz",
+      "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==",
       "dev": true,
       "requires": {
-        "ansi-regex": "^3.0.0",
-        "ansi-styles": "^3.2.0"
+        "@types/node": "*",
+        "jest-util": "^29.5.0",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
           "dev": true
         },
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+        "supports-color": {
+          "version": "8.1.1",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
           "dev": true,
           "requires": {
-            "color-convert": "^1.9.0"
+            "has-flag": "^4.0.0"
           }
         }
       }
     },
-    "private": {
-      "version": "0.1.8",
-      "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
-      "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+    "js-sdsl": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
+      "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==",
       "dev": true
     },
-    "process-nextick-args": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-      "dev": true
-    },
-    "progress": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
-      "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "dev": true
     },
-    "prompts": {
-      "version": "0.1.10",
-      "resolved": "https://registry.npmjs.org/prompts/-/prompts-0.1.10.tgz",
-      "integrity": "sha512-/MPwms6+g/m6fvXZlQyOL4m4ziDim2+Wc6CdWVjp+nVCkzEkK2N4rR74m/bbGf+dkta+/SBpo1FfES8Wgrk/Fw==",
+    "js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
       "dev": true,
       "requires": {
-        "clorox": "^1.0.3",
-        "sisteransi": "^0.1.1"
+        "argparse": "^2.0.1"
       }
     },
-    "prop-types": {
-      "version": "15.6.2",
-      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
-      "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
-      "dev": true,
-      "requires": {
-        "loose-envify": "^1.3.1",
-        "object-assign": "^4.1.1"
-      }
+    "jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+      "dev": true
     },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+    "json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
       "dev": true
     },
-    "punycode": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
       "dev": true
     },
-    "qs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
       "dev": true
     },
-    "randomatic": {
-      "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
-      "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "kind-of": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
+    "json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true
     },
-    "read-pkg": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
-      "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+    "jsx-ast-utils": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz",
+      "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==",
       "dev": true,
       "requires": {
-        "load-json-file": "^2.0.0",
-        "normalize-package-data": "^2.3.2",
-        "path-type": "^2.0.0"
+        "array-includes": "^3.1.5",
+        "object.assign": "^4.1.3"
       }
     },
-    "read-pkg-up": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
-      "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
-      "dev": true,
-      "requires": {
-        "find-up": "^2.0.0",
-        "read-pkg": "^2.0.0"
-      },
-      "dependencies": {
-        "find-up": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
-          "dev": true,
-          "requires": {
-            "locate-path": "^2.0.0"
-          }
-        }
-      }
+    "kleur": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+      "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+      "dev": true
+    },
+    "language-subtag-registry": {
+      "version": "0.3.22",
+      "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+      "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+      "dev": true
     },
-    "readable-stream": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+    "language-tags": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
+      "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
       "dev": true,
       "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
+        "language-subtag-registry": "~0.3.2"
       }
     },
-    "readdirp": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
-      "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
+    "leven": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+      "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+      "dev": true
+    },
+    "levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
-        "minimatch": "^3.0.2",
-        "readable-stream": "^2.0.2",
-        "set-immediate-shim": "^1.0.1"
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
       }
     },
-    "realpath-native": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.0.1.tgz",
-      "integrity": "sha512-W14EcXuqUvKP8dkWkD7B95iMy77lpMnlFXbbk409bQtNCbeu0kvRE5reo+yIZ3JXxg6frbGsz2DLQ39lrCB40g==",
+    "lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
       "dev": true,
       "requires": {
-        "util.promisify": "^1.0.0"
+        "p-locate": "^5.0.0"
       }
     },
-    "regenerate": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
-      "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
+    "lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
       "dev": true
     },
-    "regenerator-runtime": {
-      "version": "0.11.1",
-      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
-      "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true
     },
-    "regenerator-transform": {
-      "version": "0.10.1",
-      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
-      "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
       "dev": true,
+      "peer": true,
       "requires": {
-        "babel-runtime": "^6.18.0",
-        "babel-types": "^6.19.0",
-        "private": "^0.1.6"
+        "js-tokens": "^3.0.0 || ^4.0.0"
       }
     },
-    "regex-cache": {
-      "version": "0.4.4",
-      "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
-      "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
+    "lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "is-equal-shallow": "^0.1.3"
+        "yallist": "^3.0.2"
       }
     },
-    "regex-not": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+    "make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^3.0.2",
-        "safe-regex": "^1.1.0"
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        }
       }
     },
-    "regexpp": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
-      "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
-      "dev": true
+    "makeerror": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+      "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+      "dev": true,
+      "requires": {
+        "tmpl": "1.0.5"
+      }
     },
-    "regexpu-core": {
+    "merge-stream": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
-      "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
+    "merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
       "dev": true,
       "requires": {
-        "regenerate": "^1.2.1",
-        "regjsgen": "^0.2.0",
-        "regjsparser": "^0.1.4"
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
       }
     },
-    "regjsgen": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
-      "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
+    "mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
       "dev": true
     },
-    "regjsparser": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
-      "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+    "minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
       "requires": {
-        "jsesc": "~0.5.0"
-      },
-      "dependencies": {
-        "jsesc": {
-          "version": "0.5.0",
-          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
-          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
-          "dev": true
-        }
+        "brace-expansion": "^1.1.7"
       }
     },
-    "remove-trailing-separator": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+    "minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
       "dev": true
     },
-    "repeat-element": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
-      "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
       "dev": true
     },
-    "repeat-string": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
       "dev": true
     },
-    "repeating": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
-      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
-      "dev": true,
-      "requires": {
-        "is-finite": "^1.0.0"
-      }
-    },
-    "request": {
-      "version": "2.87.0",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
-      "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
-      "dev": true,
-      "requires": {
-        "aws-sign2": "~0.7.0",
-        "aws4": "^1.6.0",
-        "caseless": "~0.12.0",
-        "combined-stream": "~1.0.5",
-        "extend": "~3.0.1",
-        "forever-agent": "~0.6.1",
-        "form-data": "~2.3.1",
-        "har-validator": "~5.0.3",
-        "http-signature": "~1.2.0",
-        "is-typedarray": "~1.0.0",
-        "isstream": "~0.1.2",
-        "json-stringify-safe": "~5.0.1",
-        "mime-types": "~2.1.17",
-        "oauth-sign": "~0.8.2",
-        "performance-now": "^2.1.0",
-        "qs": "~6.5.1",
-        "safe-buffer": "^5.1.1",
-        "tough-cookie": "~2.3.3",
-        "tunnel-agent": "^0.6.0",
-        "uuid": "^3.1.0"
-      }
-    },
-    "request-promise-core": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
-      "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
+    "node-int64": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+      "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+      "dev": true
+    },
+    "node-releases": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
+      "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+      "dev": true
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
       "dev": true,
       "requires": {
-        "lodash": "^4.13.1"
+        "path-key": "^3.0.0"
       }
     },
-    "request-promise-native": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
-      "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
       "dev": true,
-      "requires": {
-        "request-promise-core": "1.1.1",
-        "stealthy-require": "^1.1.0",
-        "tough-cookie": ">=2.3.3"
-      }
+      "peer": true
     },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+    "object-inspect": {
+      "version": "1.12.3",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+      "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
       "dev": true
     },
-    "require-main-filename": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
-      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+    "object-is": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+      "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
+      }
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
       "dev": true
     },
-    "require-uncached": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
-      "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+    "object.assign": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+      "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
       "dev": true,
       "requires": {
-        "caller-path": "^0.1.0",
-        "resolve-from": "^1.0.0"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "has-symbols": "^1.0.3",
+        "object-keys": "^1.1.1"
       }
     },
-    "resolve": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
-      "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
+    "object.entries": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz",
+      "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==",
       "dev": true,
       "requires": {
-        "path-parse": "^1.0.5"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
       }
     },
-    "resolve-cwd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+    "object.fromentries": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
+      "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
       "dev": true,
       "requires": {
-        "resolve-from": "^3.0.0"
-      },
-      "dependencies": {
-        "resolve-from": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
-          "dev": true
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
       }
     },
-    "resolve-from": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
-      "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
-      "dev": true
-    },
-    "resolve-url": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
-      "dev": true
-    },
-    "restore-cursor": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
-      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+    "object.hasown": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz",
+      "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==",
       "dev": true,
+      "peer": true,
       "requires": {
-        "onetime": "^2.0.0",
-        "signal-exit": "^3.0.2"
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
       }
     },
-    "ret": {
-      "version": "0.1.15",
-      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
-      "dev": true
-    },
-    "right-align": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
-      "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+    "object.values": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
+      "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "align-text": "^0.1.1"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
       }
     },
-    "rimraf": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
-      "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
       "dev": true,
       "requires": {
-        "glob": "^7.0.5"
+        "wrappy": "1"
       }
     },
-    "rsvp": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
-      "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==",
-      "dev": true
-    },
-    "run-async": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
-      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+    "onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
       "dev": true,
       "requires": {
-        "is-promise": "^2.1.0"
+        "mimic-fn": "^2.1.0"
       }
     },
-    "rx-lite": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
-      "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
-      "dev": true
-    },
-    "rx-lite-aggregates": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
-      "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+    "optionator": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+      "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
       "dev": true,
       "requires": {
-        "rx-lite": "*"
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
       }
     },
-    "safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "dev": true
-    },
-    "safe-regex": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+    "p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
       "dev": true,
       "requires": {
-        "ret": "~0.1.10"
+        "yocto-queue": "^0.1.0"
       }
     },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
-      "dev": true
-    },
-    "sane": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz",
-      "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=",
+    "p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
       "dev": true,
       "requires": {
-        "anymatch": "^2.0.0",
-        "capture-exit": "^1.2.0",
-        "exec-sh": "^0.2.0",
-        "fb-watchman": "^2.0.0",
-        "fsevents": "^1.2.3",
-        "micromatch": "^3.1.4",
-        "minimist": "^1.1.1",
-        "walker": "~1.0.5",
-        "watch": "~0.18.0"
-      },
-      "dependencies": {
-        "anymatch": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-          "dev": true,
-          "requires": {
-            "micromatch": "^3.1.4",
-            "normalize-path": "^2.1.1"
-          }
-        },
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-          "dev": true,
-          "requires": {
-            "debug": "^2.3.3",
-            "define-property": "^0.2.5",
-            "extend-shallow": "^2.0.1",
-            "posix-character-classes": "^0.1.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^0.1.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "^0.1.6",
-                "is-data-descriptor": "^0.1.4",
-                "kind-of": "^5.0.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
-          }
-        },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "^0.3.2",
-            "define-property": "^1.0.0",
-            "expand-brackets": "^2.1.4",
-            "extend-shallow": "^2.0.1",
-            "fragment-cache": "^0.2.1",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^1.0.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fsevents": {
-          "version": "1.2.4",
-          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
-          "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "nan": "^2.9.2",
-            "node-pre-gyp": "^0.10.0"
-          },
-          "dependencies": {
-            "abbrev": {
-              "version": "1.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "ansi-regex": {
-              "version": "2.1.1",
-              "bundled": true,
-              "dev": true
-            },
-            "aproba": {
-              "version": "1.2.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "are-we-there-yet": {
-              "version": "1.1.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "delegates": "^1.0.0",
-                "readable-stream": "^2.0.6"
-              }
-            },
-            "balanced-match": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true
-            },
-            "brace-expansion": {
-              "version": "1.1.11",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "balanced-match": "^1.0.0",
-                "concat-map": "0.0.1"
-              }
-            },
-            "chownr": {
-              "version": "1.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "code-point-at": {
-              "version": "1.1.0",
-              "bundled": true,
-              "dev": true
-            },
-            "concat-map": {
-              "version": "0.0.1",
-              "bundled": true,
-              "dev": true
-            },
-            "console-control-strings": {
-              "version": "1.1.0",
-              "bundled": true,
-              "dev": true
-            },
-            "core-util-is": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "debug": {
-              "version": "2.6.9",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "ms": "2.0.0"
-              }
-            },
-            "deep-extend": {
-              "version": "0.5.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "delegates": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "detect-libc": {
-              "version": "1.0.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "fs-minipass": {
-              "version": "1.2.5",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "minipass": "^2.2.1"
-              }
-            },
-            "fs.realpath": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "gauge": {
-              "version": "2.7.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "aproba": "^1.0.3",
-                "console-control-strings": "^1.0.0",
-                "has-unicode": "^2.0.0",
-                "object-assign": "^4.1.0",
-                "signal-exit": "^3.0.0",
-                "string-width": "^1.0.1",
-                "strip-ansi": "^3.0.1",
-                "wide-align": "^1.1.0"
-              }
-            },
-            "glob": {
-              "version": "7.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "fs.realpath": "^1.0.0",
-                "inflight": "^1.0.4",
-                "inherits": "2",
-                "minimatch": "^3.0.4",
-                "once": "^1.3.0",
-                "path-is-absolute": "^1.0.0"
-              }
-            },
-            "has-unicode": {
-              "version": "2.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "iconv-lite": {
-              "version": "0.4.21",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "safer-buffer": "^2.1.0"
-              }
-            },
-            "ignore-walk": {
-              "version": "3.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "minimatch": "^3.0.4"
-              }
-            },
-            "inflight": {
-              "version": "1.0.6",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "once": "^1.3.0",
-                "wrappy": "1"
-              }
-            },
-            "inherits": {
-              "version": "2.0.3",
-              "bundled": true,
-              "dev": true
-            },
-            "ini": {
-              "version": "1.3.5",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "is-fullwidth-code-point": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "number-is-nan": "^1.0.0"
-              }
-            },
-            "isarray": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "minimatch": {
-              "version": "3.0.4",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "brace-expansion": "^1.1.7"
-              }
-            },
-            "minimist": {
-              "version": "0.0.8",
-              "bundled": true,
-              "dev": true
-            },
-            "minipass": {
-              "version": "2.2.4",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "safe-buffer": "^5.1.1",
-                "yallist": "^3.0.0"
-              }
-            },
-            "minizlib": {
-              "version": "1.1.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "minipass": "^2.2.1"
-              }
-            },
-            "mkdirp": {
-              "version": "0.5.1",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "minimist": "0.0.8"
-              }
-            },
-            "ms": {
-              "version": "2.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "needle": {
-              "version": "2.2.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "debug": "^2.1.2",
-                "iconv-lite": "^0.4.4",
-                "sax": "^1.2.4"
-              }
-            },
-            "node-pre-gyp": {
-              "version": "0.10.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "detect-libc": "^1.0.2",
-                "mkdirp": "^0.5.1",
-                "needle": "^2.2.0",
-                "nopt": "^4.0.1",
-                "npm-packlist": "^1.1.6",
-                "npmlog": "^4.0.2",
-                "rc": "^1.1.7",
-                "rimraf": "^2.6.1",
-                "semver": "^5.3.0",
-                "tar": "^4"
-              }
-            },
-            "nopt": {
-              "version": "4.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "abbrev": "1",
-                "osenv": "^0.1.4"
-              }
-            },
-            "npm-bundled": {
-              "version": "1.0.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "npm-packlist": {
-              "version": "1.1.10",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "ignore-walk": "^3.0.1",
-                "npm-bundled": "^1.0.1"
-              }
-            },
-            "npmlog": {
-              "version": "4.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "are-we-there-yet": "~1.1.2",
-                "console-control-strings": "~1.1.0",
-                "gauge": "~2.7.3",
-                "set-blocking": "~2.0.0"
-              }
-            },
-            "number-is-nan": {
-              "version": "1.0.1",
-              "bundled": true,
-              "dev": true
-            },
-            "object-assign": {
-              "version": "4.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "once": {
-              "version": "1.4.0",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "wrappy": "1"
-              }
-            },
-            "os-homedir": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "os-tmpdir": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "osenv": {
-              "version": "0.1.5",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "os-homedir": "^1.0.0",
-                "os-tmpdir": "^1.0.0"
-              }
-            },
-            "path-is-absolute": {
-              "version": "1.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "process-nextick-args": {
-              "version": "2.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "rc": {
-              "version": "1.2.7",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "deep-extend": "^0.5.1",
-                "ini": "~1.3.0",
-                "minimist": "^1.2.0",
-                "strip-json-comments": "~2.0.1"
-              },
-              "dependencies": {
-                "minimist": {
-                  "version": "1.2.0",
-                  "bundled": true,
-                  "dev": true,
-                  "optional": true
-                }
-              }
-            },
-            "readable-stream": {
-              "version": "2.3.6",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "core-util-is": "~1.0.0",
-                "inherits": "~2.0.3",
-                "isarray": "~1.0.0",
-                "process-nextick-args": "~2.0.0",
-                "safe-buffer": "~5.1.1",
-                "string_decoder": "~1.1.1",
-                "util-deprecate": "~1.0.1"
-              }
-            },
-            "rimraf": {
-              "version": "2.6.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "glob": "^7.0.5"
-              }
-            },
-            "safe-buffer": {
-              "version": "5.1.1",
-              "bundled": true,
-              "dev": true
-            },
-            "safer-buffer": {
-              "version": "2.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "sax": {
-              "version": "1.2.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "semver": {
-              "version": "5.5.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "set-blocking": {
-              "version": "2.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "signal-exit": {
-              "version": "3.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "string-width": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "code-point-at": "^1.0.0",
-                "is-fullwidth-code-point": "^1.0.0",
-                "strip-ansi": "^3.0.0"
-              }
-            },
-            "string_decoder": {
-              "version": "1.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "safe-buffer": "~5.1.0"
-              }
-            },
-            "strip-ansi": {
-              "version": "3.0.1",
-              "bundled": true,
-              "dev": true,
-              "requires": {
-                "ansi-regex": "^2.0.0"
-              }
-            },
-            "strip-json-comments": {
-              "version": "2.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "tar": {
-              "version": "4.4.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "chownr": "^1.0.1",
-                "fs-minipass": "^1.2.5",
-                "minipass": "^2.2.4",
-                "minizlib": "^1.1.0",
-                "mkdirp": "^0.5.0",
-                "safe-buffer": "^5.1.1",
-                "yallist": "^3.0.2"
-              }
-            },
-            "util-deprecate": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "wide-align": {
-              "version": "1.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "string-width": "^1.0.2"
-              }
-            },
-            "wrappy": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true
-            },
-            "yallist": {
-              "version": "3.0.2",
-              "bundled": true,
-              "dev": true
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-          "dev": true,
-          "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
-          }
-        },
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
+        "p-limit": "^3.0.2"
       }
     },
-    "sax": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
-      "dev": true
-    },
-    "semver": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
-      "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
-      "dev": true
-    },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
       "dev": true
     },
-    "set-immediate-shim": {
+    "parent-module": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
-      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
-      "dev": true,
-      "optional": true
-    },
-    "set-value": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
-      "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-extendable": "^0.1.1",
-        "is-plain-object": "^2.0.3",
-        "split-string": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
+        "callsites": "^3.0.0"
       }
     },
-    "shebang-command": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+    "parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
       "dev": true,
       "requires": {
-        "shebang-regex": "^1.0.0"
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
       }
     },
-    "shebang-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+    "path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
       "dev": true
     },
-    "shellwords": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
-      "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
       "dev": true
     },
-    "signal-exit": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+    "path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
       "dev": true
     },
-    "sisteransi": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz",
-      "integrity": "sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g==",
+    "path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
       "dev": true
     },
-    "slash": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
-      "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+    "path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
-    "slice-ansi": {
+    "picocolors": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
-      "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
-      "dev": true,
-      "requires": {
-        "is-fullwidth-code-point": "^2.0.0"
-      }
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "dev": true
+    },
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true
+    },
+    "pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true
     },
-    "snapdragon": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
-      "dev": true,
-      "requires": {
-        "base": "^0.11.1",
-        "debug": "^2.2.0",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "map-cache": "^0.2.2",
-        "source-map": "^0.5.6",
-        "source-map-resolve": "^0.5.0",
-        "use": "^3.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
+    "pirates": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
+      "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
+      "dev": true
     },
-    "snapdragon-node": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+    "pkg-dir": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
       "dev": true,
       "requires": {
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.0",
-        "snapdragon-util": "^3.0.1"
+        "find-up": "^4.0.0"
       },
       "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
           "dev": true,
           "requires": {
-            "is-descriptor": "^1.0.0"
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
           }
         },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "p-locate": "^4.1.0"
           }
         },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
           "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "p-try": "^2.0.0"
           }
         },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "p-limit": "^2.2.0"
           }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
-    "snapdragon-util": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+    "pngjs": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
+      "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
+      "dev": true
+    },
+    "prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true
+    },
+    "pretty-format": {
+      "version": "29.5.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz",
+      "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.2.0"
+        "@jest/schemas": "^29.4.3",
+        "ansi-styles": "^5.0.0",
+        "react-is": "^18.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+          "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+          "dev": true
+        }
       }
     },
-    "source-map": {
-      "version": "0.5.7",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-      "dev": true
-    },
-    "source-map-resolve": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
-      "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+    "prompts": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+      "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
       "dev": true,
       "requires": {
-        "atob": "^2.1.1",
-        "decode-uri-component": "^0.2.0",
-        "resolve-url": "^0.2.1",
-        "source-map-url": "^0.4.0",
-        "urix": "^0.1.0"
+        "kleur": "^3.0.3",
+        "sisteransi": "^1.0.5"
       }
     },
-    "source-map-support": {
-      "version": "0.4.18",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
-      "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+    "prop-types": {
+      "version": "15.8.1",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
       "dev": true,
+      "peer": true,
       "requires": {
-        "source-map": "^0.5.6"
+        "loose-envify": "^1.4.0",
+        "object-assign": "^4.1.1",
+        "react-is": "^16.13.1"
+      },
+      "dependencies": {
+        "react-is": {
+          "version": "16.13.1",
+          "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+          "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+          "dev": true,
+          "peer": true
+        }
       }
     },
-    "source-map-url": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
-      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+    "punycode": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+      "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
       "dev": true
     },
-    "spawn-sync": {
-      "version": "1.0.15",
-      "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
-      "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=",
+    "pure-rand": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz",
+      "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==",
+      "dev": true
+    },
+    "queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true
+    },
+    "react-is": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+      "dev": true
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
       "dev": true,
+      "optional": true,
       "requires": {
-        "concat-stream": "^1.4.7",
-        "os-shim": "^0.1.2"
+        "picomatch": "^2.2.1"
       }
     },
-    "spdx-correct": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
-      "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
+    "regenerate": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+      "dev": true
+    },
+    "regenerate-unicode-properties": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz",
+      "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==",
       "dev": true,
       "requires": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
+        "regenerate": "^1.4.2"
       }
     },
-    "spdx-exceptions": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
-      "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
+    "regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
       "dev": true
     },
-    "spdx-expression-parse": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+    "regenerator-transform": {
+      "version": "0.15.1",
+      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz",
+      "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==",
       "dev": true,
       "requires": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
+        "@babel/runtime": "^7.8.4"
       }
     },
-    "spdx-license-ids": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
-      "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==",
-      "dev": true
-    },
-    "split-string": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+    "regexp.prototype.flags": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^3.0.0"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "functions-have-names": "^1.2.2"
       }
     },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+    "regexpp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
       "dev": true
     },
-    "sshpk": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
-      "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
+    "regexpu-core": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz",
+      "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==",
       "dev": true,
       "requires": {
-        "asn1": "~0.2.3",
-        "assert-plus": "^1.0.0",
-        "bcrypt-pbkdf": "^1.0.0",
-        "dashdash": "^1.12.0",
-        "ecc-jsbn": "~0.1.1",
-        "getpass": "^0.1.1",
-        "jsbn": "~0.1.0",
-        "tweetnacl": "~0.14.0"
+        "@babel/regjsgen": "^0.8.0",
+        "regenerate": "^1.4.2",
+        "regenerate-unicode-properties": "^10.1.0",
+        "regjsparser": "^0.9.1",
+        "unicode-match-property-ecmascript": "^2.0.0",
+        "unicode-match-property-value-ecmascript": "^2.1.0"
       }
     },
-    "stack-utils": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz",
-      "integrity": "sha1-1PM6tU6OOHeLDKXP07OvsS22hiA=",
-      "dev": true
-    },
-    "static-extend": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+    "regjsparser": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
+      "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
       "dev": true,
       "requires": {
-        "define-property": "^0.2.5",
-        "object-copy": "^0.1.0"
+        "jsesc": "~0.5.0"
       },
       "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
+        "jsesc": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+          "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
+          "dev": true
         }
       }
     },
-    "stealthy-require": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
-      "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
       "dev": true
     },
-    "string-length": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
-      "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=",
+    "resolve": {
+      "version": "1.22.2",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
+      "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
       "dev": true,
       "requires": {
-        "astral-regex": "^1.0.0",
-        "strip-ansi": "^4.0.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        }
+        "is-core-module": "^2.11.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
       }
     },
-    "string-width": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+    "resolve-cwd": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+      "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
       "dev": true,
       "requires": {
-        "is-fullwidth-code-point": "^2.0.0",
-        "strip-ansi": "^4.0.0"
+        "resolve-from": "^5.0.0"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
           "dev": true
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
         }
       }
     },
-    "string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "resolve.exports": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
+      "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
+      "dev": true
+    },
+    "reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
       "dev": true,
       "requires": {
-        "safe-buffer": "~5.1.0"
+        "glob": "^7.1.3"
       }
     },
-    "strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+    "run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
       "dev": true,
       "requires": {
-        "ansi-regex": "^2.0.0"
+        "queue-microtask": "^1.2.2"
       }
     },
-    "strip-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-      "dev": true
-    },
-    "strip-eof": {
+    "safe-regex-test": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
-      "dev": true
+      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+      "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.3",
+        "is-regex": "^1.1.4"
+      }
     },
-    "strip-json-comments": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+    "semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
       "dev": true
     },
-    "supports-color": {
+    "shebang-command": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-      "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-      "dev": true
-    },
-    "symbol-tree": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
-      "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
-      "dev": true
-    },
-    "table": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
-      "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
       "dev": true,
       "requires": {
-        "ajv": "^5.2.3",
-        "ajv-keywords": "^2.1.0",
-        "chalk": "^2.1.0",
-        "lodash": "^4.17.4",
-        "slice-ansi": "1.0.0",
-        "string-width": "^2.1.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-          "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "shebang-regex": "^3.0.0"
       }
     },
-    "test-exclude": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.1.tgz",
-      "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==",
+    "shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true
+    },
+    "side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
       "dev": true,
-      "requires": {
-        "arrify": "^1.0.1",
-        "micromatch": "^3.1.8",
-        "object-assign": "^4.1.0",
-        "read-pkg-up": "^1.0.1",
-        "require-main-filename": "^1.0.1"
-      },
-      "dependencies": {
-        "arr-diff": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-          "dev": true
-        },
-        "array-unique": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "expand-brackets": {
-          "version": "2.1.4",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-          "dev": true,
-          "requires": {
-            "debug": "^2.3.3",
-            "define-property": "^0.2.5",
-            "extend-shallow": "^2.0.1",
-            "posix-character-classes": "^0.1.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "0.2.5",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^0.1.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            },
-            "is-accessor-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-data-descriptor": {
-              "version": "0.1.4",
-              "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-              "dev": true,
-              "requires": {
-                "kind-of": "^3.0.2"
-              },
-              "dependencies": {
-                "kind-of": {
-                  "version": "3.2.2",
-                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-                  "dev": true,
-                  "requires": {
-                    "is-buffer": "^1.1.5"
-                  }
-                }
-              }
-            },
-            "is-descriptor": {
-              "version": "0.1.6",
-              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-              "dev": true,
-              "requires": {
-                "is-accessor-descriptor": "^0.1.6",
-                "is-data-descriptor": "^0.1.4",
-                "kind-of": "^5.0.0"
-              }
-            },
-            "kind-of": {
-              "version": "5.1.0",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-              "dev": true
-            }
-          }
-        },
-        "extglob": {
-          "version": "2.0.4",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-          "dev": true,
-          "requires": {
-            "array-unique": "^0.3.2",
-            "define-property": "^1.0.0",
-            "expand-brackets": "^2.1.4",
-            "extend-shallow": "^2.0.1",
-            "fragment-cache": "^0.2.1",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "define-property": {
-              "version": "1.0.0",
-              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-              "dev": true,
-              "requires": {
-                "is-descriptor": "^1.0.0"
-              }
-            },
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-          "dev": true
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        },
-        "load-json-file": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
-          "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "parse-json": "^2.2.0",
-            "pify": "^2.0.0",
-            "pinkie-promise": "^2.0.0",
-            "strip-bom": "^2.0.0"
-          }
-        },
-        "micromatch": {
-          "version": "3.1.10",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-          "dev": true,
-          "requires": {
-            "arr-diff": "^4.0.0",
-            "array-unique": "^0.3.2",
-            "braces": "^2.3.1",
-            "define-property": "^2.0.2",
-            "extend-shallow": "^3.0.2",
-            "extglob": "^2.0.4",
-            "fragment-cache": "^0.2.1",
-            "kind-of": "^6.0.2",
-            "nanomatch": "^1.2.9",
-            "object.pick": "^1.3.0",
-            "regex-not": "^1.0.0",
-            "snapdragon": "^0.8.1",
-            "to-regex": "^3.0.2"
-          }
-        },
-        "path-type": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-          "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "pify": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "read-pkg": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
-          "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
-          "dev": true,
-          "requires": {
-            "load-json-file": "^1.0.0",
-            "normalize-package-data": "^2.3.2",
-            "path-type": "^1.0.0"
-          }
-        },
-        "read-pkg-up": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
-          "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
-          "dev": true,
-          "requires": {
-            "find-up": "^1.0.0",
-            "read-pkg": "^1.0.0"
-          }
-        },
-        "strip-bom": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
-          "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
-          "dev": true,
-          "requires": {
-            "is-utf8": "^0.2.0"
-          }
-        }
+      "requires": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
       }
     },
-    "text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+    "signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "dev": true
     },
-    "throat": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
-      "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=",
+    "sisteransi": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+      "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
       "dev": true
     },
-    "through": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+    "slash": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+      "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+      "dev": true
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
       "dev": true
     },
-    "tmp": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
-      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+    "source-map-support": {
+      "version": "0.5.13",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+      "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
       "dev": true,
       "requires": {
-        "os-tmpdir": "~1.0.2"
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
       }
     },
-    "tmpl": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
-      "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
-      "dev": true
-    },
-    "to-fast-properties": {
+    "sprintf-js": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
-      "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
       "dev": true
     },
-    "to-object-path": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+    "stack-utils": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+      "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
+        "escape-string-regexp": "^2.0.0"
+      },
+      "dependencies": {
+        "escape-string-regexp": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+          "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+          "dev": true
+        }
       }
     },
-    "to-regex": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+    "stop-iteration-iterator": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
+      "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
       "dev": true,
       "requires": {
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "regex-not": "^1.0.2",
-        "safe-regex": "^1.1.0"
+        "internal-slot": "^1.0.4"
       }
     },
-    "to-regex-range": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+    "string-length": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+      "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+      "dev": true,
+      "requires": {
+        "char-regex": "^1.0.2",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
       "dev": true,
       "requires": {
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1"
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
       },
       "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          }
+        "emoji-regex": {
+          "version": "8.0.0",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+          "dev": true
         }
       }
     },
-    "tough-cookie": {
-      "version": "2.3.4",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
-      "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
+    "string.prototype.matchall": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
+      "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==",
       "dev": true,
+      "peer": true,
       "requires": {
-        "punycode": "^1.4.1"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "get-intrinsic": "^1.1.3",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.3",
+        "regexp.prototype.flags": "^1.4.3",
+        "side-channel": "^1.0.4"
       }
     },
-    "tr46": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-      "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+    "string.prototype.trim": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
+      "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
       "dev": true,
       "requires": {
-        "punycode": "^2.1.0"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-          "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-          "dev": true
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
       }
     },
-    "trim-right": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
-      "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
-      "dev": true
-    },
-    "tunnel-agent": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+    "string.prototype.trimend": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
+      "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.0.1"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
       }
     },
-    "tweetnacl": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+    "string.prototype.trimstart": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
+      "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      }
     },
-    "type-check": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
-      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+    "strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
       "dev": true,
       "requires": {
-        "prelude-ls": "~1.1.2"
+        "ansi-regex": "^5.0.1"
       }
     },
-    "typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+    "strip-bom": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+      "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+      "dev": true
+    },
+    "strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true
+    },
+    "strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
       "dev": true
     },
-    "uglify-js": {
-      "version": "2.8.29",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
-      "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
-      "optional": true,
       "requires": {
-        "source-map": "~0.5.1",
-        "uglify-to-browserify": "~1.0.0",
-        "yargs": "~3.10.0"
-      },
-      "dependencies": {
-        "yargs": {
-          "version": "3.10.0",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
-          "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "camelcase": "^1.0.2",
-            "cliui": "^2.1.0",
-            "decamelize": "^1.0.0",
-            "window-size": "0.1.0"
-          }
-        }
+        "has-flag": "^3.0.0"
       }
     },
-    "uglify-to-browserify": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
-      "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+    "supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true
+    },
+    "test-exclude": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^7.1.4",
+        "minimatch": "^3.0.4"
+      }
     },
-    "union-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
-      "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true
+    },
+    "tmpl": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+      "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+      "dev": true
+    },
+    "to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+      "dev": true
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
       "requires": {
-        "arr-union": "^3.1.0",
-        "get-value": "^2.0.6",
-        "is-extendable": "^0.1.1",
-        "set-value": "^0.4.3"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "set-value": {
-          "version": "0.4.3",
-          "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
-          "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-extendable": "^0.1.1",
-            "is-plain-object": "^2.0.1",
-            "to-object-path": "^0.3.0"
-          }
-        }
+        "is-number": "^7.0.0"
       }
     },
-    "unset-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+    "tsconfig-paths": {
+      "version": "3.14.2",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
+      "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
       "dev": true,
       "requires": {
-        "has-value": "^0.3.1",
-        "isobject": "^3.0.0"
+        "@types/json5": "^0.0.29",
+        "json5": "^1.0.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
       },
       "dependencies": {
-        "has-value": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+        "json5": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+          "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
           "dev": true,
           "requires": {
-            "get-value": "^2.0.3",
-            "has-values": "^0.1.4",
-            "isobject": "^2.0.0"
-          },
-          "dependencies": {
-            "isobject": {
-              "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-              "dev": true,
-              "requires": {
-                "isarray": "1.0.0"
-              }
-            }
+            "minimist": "^1.2.0"
           }
         },
-        "has-values": {
-          "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
-          "dev": true
-        },
-        "isobject": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+        "strip-bom": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+          "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
           "dev": true
         }
       }
     },
-    "urix": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+    "tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
       "dev": true
     },
-    "urlgrey": {
-      "version": "0.4.4",
-      "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz",
-      "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=",
-      "dev": true
+    "tsutils": {
+      "version": "3.21.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+      "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.8.1"
+      }
     },
-    "use": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz",
-      "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==",
+    "type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
       "dev": true,
       "requires": {
-        "kind-of": "^6.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
+        "prelude-ls": "^1.2.1"
       }
     },
-    "user-home": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",
-      "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
+    "type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
       "dev": true
     },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+    "type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
       "dev": true
     },
-    "util.promisify": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
-      "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
+    "typed-array-length": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+      "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
       "dev": true,
       "requires": {
-        "define-properties": "^1.1.2",
-        "object.getownpropertydescriptors": "^2.0.3"
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "is-typed-array": "^1.1.9"
       }
     },
-    "uuid": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
-      "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
-      "dev": true
+    "typescript": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
+      "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
+      "dev": true,
+      "peer": true
     },
-    "v8flags": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz",
-      "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=",
+    "unbox-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+      "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
       "dev": true,
       "requires": {
-        "user-home": "^1.1.1"
+        "call-bind": "^1.0.2",
+        "has-bigints": "^1.0.2",
+        "has-symbols": "^1.0.3",
+        "which-boxed-primitive": "^1.0.2"
       }
     },
-    "validate-npm-package-license": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
-      "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
+    "unicode-canonical-property-names-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
+      "dev": true
+    },
+    "unicode-match-property-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
       "dev": true,
       "requires": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
+        "unicode-canonical-property-names-ecmascript": "^2.0.0",
+        "unicode-property-aliases-ecmascript": "^2.0.0"
       }
     },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+    "unicode-match-property-value-ecmascript": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
+      "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
+      "dev": true
+    },
+    "unicode-property-aliases-ecmascript": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
+      "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
+      "dev": true
+    },
+    "update-browserslist-db": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+      "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
+        "escalade": "^3.1.1",
+        "picocolors": "^1.0.0"
       }
     },
-    "w3c-hr-time": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
-      "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
       "dev": true,
       "requires": {
-        "browser-process-hrtime": "^0.1.2"
+        "punycode": "^2.1.0"
       }
     },
-    "walker": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
-      "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=",
+    "v8-to-istanbul": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+      "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
       "dev": true,
       "requires": {
-        "makeerror": "1.0.x"
+        "@jridgewell/trace-mapping": "^0.3.12",
+        "@types/istanbul-lib-coverage": "^2.0.1",
+        "convert-source-map": "^1.6.0"
       }
     },
-    "watch": {
-      "version": "0.18.0",
-      "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz",
-      "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=",
+    "walker": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+      "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
       "dev": true,
       "requires": {
-        "exec-sh": "^0.2.0",
-        "minimist": "^1.2.0"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
+        "makeerror": "1.0.12"
       }
     },
-    "webidl-conversions": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
-      "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
-      "dev": true
-    },
-    "whatwg-encoding": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz",
-      "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==",
+    "which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
       "dev": true,
       "requires": {
-        "iconv-lite": "0.4.19"
-      },
-      "dependencies": {
-        "iconv-lite": {
-          "version": "0.4.19",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
-          "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
-          "dev": true
-        }
+        "isexe": "^2.0.0"
       }
     },
-    "whatwg-mimetype": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz",
-      "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==",
-      "dev": true
-    },
-    "whatwg-url": {
-      "version": "6.5.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
-      "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
+    "which-boxed-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
       "dev": true,
       "requires": {
-        "lodash.sortby": "^4.7.0",
-        "tr46": "^1.0.1",
-        "webidl-conversions": "^4.0.2"
+        "is-bigint": "^1.0.1",
+        "is-boolean-object": "^1.1.0",
+        "is-number-object": "^1.0.4",
+        "is-string": "^1.0.5",
+        "is-symbol": "^1.0.3"
       }
     },
-    "which": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
-      "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
+    "which-collection": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
+      "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
       "dev": true,
       "requires": {
-        "isexe": "^2.0.0"
+        "is-map": "^2.0.1",
+        "is-set": "^2.0.1",
+        "is-weakmap": "^2.0.1",
+        "is-weakset": "^2.0.1"
       }
     },
-    "which-module": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
-      "dev": true
-    },
-    "window-size": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
-      "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+    "which-typed-array": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
+      "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
       "dev": true,
-      "optional": true
+      "requires": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0",
+        "is-typed-array": "^1.1.10"
+      }
     },
-    "wordwrap": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
-      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+    "word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
       "dev": true
     },
     "wrap-ansi": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
-      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
       "dev": true,
       "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1"
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
       },
       "dependencies": {
-        "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "number-is-nan": "^1.0.0"
+            "color-convert": "^2.0.1"
           }
         },
-        "string-width": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "code-point-at": "^1.0.0",
-            "is-fullwidth-code-point": "^1.0.0",
-            "strip-ansi": "^3.0.0"
+            "color-name": "~1.1.4"
           }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
         }
       }
     },
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
       "dev": true
     },
-    "write": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
-      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
-      "dev": true,
-      "requires": {
-        "mkdirp": "^0.5.1"
-      }
-    },
     "write-file-atomic": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
-      "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+      "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.11",
         "imurmurhash": "^0.1.4",
-        "signal-exit": "^3.0.2"
-      }
-    },
-    "ws": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
-      "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
-      "dev": true,
-      "requires": {
-        "async-limiter": "~1.0.0",
-        "safe-buffer": "~5.1.0"
+        "signal-exit": "^3.0.7"
       }
     },
-    "xml-name-validator": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
-      "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
-      "dev": true
-    },
     "y18n": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
-      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
       "dev": true
     },
     "yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
       "dev": true
     },
     "yargs": {
-      "version": "11.1.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
-      "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
+      "version": "17.7.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+      "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
       "dev": true,
       "requires": {
-        "cliui": "^4.0.0",
-        "decamelize": "^1.1.1",
-        "find-up": "^2.1.0",
-        "get-caller-file": "^1.0.1",
-        "os-locale": "^2.0.0",
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
         "require-directory": "^2.1.1",
-        "require-main-filename": "^1.0.1",
-        "set-blocking": "^2.0.0",
-        "string-width": "^2.0.0",
-        "which-module": "^2.0.0",
-        "y18n": "^3.2.1",
-        "yargs-parser": "^9.0.2"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "cliui": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
-          "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
-          "dev": true,
-          "requires": {
-            "string-width": "^2.1.1",
-            "strip-ansi": "^4.0.0",
-            "wrap-ansi": "^2.0.0"
-          }
-        },
-        "find-up": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
-          "dev": true,
-          "requires": {
-            "locate-path": "^2.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        }
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
       }
     },
     "yargs-parser": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz",
-      "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=",
-      "dev": true,
-      "requires": {
-        "camelcase": "^4.1.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-          "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
-          "dev": true
-        }
-      }
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true
+    },
+    "yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true
     }
   }
 }
diff --git a/package.json b/package.json
index 8c28349f47..95d6d28942 100644
--- a/package.json
+++ b/package.json
@@ -2,17 +2,6 @@
   "name": "javascript-algorithms-and-data-structures",
   "version": "0.0.4",
   "description": "Algorithms and data-structures implemented on JavaScript",
-  "main": "index.js",
-  "scripts": {
-    "lint": "eslint ./src/*",
-    "test": "jest",
-    "ci": "npm run lint && npm run test -- --coverage",
-    "codecov": "codecov"
-  },
-  "pre-commit": [
-    "lint",
-    "test"
-  ],
   "repository": {
     "type": "git",
     "url": "git+https://github.com/trekhleb/javascript-algorithms.git"
@@ -27,27 +16,39 @@
     "javascript-algorithms",
     "sorting-algorithms",
     "graph",
-    "tree"
+    "tree",
+    "interview",
+    "interview-preparation"
   ],
-  "author": "Oleksii Trekhleb (https://www.linkedin.com/in/trekhleb/)",
+  "author": "Oleksii Trekhleb (https://trekhleb.dev)",
   "license": "MIT",
   "bugs": {
     "url": "https://github.com/trekhleb/javascript-algorithms/issues"
   },
   "homepage": "https://github.com/trekhleb/javascript-algorithms#readme",
+  "main": "index.js",
+  "scripts": {
+    "lint": "eslint ./src/**",
+    "test": "jest",
+    "coverage": "npm run test -- --coverage",
+    "ci": "npm run lint && npm run coverage",
+    "prepare": "husky install"
+  },
   "devDependencies": {
-    "@types/jest": "^23.1.4",
-    "babel-cli": "^6.26.0",
-    "babel-preset-env": "^1.7.0",
-    "codecov": "^3.0.2",
-    "eslint": "^4.19.1",
-    "eslint-config-airbnb": "^17.0.0",
-    "eslint-plugin-import": "^2.13.0",
-    "eslint-plugin-jest": "^21.17.0",
-    "eslint-plugin-jsx-a11y": "^6.1.0",
-    "eslint-plugin-react": "^7.10.0",
-    "jest": "^23.3.0",
-    "pre-commit": "^1.2.2"
+    "@babel/cli": "7.20.7",
+    "@babel/preset-env": "7.20.2",
+    "@types/jest": "29.4.0",
+    "eslint": "8.33.0",
+    "eslint-config-airbnb": "19.0.4",
+    "eslint-plugin-import": "2.27.5",
+    "eslint-plugin-jest": "27.2.1",
+    "eslint-plugin-jsx-a11y": "6.7.1",
+    "husky": "8.0.3",
+    "jest": "29.4.1",
+    "pngjs": "^7.0.0"
   },
-  "dependencies": {}
+  "engines": {
+    "node": ">=16.15.0",
+    "npm": ">=8.5.5"
+  }
 }
diff --git a/src/algorithms/cryptography/caesar-cipher/README.md b/src/algorithms/cryptography/caesar-cipher/README.md
new file mode 100644
index 0000000000..d648a62ee9
--- /dev/null
+++ b/src/algorithms/cryptography/caesar-cipher/README.md
@@ -0,0 +1,33 @@
+# Caesar Cipher Algorithm
+
+_Read this in other languages:_
+[_Русский_](README.ru-RU.md)
+
+In cryptography, a **Caesar cipher**, also known as **Caesar's cipher**, the **shift cipher**, **Caesar's code** or **Caesar shift**, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of `3`, `D` would be replaced by `A`, `E` would become `B`, and so on. The method is named after Julius Caesar, who used it in his private correspondence.
+
+![Caesar Cipher Algorithm](https://upload.wikimedia.org/wikipedia/commons/4/4a/Caesar_cipher_left_shift_of_3.svg)
+
+## Example
+
+The transformation can be represented by aligning two alphabets; the cipher alphabet is the plain alphabet rotated left or right by some number of positions. For instance, here is a Caesar cipher using a left rotation of three places, equivalent to a right shift of 23 (the shift parameter is used as the key):
+
+```text
+Plain:    ABCDEFGHIJKLMNOPQRSTUVWXYZ
+Cipher:   XYZABCDEFGHIJKLMNOPQRSTUVW
+```
+
+When encrypting, a person looks up each letter of the message in the "plain" line and writes down the corresponding letter in the "cipher" line.
+
+```text
+Plaintext:  THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
+Ciphertext: QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD
+```
+
+## Complexity
+
+- Time: `O(|n|)`
+- Space: `O(|n|)`
+
+## References
+
+- [Caesar cipher on Wikipedia](https://en.wikipedia.org/wiki/Caesar_cipher)
diff --git a/src/algorithms/cryptography/caesar-cipher/README.ru-RU.md b/src/algorithms/cryptography/caesar-cipher/README.ru-RU.md
new file mode 100644
index 0000000000..dee4d14cd3
--- /dev/null
+++ b/src/algorithms/cryptography/caesar-cipher/README.ru-RU.md
@@ -0,0 +1,29 @@
+# Алгоритм шифра Цезаря
+
+В криптографии **шифр Цезаря**, также известный как **шифр сдвига**, **код Цезаря** или **сдвиг Цезаря**, является одним из самых простых и широко известных методов шифрования. Это вид шифра подстановки, в котором каждый символ в открытом тексте заменяется символом, находящимся на некотором постоянном числе позиций левее или правее него в алфавите. Например, в шифре со сдвигом вправо на `3`, `D` была бы заменена на `A`, `E` станет `B`, и так далее. Метод назван в честь Юлия Цезаря, который использовал его в своей личной переписке.
+
+![Алгоритм шифра Цезаря](https://upload.wikimedia.org/wikipedia/commons/4/4a/Caesar_cipher_left_shift_of_3.svg)
+
+## Пример
+Это преобразование можно представить как выравнивание двух алфавитов; алфавит шифра - это обычный алфавит, повёрнутый влево или вправо на некоторое количество позиций. Например, здесь приведен шифр Цезаря, использующий поворот влево на три позиции, что эквивалентно сдвигу вправо на 23 (параметр сдвига используется в качестве ключа):
+
+```text
+Обычный:    ABCDEFGHIJKLMNOPQRSTUVWXYZ
+Шифрованный:   XYZABCDEFGHIJKLMNOPQRSTUVW
+```
+
+При шифровании человек просматривает каждую букву сообщения в "открытой" строке и записывает соответствующую букву в "шифрованной" строке.
+
+```text
+Обычный текст:     THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
+Шифрованный текст: QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD
+```
+
+## Сложность
+
+- Время: `O(|n|)`
+- Пространство: `O(|n|)`
+
+## Ссылки
+
+- [Шифр Цезаря на Wikipedia](https://ru.wikipedia.org/wiki/Шифр_Цезаря)
diff --git a/src/algorithms/cryptography/caesar-cipher/__test__/caesarCipher.test.js b/src/algorithms/cryptography/caesar-cipher/__test__/caesarCipher.test.js
new file mode 100644
index 0000000000..90521cedf8
--- /dev/null
+++ b/src/algorithms/cryptography/caesar-cipher/__test__/caesarCipher.test.js
@@ -0,0 +1,40 @@
+import { caesarCipherEncrypt, caesarCipherDecrypt } from '../caesarCipher';
+
+describe('caesarCipher', () => {
+  it('should not change a string with zero shift', () => {
+    expect(caesarCipherEncrypt('abcd', 0)).toBe('abcd');
+    expect(caesarCipherDecrypt('abcd', 0)).toBe('abcd');
+  });
+
+  it('should cipher a string with different shifts', () => {
+    expect(caesarCipherEncrypt('abcde', 3)).toBe('defgh');
+    expect(caesarCipherDecrypt('defgh', 3)).toBe('abcde');
+
+    expect(caesarCipherEncrypt('abcde', 1)).toBe('bcdef');
+    expect(caesarCipherDecrypt('bcdef', 1)).toBe('abcde');
+
+    expect(caesarCipherEncrypt('xyz', 1)).toBe('yza');
+    expect(caesarCipherDecrypt('yza', 1)).toBe('xyz');
+  });
+
+  it('should be case insensitive', () => {
+    expect(caesarCipherEncrypt('ABCDE', 3)).toBe('defgh');
+  });
+
+  it('should correctly handle an empty strings', () => {
+    expect(caesarCipherEncrypt('', 3)).toBe('');
+  });
+
+  it('should not cipher unknown chars', () => {
+    expect(caesarCipherEncrypt('ab2cde', 3)).toBe('de2fgh');
+    expect(caesarCipherDecrypt('de2fgh', 3)).toBe('ab2cde');
+  });
+
+  it('should encrypt and decrypt full phrases', () => {
+    expect(caesarCipherEncrypt('THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG', 23))
+      .toBe('qeb nrfzh yoltk clu grjmp lsbo qeb ixwv ald');
+
+    expect(caesarCipherDecrypt('qeb nrfzh yoltk clu grjmp lsbo qeb ixwv ald', 23))
+      .toBe('the quick brown fox jumps over the lazy dog');
+  });
+});
diff --git a/src/algorithms/cryptography/caesar-cipher/caesarCipher.js b/src/algorithms/cryptography/caesar-cipher/caesarCipher.js
new file mode 100644
index 0000000000..ba92299062
--- /dev/null
+++ b/src/algorithms/cryptography/caesar-cipher/caesarCipher.js
@@ -0,0 +1,58 @@
+// Create alphabet array: ['a', 'b', 'c', ..., 'z'].
+const englishAlphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
+
+/**
+ * Generates a cipher map out of the alphabet.
+ * Example with a shift 3: {'a': 'd', 'b': 'e', 'c': 'f', ...}
+ *
+ * @param {string[]} alphabet - i.e. ['a', 'b', 'c', ... , 'z']
+ * @param {number} shift - i.e. 3
+ * @return {Object} - i.e. {'a': 'd', 'b': 'e', 'c': 'f', ..., 'z': 'c'}
+ */
+const getCipherMap = (alphabet, shift) => {
+  return alphabet
+    .reduce((charsMap, currentChar, charIndex) => {
+      const charsMapClone = { ...charsMap };
+      // Making the shift to be cyclic (i.e. with a shift of 1 - 'z' would be mapped to 'a').
+      let encryptedCharIndex = (charIndex + shift) % alphabet.length;
+      // Support negative shifts for creating a map for decryption
+      // (i.e. with shift -1 - 'a' would be mapped to 'z').
+      if (encryptedCharIndex < 0) {
+        encryptedCharIndex += alphabet.length;
+      }
+      charsMapClone[currentChar] = alphabet[encryptedCharIndex];
+      return charsMapClone;
+    }, {});
+};
+
+/**
+ * @param {string} str
+ * @param {number} shift
+ * @param {string[]} alphabet
+ * @return {string}
+ */
+export const caesarCipherEncrypt = (str, shift, alphabet = englishAlphabet) => {
+  // Create a cipher map:
+  const cipherMap = getCipherMap(alphabet, shift);
+  return str
+    .toLowerCase()
+    .split('')
+    .map((char) => cipherMap[char] || char)
+    .join('');
+};
+
+/**
+ * @param {string} str
+ * @param {number} shift
+ * @param {string[]} alphabet
+ * @return {string}
+ */
+export const caesarCipherDecrypt = (str, shift, alphabet = englishAlphabet) => {
+  // Create a cipher map:
+  const cipherMap = getCipherMap(alphabet, -shift);
+  return str
+    .toLowerCase()
+    .split('')
+    .map((char) => cipherMap[char] || char)
+    .join('');
+};
diff --git a/src/algorithms/cryptography/hill-cipher/README.md b/src/algorithms/cryptography/hill-cipher/README.md
new file mode 100644
index 0000000000..bf496e2be9
--- /dev/null
+++ b/src/algorithms/cryptography/hill-cipher/README.md
@@ -0,0 +1,96 @@
+# Hill Cipher
+
+The **Hill cipher** is a [polygraphic substitution](https://en.wikipedia.org/wiki/Polygraphic_substitution) cipher based on linear algebra.
+
+Each letter is represented by a number [modulo](https://en.wikipedia.org/wiki/Modular_arithmetic) `26`. Though this is not an essential feature of the cipher, this simple scheme is often used:
+
+| **Letter** | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
+| ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| **Number** | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
+
+## Encryption
+
+To encrypt a message, each block of `n` letters (considered as an `n`-component vector) is multiplied by an invertible `n × n` matrix, against modulus `26`.
+
+The matrix used for encryption is the _cipher key_, and it should be chosen randomly from the set of invertible `n × n` matrices (modulo `26`). The cipher can, of course, be adapted to an alphabet with any number of letters; all arithmetic just needs to be done modulo the number of letters instead of modulo `26`.
+
+Consider the message `ACT`, and the key below (or `GYB/NQK/URP` in letters):
+
+```
+| 6   24   1  |
+| 13  16   10 |
+| 20  17   15 |
+```
+
+Since `A` is`0`, `C` is `2` and `T` is `19`, the message is the vector:
+
+```
+|  0  |
+|  2  |
+|  19 |
+```
+
+Thus, the enciphered vector is given by:
+
+```
+| 6   24   1  |  |  0  |   |  67  |   |  15 |
+| 13  16   10 |  |  2  | = |  222 | ≡ |  14 | (mod 26)
+| 20  17   15 |  |  19 |   |  319 |   |  7  |
+```
+
+which corresponds to a ciphertext of `POH`.
+
+Now, suppose that our message is instead `CAT` (notice how we're using the same letters as in `ACT` here), or:
+
+```
+|  2  |
+|  0  |
+|  19 |
+```
+
+This time, the enciphered vector is given by:
+
+```
+| 6   24   1  |  |  2  |   |  31  |   |  5  |
+| 13  16   10 |  |  0  | = |  216 | ≡ |  8  | (mod 26)
+| 20  17   15 |  |  19 |   |  325 |   |  13 |
+```
+
+which corresponds to a ciphertext of `FIN`. Every letter has changed.
+
+## Decryption
+
+To decrypt the message, each block is multiplied by the inverse of the matrix used for encryption. We turn the ciphertext back into a vector, then simply multiply by the inverse matrix of the key matrix (`IFK/VIV/VMI` in letters). (See [matrix inversion](https://en.wikipedia.org/wiki/Matrix_inversion) for methods to calculate the inverse matrix.) We find that, modulo 26, the inverse of the matrix used in the previous example is:
+
+```
+                -1
+| 6   24   1  |                | 8   5    10 |
+| 13  16   10 |    (mod 26) ≡  | 21  8    21 |
+| 20  17   15 |                | 21  12   8  |
+```
+
+Taking the previous example ciphertext of `POH`, we get:
+
+```
+| 8   5    10 |  |  15 |   |  260 |   |  0  |
+| 21  8    21 |  |  14 | = |  574 | ≡ |  2  | (mod 26)
+| 21  12   8  |  |  7  |   |  539 |   |  19 |
+```
+
+which gets us back to `ACT`, as expected.
+
+## Defining the encrypting matrix
+
+Two complications exist in picking the encrypting matrix:
+
+1. Not all matrices have an inverse. The matrix will have an inverse if and only if its [determinant](https://en.wikipedia.org/wiki/Determinant) is not zero.
+2. The determinant of the encrypting matrix must not have any common factors with the modular base.
+
+Thus, if we work modulo `26` as above, the determinant must be nonzero, and must not be divisible by `2` or `13`. If the determinant is `0`, or has common factors with the modular base, then the matrix cannot be used in the Hill cipher, and another matrix must be chosen (otherwise it will not be possible to decrypt). Fortunately, matrices which satisfy the conditions to be used in the Hill cipher are fairly common.
+
+## References
+
+- [Hill cipher on Wikipedia](https://en.wikipedia.org/wiki/Hill_cipher)
+- [Matrix inversion on MathIsFun](https://www.mathsisfun.com/algebra/matrix-inverse.html)
+- [GeeksForGeeks](https://www.geeksforgeeks.org/hill-cipher/)
+
diff --git a/src/algorithms/cryptography/hill-cipher/_test_/hillCipher.test.js b/src/algorithms/cryptography/hill-cipher/_test_/hillCipher.test.js
new file mode 100644
index 0000000000..f540ae9ff1
--- /dev/null
+++ b/src/algorithms/cryptography/hill-cipher/_test_/hillCipher.test.js
@@ -0,0 +1,46 @@
+import { hillCipherEncrypt, hillCipherDecrypt } from '../hillCipher';
+
+describe('hillCipher', () => {
+  it('should throw an exception when trying to decipher', () => {
+    expect(hillCipherDecrypt).toThrowError('This method is not implemented yet');
+  });
+
+  it('should throw an error when message or keyString contains none letter character', () => {
+    const invalidCharacterInMessage = () => {
+      hillCipherEncrypt('hell3', 'helloworld');
+    };
+    const invalidCharacterInKeyString = () => {
+      hillCipherEncrypt('hello', 'hel12world');
+    };
+    expect(invalidCharacterInMessage).toThrowError(
+      'The message and key string can only contain letters',
+    );
+    expect(invalidCharacterInKeyString).toThrowError(
+      'The message and key string can only contain letters',
+    );
+  });
+
+  it('should throw an error when the length of the keyString has a square root which is not integer', () => {
+    const invalidLengthOfKeyString = () => {
+      hillCipherEncrypt('ab', 'ab');
+    };
+    expect(invalidLengthOfKeyString).toThrowError(
+      'Invalid key string length. The square root of the key string must be an integer',
+    );
+  });
+
+  it('should throw an error when the length of the keyString does not equal to the power of length of the message', () => {
+    const invalidLengthOfKeyString = () => {
+      hillCipherEncrypt('ab', 'aaabbbccc');
+    };
+    expect(invalidLengthOfKeyString).toThrowError(
+      'Invalid key string length. The key length must be a square of message length',
+    );
+  });
+
+  it('should encrypt passed message using Hill Cipher', () => {
+    expect(hillCipherEncrypt('ACT', 'GYBNQKURP')).toBe('POH');
+    expect(hillCipherEncrypt('CAT', 'GYBNQKURP')).toBe('FIN');
+    expect(hillCipherEncrypt('GFG', 'HILLMAGIC')).toBe('SWK');
+  });
+});
diff --git a/src/algorithms/cryptography/hill-cipher/hillCipher.js b/src/algorithms/cryptography/hill-cipher/hillCipher.js
new file mode 100644
index 0000000000..1fe3033860
--- /dev/null
+++ b/src/algorithms/cryptography/hill-cipher/hillCipher.js
@@ -0,0 +1,87 @@
+import * as mtrx from '../../math/matrix/Matrix';
+
+// The code of an 'A' character (equals to 65).
+const alphabetCodeShift = 'A'.codePointAt(0);
+const englishAlphabetSize = 26;
+
+/**
+ * Generates key matrix from given keyString.
+ *
+ * @param {string} keyString - a string to build a key matrix (must be of matrixSize^2 length).
+ * @return {number[][]} keyMatrix
+ */
+const generateKeyMatrix = (keyString) => {
+  const matrixSize = Math.sqrt(keyString.length);
+  if (!Number.isInteger(matrixSize)) {
+    throw new Error(
+      'Invalid key string length. The square root of the key string must be an integer',
+    );
+  }
+  let keyStringIndex = 0;
+  return mtrx.generate(
+    [matrixSize, matrixSize],
+    // Callback to get a value of each matrix cell.
+    // The order the matrix is being filled in is from left to right, from top to bottom.
+    () => {
+      // A → 0, B → 1, ..., a → 32, b → 33, ...
+      const charCodeShifted = (keyString.codePointAt(keyStringIndex)) % alphabetCodeShift;
+      keyStringIndex += 1;
+      return charCodeShifted;
+    },
+  );
+};
+
+/**
+ * Generates a message vector from a given message.
+ *
+ * @param {string} message - the message to encrypt.
+ * @return {number[][]} messageVector
+ */
+const generateMessageVector = (message) => {
+  return mtrx.generate(
+    [message.length, 1],
+    // Callback to get a value of each matrix cell.
+    // The order the matrix is being filled in is from left to right, from top to bottom.
+    (cellIndices) => {
+      const rowIndex = cellIndices[0];
+      return message.codePointAt(rowIndex) % alphabetCodeShift;
+    },
+  );
+};
+
+/**
+ * Encrypts the given message using Hill Cipher.
+ *
+ * @param {string} message plaintext
+ * @param {string} keyString
+ * @return {string} cipherString
+ */
+export function hillCipherEncrypt(message, keyString) {
+  // The keyString and message can only contain letters.
+  const onlyLettersRegExp = /^[a-zA-Z]+$/;
+  if (!onlyLettersRegExp.test(message) || !onlyLettersRegExp.test(keyString)) {
+    throw new Error('The message and key string can only contain letters');
+  }
+
+  const keyMatrix = generateKeyMatrix(keyString);
+  const messageVector = generateMessageVector(message);
+
+  // keyString.length must equal to square of message.length
+  if (keyMatrix.length !== message.length) {
+    throw new Error('Invalid key string length. The key length must be a square of message length');
+  }
+
+  const cipherVector = mtrx.dot(keyMatrix, messageVector);
+  let cipherString = '';
+  for (let row = 0; row < cipherVector.length; row += 1) {
+    const item = cipherVector[row];
+    cipherString += String.fromCharCode((item % englishAlphabetSize) + alphabetCodeShift);
+  }
+
+  return cipherString;
+}
+
+// @TODO: Implement this method.
+export const hillCipherDecrypt = () => {
+  throw new Error('This method is not implemented yet');
+};
diff --git a/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js b/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js
index ba066c3138..d6f774efd8 100644
--- a/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js
+++ b/src/algorithms/cryptography/polynomial-hash/PolynomialHash.js
@@ -20,7 +20,7 @@ export default class PolynomialHash {
    * @return {number}
    */
   hash(word) {
-    const charCodes = Array.from(word).map(char => this.charToNumber(char));
+    const charCodes = Array.from(word).map((char) => this.charToNumber(char));
 
     let hash = 0;
     for (let charIndex = 0; charIndex < charCodes.length; charIndex += 1) {
diff --git a/src/algorithms/cryptography/rail-fence-cipher/README.md b/src/algorithms/cryptography/rail-fence-cipher/README.md
new file mode 100644
index 0000000000..d01395f554
--- /dev/null
+++ b/src/algorithms/cryptography/rail-fence-cipher/README.md
@@ -0,0 +1,28 @@
+# Rail Fence Cipher
+
+The **rail fence cipher** (also called a **zigzag cipher**) is a [transposition cipher](https://en.wikipedia.org/wiki/Transposition_cipher) in which the message is split across a set of rails on a fence for encoding. The fence is populated with the message's characters, starting at the top left and adding a character on each position, traversing them diagonally to the bottom. Upon reaching the last rail, the direction should then turn diagonal and upwards up to the very first rail in a zig-zag motion. Rinse and repeat until the message is fully disposed across the fence. The encoded message is the result of concatenating the text in each rail, from top to bottom.
+
+From [wikipedia](https://en.wikipedia.org/wiki/Rail_fence_cipher), this is what the message `WE ARE DISCOVERED. FLEE AT ONCE` looks like on a `3`-rail fence:
+
+```
+W . . . E . . . C . . . R . . . L . . . T . . . E
+. E . R . D . S . O . E . E . F . E . A . O . C .
+. . A . . . I . . . V . . . D . . . E . . . N . .
+-------------------------------------------------
+             WECRLTEERDSOEEFEAOCAIVDEN
+```
+
+The message can then be decoded by re-creating the encoded fence, with the same traversal pattern, except characters should only be added on one rail at a time. To illustrate that, a dash can be added on the rails that are not supposed to be populated yet. This is what the fence would look like after populating the first rail, the dashes represent positions that were visited but not populated.
+
+```
+W . . . E . . . C . . . R . . . L . . . T . . . E
+. - . - . - . - . - . - . - . - . - . - . - . - .
+. . - . . . - . . . - . . . - . . . - . . . - . .
+```
+
+It's time to start populating the next rail once the number of visited fence positions is equal to the number of characters in the message.
+
+## References
+
+- [Rail Fence Cipher on Wikipedia](https://en.wikipedia.org/wiki/Rail_fence_cipher)
+- [Rail Fence Cipher Calculator](https://crypto.interactive-maths.com/rail-fence-cipher.html)
diff --git a/src/algorithms/cryptography/rail-fence-cipher/__test__/railFenceCipher.test.js b/src/algorithms/cryptography/rail-fence-cipher/__test__/railFenceCipher.test.js
new file mode 100644
index 0000000000..db0c49eb6b
--- /dev/null
+++ b/src/algorithms/cryptography/rail-fence-cipher/__test__/railFenceCipher.test.js
@@ -0,0 +1,43 @@
+import { encodeRailFenceCipher, decodeRailFenceCipher } from '../railFenceCipher';
+
+describe('railFenceCipher', () => {
+  it('encodes a string correctly for base=3', () => {
+    expect(encodeRailFenceCipher('', 3)).toBe('');
+    expect(encodeRailFenceCipher('12345', 3)).toBe(
+      '15243',
+    );
+    expect(encodeRailFenceCipher('WEAREDISCOVEREDFLEEATONCE', 3)).toBe(
+      'WECRLTEERDSOEEFEAOCAIVDEN',
+    );
+    expect(encodeRailFenceCipher('Hello, World!', 3)).toBe(
+      'Hoo!el,Wrdl l',
+    );
+  });
+
+  it('decodes a string correctly for base=3', () => {
+    expect(decodeRailFenceCipher('', 3)).toBe('');
+    expect(decodeRailFenceCipher('WECRLTEERDSOEEFEAOCAIVDEN', 3)).toBe(
+      'WEAREDISCOVEREDFLEEATONCE',
+    );
+    expect(decodeRailFenceCipher('Hoo!el,Wrdl l', 3)).toBe(
+      'Hello, World!',
+    );
+    expect(decodeRailFenceCipher('15243', 3)).toBe(
+      '12345',
+    );
+  });
+
+  it('encodes a string correctly for base=4', () => {
+    expect(encodeRailFenceCipher('', 4)).toBe('');
+    expect(encodeRailFenceCipher('THEYAREATTACKINGFROMTHENORTH', 4)).toBe(
+      'TEKOOHRACIRMNREATANFTETYTGHH',
+    );
+  });
+
+  it('decodes a string correctly for base=4', () => {
+    expect(decodeRailFenceCipher('', 4)).toBe('');
+    expect(decodeRailFenceCipher('TEKOOHRACIRMNREATANFTETYTGHH', 4)).toBe(
+      'THEYAREATTACKINGFROMTHENORTH',
+    );
+  });
+});
diff --git a/src/algorithms/cryptography/rail-fence-cipher/railFenceCipher.js b/src/algorithms/cryptography/rail-fence-cipher/railFenceCipher.js
new file mode 100644
index 0000000000..7b58037e22
--- /dev/null
+++ b/src/algorithms/cryptography/rail-fence-cipher/railFenceCipher.js
@@ -0,0 +1,242 @@
+/**
+ * @typedef {string[]} Rail
+ * @typedef {Rail[]} Fence
+ * @typedef {number} Direction
+ */
+
+/**
+ * @constant DIRECTIONS
+ * @type {object}
+ * @property {Direction} UP
+ * @property {Direction} DOWN
+ */
+const DIRECTIONS = { UP: -1, DOWN: 1 };
+
+/**
+ * Builds a fence with a specific number of rows.
+ *
+ * @param {number} rowsNum
+ * @returns {Fence}
+ */
+const buildFence = (rowsNum) => Array(rowsNum)
+  .fill(null)
+  .map(() => []);
+
+/**
+ * Get next direction to move (based on the current one) while traversing the fence.
+ *
+ * @param {object} params
+ * @param {number} params.railCount - Number of rows in the fence
+ * @param {number} params.currentRail - Current row that we're visiting
+ * @param {Direction} params.direction - Current direction
+ * @returns {Direction} - The next direction to take
+ */
+const getNextDirection = ({ railCount, currentRail, direction }) => {
+  switch (currentRail) {
+    case 0:
+      // Go down if we're on top of the fence.
+      return DIRECTIONS.DOWN;
+    case railCount - 1:
+      // Go up if we're at the bottom of the fence.
+      return DIRECTIONS.UP;
+    default:
+      // Continue with the same direction if we're in the middle of the fence.
+      return direction;
+  }
+};
+
+/**
+ * @param {number} targetRailIndex
+ * @param {string} letter
+ * @returns {Function}
+ */
+const addCharToRail = (targetRailIndex, letter) => {
+  /**
+   * Given a rail, adds a char to it if it matches a targetIndex.
+   *
+   * @param {Rail} rail
+   * @param {number} currentRail
+   * @returns {Rail}
+   */
+  function onEachRail(rail, currentRail) {
+    return currentRail === targetRailIndex
+      ? [...rail, letter]
+      : rail;
+  }
+  return onEachRail;
+};
+
+/**
+ * Hangs the characters on the fence.
+ *
+ * @param {object} params
+ * @param {Fence} params.fence
+ * @param {number} params.currentRail
+ * @param {Direction} params.direction
+ * @param {string[]} params.chars
+ * @returns {Fence}
+ */
+const fillEncodeFence = ({
+  fence,
+  currentRail,
+  direction,
+  chars,
+}) => {
+  if (chars.length === 0) {
+    // All chars have been placed on a fence.
+    return fence;
+  }
+
+  const railCount = fence.length;
+
+  // Getting the next character to place on a fence.
+  const [letter, ...nextChars] = chars;
+  const nextDirection = getNextDirection({
+    railCount,
+    currentRail,
+    direction,
+  });
+
+  return fillEncodeFence({
+    fence: fence.map(addCharToRail(currentRail, letter)),
+    currentRail: currentRail + nextDirection,
+    direction: nextDirection,
+    chars: nextChars,
+  });
+};
+
+/**
+ * @param {object} params
+ * @param {number} params.strLen
+ * @param {string[]} params.chars
+ * @param {Fence} params.fence
+ * @param {number} params.targetRail
+ * @param {Direction} params.direction
+ * @param {number[]} params.coords
+ * @returns {Fence}
+ */
+const fillDecodeFence = (params) => {
+  const {
+    strLen, chars, fence, targetRail, direction, coords,
+  } = params;
+
+  const railCount = fence.length;
+
+  if (chars.length === 0) {
+    return fence;
+  }
+
+  const [currentRail, currentColumn] = coords;
+  const shouldGoNextRail = currentColumn === strLen - 1;
+  const nextDirection = shouldGoNextRail
+    ? DIRECTIONS.DOWN
+    : getNextDirection(
+      { railCount, currentRail, direction },
+    );
+  const nextRail = shouldGoNextRail ? targetRail + 1 : targetRail;
+  const nextCoords = [
+    shouldGoNextRail ? 0 : currentRail + nextDirection,
+    shouldGoNextRail ? 0 : currentColumn + 1,
+  ];
+
+  const shouldAddChar = currentRail === targetRail;
+  const [currentChar, ...remainderChars] = chars;
+  const nextString = shouldAddChar ? remainderChars : chars;
+  const nextFence = shouldAddChar ? fence.map(addCharToRail(currentRail, currentChar)) : fence;
+
+  return fillDecodeFence({
+    strLen,
+    chars: nextString,
+    fence: nextFence,
+    targetRail: nextRail,
+    direction: nextDirection,
+    coords: nextCoords,
+  });
+};
+
+/**
+ * @param {object} params
+ * @param {number} params.strLen
+ * @param {Fence} params.fence
+ * @param {number} params.currentRail
+ * @param {Direction} params.direction
+ * @param {number[]} params.code
+ * @returns {string}
+ */
+const decodeFence = (params) => {
+  const {
+    strLen,
+    fence,
+    currentRail,
+    direction,
+    code,
+  } = params;
+
+  if (code.length === strLen) {
+    return code.join('');
+  }
+
+  const railCount = fence.length;
+
+  const [currentChar, ...nextRail] = fence[currentRail];
+  const nextDirection = getNextDirection(
+    { railCount, currentRail, direction },
+  );
+
+  return decodeFence({
+    railCount,
+    strLen,
+    currentRail: currentRail + nextDirection,
+    direction: nextDirection,
+    code: [...code, currentChar],
+    fence: fence.map((rail, idx) => (idx === currentRail ? nextRail : rail)),
+  });
+};
+
+/**
+ * Encodes the message using Rail Fence Cipher.
+ *
+ * @param {string} string - The string to be encoded
+ * @param {number} railCount - The number of rails in a fence
+ * @returns {string} - Encoded string
+ */
+export const encodeRailFenceCipher = (string, railCount) => {
+  const fence = buildFence(railCount);
+
+  const filledFence = fillEncodeFence({
+    fence,
+    currentRail: 0,
+    direction: DIRECTIONS.DOWN,
+    chars: string.split(''),
+  });
+
+  return filledFence.flat().join('');
+};
+
+/**
+ * Decodes the message using Rail Fence Cipher.
+ *
+ * @param {string} string - Encoded string
+ * @param {number} railCount - The number of rows in a fence
+ * @returns {string} - Decoded string.
+ */
+export const decodeRailFenceCipher = (string, railCount) => {
+  const strLen = string.length;
+  const emptyFence = buildFence(railCount);
+  const filledFence = fillDecodeFence({
+    strLen,
+    chars: string.split(''),
+    fence: emptyFence,
+    targetRail: 0,
+    direction: DIRECTIONS.DOWN,
+    coords: [0, 0],
+  });
+
+  return decodeFence({
+    strLen,
+    fence: filledFence,
+    currentRail: 0,
+    direction: DIRECTIONS.DOWN,
+    code: [],
+  });
+};
diff --git a/src/algorithms/graph/articulation-points/articulationPoints.js b/src/algorithms/graph/articulation-points/articulationPoints.js
index 038df06d01..0cc1db6e89 100644
--- a/src/algorithms/graph/articulation-points/articulationPoints.js
+++ b/src/algorithms/graph/articulation-points/articulationPoints.js
@@ -66,7 +66,7 @@ export default function articulationPoints(graph) {
       // Get minimum low discovery time from all neighbors.
       /** @param {GraphVertex} neighbor */
       visitedSet[currentVertex.getKey()].lowDiscoveryTime = currentVertex.getNeighbors()
-        .filter(earlyNeighbor => earlyNeighbor.getKey() !== previousVertex.getKey())
+        .filter((earlyNeighbor) => earlyNeighbor.getKey() !== previousVertex.getKey())
         /**
          * @param {number} lowestDiscoveryTime
          * @param {GraphVertex} neighbor
diff --git a/src/algorithms/graph/breadth-first-search/README.md b/src/algorithms/graph/breadth-first-search/README.md
index d9a076151a..06073abd8b 100644
--- a/src/algorithms/graph/breadth-first-search/README.md
+++ b/src/algorithms/graph/breadth-first-search/README.md
@@ -1,7 +1,7 @@
 # Breadth-First Search (BFS)
 
-Breadth-first search (BFS) is an algorithm for traversing 
-or searching tree or graph data structures. It starts at
+Breadth-first search (BFS) is an algorithm for traversing, 
+searching tree, or graph data structures. It starts at
 the tree root (or some arbitrary node of a graph, sometimes 
 referred to as a 'search key') and explores the neighbor
 nodes first, before moving to the next level neighbors.
@@ -13,3 +13,4 @@ nodes first, before moving to the next level neighbors.
 - [Wikipedia](https://en.wikipedia.org/wiki/Breadth-first_search)
 - [Tree Traversals (Inorder, Preorder and Postorder)](https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/)
 - [BFS vs DFS](https://www.geeksforgeeks.org/bfs-vs-dfs-binary-tree/)
+- [BFS Visualization](https://www.cs.usfca.edu/~galles/visualization/BFS.html)
diff --git a/src/algorithms/graph/bridges/graphBridges.js b/src/algorithms/graph/bridges/graphBridges.js
index 58c62866e8..58a64004bc 100644
--- a/src/algorithms/graph/bridges/graphBridges.js
+++ b/src/algorithms/graph/bridges/graphBridges.js
@@ -53,7 +53,7 @@ export default function graphBridges(graph) {
 
       // Check if current node is connected to any early node other then previous one.
       visitedSet[currentVertex.getKey()].lowDiscoveryTime = currentVertex.getNeighbors()
-        .filter(earlyNeighbor => earlyNeighbor.getKey() !== previousVertex.getKey())
+        .filter((earlyNeighbor) => earlyNeighbor.getKey() !== previousVertex.getKey())
         .reduce(
           /**
            * @param {number} lowestDiscoveryTime
diff --git a/src/algorithms/graph/depth-first-search/README.md b/src/algorithms/graph/depth-first-search/README.md
index 30f22ee7fd..b22a8aa9c8 100644
--- a/src/algorithms/graph/depth-first-search/README.md
+++ b/src/algorithms/graph/depth-first-search/README.md
@@ -13,3 +13,4 @@ along each branch before backtracking.
 - [Wikipedia](https://en.wikipedia.org/wiki/Depth-first_search)
 - [Tree Traversals (Inorder, Preorder and Postorder)](https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/)
 - [BFS vs DFS](https://www.geeksforgeeks.org/bfs-vs-dfs-binary-tree/)
+- [DFS Visualization](https://www.cs.usfca.edu/~galles/visualization/DFS.html)
diff --git a/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js b/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js
index 90ca744d8d..7dec9c2890 100644
--- a/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js
+++ b/src/algorithms/graph/detect-cycle/detectUndirectedCycleUsingDisjointSet.js
@@ -8,9 +8,9 @@ import DisjointSet from '../../../data-structures/disjoint-set/DisjointSet';
 export default function detectUndirectedCycleUsingDisjointSet(graph) {
   // Create initial singleton disjoint sets for each graph vertex.
   /** @param {GraphVertex} graphVertex */
-  const keyExtractor = graphVertex => graphVertex.getKey();
+  const keyExtractor = (graphVertex) => graphVertex.getKey();
   const disjointSet = new DisjointSet(keyExtractor);
-  graph.getAllVertices().forEach(graphVertex => disjointSet.makeSet(graphVertex));
+  graph.getAllVertices().forEach((graphVertex) => disjointSet.makeSet(graphVertex));
 
   // Go trough all graph edges one by one and check if edge vertices are from the
   // different sets. In this case joint those sets together. Do this until you find
diff --git a/src/algorithms/graph/dijkstra/README.ko-KR.md b/src/algorithms/graph/dijkstra/README.ko-KR.md
new file mode 100644
index 0000000000..e2595bbe23
--- /dev/null
+++ b/src/algorithms/graph/dijkstra/README.ko-KR.md
@@ -0,0 +1,16 @@
+# 다익스트라 알고리즘(Dijkstra's algorithm)
+
+다익스트라 알고리즘은 도로 네트워크 등을 나타낼 수 있는 그래프에서 노드 간의 최단 경로를 찾는 알고리즘입니다.
+
+이 알고리즘은 다양한 형태로 존재합니다. 다익스트라의 원래 형태는 두 노드 간의 최단 경로를 찾았지만, 더 일반적인 형태는 단일 노드를 "소스"노드로 수정하고 그래프의 소스에서 다른 모든 노드까지의 최단 경로를 찾아 최단 경로 트리(shortest-path tree)를 생성합니다.
+
+![Dijkstra](https://upload.wikimedia.org/wikipedia/commons/5/57/Dijkstra_Animation.gif)
+
+`a`와 `b` 사이의 최단 경로를 찾는 다익스트라 알고리즘입니다.
+가장 낮은 거리를 가지며 방문하지 않은 정점(vertex)를 선택하고, 이를 통해 방문하지 않은 각 이웃까지의 거리를 계산하며, 더 작은 경우 이웃의 거리를 업데이트합니다. 이웃에 대한 작업을 마치면 방문한 것으로 표시(빨간색으로 변경)합니다.
+
+## 참조
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
+- [On YouTube by Nathaniel Fan](https://www.youtube.com/watch?v=gdmfOwyQlcI&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [On YouTube by Tushar Roy](https://www.youtube.com/watch?v=lAXZGERcDf4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/graph/dijkstra/README.md b/src/algorithms/graph/dijkstra/README.md
index e1c5002f18..d94e2a3cdf 100644
--- a/src/algorithms/graph/dijkstra/README.md
+++ b/src/algorithms/graph/dijkstra/README.md
@@ -1,5 +1,8 @@
 # Dijkstra's Algorithm
 
+_Read this in other languages:_
+[_한국어_](README.ko-KR.md)
+
 Dijkstra's algorithm is an algorithm for finding the shortest 
 paths between nodes in a graph, which may represent, for example, 
 road networks. 
diff --git a/src/algorithms/graph/dijkstra/dijkstra.js b/src/algorithms/graph/dijkstra/dijkstra.js
index 3614376ea7..c5b47b08d3 100644
--- a/src/algorithms/graph/dijkstra/dijkstra.js
+++ b/src/algorithms/graph/dijkstra/dijkstra.js
@@ -1,30 +1,44 @@
 import PriorityQueue from '../../../data-structures/priority-queue/PriorityQueue';
 
 /**
- * @param {Graph} graph
- * @param {GraphVertex} startVertex
+ * @typedef {Object} ShortestPaths
+ * @property {Object} distances - shortest distances to all vertices
+ * @property {Object} previousVertices - shortest paths to all vertices.
+ */
+
+/**
+ * Implementation of Dijkstra algorithm of finding the shortest paths to graph nodes.
+ * @param {Graph} graph - graph we're going to traverse.
+ * @param {GraphVertex} startVertex - traversal start vertex.
+ * @return {ShortestPaths}
  */
 export default function dijkstra(graph, startVertex) {
+  // Init helper variables that we will need for Dijkstra algorithm.
   const distances = {};
   const visitedVertices = {};
   const previousVertices = {};
   const queue = new PriorityQueue();
 
   // Init all distances with infinity assuming that currently we can't reach
-  // any of the vertices except start one.
+  // any of the vertices except the start one.
   graph.getAllVertices().forEach((vertex) => {
     distances[vertex.getKey()] = Infinity;
     previousVertices[vertex.getKey()] = null;
   });
+
+  // We are already at the startVertex so the distance to it is zero.
   distances[startVertex.getKey()] = 0;
 
   // Init vertices queue.
   queue.add(startVertex, distances[startVertex.getKey()]);
 
+  // Iterate over the priority queue of vertices until it is empty.
   while (!queue.isEmpty()) {
+    // Fetch next closest vertex.
     const currentVertex = queue.poll();
 
-    graph.getNeighbors(currentVertex).forEach((neighbor) => {
+    // Iterate over every unvisited neighbor of the current vertex.
+    currentVertex.getNeighbors().forEach((neighbor) => {
       // Don't visit already visited vertices.
       if (!visitedVertices[neighbor.getKey()]) {
         // Update distances to every neighbor from current vertex.
@@ -33,15 +47,16 @@ export default function dijkstra(graph, startVertex) {
         const existingDistanceToNeighbor = distances[neighbor.getKey()];
         const distanceToNeighborFromCurrent = distances[currentVertex.getKey()] + edge.weight;
 
+        // If we've found shorter path to the neighbor - update it.
         if (distanceToNeighborFromCurrent < existingDistanceToNeighbor) {
           distances[neighbor.getKey()] = distanceToNeighborFromCurrent;
 
-          // Change priority.
+          // Change priority of the neighbor in a queue since it might have became closer.
           if (queue.hasValue(neighbor)) {
             queue.changePriority(neighbor, distances[neighbor.getKey()]);
           }
 
-          // Remember previous vertex.
+          // Remember previous closest vertex.
           previousVertices[neighbor.getKey()] = currentVertex;
         }
 
@@ -52,10 +67,12 @@ export default function dijkstra(graph, startVertex) {
       }
     });
 
-    // Add current vertex to visited ones.
+    // Add current vertex to visited ones to avoid visiting it again later.
     visitedVertices[currentVertex.getKey()] = currentVertex;
   }
 
+  // Return the set of shortest distances to all vertices and the set of
+  // shortest paths to all vertices in a graph.
   return {
     distances,
     previousVertices,
diff --git a/src/algorithms/graph/eulerian-path/eulerianPath.js b/src/algorithms/graph/eulerian-path/eulerianPath.js
index 0222fcbff7..c82c6fd134 100644
--- a/src/algorithms/graph/eulerian-path/eulerianPath.js
+++ b/src/algorithms/graph/eulerian-path/eulerianPath.js
@@ -75,7 +75,7 @@ export default function eulerianPath(graph) {
       [edgeToDelete] = currentEdges;
     } else {
       // If there are many edges left then we need to peek any of those except bridges.
-      [edgeToDelete] = currentEdges.filter(edge => !bridges[edge.getKey()]);
+      [edgeToDelete] = currentEdges.filter((edge) => !bridges[edge.getKey()]);
     }
 
     // Detect next current vertex.
diff --git a/src/algorithms/graph/hamiltonian-cycle/hamiltonianCycle.js b/src/algorithms/graph/hamiltonian-cycle/hamiltonianCycle.js
index 329821fe35..2a33bf7d69 100644
--- a/src/algorithms/graph/hamiltonian-cycle/hamiltonianCycle.js
+++ b/src/algorithms/graph/hamiltonian-cycle/hamiltonianCycle.js
@@ -20,7 +20,7 @@ function isSafe(adjacencyMatrix, verticesIndices, cycle, vertexCandidate) {
   }
 
   // Check if vertexCandidate is being added to the path for the first time.
-  const candidateDuplicate = cycle.find(vertex => vertex.getKey() === vertexCandidate.getKey());
+  const candidateDuplicate = cycle.find((vertex) => vertex.getKey() === vertexCandidate.getKey());
 
   return !candidateDuplicate;
 }
@@ -61,7 +61,7 @@ function hamiltonianCycleRecursive({
   cycle,
 }) {
   // Clone cycle in order to prevent it from modification by other DFS branches.
-  const currentCycle = [...cycle].map(vertex => new GraphVertex(vertex.value));
+  const currentCycle = [...cycle].map((vertex) => new GraphVertex(vertex.value));
 
   if (vertices.length === currentCycle.length) {
     // Hamiltonian path is found.
diff --git a/src/algorithms/graph/kruskal/README.ko-KR.md b/src/algorithms/graph/kruskal/README.ko-KR.md
new file mode 100644
index 0000000000..eefb6a6756
--- /dev/null
+++ b/src/algorithms/graph/kruskal/README.ko-KR.md
@@ -0,0 +1,29 @@
+# 크루스칼 알고리즘
+
+크루스칼 알고리즘은 두 트리를 연결하는 최소 간선 가중치를 찾는 최소 신장 트리 알고리즘입니다.
+각 단계에서 비용을 더하는 연결된 가중 그래프에 대한 최소 신장 트리를 찾기 때문에 그래프 이론에서의 그리디 알고리즘입니다. 즉, 트리의 모든 간선의 총 가중치가 최소화되는 모든 정점을 포함하는 트리를 형성하는 간선의 하위 집합을 찾습니다. 그래프가 연결되어 있지 않으면 최소 신장 포레스트(연결된 각 구성 요소의 최소 신장 트리)를 찾습니다.
+
+![Kruskal Algorithm](https://upload.wikimedia.org/wikipedia/commons/5/5c/MST_kruskal_en.gif)
+
+![Kruskal Demo](https://upload.wikimedia.org/wikipedia/commons/b/bb/KruskalDemo.gif)
+
+유클리드 거리를 기반으로 한 크루스칼 알고리즘의 데모입니다.
+
+## 최소 신장 트리
+
+**최소 신장 트리(MST)** 또는 최소 가중치 신장 트리는 연결된 간선 가중치 무 방향 그래프의 간선의 하위 집합으로, 사이클 없이 가능한 최소 총 간선 가중치로 모든 정점을 연결합니다. 즉, 간선 가중치의 합이 가능한 작은 신장 트리입니다. 보다 일반적으로, 간선-가중치 비방향 그래프(꼭 연결되지는 않음)에는 연결된 구성 요소에 대한 최소 신장 트리의 결합인 최소 신장 포레스트(minimum spanning forest)가 있습니다.
+
+![Minimum Spanning Tree](https://upload.wikimedia.org/wikipedia/commons/d/d2/Minimum_spanning_tree.svg)
+
+평면 그래프와 해당 최소 신장 트리입니다. 각 간선은 가중치로 레이블이 지정되며, 이 값은 길이에 거의 비례합니다.
+
+![Minimum Spanning Tree](https://upload.wikimedia.org/wikipedia/commons/c/c9/Multiple_minimum_spanning_trees.svg)
+
+이 그림은 그래프에 최소 신장 트리가 두 개 이상 있을 수 있음을 보여 줍니다. 그림에서 그래프 아래의 두 트리는 주어진 그래프에서 최소 신장 트리가 될 수 있는 두 가지 경우입니다.
+
+## 참조
+
+- [Minimum Spanning Tree on Wikipedia](https://en.wikipedia.org/wiki/Minimum_spanning_tree)
+- [Kruskal's Algorithm on Wikipedia](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm)
+- [Kruskal's Algorithm on YouTube by Tushar Roy](https://www.youtube.com/watch?v=fAuF0EuZVCk&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [Kruskal's Algorithm on YouTube by Michael Sambol](https://www.youtube.com/watch?v=71UQH7Pr9kU&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/graph/kruskal/README.md b/src/algorithms/graph/kruskal/README.md
index eb8a0ebf42..1a71390e2f 100644
--- a/src/algorithms/graph/kruskal/README.md
+++ b/src/algorithms/graph/kruskal/README.md
@@ -1,5 +1,8 @@
 # Kruskal's Algorithm
 
+_Read this in other languages:_
+[_한국어_](README.ko-KR.md)
+
 Kruskal's algorithm is a minimum-spanning-tree algorithm which 
 finds an edge of the least possible weight that connects any two 
 trees in the forest. It is a greedy algorithm in graph theory 
diff --git a/src/algorithms/graph/kruskal/kruskal.js b/src/algorithms/graph/kruskal/kruskal.js
index dd9968a001..296616a142 100644
--- a/src/algorithms/graph/kruskal/kruskal.js
+++ b/src/algorithms/graph/kruskal/kruskal.js
@@ -33,7 +33,7 @@ export default function kruskal(graph) {
   const sortedEdges = new QuickSort(sortingCallbacks).sort(graph.getAllEdges());
 
   // Create disjoint sets for all graph vertices.
-  const keyCallback = graphVertex => graphVertex.getKey();
+  const keyCallback = (graphVertex) => graphVertex.getKey();
   const disjointSet = new DisjointSet(keyCallback);
 
   graph.getAllVertices().forEach((graphVertex) => {
diff --git a/src/algorithms/image-processing/seam-carving/README.md b/src/algorithms/image-processing/seam-carving/README.md
new file mode 100644
index 0000000000..87004f7abe
--- /dev/null
+++ b/src/algorithms/image-processing/seam-carving/README.md
@@ -0,0 +1,509 @@
+# Content-aware image resizing in JavaScript
+
+![Content-aware image resizing in JavaScript](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/01-cover-02.png)
+
+> There is an [interactive version of this post](https://trekhleb.dev/blog/2021/content-aware-image-resizing-in-javascript/) available where you can upload and resize your custom images.
+
+## TL;DR
+
+There are many great articles written about the *Seam Carving algorithm* already, but I couldn't resist the temptation to explore this elegant, powerful, and *yet simple* algorithm on my own, and to write about my personal experience with it. Another point that drew my attention (as a creator of [javascript-algorithms](https://github.com/trekhleb/javascript-algorithms) repo) was the fact that *Dynamic Programming (DP)* approach might be smoothly applied to solve it. And, if you're like me and still on your "learning algorithms" journey, this algorithmic solution may enrich your personal DP arsenal.
+
+So, with this article I want to do three things:
+
+1. Provide you with an interactive **content-aware resizer** so that you could play around with resizing your own images
+2. Explain the idea behind the **Seam Carving algorithm**
+3. Explain the **dynamic programming approach** to implement the algorithm (we'll be using TypeScript for it)
+
+### Content-aware image resizing
+
+*Content-aware image resizing* might be applied when it comes to changing the image proportions (i.e. reducing the width while keeping the height) and when losing some parts of the image is not desirable. Doing the straightforward image scaling in this case would distort the objects in it. To preserve the proportions of the objects while changing the image proportions we may use the [Seam Carving algorithm](https://perso.crans.org/frenoy/matlab2012/seamcarving.pdf) that was introduced by *Shai Avidan* and *Ariel Shamir*.
+
+The example below shows how the original image width was reduced by 50% using *content-aware resizing* (left image) and *straightforward scaling* (right image). In this particular case, the left image looks more natural since the proportions of the balloons were preserved.
+
+![Content-aware image resizing](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/01-resizing-options.png)
+
+The Seam Carving algorithm's idea is to find the *seam* (continuous sequence of pixels) with the lowest contribution to the image content and then *carve* (remove) it. This process repeats over and over again until we get the required image width or height. In the example below you may see that the hot air balloon pixels contribute more to the content of the image than the sky pixels. Thus, the sky pixels are being removed first.
+
+![JS IMAGE CARVER DEMO](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/10-demo-01.gif)
+
+Finding the seam with the lowest energy is a computationally expensive task (especially for large images). To make the seam search faster the *dynamic programming* approach might be applied (we will go through the implementation details below).
+
+### Objects removal
+
+The importance of each pixel (so-called pixel's energy) is being calculated based on its color (`R`, `G`, `B`, `A`) difference between two neighbor pixels. Now, if we set the pixel energy to some really low level artificially (i.e. by drawing a mask on top of them), the Seam Carving algorithm would perform an **object removal** for us for free.
+
+![JS IMAGE CARVER OBJECT REMOVAL DEMO](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/10-demo-02.gif)
+
+### JS IMAGE CARVER demo
+
+I've created the [JS IMAGE CARVER](https://trekhleb.dev/js-image-carver/) web-app (and also [open-sourced it on GitHub](https://github.com/trekhleb/js-image-carver)) that you may use to play around with resizing of your custom images.
+
+### More examples
+
+Here are some more examples of how the algorithm copes with more complex backgrounds.
+
+Mountains on the background are being shrunk smoothly without visible seams.
+
+![Resizing demo with more complex backgrounds](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/11-demo-01.png)
+
+The same goes for the ocean waves. The algorithm preserved the wave structure without distorting the surfers.
+
+![Resizing demo with more complex backgrounds](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/11-demo-02.png)
+
+We need to keep in mind that the Seam Carving algorithm is not a silver bullet, and it may fail to resize the images where *most of the pixels are edges* (look important to the algorithm). In this case, it starts distorting even the important parts of the image. In the example below the content-aware image resizing looks pretty similar to a straightforward scaling since for the algorithm all the pixels look important, and it is hard for it to distinguish Van Gogh's face from the background.
+
+![Example when the algorithm does not work as expected](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/12-demo-01.png)
+
+## How Seam Carving algorithms works
+
+Imagine we have a `1000 x 500 px` picture, and we want to change its size to `500 x 500 px` to make it square (let's say the square ratio would better fit the Instagram feed). We might want to set up several **requirements to the resizing process** in this case:
+
+- *Preserve the important parts of the image* (i.e. if there were 5 trees before the resizing we want to have 5 trees after resizing as well).
+- *Preserve the proportions* of the important parts of the image (i.e. circle car wheels should not be squeezed to the ellipse wheels)
+
+To avoid changing the important parts of the image we may find the **continuous sequence of pixels (the seam)**, that goes from top to bottom and has *the lowest contribution to the content* of the image (avoids important parts) and then remove it. The seam removal will shrink the image by 1 pixel. We will then repeat this step until the image will get the desired width.
+
+The question is how to define *the importance of the pixel* and its contribution to the content (in the original paper the authors are using the term **energy of the pixel**). One of the ways to do it is to treat all the pixels that form the edges as important ones. In case if a pixel is a part of the edge its color would have a greater difference between the neighbors (left and right pixels) than the pixel that isn't a part of the edge.
+
+![Pixels color difference](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/30-pixel-energy-comparison.png)
+
+Assuming that the color of a pixel is represented by *4* numbers (`R` - red, `G` - green, `B` - blue, `A` - alpha) we may use the following formula to calculate the color difference (the pixel energy):
+
+![Pixel energy formula](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/20-energy-formula.png)
+
+Where:
+
+- `mEnergy` - *Energy* (importance) of the *middle* pixel (`[0..626]` if rounded)
+- `lR` - *Red* channel value for the *left* pixel (`[0..255]`)
+- `mR` - *Red* channel value for the *middle* pixel (`[0..255]`)
+- `rR` - *Red* channel value for the *right* pixel (`[0..255]`)
+- `lG` - *Green* channel value for the *left* pixel (`[0..255]`)
+- and so on...
+
+In the formula above we're omitting the alpha (transparency) channel, for now, assuming that there are no transparent pixels in the image. Later we will use the alpha channel for masking and for object removal.
+
+![Example of pixel energy calculation](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/30-pixel-energy-calculation-example.png)
+
+Now, since we know how to find the energy of one pixel, we can calculate, so-called, **energy map** which will contain the energies of each pixel of the image. On each resizing step the energy map should be re-calculated (at least partially, more about it below) and would have the same size as the image.
+
+For example, on the 1st resizing step we will have a `1000 x 500` image and a `1000 x 500` energy map. On the 2nd resizing step we will remove the seam from the image and re-calculate the energy map based on the new shrunk image. Thus, we will get a `999 x 500` image and a `999 x 500` energy map.
+
+The higher the energy of the pixel the more likely it is a part of an edge, and it is important for the image content and the less likely that we need to remove it.
+
+To visualize the energy map we may assign a brighter color to the pixels with the higher energy and darker colors to the pixels with the lower energy. Here is an artificial example of how the random part of the energy map might look like. You may see the bright line which represents the edge and which we want to preserve during the resizing.
+
+![Energy map sketch](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/30-energy-map-padding.png)
+
+Here is a real example of the energy map for the demo image you saw above (with hot air balloons).
+
+![Energy map example](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/40-energy-map.png)
+
+You may play around with your custom images and see how the energy map would look like in the [interactive version of the post](https://trekhleb.dev/blog/2021/content-aware-image-resizing-in-javascript/).
+
+We may use the energy map to find the seams (one after another) with the lowest energy and by doing this to decide which pixels should be ultimately deleted.
+
+![Searching the seam](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/41-seam-search.png)
+
+Finding the seam with the lowest energy is not a trivial task and requires exploring many possible pixel combinations before making the decision. We will apply the dynamic programming approach to speed it up.
+
+In the example below, you may see the energy map with the first lowest energy seam that was found for it.
+
+![Energy map example with seam](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/40-energy-map-with-seam.png)
+
+In the examples above we were reducing the width of the image. A similar approach may be taken to reduce the image height. We need to "rotate" the approach though:
+
+- start using *top* and *bottom* pixel neighbors (instead of *left* and *right* ones) to calculate the pixel energy
+- when searching for a seam we need to move from *left* to *right* (instead of from *up* to *bottom*)
+
+## Implementation in TypeScript
+
+> You may find the source code, and the functions mentioned below in the [js-image-carver](https://github.com/trekhleb/js-image-carver) repository.
+
+To implement the algorithm we will be using TypeScript. If you want a JavaScript version, you may ignore (remove) type definitions and their usages.
+
+For simplicity reasons let's implement the seam carving algorithm only for the image *width* reduction.
+
+### Content-aware width resizing (the entry function)
+
+First, let's define some common types that we're going to use while implementing the algorithm.
+
+```typescript
+// Type that describes the image size (width and height).
+type ImageSize = { w: number, h: number };
+
+// The coordinate of the pixel.
+type Coordinate = { x: number, y: number };
+
+// The seam is a sequence of pixels (coordinates).
+type Seam = Coordinate[];
+
+// Energy map is a 2D array that has the same width and height
+// as the image the map is being calculated for.
+type EnergyMap = number[][];
+
+// Type that describes the image pixel's RGBA color.
+type Color = [
+  r: number, // Red
+  g: number, // Green
+  b: number, // Blue
+  a: number, // Alpha (transparency)
+] | Uint8ClampedArray;
+```
+
+On the high level the algorithm consists of the following steps:
+
+1. Calculate the **energy map** for the current version of the image.
+2. Find the **seam** with the lowest energy based on the energy map (this is where we will apply Dynamic Programming).
+3. **Delete the seam** with the lowest energy seam from the image.
+4. **Repeat** until the image width is reduced to the desired value.
+
+```typescript
+type ResizeImageWidthArgs = {
+  img: ImageData, // Image data we want to resize.
+  toWidth: number, // Final image width we want the image to shrink to.
+};
+
+type ResizeImageWidthResult = {
+  img: ImageData, // Resized image data.
+  size: ImageSize, // Resized image size (w x h).
+};
+
+// Performs the content-aware image width resizing using the seam carving method.
+export const resizeImageWidth = (
+  { img, toWidth }: ResizeImageWidthArgs,
+): ResizeImageWidthResult => {
+  // For performance reasons we want to avoid changing the img data array size.
+  // Instead we'll just keep the record of the resized image width and height separately.
+  const size: ImageSize = { w: img.width, h: img.height };
+
+  // Calculating the number of pixels to remove.
+  const pxToRemove = img.width - toWidth;
+  if (pxToRemove < 0) {
+    throw new Error('Upsizing is not supported for now');
+  }
+
+  let energyMap: EnergyMap | null = null;
+  let seam: Seam | null = null;
+
+  // Removing the lowest energy seams one by one.
+  for (let i = 0; i < pxToRemove; i += 1) {
+    // 1. Calculate the energy map for the current version of the image.
+    energyMap = calculateEnergyMap(img, size);
+
+    // 2. Find the seam with the lowest energy based on the energy map.
+    seam = findLowEnergySeam(energyMap, size);
+
+    // 3. Delete the seam with the lowest energy seam from the image.
+    deleteSeam(img, seam, size);
+
+    // Reduce the image width, and continue iterations.
+    size.w -= 1;
+  }
+
+  // Returning the resized image and its final size.
+  // The img is actually a reference to the ImageData, so technically
+  // the caller of the function already has this pointer. But let's
+  // still return it for better code readability.
+  return { img, size };
+};
+```
+
+The image that needs to be resized is being passed to the function in [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData) format. You may draw the image on the canvas and then extract the ImageData from the canvas like this:
+
+```javascript
+const ctx = canvas.getContext('2d');
+const imgData = ctx.getImageData(0, 0, imgWidth, imgHeight);
+```
+
+> The way of uploading and drawing images in JavaScript is out of scope for this article, but you may find the complete source code of how it may be done using React in [js-image-carver](https://github.com/trekhleb/js-image-carver) repo.
+
+Let's break down each step ony be one and implement the `calculateEnergyMap()`, `findLowEnergySeam()` and `deleteSeam()` functions.
+
+### Calculating the pixel's energy
+
+Here we apply the color difference formula described above. For the left and right borders (when there are no left or right neighbors), we ignore the neighbors and don't take them into account during the energy calculation.
+
+```typescript
+// Calculates the energy of a pixel.
+const getPixelEnergy = (left: Color | null, middle: Color, right: Color | null): number => {
+  // Middle pixel is the pixel we're calculating the energy for.
+  const [mR, mG, mB] = middle;
+
+  // Energy from the left pixel (if it exists).
+  let lEnergy = 0;
+  if (left) {
+    const [lR, lG, lB] = left;
+    lEnergy = (lR - mR) ** 2 + (lG - mG) ** 2 + (lB - mB) ** 2;
+  }
+
+  // Energy from the right pixel (if it exists).
+  let rEnergy = 0;
+  if (right) {
+    const [rR, rG, rB] = right;
+    rEnergy = (rR - mR) ** 2 + (rG - mG) ** 2 + (rB - mB) ** 2;
+  }
+
+  // Resulting pixel energy.
+  return Math.sqrt(lEnergy + rEnergy);
+};
+```
+
+### Calculating the energy map
+
+The image we're working with has the [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData) format. It means that all the pixels (and their colors) are stored in a flat (*1D*) [Uint8ClampedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray) array. For readability purposes let's introduce the couple of helper functions that will allow us to work with the Uint8ClampedArray array as with a *2D* matrix instead.
+
+```typescript
+// Helper function that returns the color of the pixel.
+const getPixel = (img: ImageData, { x, y }: Coordinate): Color => {
+  // The ImageData data array is a flat 1D array.
+  // Thus we need to convert x and y coordinates to the linear index.
+  const i = y * img.width + x;
+  const cellsPerColor = 4; // RGBA
+  // For better efficiency, instead of creating a new sub-array we return
+  // a pointer to the part of the ImageData array.
+  return img.data.subarray(i * cellsPerColor, i * cellsPerColor + cellsPerColor);
+};
+
+// Helper function that sets the color of the pixel.
+const setPixel = (img: ImageData, { x, y }: Coordinate, color: Color): void => {
+  // The ImageData data array is a flat 1D array.
+  // Thus we need to convert x and y coordinates to the linear index.
+  const i = y * img.width + x;
+  const cellsPerColor = 4; // RGBA
+  img.data.set(color, i * cellsPerColor);
+};
+```
+
+To calculate the energy map we go through each image pixel and call the previously described `getPixelEnergy()` function against it.
+
+```typescript
+// Helper function that creates a matrix (2D array) of specific
+// size (w x h) and fills it with specified value.
+const matrix = <T>(w: number, h: number, filler: T): T[][] => {
+  return new Array(h)
+    .fill(null)
+    .map(() => {
+      return new Array(w).fill(filler);
+    });
+};
+
+// Calculates the energy of each pixel of the image.
+const calculateEnergyMap = (img: ImageData, { w, h }: ImageSize): EnergyMap => {
+  // Create an empty energy map where each pixel has infinitely high energy.
+  // We will update the energy of each pixel.
+  const energyMap: number[][] = matrix<number>(w, h, Infinity);
+  for (let y = 0; y < h; y += 1) {
+    for (let x = 0; x < w; x += 1) {
+      // Left pixel might not exist if we're on the very left edge of the image.
+      const left = (x - 1) >= 0 ? getPixel(img, { x: x - 1, y }) : null;
+      // The color of the middle pixel that we're calculating the energy for.
+      const middle = getPixel(img, { x, y });
+      // Right pixel might not exist if we're on the very right edge of the image.
+      const right = (x + 1) < w ? getPixel(img, { x: x + 1, y }) : null;
+      energyMap[y][x] = getPixelEnergy(left, middle, right);
+    }
+  }
+  return energyMap;
+};
+```
+
+> The energy map is going to be recalculated on every resize iteration. It means that it will be recalculated, let's say, 500 times if we need to shrink the image by 500 pixels which is not optimal. To speed up the energy map calculation on the 2nd, 3rd, and further steps, we may re-calculate the energy only for those pixels that are placed around the seam that is going to be removed. For simplicity reasons this optimization is omitted here, but you may find the example source-code in [js-image-carver](https://github.com/trekhleb/js-image-carver) repo.
+
+### Finding the seam with the lowest energy (Dynamic Programming approach)
+
+> I've described some Dynamic Programming basics in [Dynamic Programming vs Divide-and-Conquer](https://trekhleb.dev/blog/2018/dynamic-programming-vs-divide-and-conquer/) article before. There is a DP example based on the minimum edit distance problem. You might want to check it out to get some more context.
+
+The issue we need to solve now is to find the path (the seam) on the energy map that goes from top to bottom and has the minimum sum of pixel energies.
+
+#### The naive approach
+
+The naive approach would be to check all possible paths one after another.
+
+![The naive approach](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/50-naive-approach.png)
+
+Going from top to bottom, for each pixel, we have 3 options (↙︎ go down-left, ↓ go down, ↘︎ go down-right). This gives us the time complexity of `O(w * 3^h)` or simply `O(3^h)`, where `w` and `h` are the width and the height of the image. This approach looks slow.
+
+#### The greedy approach
+
+We may also try to choose the next pixel as a pixel with the lowest energy, hoping that the resulting seam energy will be the smallest one.
+
+![The greedy approach](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/51-greedy-approach.png)
+
+This approach gives not the worst solution, but it cannot guarantee that we will find the best available solution. On the image above you may see how the greedy approach chose `5` instead of `10` at first and missed the chain of optimal pixels.
+
+The good part about this approach is that it is fast, and it has a time complexity of `O(w + h)`, where `w` and `h` are the width and the height of the image. In this case, the cost of the speed is the low quality of resizing. We need to find a minimum value in the first row (traversing `w` cells) and then we explore only 3 neighbor pixels for each row (traversing `h` rows).
+
+#### The dynamic programming approach
+
+You might have noticed that in the naive approach we summed up the same pixel energies over and over again while calculating the resulting seams' energy.
+
+![Repeated problems](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/52-dp-repeated-problems.png)
+
+In the example above you see that for the first two seams we are re-using the energy of the shorter seam (which has the energy of `235`). Instead of doing just one operation `235 + 70` to calculate the energy of the 2nd seam we're doing four operations `(5 + 0 + 80 + 150) + 70`.
+
+> This fact that we're re-using the energy of the previous seam to calculate the current seam's energy might be applied recursively to all the shorter seams up to the very top 1st row seam. When we have such overlapping sub-problems, [it is a sign](https://trekhleb.dev/blog/2018/dynamic-programming-vs-divide-and-conquer/) that the general problem *might* be optimized by dynamic programming approach.
+
+So, we may **save the energy of the current seam** at the particular pixel in an additional `seamsEnergies` table to make it re-usable for calculating the next seams faster (the `seamsEnergies` table will have the same size as the energy map and the image itself).
+
+Let's also keep in mind that for one particular pixel on the image (i.e. the bottom left one) we may have *several* values of the previous seams energies.
+
+![What seam to choose](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/53-dp-what-to-choose.png)
+
+Since we're looking for a seam with the lowest resulting energy it would make sense to pick the previous seam with the lowest resulting energy as well.
+
+![Seams energies example](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/56-dp-seams-energies-example.png)
+
+In general, we have three possible previous seems to choose from:
+
+![Three options to choose from](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/55-dp-three-options.png)
+
+You may think about it this way:
+
+- The cell `[1][x]`: contains the lowest possible energy of the seam that starts somewhere on the row `[0][?]` and ends up at cell `[1][x]`
+- **The current cell** `[2][3]`: contains the lowest possible energy of the seam that starts somewhere on the row `[0][?]` and ends up at cell `[2][3]`. To calculate it we need to sum up the energy of the current pixel `[2][3]` (from the energy map) with the `min(seam_energy_1_2, seam_energy_1_3, seam_energy_1_4)`
+
+If we fill the `seamsEnergies` table completely, then the minimum number in the lowest row would be the lowest possible seam energy.
+
+Let's try to fill several cells of this table to see how it works.
+
+![Seams energies map traversal](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/57-dp-seams-energies-traversal.png)
+
+After filling out the `seamsEnergies` table we may see that the lowest energy pixel has an energy of `50`. For convenience, during the `seamsEnergies` generation for each pixel, we may save not only the energy of the seam but also the coordinates of the previous lowest energy seam. This will give us the possibility to reconstruct the seam path from the bottom to the top easily.
+
+The time complexity of DP approach would be `O(w * h)`, where `w` and `h` are the width and the height of the image. We need to calculate energies for *every* pixel of the image.
+
+Here is an example of how this logic might be implemented:
+
+```typescript
+// The metadata for the pixels in the seam.
+type SeamPixelMeta = {
+  energy: number, // The energy of the pixel.
+  coordinate: Coordinate, // The coordinate of the pixel.
+  previous: Coordinate | null, // The previous pixel in a seam.
+};
+
+// Finds the seam (the sequence of pixels from top to bottom) that has the
+// lowest resulting energy using the Dynamic Programming approach.
+const findLowEnergySeam = (energyMap: EnergyMap, { w, h }: ImageSize): Seam => {
+  // The 2D array of the size of w and h, where each pixel contains the
+  // seam metadata (pixel energy, pixel coordinate and previous pixel from
+  // the lowest energy seam at this point).
+  const seamsEnergies: (SeamPixelMeta | null)[][] = matrix<SeamPixelMeta | null>(w, h, null);
+
+  // Populate the first row of the map by just copying the energies
+  // from the energy map.
+  for (let x = 0; x < w; x += 1) {
+    const y = 0;
+    seamsEnergies[y][x] = {
+      energy: energyMap[y][x],
+      coordinate: { x, y },
+      previous: null,
+    };
+  }
+
+  // Populate the rest of the rows.
+  for (let y = 1; y < h; y += 1) {
+    for (let x = 0; x < w; x += 1) {
+      // Find the top adjacent cell with minimum energy.
+      // This cell would be the tail of a seam with lowest energy at this point.
+      // It doesn't mean that this seam (path) has lowest energy globally.
+      // Instead, it means that we found a path with the lowest energy that may lead
+      // us to the current pixel with the coordinates x and y.
+      let minPrevEnergy = Infinity;
+      let minPrevX: number = x;
+      for (let i = (x - 1); i <= (x + 1); i += 1) {
+        if (i >= 0 && i < w && seamsEnergies[y - 1][i].energy < minPrevEnergy) {
+          minPrevEnergy = seamsEnergies[y - 1][i].energy;
+          minPrevX = i;
+        }
+      }
+
+      // Update the current cell.
+      seamsEnergies[y][x] = {
+        energy: minPrevEnergy + energyMap[y][x],
+        coordinate: { x, y },
+        previous: { x: minPrevX, y: y - 1 },
+      };
+    }
+  }
+
+  // Find where the minimum energy seam ends.
+  // We need to find the tail of the lowest energy seam to start
+  // traversing it from its tail to its head (from the bottom to the top).
+  let lastMinCoordinate: Coordinate | null = null;
+  let minSeamEnergy = Infinity;
+  for (let x = 0; x < w; x += 1) {
+    const y = h - 1;
+    if (seamsEnergies[y][x].energy < minSeamEnergy) {
+      minSeamEnergy = seamsEnergies[y][x].energy;
+      lastMinCoordinate = { x, y };
+    }
+  }
+
+  // Find the lowest energy energy seam.
+  // Once we know where the tail is we may traverse and assemble the lowest
+  // energy seam based on the "previous" value of the seam pixel metadata.
+  const seam: Seam = [];
+  if (!lastMinCoordinate) {
+    return seam;
+  }
+
+  const { x: lastMinX, y: lastMinY } = lastMinCoordinate;
+
+  // Adding new pixel to the seam path one by one until we reach the top.
+  let currentSeam = seamsEnergies[lastMinY][lastMinX];
+  while (currentSeam) {
+    seam.push(currentSeam.coordinate);
+    const prevMinCoordinates = currentSeam.previous;
+    if (!prevMinCoordinates) {
+      currentSeam = null;
+    } else {
+      const { x: prevMinX, y: prevMinY } = prevMinCoordinates;
+      currentSeam = seamsEnergies[prevMinY][prevMinX];
+    }
+  }
+
+  return seam;
+};
+```
+
+### Removing the seam with the lowest energy
+
+Once we found the lowest energy seam, we need to remove (to carve) the pixels that form it from the image. The removal is happening by shifting the pixels to the right of the seam by `1px` to the left. For performance reasons, we don't actually delete the last columns. Instead, the rendering component will just ignore the part of the image that lays beyond the resized image width.
+
+![Deleting the seam](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/60-deleting-example.png)
+
+```typescript
+// Deletes the seam from the image data.
+// We delete the pixel in each row and then shift the rest of the row pixels to the left.
+const deleteSeam = (img: ImageData, seam: Seam, { w }: ImageSize): void => {
+  seam.forEach(({ x: seamX, y: seamY }: Coordinate) => {
+    for (let x = seamX; x < (w - 1); x += 1) {
+      const nextPixel = getPixel(img, { x: x + 1, y: seamY });
+      setPixel(img, { x, y: seamY }, nextPixel);
+    }
+  });
+};
+```
+
+## Objects removal
+
+The Seam Carving algorithm tries to remove the seams which consist of low energy pixels first. We could leverage this fact and by assigning low energy to some pixels manually (i.e. by drawing on the image and masking out some areas of it) we could make the Seam Carving algorithm to do *objects removal* for us for free.
+
+Currently, in `getPixelEnergy()` function we were using only the `R`, `G`, `B` color channels to calculate the pixel's energy. But there is also the `A` (alpha, transparency) parameter of the color that we didn't use yet. We may use the transparency channel to tell the algorithm that transparent pixels are the pixels we want to remove. You may check the [source-code of the energy function](https://github.com/trekhleb/js-image-carver/blob/main/src/utils/contentAwareResizer.ts#L54) that takes transparency into account.
+
+Here is how the algorithm works for object removal.
+
+![JS IMAGE CARVER OBJECT REMOVAL DEMO](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/10-demo-02.gif)
+
+## Issues and what's next
+
+The [JS IMAGE CARVER](https://github.com/trekhleb/js-image-carver) web app is far from being a production ready resizer of course. Its main purpose was to experiment with the Seam Carving algorithm interactively. So the plan for the future is to continue experimentation.
+
+The [original paper](https://perso.crans.org/frenoy/matlab2012/seamcarving.pdf) describes how the Seam Carving algorithm might be used not only for the downscaling but also for the **upscaling of the images**. The upscaling, in turn, might be used to **upscale the image back to its original width after the objects' removal**.
+
+Another interesting area of experimentation might be to make the algorithm work in a **real-time**.
+
+> Those are the plans for the future, but for now, I hope that the example with image downsizing was interesting and useful for you. I also hope that you've got the idea of using dynamic programming to implement it.
+>
+> So, good luck with your own experiments!
diff --git a/src/algorithms/image-processing/seam-carving/README.ru-RU.md b/src/algorithms/image-processing/seam-carving/README.ru-RU.md
new file mode 100644
index 0000000000..444ce6df61
--- /dev/null
+++ b/src/algorithms/image-processing/seam-carving/README.ru-RU.md
@@ -0,0 +1,509 @@
+# Изменение размеров изображения с учетом его содержимого в JavaScript
+
+![Content-aware image resizing in JavaScript](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/01-cover-02.png)
+
+> Доступна [английская интерактивная версия этой статьи](https://trekhleb.dev/blog/2021/content-aware-image-resizing-in-javascript/) в которой вы можете загрузить свои собственные изображения и посмотреть, как алгоритм "справляется" с ними.
+
+## TL;DR
+
+Написано много замечательных статей об алгоритме *Seam Carving* ("Вырезание швов"), но я не смог устоять перед соблазном самостоятельно исследовать этот элегантный, мощный и в то же время простой алгоритм и написать о своем личном опыте работы с ним. Мое внимание также привлек тот факт, что для его имплементации мы можем применить *динамическое программирование (DP)*. И, если вы, как и я, все еще находитесь на пути изучения алгоритмов, то это решение может обогатить ваш личный арсенал DP.
+
+Итак, в этой статье я хочу сделать три вещи:
+
+1. Предоставить вам возможность "поиграться" с алгоритмом самостоятельно при помощи **интерактивного ресайзера**.
+2. Объяснить **идею алгоритма Seam Carving**.
+3. Объяснить как можно **применить динамическое программирование** для имплементации алгоритма (мы будем писать на TypeScript).
+
+### Изменение размеров изображений с учетом их содержимого
+
+*Изменение размера изображения с учетом содержимого* (content-aware image resizing) может быть применено, когда дело доходит до изменения пропорций изображения (например, уменьшения ширины при сохранении высоты), а также когда потеря некоторых частей изображения нежелательна. Простое масштабирование изображения в этом случае исказит находящиеся в нем объекты. Для сохранения пропорций объектов при изменении пропорций изображения можно использовать [алгоритм Seam Carving](https://perso.crans.org/frenoy/matlab2012/seamcarving.pdf), который был описан *Shai Avidan* и *Ariel Shamir*.
+
+В приведенном ниже примере показано, как ширина исходного изображения была уменьшена на 50% *с учетом содержимого изображения* (слева) и *без учета содержимого изображения* (справа, простой скейлинг). В данном случае левое изображение выглядит более естественным, так как пропорции воздушных шаров в нем были сохранены.
+
+![Content-aware image resizing](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/01-resizing-options.png)
+
+Идея алгоритма Seam Carving заключается в том, чтобы найти *шов* (seam, непрерывную последовательность пикселей) с наименьшим влиянием на содержание изображения, а затем его *вырезать* (carve). Этот процесс повторяется снова и снова, пока мы не получим требуемую ширину или высоту изображения. В примере ниже интуитивно видно, что пиксели воздушных шаров вносят больший "вклад" в содержание и смысл изображения, чем пиксели неба. Таким образом, сначала удаляются пиксели неба.
+
+![JS IMAGE CARVER DEMO](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/10-demo-01.gif)
+
+Поиск шва с наименьшей энергией (с наименьшим вкладом в содержимое изображения) является вычислительно дорогостоящей операцией (особенно для больших изображений). Для ускорения поиска шва может быть применено *динамическое программирование* (мы рассмотрим детали реализации ниже).
+
+### Удаление объектов
+
+Важность каждого пикселя (так называемая энергия пикселя) вычисляется исходя из его цветовой разницы (`R`, `G`, `B`, `A`) между двумя соседними пикселями. Если же мы вручную зададим некоторым пикселям низкий уровень энергии (например нарисовав маску поверх них), то алгоритм Seam Carving выполнит для нас **удаление помеченного объекта**, как говорится, "забесплатно".
+
+![JS IMAGE CARVER OBJECT REMOVAL DEMO](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/10-demo-02.gif)
+
+### Интерактивный ресзайзер
+
+Для этой статьи я создал приложение [JS IMAGE CARVER](https://trekhleb.dev/js-image-carver/) (доступен также и [исходный код на GitHub](https://github.com/trekhleb/js-image-carver)), которым вы можете воспользоваться для ресайза своих изображений и увидеть в реальном времени, как работает алгоритм.
+
+### Другие примеры ресайза
+
+Вот еще несколько примеров того, как алгоритм справляется с более сложным фоном.
+
+Горы на заднем плане плавно сжимаются, без видимых швов.
+
+![Resizing demo with more complex backgrounds](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/11-demo-01.png)
+
+То же самое и с океанскими волнами. Алгоритм сохранил волновую структуру, не искажая серферов.
+
+![Resizing demo with more complex backgrounds](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/11-demo-02.png)
+
+Нужно помнить, что алгоритм Seam Carving не является "волшебной таблеткой", и может не сохранить пропорции важных частей изображения в том случае, когда *большая часть пикселей выглядят как края, ребра или границы* (почти все пиксели выглядят одинаково важными с точки зрения алгоритма). В приведенном ниже примере изменение размера изображения с учетом содержимого похоже на простое масштабирование, т.к. для алгоритма все пиксели выглядят важными, и ему трудно отличить лицо Ван Гога от фона.
+
+![Example when the algorithm does not work as expected](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/12-demo-01.png)
+
+## Как работает алгоритм Seam Carving
+
+Представим, что у нас есть картинка размером `1000 x 500 px`, и мы хотим уменьшить ее до `500 x 500 px` (допустим, квадратное изображение больше подходит для Instagram). В этом случае мы, возможно, захотим задать несколько **требований к процессу изменения размера**:
+
+- *Важные части изображения должны быть сохранены* (если до ресайза на фото было 5 деревьев, то и после ресайза мы хотим увидеть все те же 5 деревьев).
+- *Пропорции важных частей изображения должны быть сохранены* (круглые колеса автомобиля не должны стать овальными после ресайза).
+
+Чтобы избежать изменения важных частей изображения можно найти **непрерывную последовательность пикселей (шов)**, которая будет идти от верхней границы к нижней и иметь *наименьший вклад в содержимое* изображения (шов, который не проходит через важные части изображения), а затем удалить его. Удаление шва сожмет изображение на один пиксель. Далее надо повторять этот шаг до тех пор, пока изображение не станет нужной ширины.
+
+Вопрос в том, как определить *важность пикселя* и его вклад в содержание изображения (в оригинальной статье авторы используют термин **энергия пикселя**). Один из способов это сделать — рассматривать все пиксели, образующие края (границы, ребра), как важные. В случае, если пиксель является частью ребра, его цвет будет отличаться от соседей (левого и правого пикселей).
+
+![Pixels color difference](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/30-pixel-energy-comparison.png)
+
+Предполагая, что цвет пикселя представлен *4-мя* числами (`R` - красный, `G` - зеленый, `B` - синий, `A` - альфа, прозрачность), мы можем использовать следующую формулу для вычисления разницы в цвете (энергии пикселя):
+
+![Pixel energy formula](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/20-energy-formula.png)
+
+Где:
+
+- `mEnergy` - *Энергия* (важность) *среднего* пикселя (`[0..626]` если округлить)
+- `lR` - *Красный* цветовой канал *левого* пикселя (`[0..255]`)
+- `mR` - *Красный* цветовой канал *среднего* пикселя (`[0..255]`)
+- `rR` - *Красный* цветовой канал *правого* пикселя (`[0..255]`)
+- `lG` - *Зеленый* цветовой канал *левого* пикселя (`[0..255]`)
+- и так далее...
+
+В приведенной выше формуле мы пока не используем альфа-канал (прозрачность), предполагая, что изображение не содержит прозрачные пиксели. Позже мы будем использовать альфа-канал для маскировки и удаления объектов.
+
+![Example of pixel energy calculation](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/30-pixel-energy-calculation-example.png)
+
+Поскольку мы знаем, как найти энергию одного пикселя, мы можем вычислить так называемую **энергетическую карту**, которая будет содержать энергии каждого пикселя изображения. На каждом шаге изменения размера изображения карту энергий необходимо пересчитывать (по крайней мере частично, подробнее об этом ниже), и она будет иметь тот же размер, что и изображение.
+
+Например, на 1-м шаге у нас будет изображение размером `1000 x 500` и энергетическая карта размером `1000 x 500`. На 2-м шаге изменения размера мы удалим шов с изображения и пересчитаем карту энергий на основе нового уменьшенного изображения. Таким образом, мы получим изображение размером `999 x 500` и карту энергий размером `999 x 500`.
+
+Чем выше энергия пикселя, тем больше вероятность того, что он является частью ребра, важен для содержимого изображения и тем меньше вероятность того, что нам потребуется его удалить.
+
+Для визуализации карты энергий мы можем присвоить более яркий цвет пикселям с большей энергией и более темные цвета пикселям с меньшей энергией. Вот как может выглядеть часть карты энергий. Вы можете увидеть светлую линию, которая представляет край и которую мы хотим сохранить при изменении размера.
+
+![Energy map sketch](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/30-energy-map-padding.png)
+
+Вот реальный пример энергетической карты для изображения, которое вы видели выше (с воздушными шарами).
+
+![Energy map example](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/40-energy-map.png)
+
+Вы можете загрузить свое изображение и посмотреть, как будет выглядеть энергетическая карта в [интерактивной версии статьи](https://trekhleb.dev/blog/2021/content-aware-image-resizing-in-javascript/).
+
+Мы можем использовать энергетическую карту, чтобы найти швы (один за другим) с наименьшей энергией и тем самым решить, какие пиксели в конечном итоге должны быть удалены.
+
+![Searching the seam](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/41-seam-search.png)
+
+Поиск шва с наименьшими затратами энергии не является тривиальной задачей и требует перебора множества возможных комбинаций пикселей. Мы применим динамическое программирование для оптимизации поиска шва.
+
+В примере ниже вы можете увидеть карту энергий с первым найденным для нее швом с наименьшей энергией.
+
+![Energy map example with seam](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/40-energy-map-with-seam.png)
+
+В приведенных выше примерах мы уменьшали ширину изображения. Аналогичный подход может быть использован для уменьшения высоты изображения. Для этого нам нужно:
+
+- начать использовать соседей *сверху* и *снизу*, а не *слева* и *справа*, для вычисления энергии пикселя
+- при поиске шва нам нужно двигаться *слева* *направо*, а не *сверху* *вниз*.
+
+## Реализация алгоритма на TypeScript
+
+> Исходный код и функции, упомянутые ниже, можно найти в репозитории [js-image-carver](https://github.com/trekhleb/js-image-carver).
+
+Для реализации алгоритма мы будем использовать TypeScript. Если вам нужна версия на JavaScript, вы можете игнорировать (удалить) определения типов и их использование.
+
+Для простоты примеров мы напишем код только для уменьшения *ширины* изображения.
+
+### Уменьшение ширины с учетом содержимого изображения (исходная функция)
+
+Для начала определим некоторые общие типы, которые мы будем использовать при реализации алгоритма.
+
+```typescript
+// Type that describes the image size (width and height).
+type ImageSize = { w: number, h: number };
+
+// The coordinate of the pixel.
+type Coordinate = { x: number, y: number };
+
+// The seam is a sequence of pixels (coordinates).
+type Seam = Coordinate[];
+
+// Energy map is a 2D array that has the same width and height
+// as the image the map is being calculated for.
+type EnergyMap = number[][];
+
+// Type that describes the image pixel's RGBA color.
+type Color = [
+  r: number, // Red
+  g: number, // Green
+  b: number, // Blue
+  a: number, // Alpha (transparency)
+] | Uint8ClampedArray;
+```
+
+Для имплементации алгоритма нам необходимо выполнить следующие шаги:
+
+1. Рассчитать **карту энергии** для текущей версии изображения.
+2. Найти **шов** с наименьшей энергией на основе карты энергий (здесь мы применим динамическое программирование).
+3. **Удалить шов** с наименьшей энергией из изображения.
+4. **Повторять** до тех пор, пока ширина изображения не будет уменьшена до нужного значения.
+
+```typescript
+type ResizeImageWidthArgs = {
+  img: ImageData, // Image data we want to resize.
+  toWidth: number, // Final image width we want the image to shrink to.
+};
+
+type ResizeImageWidthResult = {
+  img: ImageData, // Resized image data.
+  size: ImageSize, // Resized image size (w x h).
+};
+
+// Performs the content-aware image width resizing using the seam carving method.
+export const resizeImageWidth = (
+  { img, toWidth }: ResizeImageWidthArgs,
+): ResizeImageWidthResult => {
+  // For performance reasons we want to avoid changing the img data array size.
+  // Instead we'll just keep the record of the resized image width and height separately.
+  const size: ImageSize = { w: img.width, h: img.height };
+
+  // Calculating the number of pixels to remove.
+  const pxToRemove = img.width - toWidth;
+  if (pxToRemove < 0) {
+    throw new Error('Upsizing is not supported for now');
+  }
+
+  let energyMap: EnergyMap | null = null;
+  let seam: Seam | null = null;
+
+  // Removing the lowest energy seams one by one.
+  for (let i = 0; i < pxToRemove; i += 1) {
+    // 1. Calculate the energy map for the current version of the image.
+    energyMap = calculateEnergyMap(img, size);
+
+    // 2. Find the seam with the lowest energy based on the energy map.
+    seam = findLowEnergySeam(energyMap, size);
+
+    // 3. Delete the seam with the lowest energy seam from the image.
+    deleteSeam(img, seam, size);
+
+    // Reduce the image width, and continue iterations.
+    size.w -= 1;
+  }
+
+  // Returning the resized image and its final size.
+  // The img is actually a reference to the ImageData, so technically
+  // the caller of the function already has this pointer. But let's
+  // still return it for better code readability.
+  return { img, size };
+};
+```
+
+Изображение, которому необходимо изменить размер, передается в функцию в формате [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData). Вы можете отобразить изображение на canvas-е, а затем извлечь ImageData из того же canvas-а следующим образом:
+
+```javascript
+const ctx = canvas.getContext('2d');
+const imgData = ctx.getImageData(0, 0, imgWidth, imgHeight);
+```
+
+> Загрузка и отрисовка изображений в JavaScript выходит за рамки данной статьи, но вы можете найти полный исходный код того, как это можно сделать с помощью React в репозитории [js-image-carver](https://github.com/trekhleb/js-image-carver).
+
+Теперь, пошагово реализуем функции `calculateEnergyMap()`, `findLowEnergySeam()` и `deleteSeam()`.
+
+### Расчет энергии пикселя
+
+Для расчета воспользуемся формулой разницы цветов, описанной выше. Для левой и правой краев изображения (когда нет левого или правого соседей) мы игнорируем соседей и не учитываем их при расчете энергии.
+
+```typescript
+// Calculates the energy of a pixel.
+const getPixelEnergy = (left: Color | null, middle: Color, right: Color | null): number => {
+  // Middle pixel is the pixel we're calculating the energy for.
+  const [mR, mG, mB] = middle;
+
+  // Energy from the left pixel (if it exists).
+  let lEnergy = 0;
+  if (left) {
+    const [lR, lG, lB] = left;
+    lEnergy = (lR - mR) ** 2 + (lG - mG) ** 2 + (lB - mB) ** 2;
+  }
+
+  // Energy from the right pixel (if it exists).
+  let rEnergy = 0;
+  if (right) {
+    const [rR, rG, rB] = right;
+    rEnergy = (rR - mR) ** 2 + (rG - mG) ** 2 + (rB - mB) ** 2;
+  }
+
+  // Resulting pixel energy.
+  return Math.sqrt(lEnergy + rEnergy);
+};
+```
+
+### Расчет энергетической карты
+
+Изображение, с которым мы работаем, имеет формат [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData). Это означает, что все пиксели (и их цвета) хранятся в одномерном массиве [Uint8ClampedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray). Для удобства чтения введем пару вспомогательных функций, которые позволят работать с массивом Uint8ClampedArray как с *2D* матрицей.
+
+```typescript
+// Helper function that returns the color of the pixel.
+const getPixel = (img: ImageData, { x, y }: Coordinate): Color => {
+  // The ImageData data array is a flat 1D array.
+  // Thus we need to convert x and y coordinates to the linear index.
+  const i = y * img.width + x;
+  const cellsPerColor = 4; // RGBA
+  // For better efficiency, instead of creating a new sub-array we return
+  // a pointer to the part of the ImageData array.
+  return img.data.subarray(i * cellsPerColor, i * cellsPerColor + cellsPerColor);
+};
+
+// Helper function that sets the color of the pixel.
+const setPixel = (img: ImageData, { x, y }: Coordinate, color: Color): void => {
+  // The ImageData data array is a flat 1D array.
+  // Thus we need to convert x and y coordinates to the linear index.
+  const i = y * img.width + x;
+  const cellsPerColor = 4; // RGBA
+  img.data.set(color, i * cellsPerColor);
+};
+```
+
+Для вычисления карты энергии мы проходим через каждый пиксель изображения и вызываем для него описанную ранее функцию `getPixelEnergy()`.
+
+```typescript
+// Helper function that creates a matrix (2D array) of specific
+// size (w x h) and fills it with specified value.
+const matrix = <T>(w: number, h: number, filler: T): T[][] => {
+  return new Array(h)
+    .fill(null)
+    .map(() => {
+      return new Array(w).fill(filler);
+    });
+};
+
+// Calculates the energy of each pixel of the image.
+const calculateEnergyMap = (img: ImageData, { w, h }: ImageSize): EnergyMap => {
+  // Create an empty energy map where each pixel has infinitely high energy.
+  // We will update the energy of each pixel.
+  const energyMap: number[][] = matrix<number>(w, h, Infinity);
+  for (let y = 0; y < h; y += 1) {
+    for (let x = 0; x < w; x += 1) {
+      // Left pixel might not exist if we're on the very left edge of the image.
+      const left = (x - 1) >= 0 ? getPixel(img, { x: x - 1, y }) : null;
+      // The color of the middle pixel that we're calculating the energy for.
+      const middle = getPixel(img, { x, y });
+      // Right pixel might not exist if we're on the very right edge of the image.
+      const right = (x + 1) < w ? getPixel(img, { x: x + 1, y }) : null;
+      energyMap[y][x] = getPixelEnergy(left, middle, right);
+    }
+  }
+  return energyMap;
+};
+```
+
+> Карта энергии будет пересчитываться при каждой итерации изменения размера. Это значит, что она будет пересчитываться, скажем, 500 раз, если нам нужно будет уменьшить изображение на 500 пикселей, что выглядит неоптимально. Чтобы ускорить вычисление карты энергии на 2-м, 3-м и последующих этапах, мы можем пересчитать энергию только для тех пикселей, которые расположены вокруг шва, который будет удален. Для простоты эта оптимизация здесь пропущена, но пример с исходным кодом можно найти в репозитории [js-image-carver](https://github.com/trekhleb/js-image-carver).
+
+### Нахождение шва с минимальной энергией (применяем динамическое программирование)
+
+> В статье [Dynamic Programming vs Divide-and-Conquer](https://trekhleb.dev/blog/2018/dynamic-programming-vs-divide-and-conquer/) я описывал некоторые аспекты динамического программирования на примере нахождения "расстояния Левенштейна" (преобразование одной строки в другую). Возможно она будет полезна для ознакомления.
+
+Проблема, которую нам необходимо решить заключается в нахождении пути (шва) на энергетической карте, который идет от верхней границы изображения к нижней и имеет минимальную энергию (сумма энергий пикселей, составляющих шов должна быть минимальной).
+
+#### "Наивный" подход (naive)
+
+Прямолинейный ("наивный") подход — перебрать все возможные пути один за другим.
+
+![The naive approach](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/50-naive-approach.png)
+
+Двигаясь сверху вниз, для каждого пикселя у нас есть 3 варианта (↙︎ идти вниз-влево, ↓ вниз, ↘︎ идти вниз-вправо). Это дает нам временную сложность `O (w * 3 ^ h)` или просто `O (3 ^ h)`, где `w` и` h` - ширина и высота изображения. Такой подход выглядит неоптимальным.
+
+#### "Жадный" подход (greedy)
+
+Жадный подход — выбирать следующий пиксель как пиксель с наименьшей энергией, надеясь, что результирующая энергия шва будет наименьшей.
+
+![The greedy approach](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/51-greedy-approach.png)
+
+Жадный подход приведет нас к не самому худшему решению, но он не сможет гарантировать, что мы найдем наилучшее доступное решение. На картинке выше видно, как мы выбрали `5` вместо `10` и пропустили цепочку оптимальных пикселей.
+
+Плюс этого подхода в том, что он быстрый и имеет временную сложность `O(w + h)`, где `w` и `h` - это ширина и высота изображения. В этом случае плата за скорость — низкое качество ресайза (много искажений). Временная сложность обусловлена тем, что нужно найти минимальное значение в первом ряду (обход `w` ячеек), а затем исследовать только 3 соседних пикселя для каждого ряда (обход `h` рядов).
+
+#### Используем динамическое программирование
+
+Вы, наверное, заметили, что в наивном подходе мы снова и снова суммировали одни и те же энергии пикселей, вычисляя энергию образовавшихся швов.
+
+![Repeated problems](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/52-dp-repeated-problems.png)
+
+В примере выше видно, что для первых двух швов мы повторно используем энергию более короткого шва (который имеет энергию `235`). Вместо одной операции `235 + 70` для вычисления энергии 2-го шва мы делаем четыре операции `(5 + 0 + 80 + 150) + 70`.
+
+> Тот факт, что мы повторно используем энергию предыдущего шва для вычисления энергии текущего шва, может быть применен рекурсивно ко всем более коротким швам до самого верхнего 1-го ряда. Когда у нас есть такие перекрывающиеся под-проблемы, [это признак](https://trekhleb.dev/blog/2018/dynamic-programming-vs-divide-and-conquer/), что общая задача *может* быть оптимизирована с использованием динамического программирования.
+
+Таким образом, мы можем **сохранить энергию текущего шва** для конкретного пикселя в дополнительной таблице `samsEnergies`, чтобы повторно использовать ее при расчете энергии следующих швов (таблица `samsEnergies` будет иметь тот же размер, что и энергетическая карта и само изображение).
+
+Обратите также внимание, что для большинства пикселей в изображении (например, для левого нижнего) мы можем иметь *несколько* значений энергий предыдущих швов.
+
+![What seam to choose](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/53-dp-what-to-choose.png)
+
+Так как мы ищем шов с наименьшей результирующей энергией, имеет смысл выбирать и предыдущий шов с наименьшей результирующей энергией.
+
+![Seams energies example](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/56-dp-seams-energies-example.png)
+
+Как правило, у нас есть три возможных предыдущих шва, которые текущий пиксель продолжает:
+
+![Three options to choose from](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/55-dp-three-options.png)
+
+Можем посмотреть на это с такой стороны:
+
+- Ячейка `[1][x]`: содержит наименьшую возможную энергию шва, который начинается где-то в ряду `[0][?] ` и заканчивается в ячейке `[1][x]`.
+- **Текущая ячейка** `[2][3]`: содержит наименьшую возможную энергию шва, который начинается где-то в ряду `[0][?] ` и заканчивается в ячейке `[2][3]`. Для вычисления нужно суммировать энергию текущего пикселя `[2][3]` (из энергетической карты) с `min(seam_energy_1_2, seam_energy_1_3, seam_energy_1_4)`.
+
+Если мы заполним таблицу `ShesamsEnergies` полностью, то минимальное число в нижнем ряду будет наименьшей возможной энергией шва.
+
+Попробуем заполнить несколько ячеек этой таблицы, чтобы посмотреть, как это работает.
+
+![Seams energies map traversal](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/57-dp-seams-energies-traversal.png)
+
+После заполнения таблицы `ShesamsEnergies` видно, что в нижнем ряду пиксель с самой низкой энергией имеет значение `50`. Для удобства во время генерации `samsEnergies` для каждого пикселя мы можем сохранить не только энергию шва, но и координаты предыдущего шва с наименьшей энергией. Это даст нам возможность легко восстанавливать траекторию шва снизу вверх.
+
+Временная сложность DP подхода составит `O(w * h)`, где `w` и `h` - это ширина и высота изображения. Обусловлена она тем, что нужно вычислить энергии для *всех* пикселей изображения.
+
+Вот пример того, как эта логика может быть реализована:
+
+```typescript
+// The metadata for the pixels in the seam.
+type SeamPixelMeta = {
+  energy: number, // The energy of the pixel.
+  coordinate: Coordinate, // The coordinate of the pixel.
+  previous: Coordinate | null, // The previous pixel in a seam.
+};
+
+// Finds the seam (the sequence of pixels from top to bottom) that has the
+// lowest resulting energy using the Dynamic Programming approach.
+const findLowEnergySeam = (energyMap: EnergyMap, { w, h }: ImageSize): Seam => {
+  // The 2D array of the size of w and h, where each pixel contains the
+  // seam metadata (pixel energy, pixel coordinate and previous pixel from
+  // the lowest energy seam at this point).
+  const seamsEnergies: (SeamPixelMeta | null)[][] = matrix<SeamPixelMeta | null>(w, h, null);
+
+  // Populate the first row of the map by just copying the energies
+  // from the energy map.
+  for (let x = 0; x < w; x += 1) {
+    const y = 0;
+    seamsEnergies[y][x] = {
+      energy: energyMap[y][x],
+      coordinate: { x, y },
+      previous: null,
+    };
+  }
+
+  // Populate the rest of the rows.
+  for (let y = 1; y < h; y += 1) {
+    for (let x = 0; x < w; x += 1) {
+      // Find the top adjacent cell with minimum energy.
+      // This cell would be the tail of a seam with lowest energy at this point.
+      // It doesn't mean that this seam (path) has lowest energy globally.
+      // Instead, it means that we found a path with the lowest energy that may lead
+      // us to the current pixel with the coordinates x and y.
+      let minPrevEnergy = Infinity;
+      let minPrevX: number = x;
+      for (let i = (x - 1); i <= (x + 1); i += 1) {
+        if (i >= 0 && i < w && seamsEnergies[y - 1][i].energy < minPrevEnergy) {
+          minPrevEnergy = seamsEnergies[y - 1][i].energy;
+          minPrevX = i;
+        }
+      }
+
+      // Update the current cell.
+      seamsEnergies[y][x] = {
+        energy: minPrevEnergy + energyMap[y][x],
+        coordinate: { x, y },
+        previous: { x: minPrevX, y: y - 1 },
+      };
+    }
+  }
+
+  // Find where the minimum energy seam ends.
+  // We need to find the tail of the lowest energy seam to start
+  // traversing it from its tail to its head (from the bottom to the top).
+  let lastMinCoordinate: Coordinate | null = null;
+  let minSeamEnergy = Infinity;
+  for (let x = 0; x < w; x += 1) {
+    const y = h - 1;
+    if (seamsEnergies[y][x].energy < minSeamEnergy) {
+      minSeamEnergy = seamsEnergies[y][x].energy;
+      lastMinCoordinate = { x, y };
+    }
+  }
+
+  // Find the lowest energy energy seam.
+  // Once we know where the tail is we may traverse and assemble the lowest
+  // energy seam based on the "previous" value of the seam pixel metadata.
+  const seam: Seam = [];
+  if (!lastMinCoordinate) {
+    return seam;
+  }
+
+  const { x: lastMinX, y: lastMinY } = lastMinCoordinate;
+
+  // Adding new pixel to the seam path one by one until we reach the top.
+  let currentSeam = seamsEnergies[lastMinY][lastMinX];
+  while (currentSeam) {
+    seam.push(currentSeam.coordinate);
+    const prevMinCoordinates = currentSeam.previous;
+    if (!prevMinCoordinates) {
+      currentSeam = null;
+    } else {
+      const { x: prevMinX, y: prevMinY } = prevMinCoordinates;
+      currentSeam = seamsEnergies[prevMinY][prevMinX];
+    }
+  }
+
+  return seam;
+};
+```
+
+### Удаление шва с минимальной энергией
+
+Как только мы нашли шов с наименьшей суммарной энергией, нам нужно удалить (вырезать) пиксели, которые образуют его из изображения. Удаление происходит путем смещения пикселей справа от шва на `1px` влево. Из соображений производительности мы не будем удалять крайний столбик пикселей. Вместо этого, компонент, отвечающий за отрисовку уменьшенного изображения просто проигнорирует ту часть изображения, которая лежит за пределами обрезанной ширины.
+
+![Deleting the seam](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/60-deleting-example.png)
+
+```typescript
+// Deletes the seam from the image data.
+// We delete the pixel in each row and then shift the rest of the row pixels to the left.
+const deleteSeam = (img: ImageData, seam: Seam, { w }: ImageSize): void => {
+  seam.forEach(({ x: seamX, y: seamY }: Coordinate) => {
+    for (let x = seamX; x < (w - 1); x += 1) {
+      const nextPixel = getPixel(img, { x: x + 1, y: seamY });
+      setPixel(img, { x, y: seamY }, nextPixel);
+    }
+  });
+};
+```
+
+## Удаление объектов с изображения
+
+Seam Carving алгоритм пытается сначала удалить швы, состоящие из низкоэнергетических пикселей. Мы могли бы использовать этот факт и, присвоив низкую энергию некоторым пикселям вручную (например, нарисовав на изображении маску), мы могли бы заставить алгоритм удалить отмеченные пиксели (*объекты*).
+
+В настоящее время в функции `getPixelEnergy()` мы используем только каналы цветов `R`, `G`, `B` для вычисления энергии пикселей. Но есть еще и параметр `A` (альфа, прозрачность), который мы не использовали. Мы можем использовать канал прозрачности, чтобы "сказать" алгоритму, что прозрачные пиксели — это те пиксели, которые мы хотим удалить. Вы можете ознакомиться с [исходным кодом функции getPixelEnergy()](https://github.com/trekhleb/js-image-carver/blob/main/src/utils/contentAwareResizer.ts#L54), которая учитывает прозрачность.
+
+Вот как при этом будет выглядеть удаление объектов:
+
+![JS IMAGE CARVER OBJECT REMOVAL DEMO](https://raw.githubusercontent.com/trekhleb/trekhleb.github.io/master/src/posts/2021/content-aware-image-resizing-in-javascript/assets/10-demo-02.gif)
+
+## Проблемы алгоритма и дальнейшие планы
+
+Приложение [JS IMAGE CARVER](https://github.com/trekhleb/js-image-carver) далеко от идеала и не является приложением production-ready качества. Основной его целью была возможность интерактивного экспериментирования с алгоритмом. Поэтому в дальнейших планах — использовать его именно для экспериментов.
+
+В [оригинальной статье](https://perso.crans.org/frenoy/matlab2012/seamcarving.pdf) описывается, как алгоритм может быть использован не только для уменьшения, но и для **увеличения изображения**. Увеличение (расширение) изображения, в свою очередь, может быть использовано для **автоматического расширения изображения до его исходной ширины после удаления объектов**.
+
+Еще одной интересной областью экспериментов может быть попытка ускорить алгоритм, чтобы он работал в режиме **реального времени**.
+
+> Таковы планы на будущее, но пока, надеюсь, пример с уменьшением изображения был интересен и полезен для вас. Также надеюсь, что вам было интересно увидеть применение динамического программирования в задачах, приближенных к реальности.
+>
+> Удачи с вашими собственными экспериментами!
diff --git a/src/algorithms/image-processing/seam-carving/__tests__/resizeImageWidth.node.js b/src/algorithms/image-processing/seam-carving/__tests__/resizeImageWidth.node.js
new file mode 100644
index 0000000000..e4f78b7a52
--- /dev/null
+++ b/src/algorithms/image-processing/seam-carving/__tests__/resizeImageWidth.node.js
@@ -0,0 +1,85 @@
+import fs from 'fs';
+import { PNG } from 'pngjs';
+
+import resizeImageWidth from '../resizeImageWidth';
+
+const testImageBeforePath = './src/algorithms/image-processing/seam-carving/__tests__/test-image-before.png';
+const testImageAfterPath = './src/algorithms/image-processing/seam-carving/__tests__/test-image-after.png';
+
+/**
+ * Compares two images and finds the number of different pixels.
+ *
+ * @param {ImageData} imgA - ImageData for the first image.
+ * @param {ImageData} imgB - ImageData for the second image.
+ * @param {number} threshold - Color difference threshold [0..255]. Smaller - stricter.
+ * @returns {number} - Number of different pixels.
+ */
+function pixelsDiff(imgA, imgB, threshold = 0) {
+  if (imgA.width !== imgB.width || imgA.height !== imgB.height) {
+    throw new Error('Images must have the same size');
+  }
+
+  let differentPixels = 0;
+  const numColorParams = 4; // RGBA
+
+  for (let pixelIndex = 0; pixelIndex < imgA.data.length; pixelIndex += numColorParams) {
+    // Get pixel's color for each image.
+    const [aR, aG, aB] = imgA.data.subarray(pixelIndex, pixelIndex + numColorParams);
+    const [bR, bG, bB] = imgB.data.subarray(pixelIndex, pixelIndex + numColorParams);
+
+    // Get average pixel's color for each image (make them greyscale).
+    const aAvgColor = Math.floor((aR + aG + aB) / 3);
+    const bAvgColor = Math.floor((bR + bG + bB) / 3);
+
+    // Compare pixel colors.
+    if (Math.abs(aAvgColor - bAvgColor) > threshold) {
+      differentPixels += 1;
+    }
+  }
+
+  return differentPixels;
+}
+
+const pngLoad = (path) => new Promise((resolve) => {
+  fs.createReadStream(path)
+    .pipe(new PNG())
+    .on('parsed', function Parsed() {
+      /** @type {ImageData} */
+      const imageData = {
+        colorSpace: 'srgb',
+        width: this.width,
+        height: this.height,
+        data: this.data,
+      };
+      resolve(imageData);
+    });
+});
+
+describe('resizeImageWidth', () => {
+  it('should perform content-aware image width reduction', async () => {
+    const imgBefore = await pngLoad(testImageBeforePath);
+    const imgAfter = await pngLoad(testImageAfterPath);
+
+    const toWidth = Math.floor(imgBefore.width / 2);
+
+    const {
+      img: imgResized,
+      size: resizedSize,
+    } = resizeImageWidth({ img: imgBefore, toWidth });
+
+    expect(imgResized).toBeDefined();
+    expect(resizedSize).toBeDefined();
+
+    expect(resizedSize).toEqual({ w: toWidth, h: imgBefore.height });
+    expect(imgResized.width).toBe(imgAfter.width);
+    expect(imgResized.height).toBe(imgAfter.height);
+
+    const colorThreshold = 50;
+    const differentPixels = pixelsDiff(imgResized, imgAfter, colorThreshold);
+
+    // Allow 10% of pixels to be different
+    const pixelsThreshold = Math.floor((imgAfter.width * imgAfter.height) / 10);
+
+    expect(differentPixels).toBeLessThanOrEqual(pixelsThreshold);
+  });
+});
diff --git a/src/algorithms/image-processing/seam-carving/__tests__/test-image-after.png b/src/algorithms/image-processing/seam-carving/__tests__/test-image-after.png
new file mode 100644
index 0000000000..8b5724cd30
Binary files /dev/null and b/src/algorithms/image-processing/seam-carving/__tests__/test-image-after.png differ
diff --git a/src/algorithms/image-processing/seam-carving/__tests__/test-image-before.png b/src/algorithms/image-processing/seam-carving/__tests__/test-image-before.png
new file mode 100644
index 0000000000..20a53207dd
Binary files /dev/null and b/src/algorithms/image-processing/seam-carving/__tests__/test-image-before.png differ
diff --git a/src/algorithms/image-processing/seam-carving/resizeImageWidth.js b/src/algorithms/image-processing/seam-carving/resizeImageWidth.js
new file mode 100644
index 0000000000..9f2c92fcee
--- /dev/null
+++ b/src/algorithms/image-processing/seam-carving/resizeImageWidth.js
@@ -0,0 +1,253 @@
+import { getPixel, setPixel } from '../utils/imageData';
+
+/**
+ * The seam is a sequence of pixels (coordinates).
+ * @typedef {PixelCoordinate[]} Seam
+ */
+
+/**
+ * Energy map is a 2D array that has the same width and height
+ * as the image the map is being calculated for.
+ * @typedef {number[][]} EnergyMap
+ */
+
+/**
+ * The metadata for the pixels in the seam.
+ * @typedef {Object} SeamPixelMeta
+ * @property {number} energy - the energy of the pixel.
+ * @property {PixelCoordinate} coordinate - the coordinate of the pixel.
+ * @property {?PixelCoordinate} previous - the previous pixel in a seam.
+ */
+
+/**
+ * Type that describes the image size (width and height)
+ * @typedef {Object} ImageSize
+ * @property {number} w - image width.
+ * @property {number} h - image height.
+ */
+
+/**
+ * @typedef {Object} ResizeImageWidthArgs
+ * @property {ImageData} img - image data we want to resize.
+ * @property {number} toWidth - final image width we want the image to shrink to.
+ */
+
+/**
+ * @typedef {Object} ResizeImageWidthResult
+ * @property {ImageData} img - resized image data.
+ * @property {ImageSize} size - resized image size.
+ */
+
+/**
+ * Helper function that creates a matrix (2D array) of specific
+ * size (w x h) and fills it with specified value.
+ * @param {number} w
+ * @param {number} h
+ * @param {?(number | SeamPixelMeta)} filler
+ * @returns {?(number | SeamPixelMeta)[][]}
+ */
+const matrix = (w, h, filler) => {
+  return new Array(h)
+    .fill(null)
+    .map(() => {
+      return new Array(w).fill(filler);
+    });
+};
+
+/**
+ * Calculates the energy of a pixel.
+ * @param {?PixelColor} left
+ * @param {PixelColor} middle
+ * @param {?PixelColor} right
+ * @returns {number}
+ */
+const getPixelEnergy = (left, middle, right) => {
+  // Middle pixel is the pixel we're calculating the energy for.
+  const [mR, mG, mB] = middle;
+
+  // Energy from the left pixel (if it exists).
+  let lEnergy = 0;
+  if (left) {
+    const [lR, lG, lB] = left;
+    lEnergy = (lR - mR) ** 2 + (lG - mG) ** 2 + (lB - mB) ** 2;
+  }
+
+  // Energy from the right pixel (if it exists).
+  let rEnergy = 0;
+  if (right) {
+    const [rR, rG, rB] = right;
+    rEnergy = (rR - mR) ** 2 + (rG - mG) ** 2 + (rB - mB) ** 2;
+  }
+
+  // Resulting pixel energy.
+  return Math.sqrt(lEnergy + rEnergy);
+};
+
+/**
+ * Calculates the energy of each pixel of the image.
+ * @param {ImageData} img
+ * @param {ImageSize} size
+ * @returns {EnergyMap}
+ */
+const calculateEnergyMap = (img, { w, h }) => {
+  // Create an empty energy map where each pixel has infinitely high energy.
+  // We will update the energy of each pixel.
+  const energyMap = matrix(w, h, Infinity);
+  for (let y = 0; y < h; y += 1) {
+    for (let x = 0; x < w; x += 1) {
+      // Left pixel might not exist if we're on the very left edge of the image.
+      const left = (x - 1) >= 0 ? getPixel(img, { x: x - 1, y }) : null;
+      // The color of the middle pixel that we're calculating the energy for.
+      const middle = getPixel(img, { x, y });
+      // Right pixel might not exist if we're on the very right edge of the image.
+      const right = (x + 1) < w ? getPixel(img, { x: x + 1, y }) : null;
+      energyMap[y][x] = getPixelEnergy(left, middle, right);
+    }
+  }
+  return energyMap;
+};
+
+/**
+ * Finds the seam (the sequence of pixels from top to bottom) that has the
+ * lowest resulting energy using the Dynamic Programming approach.
+ * @param {EnergyMap} energyMap
+ * @param {ImageSize} size
+ * @returns {Seam}
+ */
+const findLowEnergySeam = (energyMap, { w, h }) => {
+  // The 2D array of the size of w and h, where each pixel contains the
+  // seam metadata (pixel energy, pixel coordinate and previous pixel from
+  // the lowest energy seam at this point).
+  const seamPixelsMap = matrix(w, h, null);
+
+  // Populate the first row of the map by just copying the energies
+  // from the energy map.
+  for (let x = 0; x < w; x += 1) {
+    const y = 0;
+    seamPixelsMap[y][x] = {
+      energy: energyMap[y][x],
+      coordinate: { x, y },
+      previous: null,
+    };
+  }
+
+  // Populate the rest of the rows.
+  for (let y = 1; y < h; y += 1) {
+    for (let x = 0; x < w; x += 1) {
+      // Find the top adjacent cell with minimum energy.
+      // This cell would be the tail of a seam with lowest energy at this point.
+      // It doesn't mean that this seam (path) has lowest energy globally.
+      // Instead, it means that we found a path with the lowest energy that may lead
+      // us to the current pixel with the coordinates x and y.
+      let minPrevEnergy = Infinity;
+      let minPrevX = x;
+      for (let i = (x - 1); i <= (x + 1); i += 1) {
+        if (i >= 0 && i < w && seamPixelsMap[y - 1][i].energy < minPrevEnergy) {
+          minPrevEnergy = seamPixelsMap[y - 1][i].energy;
+          minPrevX = i;
+        }
+      }
+
+      // Update the current cell.
+      seamPixelsMap[y][x] = {
+        energy: minPrevEnergy + energyMap[y][x],
+        coordinate: { x, y },
+        previous: { x: minPrevX, y: y - 1 },
+      };
+    }
+  }
+
+  // Find where the minimum energy seam ends.
+  // We need to find the tail of the lowest energy seam to start
+  // traversing it from its tail to its head (from the bottom to the top).
+  let lastMinCoordinate = null;
+  let minSeamEnergy = Infinity;
+  for (let x = 0; x < w; x += 1) {
+    const y = h - 1;
+    if (seamPixelsMap[y][x].energy < minSeamEnergy) {
+      minSeamEnergy = seamPixelsMap[y][x].energy;
+      lastMinCoordinate = { x, y };
+    }
+  }
+
+  // Find the lowest energy energy seam.
+  // Once we know where the tail is we may traverse and assemble the lowest
+  // energy seam based on the "previous" value of the seam pixel metadata.
+  const seam = [];
+
+  const { x: lastMinX, y: lastMinY } = lastMinCoordinate;
+
+  // Adding new pixel to the seam path one by one until we reach the top.
+  let currentSeam = seamPixelsMap[lastMinY][lastMinX];
+  while (currentSeam) {
+    seam.push(currentSeam.coordinate);
+    const prevMinCoordinates = currentSeam.previous;
+    if (!prevMinCoordinates) {
+      currentSeam = null;
+    } else {
+      const { x: prevMinX, y: prevMinY } = prevMinCoordinates;
+      currentSeam = seamPixelsMap[prevMinY][prevMinX];
+    }
+  }
+
+  return seam;
+};
+
+/**
+ * Deletes the seam from the image data.
+ * We delete the pixel in each row and then shift the rest of the row pixels to the left.
+ * @param {ImageData} img
+ * @param {Seam} seam
+ * @param {ImageSize} size
+ */
+const deleteSeam = (img, seam, { w }) => {
+  seam.forEach(({ x: seamX, y: seamY }) => {
+    for (let x = seamX; x < (w - 1); x += 1) {
+      const nextPixel = getPixel(img, { x: x + 1, y: seamY });
+      setPixel(img, { x, y: seamY }, nextPixel);
+    }
+  });
+};
+
+/**
+ * Performs the content-aware image width resizing using the seam carving method.
+ * @param {ResizeImageWidthArgs} args
+ * @returns {ResizeImageWidthResult}
+ */
+const resizeImageWidth = ({ img, toWidth }) => {
+  /**
+   * For performance reasons we want to avoid changing the img data array size.
+   * Instead we'll just keep the record of the resized image width and height separately.
+   * @type {ImageSize}
+   */
+  const size = { w: img.width, h: img.height };
+
+  // Calculating the number of pixels to remove.
+  const pxToRemove = img.width - toWidth;
+
+  let energyMap = null;
+  let seam = null;
+
+  // Removing the lowest energy seams one by one.
+  for (let i = 0; i < pxToRemove; i += 1) {
+    // 1. Calculate the energy map for the current version of the image.
+    energyMap = calculateEnergyMap(img, size);
+
+    // 2. Find the seam with the lowest energy based on the energy map.
+    seam = findLowEnergySeam(energyMap, size);
+
+    // 3. Delete the seam with the lowest energy seam from the image.
+    deleteSeam(img, seam, size);
+
+    // Reduce the image width, and continue iterations.
+    size.w -= 1;
+  }
+
+  // Returning the resized image and its final size.
+  // The img is actually a reference to the ImageData, so technically
+  // the caller of the function already has this pointer. But let's
+  // still return it for better code readability.
+  return { img, size };
+};
+
+export default resizeImageWidth;
diff --git a/src/algorithms/image-processing/utils/imageData.js b/src/algorithms/image-processing/utils/imageData.js
new file mode 100644
index 0000000000..de397b8ead
--- /dev/null
+++ b/src/algorithms/image-processing/utils/imageData.js
@@ -0,0 +1,39 @@
+/**
+ * @typedef {ArrayLike<number> | Uint8ClampedArray} PixelColor
+ */
+
+/**
+ * @typedef {Object} PixelCoordinate
+ * @property {number} x - horizontal coordinate.
+ * @property {number} y - vertical coordinate.
+ */
+
+/**
+ * Helper function that returns the color of the pixel.
+ * @param {ImageData} img
+ * @param {PixelCoordinate} coordinate
+ * @returns {PixelColor}
+ */
+export const getPixel = (img, { x, y }) => {
+  // The ImageData data array is a flat 1D array.
+  // Thus we need to convert x and y coordinates to the linear index.
+  const i = y * img.width + x;
+  const cellsPerColor = 4; // RGBA
+  // For better efficiency, instead of creating a new sub-array we return
+  // a pointer to the part of the ImageData array.
+  return img.data.subarray(i * cellsPerColor, i * cellsPerColor + cellsPerColor);
+};
+
+/**
+ * Helper function that sets the color of the pixel.
+ * @param {ImageData} img
+ * @param {PixelCoordinate} coordinate
+ * @param {PixelColor} color
+ */
+export const setPixel = (img, { x, y }, color) => {
+  // The ImageData data array is a flat 1D array.
+  // Thus we need to convert x and y coordinates to the linear index.
+  const i = y * img.width + x;
+  const cellsPerColor = 4; // RGBA
+  img.data.set(color, i * cellsPerColor);
+};
diff --git a/src/algorithms/linked-list/reverse-traversal/README.md b/src/algorithms/linked-list/reverse-traversal/README.md
new file mode 100644
index 0000000000..7e3f3cd6bb
--- /dev/null
+++ b/src/algorithms/linked-list/reverse-traversal/README.md
@@ -0,0 +1,23 @@
+# Reversed Linked List Traversal
+
+_Read this in other languages:_
+[_中文_](README.zh-CN.md),
+[_Português_](README.pt-BR.md)
+
+The task is to traverse the given linked list in reversed order.
+
+For example for the following linked list: 
+
+![](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+
+The order of traversal should be:
+
+```text
+37 → 99 → 12
+```
+
+The time complexity is `O(n)` because we visit every node only once.
+
+## Reference
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
diff --git a/src/algorithms/linked-list/reverse-traversal/README.pt-BR.md b/src/algorithms/linked-list/reverse-traversal/README.pt-BR.md
new file mode 100644
index 0000000000..b2761ad720
--- /dev/null
+++ b/src/algorithms/linked-list/reverse-traversal/README.pt-BR.md
@@ -0,0 +1,23 @@
+# Travessia de Lista Encadeada Reversa
+
+_Leia isso em outros idiomas:_
+[_中文_](README.zh-CN.md),
+[_English_](README.md)
+
+A tarefa é percorrer a lista encadeada fornecida em ordem inversa.
+
+Por exemplo, para a seguinte lista vinculada:
+
+![](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+
+A ordem de travessia deve ser:
+
+```texto
+37 → 99 → 12
+```
+
+A complexidade de tempo é `O(n)` porque visitamos cada nó apenas uma vez.
+
+## Referência
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
diff --git a/src/algorithms/linked-list/reverse-traversal/README.zh-CN.md b/src/algorithms/linked-list/reverse-traversal/README.zh-CN.md
new file mode 100644
index 0000000000..cf0217a5b1
--- /dev/null
+++ b/src/algorithms/linked-list/reverse-traversal/README.zh-CN.md
@@ -0,0 +1,19 @@
+# 链表倒序遍历
+
+我们的任务是倒序遍历给定的链表
+
+比如下面的链表
+
+![](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+
+遍历的顺序应该是
+
+```text
+37 → 99 → 12
+```
+
+因为我们每个节点只访问一次，时间复杂度应该是`O(n)`
+
+## 参考
+
+- [Wikipedia](https://zh.wikipedia.org/wiki/%E9%93%BE%E8%A1%A8)
diff --git a/src/algorithms/linked-list/reverse-traversal/__test__/reverseTraversal.test.js b/src/algorithms/linked-list/reverse-traversal/__test__/reverseTraversal.test.js
new file mode 100644
index 0000000000..2fb5af9ae7
--- /dev/null
+++ b/src/algorithms/linked-list/reverse-traversal/__test__/reverseTraversal.test.js
@@ -0,0 +1,35 @@
+import LinkedList from '../../../../data-structures/linked-list/LinkedList';
+import reverseTraversal from '../reverseTraversal';
+
+describe('reverseTraversal', () => {
+  it('should traverse linked list in reverse order', () => {
+    const linkedList = new LinkedList();
+
+    linkedList
+      .append(1)
+      .append(2)
+      .append(3);
+
+    const traversedNodeValues = [];
+    const traversalCallback = (nodeValue) => {
+      traversedNodeValues.push(nodeValue);
+    };
+
+    reverseTraversal(linkedList, traversalCallback);
+
+    expect(traversedNodeValues).toEqual([3, 2, 1]);
+  });
+});
+
+// it('should reverse traversal the linked list with callback', () => {
+//   const linkedList = new LinkedList();
+//
+//   linkedList
+//     .append(1)
+//     .append(2)
+//     .append(3);
+//
+//   expect(linkedList.toString()).toBe('1,2,3');
+//   expect(linkedList.reverseTraversal(linkedList.head, value => value * 2)).toEqual([6, 4, 2]);
+//   expect(() => linkedList.reverseTraversal(linkedList.head)).toThrow();
+// });
diff --git a/src/algorithms/linked-list/reverse-traversal/reverseTraversal.js b/src/algorithms/linked-list/reverse-traversal/reverseTraversal.js
new file mode 100644
index 0000000000..d39260aab2
--- /dev/null
+++ b/src/algorithms/linked-list/reverse-traversal/reverseTraversal.js
@@ -0,0 +1,24 @@
+/**
+ * Traversal callback function.
+ * @callback traversalCallback
+ * @param {*} nodeValue
+ */
+
+/**
+ * @param {LinkedListNode} node
+ * @param {traversalCallback} callback
+ */
+function reverseTraversalRecursive(node, callback) {
+  if (node) {
+    reverseTraversalRecursive(node.next, callback);
+    callback(node.value);
+  }
+}
+
+/**
+ * @param {LinkedList} linkedList
+ * @param {traversalCallback} callback
+ */
+export default function reverseTraversal(linkedList, callback) {
+  reverseTraversalRecursive(linkedList.head, callback);
+}
diff --git a/src/algorithms/linked-list/traversal/README.md b/src/algorithms/linked-list/traversal/README.md
new file mode 100644
index 0000000000..09b177494c
--- /dev/null
+++ b/src/algorithms/linked-list/traversal/README.md
@@ -0,0 +1,24 @@
+# Linked List Traversal
+
+_Read this in other languages:_
+[_Русский_](README.ru-RU.md),
+[_中文_](README.zh-CN.md),
+[_Português_](README.pt-BR.md)
+
+The task is to traverse the given linked list in straight order.
+
+For example for the following linked list:
+
+![Singly linked list](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+
+The order of traversal should be:
+
+```text
+12 → 99 → 37
+```
+
+The time complexity is `O(n)` because we visit every node only once.
+
+## Reference
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
diff --git a/src/algorithms/linked-list/traversal/README.pt-BR.md b/src/algorithms/linked-list/traversal/README.pt-BR.md
new file mode 100644
index 0000000000..0fae9ed01a
--- /dev/null
+++ b/src/algorithms/linked-list/traversal/README.pt-BR.md
@@ -0,0 +1,24 @@
+# Travessia de Lista Encadeada
+
+_Leia isso em outros idiomas:_
+[_Русский_](README.ru-RU.md),
+[_中文_](README.zh-CN.md),
+[_English_](README.md)
+
+A tarefa é percorrer a lista encadeada fornecida em ordem direta.
+
+Por exemplo, para a seguinte lista vinculada:
+
+![Singly linked list](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+
+A ordem de travessia deve ser:
+
+```texto
+12 → 99 → 37
+```
+
+A complexidade de tempo é `O(n)` porque visitamos cada nó apenas uma vez.
+
+## Referência
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
diff --git a/src/algorithms/linked-list/traversal/README.ru-RU.md b/src/algorithms/linked-list/traversal/README.ru-RU.md
new file mode 100644
index 0000000000..fa3cfb2fb1
--- /dev/null
+++ b/src/algorithms/linked-list/traversal/README.ru-RU.md
@@ -0,0 +1,19 @@
+# Обход связного списка
+
+Задача состоит в том, чтобы обойти связный список в прямом порядке.
+
+Например, для следующего связного списка:
+
+![Singly linked list](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+
+Порядок обхода будет такой:
+
+```text
+12 → 99 → 37
+```
+
+Временная сложность - `O(n)`, потому что мы посещаем каждый узел только один раз.
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A1%D0%B2%D1%8F%D0%B7%D0%BD%D1%8B%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA)
diff --git a/src/algorithms/linked-list/traversal/README.zh-CN.md b/src/algorithms/linked-list/traversal/README.zh-CN.md
new file mode 100644
index 0000000000..19a01502b5
--- /dev/null
+++ b/src/algorithms/linked-list/traversal/README.zh-CN.md
@@ -0,0 +1,19 @@
+# 链表遍历
+
+我们的任务是顺序遍历给定的链表
+
+比如下面的链表
+
+![Singly linked list](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+
+遍历的顺序应该是
+
+```text
+12 → 99 → 37
+```
+
+因为我们每个节点只访问一次，时间复杂度应该是`O(n)`
+
+## 参考
+
+- [Wikipedia](https://zh.wikipedia.org/wiki/%E9%93%BE%E8%A1%A8)
\ No newline at end of file
diff --git a/src/algorithms/linked-list/traversal/__test__/traversal.test.js b/src/algorithms/linked-list/traversal/__test__/traversal.test.js
new file mode 100644
index 0000000000..77ce79b69f
--- /dev/null
+++ b/src/algorithms/linked-list/traversal/__test__/traversal.test.js
@@ -0,0 +1,22 @@
+import LinkedList from '../../../../data-structures/linked-list/LinkedList';
+import traversal from '../traversal';
+
+describe('traversal', () => {
+  it('should traverse linked list', () => {
+    const linkedList = new LinkedList();
+
+    linkedList
+      .append(1)
+      .append(2)
+      .append(3);
+
+    const traversedNodeValues = [];
+    const traversalCallback = (nodeValue) => {
+      traversedNodeValues.push(nodeValue);
+    };
+
+    traversal(linkedList, traversalCallback);
+
+    expect(traversedNodeValues).toEqual([1, 2, 3]);
+  });
+});
diff --git a/src/algorithms/linked-list/traversal/traversal.js b/src/algorithms/linked-list/traversal/traversal.js
new file mode 100644
index 0000000000..cfbfe4fb59
--- /dev/null
+++ b/src/algorithms/linked-list/traversal/traversal.js
@@ -0,0 +1,18 @@
+/**
+ * Traversal callback function.
+ * @callback traversalCallback
+ * @param {*} nodeValue
+ */
+
+/**
+ * @param {LinkedList} linkedList
+ * @param {traversalCallback} callback
+ */
+export default function traversal(linkedList, callback) {
+  let currentNode = linkedList.head;
+
+  while (currentNode) {
+    callback(currentNode.value);
+    currentNode = currentNode.next;
+  }
+}
diff --git a/src/algorithms/math/binary-floating-point/README.md b/src/algorithms/math/binary-floating-point/README.md
new file mode 100644
index 0000000000..d1ae72731e
--- /dev/null
+++ b/src/algorithms/math/binary-floating-point/README.md
@@ -0,0 +1,103 @@
+# Binary representation of floating-point numbers
+
+Have you ever wondered how computers store the floating-point numbers like `3.1416` (𝝿) or `9.109 × 10⁻³¹` (the mass of the electron in kg) in the memory which is limited by a finite number of ones and zeroes (aka bits)?
+
+It seems pretty straightforward for integers (i.e. `17`). Let's say we have 16 bits (2 bytes) to store the number. In 16 bits we may store the integers in a range of `[0, 65535]`:
+
+```text
+(0000000000000000)₂ = (0)₁₀
+
+(0000000000010001)₂ =
+    (1 × 2⁴) +
+    (0 × 2³) +
+    (0 × 2²) +
+    (0 × 2¹) +
+    (1 × 2⁰) = (17)₁₀
+
+(1111111111111111)₂ =
+    (1 × 2¹⁵) +
+    (1 × 2¹⁴) +
+    (1 × 2¹³) +
+    (1 × 2¹²) +
+    (1 × 2¹¹) +
+    (1 × 2¹⁰) +
+    (1 × 2⁹) +
+    (1 × 2⁸) +
+    (1 × 2⁷) +
+    (1 × 2⁶) +
+    (1 × 2⁵) +
+    (1 × 2⁴) +
+    (1 × 2³) +
+    (1 × 2²) +
+    (1 × 2¹) +
+    (1 × 2⁰) = (65535)₁₀
+```
+
+If we need a signed integer we may use [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement) and shift the range of `[0, 65535]` towards the negative numbers. In this case, our 16 bits would represent the numbers in a range of `[-32768, +32767]`.
+
+As you might have noticed, this approach won't allow you to represent the numbers like `-27.15625` (numbers after the decimal point are just being ignored).
+
+We're not the first ones who have noticed this issue though. Around ≈36 years ago some smart folks overcame this limitation by introducing the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard for floating-point arithmetic.
+
+The IEEE 754 standard describes the way (the framework) of using those 16 bits (or 32, or 64 bits) to store the numbers of wider range, including the small floating numbers (smaller than 1 and closer to 0).
+
+To get the idea behind the standard we might recall the [scientific notation](https://en.wikipedia.org/wiki/Scientific_notation) - a way of expressing numbers that are too large or too small (usually would result in a long string of digits) to be conveniently written in decimal form.
+
+![Scientific number notation](images/03-scientific-notation.png)
+
+As you may see from the image, the number representation might be split into three parts:
+
+- **sign**
+- **fraction (aka significand)** - the valuable digits (the meaning, the payload) of the number
+- **exponent** - controls how far and in which direction to move the decimal point in the fraction
+
+The **base** part we may omit by just agreeing on what it will be equal to. In our case, we'll be using `2` as a base.
+
+Instead of using all 16 bits (or 32 bits, or 64 bits) to store the fraction of the number, we may share the bits and store a sign, exponent, and fraction at the same time. Depending on the number of bits that we're going to use to store the number we end up with the following splits:
+
+| Floating-point format | Total bits | Sign bits | Exponent bits | Fraction bits | Base |
+| :-------------------- | :--------: | :-------: | :-----------: | :--------------: | :--: |
+| [Half-precision](https://en.wikipedia.org/wiki/Half-precision_floating-point_format)        | 16         | 1         | 5             | 10               | 2    |
+| [Single-precision](https://en.wikipedia.org/wiki/Single-precision_floating-point_format)      | 32         | 1         | 8             | 23               | 2    |
+| [Double-precision](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)      | 64         | 1         | 11            | 52               | 2    |
+
+With this approach, the number of bits for the fraction has been reduced (i.e. for the 16-bits number it was reduced from 16 bits to 10 bits). It means that the fraction might take a narrower range of values now (losing some precision). However, since we also have an exponent part, it will actually increase the ultimate number range and also allow us to describe the numbers between 0 and 1 (if the exponent is negative).
+
+> For example, a signed 32-bit integer variable has a maximum value of 2³¹ − 1 = 2,147,483,647, whereas an IEEE 754 32-bit base-2 floating-point variable has a maximum value of ≈ 3.4028235 × 10³⁸.
+
+To make it possible to have a negative exponent, the IEEE 754 standard uses the [biased exponent](https://en.wikipedia.org/wiki/Exponent_bias). The idea is simple - subtract the bias from the exponent value to make it negative. For example, if the exponent has 5 bits, it might take the values from the range of `[0, 31]` (all values are positive here). But if we subtract the value of `15` from it, the range will be `[-15, 16]`. The number `15` is called bias, and it is being calculated by the following formula:
+
+```
+exponent_bias = 2 ^ (k−1) − 1
+
+k - number of exponent bits
+```
+
+I've tried to describe the logic behind the converting of floating-point numbers from a binary format back to the decimal format on the image below. Hopefully, it will give you a better understanding of how the IEEE 754 standard works. The 16-bits number is being used here for simplicity, but the same approach works for 32-bits and 64-bits numbers as well.
+
+![Half-precision floating point number format explained in one picture](images/02-half-precision-floating-point-number-explained.png)
+
+> Checkout the [interactive version of this diagram](https://trekhleb.dev/blog/2021/binary-floating-point/) to play around with setting bits on and off, and seeing how it would influence the final result
+
+Here is the number ranges that different floating-point formats support:
+
+| Floating-point format | Exp min | Exp max | Range            | Min positive |
+| :-------------------- | :------ | :------ | :--------------- | :----------- |
+| Half-precision        | −14     | +15     | ±65,504          | 6.10 × 10⁻⁵  |
+| Single-precision      | −126    | +127    | ±3.4028235 × 10³⁸| 1.18 × 10⁻³⁸ |
+
+Be aware that this is by no means a complete and sufficient overview of the IEEE 754 standard. It is rather a simplified and basic overview. Several corner cases were omitted in the examples above for simplicity of presentation (i.e. `-0`, `-∞`, `+∞` and `NaN` (not a number) values)
+
+## Code examples
+
+- See the [bitsToFloat.js](bitsToFloat.js) for the example of how to convert array of bits to the floating point number (the example is a bit artificial but still it gives the overview of how the conversion is going on)
+- See the [floatAsBinaryString.js](floatAsBinaryString.js) for the example of how to see the actual binary representation of the floating-point number in JavaScript
+
+## References
+
+You might also want to check out the following resources to get a deeper understanding of the binary representation of floating-point numbers:
+
+- [Interactive version of this article](https://trekhleb.dev/blog/2021/binary-floating-point/) (allows setting the bits manually and seeing the resulting floating number)
+- [Here is what you need to know about JavaScript’s Number type](https://indepth.dev/posts/1139/here-is-what-you-need-to-know-about-javascripts-number-type)
+- [Float Exposed](https://float.exposed/)
+- [IEEE754 Visualization](https://bartaz.github.io/ieee754-visualization/)
diff --git a/src/algorithms/math/binary-floating-point/__tests__/bitsToFloat.test.js b/src/algorithms/math/binary-floating-point/__tests__/bitsToFloat.test.js
new file mode 100644
index 0000000000..53afe691a2
--- /dev/null
+++ b/src/algorithms/math/binary-floating-point/__tests__/bitsToFloat.test.js
@@ -0,0 +1,32 @@
+import { testCases16Bits, testCases32Bits, testCases64Bits } from '../testCases';
+import { bitsToFloat16, bitsToFloat32, bitsToFloat64 } from '../bitsToFloat';
+
+describe('bitsToFloat16', () => {
+  it('should convert floating point binary bits to floating point decimal number', () => {
+    for (let testCaseIndex = 0; testCaseIndex < testCases16Bits.length; testCaseIndex += 1) {
+      const [decimal, binary] = testCases16Bits[testCaseIndex];
+      const bits = binary.split('').map((bitString) => parseInt(bitString, 10));
+      expect(bitsToFloat16(bits)).toBeCloseTo(decimal, 4);
+    }
+  });
+});
+
+describe('bitsToFloat32', () => {
+  it('should convert floating point binary bits to floating point decimal number', () => {
+    for (let testCaseIndex = 0; testCaseIndex < testCases32Bits.length; testCaseIndex += 1) {
+      const [decimal, binary] = testCases32Bits[testCaseIndex];
+      const bits = binary.split('').map((bitString) => parseInt(bitString, 10));
+      expect(bitsToFloat32(bits)).toBeCloseTo(decimal, 7);
+    }
+  });
+});
+
+describe('bitsToFloat64', () => {
+  it('should convert floating point binary bits to floating point decimal number', () => {
+    for (let testCaseIndex = 0; testCaseIndex < testCases64Bits.length; testCaseIndex += 1) {
+      const [decimal, binary] = testCases64Bits[testCaseIndex];
+      const bits = binary.split('').map((bitString) => parseInt(bitString, 10));
+      expect(bitsToFloat64(bits)).toBeCloseTo(decimal, 14);
+    }
+  });
+});
diff --git a/src/algorithms/math/binary-floating-point/__tests__/floatAsBinaryString.test.js b/src/algorithms/math/binary-floating-point/__tests__/floatAsBinaryString.test.js
new file mode 100644
index 0000000000..efb8a2ba5e
--- /dev/null
+++ b/src/algorithms/math/binary-floating-point/__tests__/floatAsBinaryString.test.js
@@ -0,0 +1,20 @@
+import { floatAs32BinaryString, floatAs64BinaryString } from '../floatAsBinaryString';
+import { testCases32Bits, testCases64Bits } from '../testCases';
+
+describe('floatAs32Binary', () => {
+  it('should create a binary representation of the floating numbers', () => {
+    for (let testCaseIndex = 0; testCaseIndex < testCases32Bits.length; testCaseIndex += 1) {
+      const [decimal, binary] = testCases32Bits[testCaseIndex];
+      expect(floatAs32BinaryString(decimal)).toBe(binary);
+    }
+  });
+});
+
+describe('floatAs64Binary', () => {
+  it('should create a binary representation of the floating numbers', () => {
+    for (let testCaseIndex = 0; testCaseIndex < testCases64Bits.length; testCaseIndex += 1) {
+      const [decimal, binary] = testCases64Bits[testCaseIndex];
+      expect(floatAs64BinaryString(decimal)).toBe(binary);
+    }
+  });
+});
diff --git a/src/algorithms/math/binary-floating-point/bitsToFloat.js b/src/algorithms/math/binary-floating-point/bitsToFloat.js
new file mode 100644
index 0000000000..6d1ef0d851
--- /dev/null
+++ b/src/algorithms/math/binary-floating-point/bitsToFloat.js
@@ -0,0 +1,119 @@
+/**
+ * Sequence of 0s and 1s.
+ * @typedef {number[]} Bits
+ */
+
+/**
+ * @typedef {{
+ *   signBitsCount: number,
+ *   exponentBitsCount: number,
+ *   fractionBitsCount: number,
+ * }} PrecisionConfig
+ */
+
+/**
+ * @typedef {{
+ *   half: PrecisionConfig,
+ *   single: PrecisionConfig,
+ *   double: PrecisionConfig
+ * }} PrecisionConfigs
+ */
+
+/**
+ * ┌───────────────── sign bit
+ * │   ┌───────────── exponent bits
+ * │   │       ┌───── fraction bits
+ * │   │       │
+ * X XXXXX XXXXXXXXXX
+ *
+ * @type {PrecisionConfigs}
+ */
+const precisionConfigs = {
+  // @see: https://en.wikipedia.org/wiki/Half-precision_floating-point_format
+  half: {
+    signBitsCount: 1,
+    exponentBitsCount: 5,
+    fractionBitsCount: 10,
+  },
+  // @see: https://en.wikipedia.org/wiki/Single-precision_floating-point_format
+  single: {
+    signBitsCount: 1,
+    exponentBitsCount: 8,
+    fractionBitsCount: 23,
+  },
+  // @see: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+  double: {
+    signBitsCount: 1,
+    exponentBitsCount: 11,
+    fractionBitsCount: 52,
+  },
+};
+
+/**
+ * Converts the binary representation of the floating point number to decimal float number.
+ *
+ * @param {Bits} bits - sequence of bits that represents the floating point number.
+ * @param {PrecisionConfig} precisionConfig - half/single/double precision config.
+ * @return {number} - floating point number decoded from its binary representation.
+ */
+function bitsToFloat(bits, precisionConfig) {
+  const { signBitsCount, exponentBitsCount } = precisionConfig;
+
+  // Figuring out the sign.
+  const sign = (-1) ** bits[0]; // -1^1 = -1, -1^0 = 1
+
+  // Calculating the exponent value.
+  const exponentBias = 2 ** (exponentBitsCount - 1) - 1;
+  const exponentBits = bits.slice(signBitsCount, signBitsCount + exponentBitsCount);
+  const exponentUnbiased = exponentBits.reduce(
+    (exponentSoFar, currentBit, bitIndex) => {
+      const bitPowerOfTwo = 2 ** (exponentBitsCount - bitIndex - 1);
+      return exponentSoFar + currentBit * bitPowerOfTwo;
+    },
+    0,
+  );
+  const exponent = exponentUnbiased - exponentBias;
+
+  // Calculating the fraction value.
+  const fractionBits = bits.slice(signBitsCount + exponentBitsCount);
+  const fraction = fractionBits.reduce(
+    (fractionSoFar, currentBit, bitIndex) => {
+      const bitPowerOfTwo = 2 ** -(bitIndex + 1);
+      return fractionSoFar + currentBit * bitPowerOfTwo;
+    },
+    0,
+  );
+
+  // Putting all parts together to calculate the final number.
+  return sign * (2 ** exponent) * (1 + fraction);
+}
+
+/**
+ *  Converts the 16-bit binary representation of the floating point number to decimal float number.
+ *
+ * @param {Bits} bits - sequence of bits that represents the floating point number.
+ * @return {number} - floating point number decoded from its binary representation.
+ */
+export function bitsToFloat16(bits) {
+  return bitsToFloat(bits, precisionConfigs.half);
+}
+
+/**
+ * Converts the 32-bit binary representation of the floating point number to decimal float number.
+ *
+ * @param {Bits} bits - sequence of bits that represents the floating point number.
+ * @return {number} - floating point number decoded from its binary representation.
+ */
+export function bitsToFloat32(bits) {
+  return bitsToFloat(bits, precisionConfigs.single);
+}
+
+/**
+ * Converts the 64-bit binary representation of the floating point number to decimal float number.
+ *
+ * @param {Bits} bits - sequence of bits that represents the floating point number.
+ * @return {number} - floating point number decoded from its binary representation.
+ */
+export function bitsToFloat64(bits) {
+  return bitsToFloat(bits, precisionConfigs.double);
+}
diff --git a/src/algorithms/math/binary-floating-point/floatAsBinaryString.js b/src/algorithms/math/binary-floating-point/floatAsBinaryString.js
new file mode 100644
index 0000000000..aa907ac049
--- /dev/null
+++ b/src/algorithms/math/binary-floating-point/floatAsBinaryString.js
@@ -0,0 +1,61 @@
+// @see: https://en.wikipedia.org/wiki/Single-precision_floating-point_format
+const singlePrecisionBytesLength = 4; // 32 bits
+
+// @see: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+const doublePrecisionBytesLength = 8; // 64 bits
+
+const bitsInByte = 8;
+
+/**
+ * Converts the float number into its IEEE 754 binary representation.
+ * @see: https://en.wikipedia.org/wiki/IEEE_754
+ *
+ * @param {number} floatNumber - float number in decimal format.
+ * @param {number} byteLength - number of bytes to use to store the float number.
+ * @return {string} - binary string representation of the float number.
+ */
+function floatAsBinaryString(floatNumber, byteLength) {
+  let numberAsBinaryString = '';
+
+  const arrayBuffer = new ArrayBuffer(byteLength);
+  const dataView = new DataView(arrayBuffer);
+
+  const byteOffset = 0;
+  const littleEndian = false;
+
+  if (byteLength === singlePrecisionBytesLength) {
+    dataView.setFloat32(byteOffset, floatNumber, littleEndian);
+  } else {
+    dataView.setFloat64(byteOffset, floatNumber, littleEndian);
+  }
+
+  for (let byteIndex = 0; byteIndex < byteLength; byteIndex += 1) {
+    let bits = dataView.getUint8(byteIndex).toString(2);
+    if (bits.length < bitsInByte) {
+      bits = new Array(bitsInByte - bits.length).fill('0').join('') + bits;
+    }
+    numberAsBinaryString += bits;
+  }
+
+  return numberAsBinaryString;
+}
+
+/**
+ * Converts the float number into its IEEE 754 64-bits binary representation.
+ *
+ * @param {number} floatNumber - float number in decimal format.
+ * @return {string} - 64 bits binary string representation of the float number.
+ */
+export function floatAs64BinaryString(floatNumber) {
+  return floatAsBinaryString(floatNumber, doublePrecisionBytesLength);
+}
+
+/**
+ * Converts the float number into its IEEE 754 32-bits binary representation.
+ *
+ * @param {number} floatNumber - float number in decimal format.
+ * @return {string} - 32 bits binary string representation of the float number.
+ */
+export function floatAs32BinaryString(floatNumber) {
+  return floatAsBinaryString(floatNumber, singlePrecisionBytesLength);
+}
diff --git a/src/algorithms/math/binary-floating-point/images/02-half-precision-floating-point-number-explained.png b/src/algorithms/math/binary-floating-point/images/02-half-precision-floating-point-number-explained.png
new file mode 100644
index 0000000000..1bc0a5b835
Binary files /dev/null and b/src/algorithms/math/binary-floating-point/images/02-half-precision-floating-point-number-explained.png differ
diff --git a/src/algorithms/math/binary-floating-point/images/03-scientific-notation.png b/src/algorithms/math/binary-floating-point/images/03-scientific-notation.png
new file mode 100644
index 0000000000..449aa6faf5
Binary files /dev/null and b/src/algorithms/math/binary-floating-point/images/03-scientific-notation.png differ
diff --git a/src/algorithms/math/binary-floating-point/testCases.js b/src/algorithms/math/binary-floating-point/testCases.js
new file mode 100644
index 0000000000..54b9b8029b
--- /dev/null
+++ b/src/algorithms/math/binary-floating-point/testCases.js
@@ -0,0 +1,71 @@
+/**
+ * @typedef {[number, string]} TestCase
+ * @property {number} decimal
+ * @property {string} binary
+ */
+
+/**
+ * @type {TestCase[]}
+ */
+export const testCases16Bits = [
+  [-65504, '1111101111111111'],
+  [-10344, '1111000100001101'],
+  [-27.15625, '1100111011001010'],
+  [-1, '1011110000000000'],
+  [-0.09997558, '1010111001100110'],
+  [0, '0000000000000000'],
+  [5.9604644775390625e-8, '0000000000000001'],
+  [0.000004529, '0000000001001100'],
+  [0.0999755859375, '0010111001100110'],
+  [0.199951171875, '0011001001100110'],
+  [0.300048828125, '0011010011001101'],
+  [1, '0011110000000000'],
+  [1.5, '0011111000000000'],
+  [1.75, '0011111100000000'],
+  [1.875, '0011111110000000'],
+  [65504, '0111101111111111'],
+];
+
+/**
+ * @type {TestCase[]}
+ */
+export const testCases32Bits = [
+  [-3.40282346638528859812e+38, '11111111011111111111111111111111'],
+  [-10345.5595703125, '11000110001000011010011000111101'],
+  [-27.15625, '11000001110110010100000000000000'],
+  [-1, '10111111100000000000000000000000'],
+  [-0.1, '10111101110011001100110011001101'],
+  [0, '00000000000000000000000000000000'],
+  [1.40129846432481707092e-45, '00000000000000000000000000000001'],
+  [0.000004560, '00110110100110010000001000011010'],
+  [0.1, '00111101110011001100110011001101'],
+  [0.2, '00111110010011001100110011001101'],
+  [0.3, '00111110100110011001100110011010'],
+  [1, '00111111100000000000000000000000'],
+  [1.5, '00111111110000000000000000000000'],
+  [1.75, '00111111111000000000000000000000'],
+  [1.875, '00111111111100000000000000000000'],
+  [3.40282346638528859812e+38, '01111111011111111111111111111111'],
+];
+
+/**
+ * @type {TestCase[]}
+ */
+export const testCases64Bits = [
+  [-1.79769313486231570815e+308, '1111111111101111111111111111111111111111111111111111111111111111'],
+  [-10345.5595703125, '1100000011000100001101001100011110100000000000000000000000000000'],
+  [-27.15625, '1100000000111011001010000000000000000000000000000000000000000000'],
+  [-1, '1011111111110000000000000000000000000000000000000000000000000000'],
+  [-0.1, '1011111110111001100110011001100110011001100110011001100110011010'],
+  [0, '0000000000000000000000000000000000000000000000000000000000000000'],
+  [4.94065645841246544177e-324, '0000000000000000000000000000000000000000000000000000000000000001'],
+  [0.000004560, '0011111011010011001000000100001101000001011100110011110011100100'],
+  [0.1, '0011111110111001100110011001100110011001100110011001100110011010'],
+  [0.2, '0011111111001001100110011001100110011001100110011001100110011010'],
+  [0.3, '0011111111010011001100110011001100110011001100110011001100110011'],
+  [1, '0011111111110000000000000000000000000000000000000000000000000000'],
+  [1.5, '0011111111111000000000000000000000000000000000000000000000000000'],
+  [1.75, '0011111111111100000000000000000000000000000000000000000000000000'],
+  [1.875, '0011111111111110000000000000000000000000000000000000000000000000'],
+  [1.79769313486231570815e+308, '0111111111101111111111111111111111111111111111111111111111111111'],
+];
diff --git a/src/algorithms/math/bits/README.fr-FR.md b/src/algorithms/math/bits/README.fr-FR.md
new file mode 100644
index 0000000000..64ccd2dde1
--- /dev/null
+++ b/src/algorithms/math/bits/README.fr-FR.md
@@ -0,0 +1,295 @@
+# Manipulation de bits
+
+_Read this in other languages:_
+[english](README.md).
+
+#### Vérifier un bit (_get_)
+
+Cette méthode décale le bit correspondant (_bit shifting_) à la position zéro.
+Ensuite, nous exécutons l'opération `AND` avec un masque comme `0001`.
+Cela efface tous les bits du nombre original sauf le correspondant.
+Si le bit pertinent est `1`, le résultat est `1`, sinon le résultat est `0`.
+
+> Voir [getBit.js](getBit.js) pour plus de détails.
+
+#### Mettre un bit à 1(_set_)
+
+Cette méthode met un bit à `1` en fonction d'un rang (`bitPosition`),
+créant ainsi une valeur qui ressemble à `00100`.
+Ensuite, nous effectuons l'opération `OU` qui met un bit spécifique
+en `1` sans affecter les autres bits du nombre.
+
+> Voir [setBit.js](setBit.js) pour plus de détails.
+
+#### Mettre un bit à 0 (_clear_)
+
+Cette méthode met un bit à `1` en fonction d'un rang (`bitPosition`),
+créant ainsi une valeur qui ressemble à `00100`.
+Puis on inverse ce masque de bits pour obtenir un nombre ressemblant à `11011`.
+Enfin, l'opération `AND` est appliquée au nombre et au masque.
+Cette opération annule le bit.
+
+> Voir [clearBit.js](clearBit.js) pour plus de détails.
+
+#### Mettre à jour un Bit (_update_)
+
+Cette méthode est une combinaison de l'"annulation de bit"
+et du "forçage de bit".
+
+> Voir [updateBit.js](updateBit.js) pour plus de détails.
+
+#### Vérifier si un nombre est pair (_isEven_)
+
+Cette méthode détermine si un nombre donné est pair.
+Elle s'appuie sur le fait que les nombres impairs ont leur dernier
+bit droit à `1`.
+
+```text
+Nombre: 5 = 0b0101
+isEven: false
+
+Nombre: 4 = 0b0100
+isEven: true
+```
+
+> Voir [isEven.js](isEven.js) pour plus de détails.
+
+#### Vérifier si un nombre est positif (_isPositive_)
+
+Cette méthode détermine un le nombre donné est positif.
+Elle s'appuie sur le fait que tous les nombres positifs
+ont leur bit le plus à gauche à `0`.
+Cependant, si le nombre fourni est zéro
+ou zéro négatif, il doit toujours renvoyer `false`.
+
+```text
+Nombre: 1 = 0b0001
+isPositive: true
+
+Nombre: -1 = -0b0001
+isPositive: false
+```
+
+> Voir [isPositive.js](isPositive.js) pour plus de détails.
+
+#### Multiplier par deux
+
+Cette méthode décale un nombre donné d'un bit vers la gauche.
+Ainsi, toutes les composantes du nombre binaire (en puissances de deux) sont
+multipliées par deux et donc le nombre lui-même est
+multiplié par deux.
+
+```
+Avant le décalage
+Nombre: 0b0101 = 5
+Puissances de deux: 0 + 2^2 + 0 + 2^0
+
+Après le décalage
+Nombre: 0b1010 = 10
+Puissances de deux: 2^3 + 0 + 2^1 + 0
+```
+
+> Voir [multiplyByTwo.js](multiplyByTwo.js) pour plus de détails.
+
+#### Diviser par deux
+
+Cette méthode décale un nombre donné d'un bit vers la droite.
+Ainsi, toutes les composantes du nombre binaire (en puissances de deux) sont
+divisées par deux et donc le nombre lui-même est
+divisé par deux, sans reste.
+
+```
+Avant le décalage
+Nombre: 0b0101 = 5
+Puissances de deux: 0 + 2^2 + 0 + 2^0
+
+Après le décalage
+Nombre: 0b0010 = 2
+Puissances de deux: 0 + 0 + 2^1 + 0
+```
+
+> Voir [divideByTwo.js](divideByTwo.js) pour plus de détails.
+
+#### Inverser le signe (_Switch Sign_)
+
+Cette méthode rend positifs les nombres négatifs, et vice-versa.
+Pour ce faire, elle s'appuie sur l'approche "Complément à deux",
+qui inverse tous les bits du nombre et y ajoute 1.
+
+```
+1101 -3
+1110 -2
+1111 -1
+0000  0
+0001  1
+0010  2
+0011  3
+```
+
+> Voir [switchSign.js](switchSign.js) pour plus de détails.
+
+#### Multiplier deux nombres signés
+
+Cette méthode multiplie deux nombres entiers signés
+à l'aide d'opérateurs bit à bit.
+Cette méthode est basée sur les faits suivants:
+
+```text
+a * b peut être écrit sous les formes suivantes:
+  0                     si a est zero ou b est zero ou les deux sont zero
+  2a * (b/2)            si b est pair
+  2a * (b - 1)/2 + a    si b est impair et positif
+  2a * (b + 1)/2 - a    si b est impair et negatif
+```
+
+L'avantage de cette approche est qu'à chaque étape de la récursion
+l'un des opérandes est réduit à la moitié de sa valeur d'origine.
+Par conséquent, la complexité d'exécution est `O(log(b))`
+où `b` est l'opérande qui se réduit de moitié à chaque récursion.
+
+> Voir [multiply.js](multiply.js) pour plus de détails.
+
+#### Multiplier deux nombres positifs
+
+Cette méthode multiplie deux nombres entiers à l'aide d'opérateurs bit à bit.
+Cette méthode s'appuie sur le fait que "Chaque nombre peut être lu
+comme une somme de puissances de 2".
+
+L'idée principale de la multiplication bit à bit
+est que chaque nombre peut être divisé en somme des puissances de deux:
+
+Ainsi
+
+```text
+19 = 2^4 + 2^1 + 2^0
+```
+
+Donc multiplier `x` par `19` est equivalent à :
+
+```text
+x * 19 = x * 2^4 + x * 2^1 + x * 2^0
+```
+
+Nous devons maintenant nous rappeler que `x * 2 ^ 4` équivaut
+à déplacer`x` vers la gauche par `4` bits (`x << 4`).
+
+> Voir [multiplyUnsigned.js](multiplyUnsigned.js) pour plus de détails.
+
+#### Compter les bits à 1
+
+This method counts the number of set bits in a number using bitwise operators.
+The main idea is that we shift the number right by one bit at a time and check
+the result of `&` operation that is `1` if bit is set and `0` otherwise.
+
+Cette méthode décompte les bits à `1` d'un nombre
+à l'aide d'opérateurs bit à bit.
+L'idée principale est de décaler le nombre vers la droite, un bit à la fois,
+et de vérifier le résultat de l'opération `&` :
+`1` si le bit est défini et `0` dans le cas contraire.
+
+```text
+Nombre: 5 = 0b0101
+Décompte des bits à 1 = 2
+```
+
+> Voir [countSetBits.js](countSetBits.js) pour plus de détails.
+
+#### Compter les bits nécessaire pour remplacer un nombre
+
+This methods outputs the number of bits required to convert one number to another.
+This makes use of property that when numbers are `XOR`-ed the result will be number
+of different bits.
+
+Cette méthode retourne le nombre de bits requis
+pour convertir un nombre en un autre.
+Elle repose sur la propriété suivante:
+lorsque les nombres sont évalués via `XOR`, le résultat est le nombre
+de bits différents entre les deux.
+
+```
+5 = 0b0101
+1 = 0b0001
+Nombre de bits pour le remplacement: 1
+```
+
+> Voir [bitsDiff.js](bitsDiff.js) pour plus de détails.
+
+#### Calculer les bits significatifs d'un nombre
+
+Pour connaître les bits significatifs d'un nombre,
+on peut décaler `1` d'un bit à gauche plusieurs fois d'affilée
+jusqu'à ce que ce nombre soit plus grand que le nombre à comparer.
+
+```
+5 = 0b0101
+Décompte des bits significatifs: 3
+On décale 1 quatre fois pour dépasser 5.
+```
+
+> Voir [bitLength.js](bitLength.js) pour plus de détails.
+
+#### Vérifier si un nombre est une puissance de 2
+
+Cette méthode vérifie si un nombre donné est une puissance de deux.
+Elle s'appuie sur la propriété suivante.
+Disons que `powerNumber` est une puissance de deux (c'est-à-dire 2, 4, 8, 16 etc.).
+Si nous faisons l'opération `&` entre `powerNumber` et `powerNumber - 1`,
+elle retournera`0` (dans le cas où le nombre est une puissance de deux).
+
+```
+Nombre: 4 = 0b0100
+Nombre: 3 = (4 - 1) = 0b0011
+4 & 3 = 0b0100 & 0b0011 = 0b0000 <-- Égal à zéro, car c'est une puissance de 2.
+
+Nombre: 10 = 0b01010
+Nombre: 9 = (10 - 1) = 0b01001
+10 & 9 = 0b01010 & 0b01001 = 0b01000 <-- Différent de 0, donc n'est pas une puissance de 2.
+```
+
+> Voir [isPowerOfTwo.js](isPowerOfTwo.js) pour plus de détails.
+
+#### Additionneur complet
+
+Cette méthode ajoute deux nombres entiers à l'aide d'opérateurs bit à bit.
+
+Elle implémente un [additionneur](https://fr.wikipedia.org/wiki/Additionneur)
+simulant un circuit électronique logique,
+pour additionner deux entiers de 32 bits,
+sous la forme « complément à deux ».
+Elle utilise la logique booléenne pour couvrir tous les cas possibles
+d'ajout de deux bits donnés:
+avec et sans retenue de l'ajout de l'étape précédente la moins significative.
+
+Légende:
+
+- `A`: Nombre `A`
+- `B`: Nombre `B`
+- `ai`: ième bit du nombre `A`
+- `bi`: ième bit du nombre `B`
+- `carryIn`: un bit retenu de la précédente étape la moins significative
+- `carryOut`: un bit retenu pour la prochaine étape la plus significative
+- `bitSum`: La somme de `ai`, `bi`, et `carryIn`
+- `resultBin`: Le résultat complet de l'ajout de l'étape actuelle avec toutes les étapes moins significatives (en binaire)
+- `resultDec`: Le résultat complet de l'ajout de l'étape actuelle avec toutes les étapes moins significatives (en decimal)
+
+```
+A = 3: 011
+B = 6: 110
+┌──────┬────┬────┬─────────┬──────────┬─────────┬───────────┬───────────┐
+│  bit │ ai │ bi │ carryIn │ carryOut │  bitSum │ resultBin │ resultDec │
+├──────┼────┼────┼─────────┼──────────┼─────────┼───────────┼───────────┤
+│   0  │ 1  │ 0  │    0    │    0     │     1   │       1   │     1     │
+│   1  │ 1  │ 1  │    0    │    1     │     0   │      01   │     1     │
+│   2  │ 0  │ 1  │    1    │    1     │     0   │     001   │     1     │
+│   3  │ 0  │ 0  │    1    │    0     │     1   │    1001   │     9     │
+└──────┴────┴────┴─────────┴──────────┴─────────┴───────────┴───────────┘
+```
+
+> Voir [fullAdder.js](fullAdder.js) pour plus de détails.  
+> Voir [Full Adder on YouTube](https://www.youtube.com/watch?v=wvJc9CZcvBc&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8).
+
+## Références
+
+- [Bit Manipulation on YouTube](https://www.youtube.com/watch?v=NLKQEOgBAnw&t=0s&index=28&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [Negative Numbers in binary on YouTube](https://www.youtube.com/watch?v=4qH4unVtJkE&t=0s&index=30&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [Bit Hacks on stanford.edu](https://graphics.stanford.edu/~seander/bithacks.html)
diff --git a/src/algorithms/math/bits/README.md b/src/algorithms/math/bits/README.md
index f4173af983..1218094287 100644
--- a/src/algorithms/math/bits/README.md
+++ b/src/algorithms/math/bits/README.md
@@ -1,11 +1,15 @@
 # Bit Manipulation
 
+_Read this in other languages:_
+[français](README.fr-FR.md),
+[简体中文](README.zh-CN.md).
+
 #### Get Bit
 
-This method shifts the relevant bit to the zeroth position. 
-Then we perform `AND` operation with one which has bit 
-pattern like `0001`.  This clears all bits from the original
-number except the relevant one. If the relevant bit is one, 
+This method shifts the relevant bit to the zeroth position.
+Then we perform `AND` operation with one which has bit
+pattern like `0001`. This clears all bits from the original
+number except the relevant one. If the relevant bit is one,
 the result is `1`, otherwise the result is `0`.
 
 > See [getBit.js](getBit.js) for further details.
@@ -24,7 +28,7 @@ other bits of the number.
 This method shifts `1` over by `bitPosition` bits, creating a
 value that looks like `00100`. Than it inverts this mask to get
 the number that looks like `11011`. Then `AND` operation is
-being applied to both the number and the mask. That operation 
+being applied to both the number and the mask. That operation
 unsets the bit.
 
 > See [clearBit.js](clearBit.js) for further details.
@@ -35,21 +39,53 @@ This method is a combination of "Clear Bit" and "Set Bit" methods.
 
 > See [updateBit.js](updateBit.js) for further details.
 
+#### isEven
+
+This method determines if the number provided is even.
+It is based on the fact that odd numbers have their last
+right bit to be set to 1.
+
+```text
+Number: 5 = 0b0101
+isEven: false
+
+Number: 4 = 0b0100
+isEven: true
+```
+
+> See [isEven.js](isEven.js) for further details.
+
+#### isPositive
+
+This method determines if the number is positive. It is based on the fact that all positive
+numbers have their leftmost bit to be set to `0`. However, if the number provided is zero
+or negative zero, it should still return `false`.
+
+```text
+Number: 1 = 0b0001
+isPositive: true
+
+Number: -1 = -0b0001
+isPositive: false
+```
+
+> See [isPositive.js](isPositive.js) for further details.
+
 #### Multiply By Two
 
 This method shifts original number by one bit to the left.
 Thus all binary number components (powers of two) are being
-multiplying by two and thus the number itself is being 
+multiplying by two and thus the number itself is being
 multiplied by two.
 
 ```
 Before the shift
 Number: 0b0101 = 5
-Powers of two: 0 + 2^2 + 0 + 2^0 
+Powers of two: 0 + 2^2 + 0 + 2^0
 
 After the shift
 Number: 0b1010 = 10
-Powers of two: 2^3 + 0 + 2^1 + 0 
+Powers of two: 2^3 + 0 + 2^1 + 0
 ```
 
 > See [multiplyByTwo.js](multiplyByTwo.js) for further details.
@@ -58,17 +94,17 @@ Powers of two: 2^3 + 0 + 2^1 + 0
 
 This method shifts original number by one bit to the right.
 Thus all binary number components (powers of two) are being
-divided by two and thus the number itself is being 
+divided by two and thus the number itself is being
 divided by two without remainder.
 
 ```
 Before the shift
 Number: 0b0101 = 5
-Powers of two: 0 + 2^2 + 0 + 2^0 
+Powers of two: 0 + 2^2 + 0 + 2^0
 
 After the shift
 Number: 0b0010 = 2
-Powers of two: 0 + 0 + 2^1 + 0 
+Powers of two: 0 + 0 + 2^1 + 0
 ```
 
 > See [divideByTwo.js](divideByTwo.js) for further details.
@@ -87,11 +123,30 @@ inverting all of the bits of the number and adding 1 to it.
 0001  1
 0010  2
 0011  3
-``` 
+```
 
 > See [switchSign.js](switchSign.js) for further details.
 
-#### Multiply Two Numbers
+#### Multiply Two Signed Numbers
+
+This method multiplies two signed integer numbers using bitwise operators.
+This method is based on the following facts:
+
+```text
+a * b can be written in the below formats:
+  0                     if a is zero or b is zero or both a and b are zeroes
+  2a * (b/2)            if b is even
+  2a * (b - 1)/2 + a    if b is odd and positive
+  2a * (b + 1)/2 - a    if b is odd and negative
+```
+
+The advantage of this approach is that in each recursive step one of the operands
+reduces to half its original value. Hence, the run time complexity is `O(log(b))` where `b` is
+the operand that reduces to half on each recursive step.
+
+> See [multiply.js](multiply.js) for further details.
+
+#### Multiply Two Unsigned Numbers
 
 This method multiplies two integer numbers using bitwise operators.
 This method is based on that "Every number can be denoted as the sum of powers of 2".
@@ -111,7 +166,7 @@ Then multiplying number `x` by `19` is equivalent of:
 x * 19 = x * 2^4 + x * 2^1 + x * 2^0
 ```
 
-Now we need to remember that `x * 2^4` is equivalent of shifting `x` left 
+Now we need to remember that `x * 2^4` is equivalent of shifting `x` left
 by `4` bits (`x << 4`).
 
 > See [multiplyUnsigned.js](multiplyUnsigned.js) for further details.
@@ -156,6 +211,62 @@ When we shift 1 four times it will become bigger than 5.
 
 > See [bitLength.js](bitLength.js) for further details.
 
+#### Is Power of Two
+
+This method checks if a number provided is power of two. It uses the following
+property. Let's say that `powerNumber` is a number that has been formed as a power
+of two (i.e. 2, 4, 8, 16 etc.). Then if we'll do `&` operation between `powerNumber`
+and `powerNumber - 1` it will return `0` (in case if number is power of two).
+
+```
+Number: 4 = 0b0100
+Number: 3 = (4 - 1) = 0b0011
+4 & 3 = 0b0100 & 0b0011 = 0b0000 <-- Equal to zero, is power of two.
+
+Number: 10 = 0b01010
+Number: 9 = (10 - 1) = 0b01001
+10 & 9 = 0b01010 & 0b01001 = 0b01000 <-- Not equal to zero, not a power of two.
+```
+
+> See [isPowerOfTwo.js](isPowerOfTwo.js) for further details.
+
+#### Full Adder
+
+This method adds up two integer numbers using bitwise operators.
+
+It implements [full adder](<https://en.wikipedia.org/wiki/Adder_(electronics)>)
+electronics circuit logic to sum two 32-bit integers in two's complement format.
+It's using the boolean logic to cover all possible cases of adding two input bits:
+with and without a "carry bit" from adding the previous less-significant stage.
+
+Legend:
+
+- `A`: Number `A`
+- `B`: Number `B`
+- `ai`: ith bit of number `A`
+- `bi`: ith bit of number `B`
+- `carryIn`: a bit carried in from the previous less-significant stage
+- `carryOut`: a bit to carry to the next most-significant stage
+- `bitSum`: The sum of `ai`, `bi`, and `carryIn`
+- `resultBin`: The full result of adding current stage with all less-significant stages (in binary)
+- `resultDec`: The full result of adding current stage with all less-significant stages (in decimal)
+
+```
+A = 3: 011
+B = 6: 110
+┌──────┬────┬────┬─────────┬──────────┬─────────┬───────────┬───────────┐
+│  bit │ ai │ bi │ carryIn │ carryOut │  bitSum │ resultBin │ resultDec │
+├──────┼────┼────┼─────────┼──────────┼─────────┼───────────┼───────────┤
+│   0  │ 1  │ 0  │    0    │    0     │     1   │       1   │     1     │
+│   1  │ 1  │ 1  │    0    │    1     │     0   │      01   │     1     │
+│   2  │ 0  │ 1  │    1    │    1     │     0   │     001   │     1     │
+│   3  │ 0  │ 0  │    1    │    0     │     1   │    1001   │     9     │
+└──────┴────┴────┴─────────┴──────────┴─────────┴───────────┴───────────┘
+```
+
+> See [fullAdder.js](fullAdder.js) for further details.
+> See [Full Adder on YouTube](https://www.youtube.com/watch?v=wvJc9CZcvBc&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8).
+
 ## References
 
 - [Bit Manipulation on YouTube](https://www.youtube.com/watch?v=NLKQEOgBAnw&t=0s&index=28&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/math/bits/README.zh-CN.md b/src/algorithms/math/bits/README.zh-CN.md
new file mode 100644
index 0000000000..9ec75069c6
--- /dev/null
+++ b/src/algorithms/math/bits/README.zh-CN.md
@@ -0,0 +1,236 @@
+# 位运算
+
+_Read this in other languages:_
+[français](README.fr-FR.md),
+[english](README.md)
+
+#### Get Bit
+
+该方法向右移动目标位到最右边，即位数组的第0个位置上。然后在该数上与形如 `0001`的二进制形式的数进行`AND`操作。这会清理掉除了目标位的所有其它位的数据。如果目标位是1，那么结果就是`1`，反之，结果是`0`;
+
+> 查看[getBit.js](getBit.js)了解更多细节。
+
+#### Set Bit
+
+该方法把`1`向左移动了`bitPosition`位，生成了一个二进制形如`00100`的值。然后我们拿该值与目标数字进行`OR`操作，就能把目标位设置位`1`而不影响其它位。
+
+> 查看[setBit.js](setBit.js)了解更多细节。
+
+#### Clear Bit
+
+该方法把`1`向左移动了`bitPosition`位，生成了一个二进制形如`00100`的值。然后反转每一位的数字，得到一个二进制形如`11011`的值。接着与目标值进行`AND`操作，就能清除掉目标位的值。
+
+> 查看[clearBit.js](clearBit.js)了解更多细节。
+
+#### Update Bit
+
+该方法组合了“Clear Bit”和“Set Bit”
+
+> 查看[updateBit.js](updateBit.js)了解更多细节。
+
+#### isEven
+
+该方法检测传入的number是否是偶数。它的实现基于奇数的最右边的位永远是`1`这个事实。
+
+```text
+Number: 5 = 0b0101
+isEven: false
+
+Number: 4 = 0b0100
+isEven: true
+```
+
+> 查看[isEven.js](isEven.js)了解更多细节。
+
+#### isPositive
+
+该方法检测传入的number是否是正数。它的实现基于正数最左边的位永远是`0`这个事实。然而如果传入的number是0或者-0，它也应该返回false。
+
+```text
+Number: 1 = 0b0001
+isPositive: true
+
+Number: -1 = -0b0001
+isPositive: false
+```
+
+> 查看[isPositive.js](isPositive.js)了解更多细节。
+
+#### Multiply By Two
+
+该方法将原始数字向左移动一位。因此所有位都将乘以2，因此数字本身也将乘以2。
+
+```
+Before the shift
+Number: 0b0101 = 5
+Powers of two: 0 + 2^2 + 0 + 2^0
+
+After the shift
+Number: 0b1010 = 10
+Powers of two: 2^3 + 0 + 2^1 + 0
+```
+
+> 查看[multiplyByTwo.js](multiplyByTwo.js)了解更多细节。
+
+#### Divide By Two
+
+该方法将原始数字向右移动一位。因此所有位都将除以2，因此数字本身也将除以2,且不会产生余数。
+
+```
+Before the shift
+Number: 0b0101 = 5
+Powers of two: 0 + 2^2 + 0 + 2^0
+
+After the shift
+Number: 0b0010 = 2
+Powers of two: 0 + 0 + 2^1 + 0
+```
+
+> 查看[divideByTwo.js](divideByTwo.js)了解更多细节。
+
+#### Switch Sign
+
+该方法将正数变成负数，反之亦然。为了做到这一点，它使用了“二进制补码”的方法，即取反所有位然后加1.
+
+```
+1101 -3
+1110 -2
+1111 -1
+0000  0
+0001  1
+0010  2
+0011  3
+```
+
+> 查看[switchSign.js](switchSign.js)了解更多细节。
+
+#### Multiply Two Signed Numbers
+
+该方法使用位运算符计算两个有符号数的乘积。实现基于以下事实：
+
+```text
+a * b 可以被改写成如下形式:
+  0                     a为0，b为0，或者a，b都为0
+  2a * (b/2)            b是偶数
+  2a * (b - 1)/2 + a    b是奇数，正数
+  2a * (b + 1)/2 - a    b是奇数，负数
+```
+
+这样转换的优势在于，递归的每一步，递归的操作数的值都减少了一半。因此，运行时的时间复杂度为`O(log(b))`,其中b是在每个递归步骤上减少为一半的操作数。
+
+
+> 查看[multiply.js](multiply.js)了解更多细节。
+
+#### Multiply Two Unsigned Numbers
+
+该方法使用位运算符计算两个无符号数的乘积。实现基于“每个数字都可以表示为一系列2的幂的和”。
+
+逐位乘法的主要思想是，每个数字都可以拆分为两个乘方的和：
+
+比如：
+
+```text
+19 = 2^4 + 2^1 + 2^0
+```
+
+然后`19`乘`x`就等价于：
+
+```text
+x * 19 = x * 2^4 + x * 2^1 + x * 2^0
+```
+
+接着我们应该意识到`x*2^4`是等价于`x`向左移动`4`位（`x << 4`）的;
+
+> 查看[multiplyUnsigned.js](multiplyUnsigned.js)了解更多细节。
+
+#### Count Set Bits
+
+该方法使用位运算符对一个数字里设置为`1`的位进行记数。主要方法是，把数字每次向右移动1位，然后使用`&`操作符取出最右边一位的值，`1`则记数加1，`0`则不计。
+
+```text
+Number: 5 = 0b0101
+Count of set bits = 2
+```
+
+> 查看[countSetBits.js](countSetBits.js)了解更多细节。
+
+#### Count Bits to Flip One Number to Another
+
+
+该方法输出把一个数字转换为另一个数字所需要转换的位数。这利用了以下特性：当数字进行`XOR`异或运算时，结果将是不同位数的数量(即异或的结果中所有被设置为1的位的数量)。
+
+```
+5 = 0b0101
+1 = 0b0001
+Count of Bits to be Flipped: 1
+```
+
+> 查看[bitsDiff.js](bitsDiff.js)了解更多细节。
+
+#### Count Bits of a Number
+
+为了计算数字的有效位数，我们需要把`1`每次向左移动一位，然后检查产生的值是否大于输入的数字。
+
+```
+5 = 0b0101
+有效位数: 3
+当我们把1向左移动4位的时候，会大于5.
+```
+
+> 查看[bitLength.js](bitLength.js)了解更多细节。
+
+#### Is Power of Two
+
+该方法检测数字是否可以表示为2的幂。它使用了以下特性，我们定义`powerNumber`是可以写成2的幂的形式的数(2,4,8,16 etc.)。然后我们会把`powerNumber`和`powerNumber - 1`进行`&`操作，它会返回`0`(如果该数字可以表示为2的幂)。
+
+```
+Number: 4 = 0b0100
+Number: 3 = (4 - 1) = 0b0011
+4 & 3 = 0b0100 & 0b0011 = 0b0000 <-- Equal to zero, is power of two.
+
+Number: 10 = 0b01010
+Number: 9 = (10 - 1) = 0b01001
+10 & 9 = 0b01010 & 0b01001 = 0b01000 <-- Not equal to zero, not a power of two.
+```
+
+> 查看[isPowerOfTwo.js](isPowerOfTwo.js)了解更多细节。
+
+#### Full Adder
+
+该方法使用位运算符计算两个数的和。
+
+它实现了[完整的加法器](<https://en.wikipedia.org/wiki/Adder_(electronics)>)电子电路逻辑，以补码的形式计算两个32位数字的和。它使用布尔逻辑来覆盖了两个位相加的所有情况：从前一位相加的时候，产没产生进位“carry bit”。
+
+Legend:
+
+- `A`: 数字 `A`
+- `B`: 数字 `B`
+- `ai`: 数字`A`以二进制表示时的位下标
+- `bi`: 数字`B`以二进制表示时的位下标
+- `carryIn`: 本次计算产生的进位
+- `carryOut`: 带入此次计算的进位
+- `bitSum`: `ai`, `bi`, 和 `carryIn` 的和
+- `resultBin`: 当前计算的结果（二进制形式）
+- `resultDec`: 当前计算的结果（十进制形式）
+
+```
+A = 3: 011
+B = 6: 110
+┌──────┬────┬────┬─────────┬──────────┬─────────┬───────────┬───────────┐
+│  bit │ ai │ bi │ carryIn │ carryOut │  bitSum │ resultBin │ resultDec │
+├──────┼────┼────┼─────────┼──────────┼─────────┼───────────┼───────────┤
+│   0  │ 1  │ 0  │    0    │    0     │     1   │       1   │     1     │
+│   1  │ 1  │ 1  │    0    │    1     │     0   │      01   │     1     │
+│   2  │ 0  │ 1  │    1    │    1     │     0   │     001   │     1     │
+│   3  │ 0  │ 0  │    1    │    0     │     1   │    1001   │     9     │
+└──────┴────┴────┴─────────┴──────────┴─────────┴───────────┴───────────┘
+```
+
+> 查看[fullAdder.js](fullAdder.js)了解更多细节。  
+> 查看[Full Adder on YouTube](https://www.youtube.com/watch?v=wvJc9CZcvBc&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8).
+
+## References
+
+- [Bit Manipulation on YouTube](https://www.youtube.com/watch?v=NLKQEOgBAnw&t=0s&index=28&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [Negative Numbers in binary on YouTube](https://www.youtube.com/watch?v=4qH4unVtJkE&t=0s&index=30&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [Bit Hacks on stanford.edu](https://graphics.stanford.edu/~seander/bithacks.html)
diff --git a/src/algorithms/math/bits/__test__/countSetBits.test.js b/src/algorithms/math/bits/__test__/countSetBits.test.js
index fb29cb0935..a6c0fb9989 100644
--- a/src/algorithms/math/bits/__test__/countSetBits.test.js
+++ b/src/algorithms/math/bits/__test__/countSetBits.test.js
@@ -11,5 +11,10 @@ describe('countSetBits', () => {
     expect(countSetBits(21)).toBe(3);
     expect(countSetBits(255)).toBe(8);
     expect(countSetBits(1023)).toBe(10);
+    expect(countSetBits(-1)).toBe(32);
+    expect(countSetBits(-21)).toBe(30);
+    expect(countSetBits(-255)).toBe(25);
+    expect(countSetBits(-1023)).toBe(23);
+    expect(countSetBits(-4294967296)).toBe(0);
   });
 });
diff --git a/src/algorithms/math/bits/__test__/fullAdder.test.js b/src/algorithms/math/bits/__test__/fullAdder.test.js
new file mode 100644
index 0000000000..529187dc4f
--- /dev/null
+++ b/src/algorithms/math/bits/__test__/fullAdder.test.js
@@ -0,0 +1,18 @@
+import fullAdder from '../fullAdder';
+
+describe('fullAdder', () => {
+  it('should add up two numbers', () => {
+    expect(fullAdder(0, 0)).toBe(0);
+    expect(fullAdder(2, 0)).toBe(2);
+    expect(fullAdder(0, 2)).toBe(2);
+    expect(fullAdder(1, 2)).toBe(3);
+    expect(fullAdder(2, 1)).toBe(3);
+    expect(fullAdder(6, 6)).toBe(12);
+    expect(fullAdder(-2, 4)).toBe(2);
+    expect(fullAdder(4, -2)).toBe(2);
+    expect(fullAdder(-4, -4)).toBe(-8);
+    expect(fullAdder(4, -5)).toBe(-1);
+    expect(fullAdder(2, 121)).toBe(123);
+    expect(fullAdder(121, 2)).toBe(123);
+  });
+});
diff --git a/src/algorithms/math/bits/__test__/isEven.test.js b/src/algorithms/math/bits/__test__/isEven.test.js
new file mode 100644
index 0000000000..b21bbfb00c
--- /dev/null
+++ b/src/algorithms/math/bits/__test__/isEven.test.js
@@ -0,0 +1,19 @@
+import isEven from '../isEven';
+
+describe('isEven', () => {
+  it('should detect if a number is even', () => {
+    expect(isEven(0)).toBe(true);
+    expect(isEven(2)).toBe(true);
+    expect(isEven(-2)).toBe(true);
+    expect(isEven(1)).toBe(false);
+    expect(isEven(-1)).toBe(false);
+    expect(isEven(-3)).toBe(false);
+    expect(isEven(3)).toBe(false);
+    expect(isEven(8)).toBe(true);
+    expect(isEven(9)).toBe(false);
+    expect(isEven(121)).toBe(false);
+    expect(isEven(122)).toBe(true);
+    expect(isEven(1201)).toBe(false);
+    expect(isEven(1202)).toBe(true);
+  });
+});
diff --git a/src/algorithms/math/bits/__test__/isPositive.test.js b/src/algorithms/math/bits/__test__/isPositive.test.js
new file mode 100644
index 0000000000..45dcca0e4e
--- /dev/null
+++ b/src/algorithms/math/bits/__test__/isPositive.test.js
@@ -0,0 +1,19 @@
+import isPositive from '../isPositive';
+
+describe('isPositive', () => {
+  it('should detect if a number is positive', () => {
+    expect(isPositive(1)).toBe(true);
+    expect(isPositive(2)).toBe(true);
+    expect(isPositive(3)).toBe(true);
+    expect(isPositive(5665)).toBe(true);
+    expect(isPositive(56644325)).toBe(true);
+
+    expect(isPositive(0)).toBe(false);
+    expect(isPositive(-0)).toBe(false);
+    expect(isPositive(-1)).toBe(false);
+    expect(isPositive(-2)).toBe(false);
+    expect(isPositive(-126)).toBe(false);
+    expect(isPositive(-5665)).toBe(false);
+    expect(isPositive(-56644325)).toBe(false);
+  });
+});
diff --git a/src/algorithms/math/bits/__test__/isPowerOfTwo.test.js b/src/algorithms/math/bits/__test__/isPowerOfTwo.test.js
new file mode 100644
index 0000000000..3408f2c137
--- /dev/null
+++ b/src/algorithms/math/bits/__test__/isPowerOfTwo.test.js
@@ -0,0 +1,20 @@
+import isPowerOfTwo from '../isPowerOfTwo';
+
+describe('isPowerOfTwo', () => {
+  it('should detect if the number is power of two', () => {
+    expect(isPowerOfTwo(1)).toBe(true);
+    expect(isPowerOfTwo(2)).toBe(true);
+    expect(isPowerOfTwo(3)).toBe(false);
+    expect(isPowerOfTwo(4)).toBe(true);
+    expect(isPowerOfTwo(5)).toBe(false);
+    expect(isPowerOfTwo(6)).toBe(false);
+    expect(isPowerOfTwo(7)).toBe(false);
+    expect(isPowerOfTwo(8)).toBe(true);
+    expect(isPowerOfTwo(9)).toBe(false);
+    expect(isPowerOfTwo(16)).toBe(true);
+    expect(isPowerOfTwo(23)).toBe(false);
+    expect(isPowerOfTwo(32)).toBe(true);
+    expect(isPowerOfTwo(127)).toBe(false);
+    expect(isPowerOfTwo(128)).toBe(true);
+  });
+});
diff --git a/src/algorithms/math/bits/__test__/multiply.test.js b/src/algorithms/math/bits/__test__/multiply.test.js
new file mode 100644
index 0000000000..c21957041b
--- /dev/null
+++ b/src/algorithms/math/bits/__test__/multiply.test.js
@@ -0,0 +1,18 @@
+import multiply from '../multiply';
+
+describe('multiply', () => {
+  it('should multiply two numbers', () => {
+    expect(multiply(0, 0)).toBe(0);
+    expect(multiply(2, 0)).toBe(0);
+    expect(multiply(0, 2)).toBe(0);
+    expect(multiply(1, 2)).toBe(2);
+    expect(multiply(2, 1)).toBe(2);
+    expect(multiply(6, 6)).toBe(36);
+    expect(multiply(-2, 4)).toBe(-8);
+    expect(multiply(4, -2)).toBe(-8);
+    expect(multiply(-4, -4)).toBe(16);
+    expect(multiply(4, -5)).toBe(-20);
+    expect(multiply(2, 121)).toBe(242);
+    expect(multiply(121, 2)).toBe(242);
+  });
+});
diff --git a/src/algorithms/math/bits/countSetBits.js b/src/algorithms/math/bits/countSetBits.js
index 6e24eebf26..63839d4e68 100644
--- a/src/algorithms/math/bits/countSetBits.js
+++ b/src/algorithms/math/bits/countSetBits.js
@@ -11,7 +11,7 @@ export default function countSetBits(originalNumber) {
     setBitsCount += number & 1;
 
     // Shift number right by one bit to investigate other bits.
-    number >>= 1;
+    number >>>= 1;
   }
 
   return setBitsCount;
diff --git a/src/algorithms/math/bits/fullAdder.js b/src/algorithms/math/bits/fullAdder.js
new file mode 100644
index 0000000000..37d74bb911
--- /dev/null
+++ b/src/algorithms/math/bits/fullAdder.js
@@ -0,0 +1,70 @@
+import getBit from './getBit';
+
+/**
+ * Add two numbers using only binary operators.
+ *
+ * This is an implementation of full adders logic circuit.
+ * https://en.wikipedia.org/wiki/Adder_(electronics)
+ * Inspired by: https://www.youtube.com/watch?v=wvJc9CZcvBc
+ *
+ * Table(1)
+ *  INPUT  | OUT
+ *  C Ai Bi | C Si | Row
+ * -------- | -----| ---
+ *  0  0  0 | 0  0 | 1
+ *  0  0  1 | 0  1 | 2
+ *  0  1  0 | 0  1 | 3
+ *  0  1  1 | 1  0 | 4
+ * -------- | ---- | --
+ *  1  0  0 | 0  1 | 5
+ *  1  0  1 | 1  0 | 6
+ *  1  1  0 | 1  0 | 7
+ *  1  1  1 | 1  1 | 8
+ * ---------------------
+ *
+ * Legend:
+ * INPUT C = Carry in, from the previous less-significant stage
+ * INPUT Ai = ith bit of Number A
+ * INPUT Bi = ith bit of Number B
+ * OUT C = Carry out to the next most-significant stage
+ * OUT Si = Bit Sum, ith least significant bit of the result
+ *
+ *
+ * @param {number} a
+ * @param {number} b
+ * @return {number}
+ */
+export default function fullAdder(a, b) {
+  let result = 0;
+  let carry = 0;
+
+  // The operands of all bitwise operators are converted to signed
+  // 32-bit integers in two's complement format.
+  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Signed_32-bit_integers
+  for (let i = 0; i < 32; i += 1) {
+    const ai = getBit(a, i);
+    const bi = getBit(b, i);
+    const carryIn = carry;
+
+    // Calculate binary Ai + Bi without carry (half adder)
+    // See Table(1) rows 1 - 4: Si = Ai ^ Bi
+    const aiPlusBi = ai ^ bi;
+
+    // Calculate ith bit of the result by adding the carry bit to Ai + Bi
+    // For Table(1) rows 5 - 8 carryIn = 1: Si = Ai ^ Bi ^ 1, flip the bit
+    // Fpr Table(1) rows 1 - 4 carryIn = 0: Si = Ai ^ Bi ^ 0, a no-op.
+    const bitSum = aiPlusBi ^ carryIn;
+
+    // Carry out one to the next most-significant stage
+    // when at least one of these is true:
+    // 1) Table(1) rows 6, 7: one of Ai OR Bi is 1 AND carryIn = 1
+    // 2) Table(1) rows 4, 8: Both Ai AND Bi are 1
+    const carryOut = (aiPlusBi & carryIn) | (ai & bi);
+    carry = carryOut;
+
+    // Set ith least significant bit of the result to bitSum.
+    result |= bitSum << i;
+  }
+
+  return result;
+}
diff --git a/src/algorithms/math/bits/isEven.js b/src/algorithms/math/bits/isEven.js
new file mode 100644
index 0000000000..843b4d281e
--- /dev/null
+++ b/src/algorithms/math/bits/isEven.js
@@ -0,0 +1,7 @@
+/**
+ * @param {number} number
+ * @return {boolean}
+ */
+export default function isEven(number) {
+  return (number & 1) === 0;
+}
diff --git a/src/algorithms/math/bits/isPositive.js b/src/algorithms/math/bits/isPositive.js
new file mode 100644
index 0000000000..0e64e8ae92
--- /dev/null
+++ b/src/algorithms/math/bits/isPositive.js
@@ -0,0 +1,13 @@
+/**
+ * @param {number} number - 32-bit integer.
+ * @return {boolean}
+ */
+export default function isPositive(number) {
+  // Zero is neither a positive nor a negative number.
+  if (number === 0) {
+    return false;
+  }
+
+  // The most significant 32nd bit can be used to determine whether the number is positive.
+  return ((number >> 31) & 1) === 0;
+}
diff --git a/src/algorithms/math/bits/isPowerOfTwo.js b/src/algorithms/math/bits/isPowerOfTwo.js
new file mode 100644
index 0000000000..28e3e673d8
--- /dev/null
+++ b/src/algorithms/math/bits/isPowerOfTwo.js
@@ -0,0 +1,7 @@
+/**
+ * @param {number} number
+ * @return bool
+ */
+export default function isPowerOfTwo(number) {
+  return (number & (number - 1)) === 0;
+}
diff --git a/src/algorithms/math/bits/multiply.js b/src/algorithms/math/bits/multiply.js
new file mode 100644
index 0000000000..da952f429e
--- /dev/null
+++ b/src/algorithms/math/bits/multiply.js
@@ -0,0 +1,41 @@
+import multiplyByTwo from './multiplyByTwo';
+import divideByTwo from './divideByTwo';
+import isEven from './isEven';
+import isPositive from './isPositive';
+
+/**
+ * Multiply two signed numbers using bitwise operations.
+ *
+ * If a is zero or b is zero or if both a and b are zeros:
+ * multiply(a, b) = 0
+ *
+ * If b is even:
+ * multiply(a, b) = multiply(2a, b/2)
+ *
+ * If b is odd and b is positive:
+ * multiply(a, b) = multiply(2a, (b-1)/2) + a
+ *
+ * If b is odd and b is negative:
+ * multiply(a, b) = multiply(2a, (b+1)/2) - a
+ *
+ * Time complexity: O(log b)
+ *
+ * @param {number} a
+ * @param {number} b
+ * @return {number}
+ */
+export default function multiply(a, b) {
+  // If a is zero or b is zero or if both a and b are zeros then the production is also zero.
+  if (b === 0 || a === 0) {
+    return 0;
+  }
+
+  // Otherwise we will have four different cases that are described above.
+  const multiplyByOddPositive = () => multiply(multiplyByTwo(a), divideByTwo(b - 1)) + a;
+  const multiplyByOddNegative = () => multiply(multiplyByTwo(a), divideByTwo(b + 1)) - a;
+
+  const multiplyByEven = () => multiply(multiplyByTwo(a), divideByTwo(b));
+  const multiplyByOdd = () => (isPositive(b) ? multiplyByOddPositive() : multiplyByOddNegative());
+
+  return isEven(b) ? multiplyByEven() : multiplyByOdd();
+}
diff --git a/src/algorithms/math/complex-number/README.fr-FR.md b/src/algorithms/math/complex-number/README.fr-FR.md
new file mode 100644
index 0000000000..0e42a484d4
--- /dev/null
+++ b/src/algorithms/math/complex-number/README.fr-FR.md
@@ -0,0 +1,237 @@
+# Nombre complexe
+
+_Read this in other languages:_
+[english](README.md).
+
+Un **nombre complexe** est un nombre qui peut s'écrire sous la forme
+`a + b * i`, tels que `a` et `b` sont des nombres réels,
+et `i` est la solution de l'équation `x^2 = −1`.
+Du fait qu'aucun _nombre réel_ ne statisfait l'équation,
+`i` est appellé _nombre imaginaire_. Étant donné le nombre complexe `a + b * i`,
+`a` est appellé _partie réelle_, et `b`, _partie imaginaire_.
+
+![Complex Number](https://www.mathsisfun.com/numbers/images/complex-example.svg)
+
+Un nombre complexe est donc la combinaison
+d'un nombre réel et d'un nombre imaginaire :
+
+![Complex Number](https://www.mathsisfun.com/numbers/images/complex-number.svg)
+
+En géométrie, les nombres complexes étendent le concept
+de ligne de nombres sur une dimension à un _plan complexe à deux dimensions_
+en utilisant l'axe horizontal pour lepartie réelle
+et l'axe vertical pour la partie imaginaire. Le nombre complexe `a + b * i`
+peut être identifié avec le point `(a, b)` dans le plan complexe.
+
+Un nombre complexe dont la partie réelle est zéro est dit _imaginaire pur_;
+les points pour ces nombres se trouvent sur l'axe vertical du plan complexe.
+Un nombre complexe dont la partie imaginaire est zéro
+peut être considéré comme un _nombre réel_; son point
+se trouve sur l'axe horizontal du plan complexe.
+
+| Nombre complexe | Partie réelle | partie imaginaire |                  |
+| :-------------- | :-----------: | :---------------: | ---------------- |
+| 3 + 2i          |       3       |         2         |                  |
+| 5               |       5       |       **0**       | Purely Real      |
+| −6i             |     **0**     |        -6         | Purely Imaginary |
+
+A complex number can be visually represented as a pair of numbers `(a, b)` forming
+a vector on a diagram called an _Argand diagram_, representing the _complex plane_.
+`Re` is the real axis, `Im` is the imaginary axis, and `i` satisfies `i^2 = −1`.
+
+Un nombre complexe peut être représenté visuellement comme une paire de nombres
+`(a, b)` formant un vecteur sur un diagramme appelé _diagramme d'Argand_,
+représentant le _plan complexe_.
+_Re_ est l'axe réel, _Im_ est l'axe imaginaire et `i` satisfait `i^2 = −1`.
+
+![Complex Number](https://upload.wikimedia.org/wikipedia/commons/a/af/Complex_number_illustration.svg)
+
+> Complexe ne veut pas dire compliqué. Cela signifie simplement que
+> les deux types de nombres, réels et imaginaires, forment ensemble un complexe
+> comme on le dirait d'un complexe de bâtiments (bâtiments réunis).
+
+## Forme polaire
+
+Une manière de définir un point `P` dans le plan complexe, autre que d'utiliser
+les coordonnées x et y, consiste à utiliser la distance entre le point `O`, le point
+dont les coordonnées sont `(0, 0)` (l'origine), et l'angle sous-tendu
+entre l'axe réel positif et le segment de droite `OP` dans le sens antihoraire.
+Cette idée conduit à la forme polaire des nombres complexes.
+
+![Polar Form](https://upload.wikimedia.org/wikipedia/commons/7/7a/Complex_number_illustration_modarg.svg)
+
+The _valeur absolue_ (ou module) d'un nombre complexe `z = x + yi` est:
+
+![Radius](https://wikimedia.org/api/rest_v1/media/math/render/svg/b59629c801aa0ddcdf17ee489e028fb9f8d4ea75)
+
+L'argument de `z` (parfois appelé « phase » ou « amplitude ») est l'angle
+du rayon `OP` avec l'axe des réels positifs, et s'écrit `arg(z)`. Comme
+avec le module, l'argument peut être trouvé à partir de la forme rectangulaire `x + yi`:
+
+![Phase](https://wikimedia.org/api/rest_v1/media/math/render/svg/7cbbdd9bb1dd5df86dd2b820b20f82995023e566)
+
+Ensemble, `r` et`φ` donnent une autre façon de représenter les nombres complexes, la
+forme polaire, car la combinaison du module et de l'argument suffit à indiquer la
+position d'un point sur le plan. Obtenir les coordonnées du rectangle d'origine
+à partir de la forme polaire se fait par la formule appelée forme trigonométrique :
+
+![Polar Form](https://wikimedia.org/api/rest_v1/media/math/render/svg/b03de1e1b7b049880b5e4870b68a57bc180ff6ce)
+
+En utilisant la formule d'Euler, cela peut être écrit comme suit:
+
+![Euler's Form](https://wikimedia.org/api/rest_v1/media/math/render/svg/0a087c772212e7375cb321d83fc1fcc715cd0ed2)
+
+## Opérations de base
+
+### Addition
+
+Pour ajouter deux nombres complexes, nous ajoutons chaque partie séparément :
+
+```text
+(a + b * i) + (c + d * i) = (a + c) + (b + d) * i
+```
+
+**Exemple**
+
+```text
+(3 + 5i) + (4 − 3i) = (3 + 4) + (5 − 3)i = 7 + 2i
+```
+
+Dans un plan complexe, l'addition ressemblera à ceci:
+
+![Complex Addition](https://www.mathsisfun.com/algebra/images/complex-plane-vector-add.svg)
+
+### Soustraction
+
+Pour soustraire deux nombres complexes, on soustrait chaque partie séparément :
+
+```text
+(a + b * i) - (c + d * i) = (a - c) + (b - d) * i
+```
+
+**Exemple**
+
+```text
+(3 + 5i) - (4 − 3i) = (3 - 4) + (5 + 3)i = -1 + 8i
+```
+
+### Multiplication
+
+Pour multiplier les nombres complexes, chaque partie du premier nombre complexe est multipliée
+par chaque partie du deuxième nombre complexe:
+
+On peut utiliser le "FOIL" (parfois traduit PEID en français), acronyme de
+**F**irsts (Premiers), **O**uters (Extérieurs), **I**nners (Intérieurs), **L**asts (Derniers)" (
+voir [Binomial Multiplication](ttps://www.mathsisfun.com/algebra/polynomials-multiplying.html) pour plus de détails):
+
+![Complex Multiplication](https://www.mathsisfun.com/algebra/images/foil-complex.svg)
+
+- Firsts: `a × c`
+- Outers: `a × di`
+- Inners: `bi × c`
+- Lasts: `bi × di`
+
+En général, cela ressemble à:
+
+```text
+(a + bi)(c + di) = ac + adi + bci + bdi^2
+```
+
+Mais il existe aussi un moyen plus rapide !
+
+Utiliser cette loi:
+
+```text
+(a + bi)(c + di) = (ac − bd) + (ad + bc)i
+```
+
+**Exemple**
+
+```text
+(3 + 2i)(1 + 7i)
+= 3×1 + 3×7i + 2i×1+ 2i×7i
+= 3 + 21i + 2i + 14i^2
+= 3 + 21i + 2i − 14   (because i^2 = −1)
+= −11 + 23i
+```
+
+```text
+(3 + 2i)(1 + 7i) = (3×1 − 2×7) + (3×7 + 2×1)i = −11 + 23i
+```
+
+### Conjugués
+
+En mathématiques, le conjugué d'un nombre complexe z
+est le nombre complexe formé de la même partie réelle que z
+mais de partie imaginaire opposée.
+
+Un conjugué vois son signe changer au milieu comme suit:
+
+![Complex Conjugate](https://www.mathsisfun.com/numbers/images/complex-conjugate.svg)
+
+Un conjugué est souvent écrit avec un trait suscrit (barre au-dessus):
+
+```text
+______
+5 − 3i   =   5 + 3i
+```
+
+Dans un plan complexe, le nombre conjugué sera mirroir par rapport aux axes réels.
+
+![Complex Conjugate](https://upload.wikimedia.org/wikipedia/commons/6/69/Complex_conjugate_picture.svg)
+
+### Division
+
+Le conjugué est utiliser pour aider à la division de nombres complexes
+
+L'astuce est de _multiplier le haut et le bas par le conjugué du bas_.
+
+**Exemple**
+
+```text
+2 + 3i
+------
+4 − 5i
+```
+
+Multiplier le haut et le bas par le conjugué de `4 − 5i`:
+
+```text
+  (2 + 3i) * (4 + 5i)   8 + 10i + 12i + 15i^2
+= ------------------- = ----------------------
+  (4 − 5i) * (4 + 5i)   16 + 20i − 20i − 25i^2
+```
+
+Et puisque `i^2 = −1`, il s'ensuit que:
+
+```text
+  8 + 10i + 12i − 15    −7 + 22i   −7   22
+= ------------------- = -------- = -- + -- * i
+  16 + 20i − 20i + 25      41      41   41
+
+```
+
+Il existe cependant un moyen plus direct.
+
+Dans l'exemple précédent, ce qui s'est passé en bas était intéressant:
+
+```text
+(4 − 5i)(4 + 5i) = 16 + 20i − 20i − 25i
+```
+
+Les termes du milieu `(20i − 20i)` s'annule! Et pusique `i^2 = −1` on retrouve:
+
+```text
+(4 − 5i)(4 + 5i) = 4^2 + 5^2
+```
+
+Ce qui est vraiment un résultat assez simple. La règle générale est:
+
+```text
+(a + bi)(a − bi) = a^2 + b^2
+```
+
+## Références
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/Nombre_complexe)
+- [Math is Fun](https://www.mathsisfun.com/numbers/complex-numbers.html)
diff --git a/src/algorithms/math/complex-number/README.md b/src/algorithms/math/complex-number/README.md
index 9a591e51e2..7629aad3f0 100644
--- a/src/algorithms/math/complex-number/README.md
+++ b/src/algorithms/math/complex-number/README.md
@@ -1,11 +1,14 @@
 # Complex Number
 
-A **complex number** is a number that can be expressed in the 
+_Read this in other languages:_
+[français](README.fr-FR.md).
+
+A **complex number** is a number that can be expressed in the
 form `a + b * i`, where `a` and `b` are real numbers, and `i` is a solution of
-the equation `x^2 = −1`. Because no *real number* satisfies this 
-equation, `i` is called an *imaginary number*. For the complex 
-number `a + b * i`, `a` is called the *real part*, and `b` is called 
-the *imaginary part*.
+the equation `x^2 = −1`. Because no _real number_ satisfies this
+equation, `i` is called an _imaginary number_. For the complex
+number `a + b * i`, `a` is called the _real part_, and `b` is called
+the _imaginary part_.
 
 ![Complex Number](https://www.mathsisfun.com/numbers/images/complex-example.svg)
 
@@ -13,56 +16,56 @@ A Complex Number is a combination of a Real Number and an Imaginary Number:
 
 ![Complex Number](https://www.mathsisfun.com/numbers/images/complex-number.svg)
 
-Geometrically, complex numbers extend the concept of the one-dimensional number 
-line to the *two-dimensional complex plane* by using the horizontal axis for the
-real part and the vertical axis for the imaginary part. The complex 
-number `a + b * i` can be identified with the point `(a, b)` in the complex plane.
+Geometrically, complex numbers extend the concept of the one-dimensional number
+line to the _two-dimensional complex plane_ by using the horizontal axis for the
+real part and the vertical axis for the imaginary part. The complex
+number `a + b * i` can be identified with the point `(a, b)` in the complex plane.
 
-A complex number whose real part is zero is said to be *purely imaginary*; the
+A complex number whose real part is zero is said to be _purely imaginary_; the
 points for these numbers lie on the vertical axis of the complex plane. A complex
-number whose imaginary part is zero can be viewed as a *real number*; its point 
+number whose imaginary part is zero can be viewed as a _real number_; its point
 lies on the horizontal axis of the complex plane.
 
-| Complex Number | Real Part | Imaginary Part |     |
-| :------------- | :-------: | :------------: | --- |
-| 3 + 2i         | 3         | 2              |     |
-| 5              | 5         | **0**          | Purely Real |
-| −6i            | **0**     | -6             | Purely Imaginary |
+| Complex Number | Real Part | Imaginary Part |                  |
+| :------------- | :-------: | :------------: | ---------------- |
+| 3 + 2i         |     3     |       2        |                  |
+| 5              |     5     |     **0**      | Purely Real      |
+| −6i            |   **0**   |       -6       | Purely Imaginary |
 
-A complex number can be visually represented as a pair of numbers `(a, b)` forming 
-a vector on a diagram called an *Argand diagram*, representing the *complex plane*.
+A complex number can be visually represented as a pair of numbers `(a, b)` forming
+a vector on a diagram called an _Argand diagram_, representing the _complex plane_.
 `Re` is the real axis, `Im` is the imaginary axis, and `i` satisfies `i^2 = −1`.
 
 ![Complex Number](https://upload.wikimedia.org/wikipedia/commons/a/af/Complex_number_illustration.svg)
 
-> Complex does not mean complicated. It means the two types of numbers, real and 
-imaginary, together form a complex, just like a building complex (buildings 
-joined together).
+> Complex does not mean complicated. It means the two types of numbers, real and
+> imaginary, together form a complex, just like a building complex (buildings
+> joined together).
 
 ## Polar Form
 
-An alternative way of defining a point `P` in the complex plane, other than using 
+An alternative way of defining a point `P` in the complex plane, other than using
 the x- and y-coordinates, is to use the distance of the point from `O`, the point
-whose coordinates are `(0, 0)` (the origin), together with the angle subtended 
-between the positive real axis and the line segment `OP` in a counterclockwise 
+whose coordinates are `(0, 0)` (the origin), together with the angle subtended
+between the positive real axis and the line segment `OP` in a counterclockwise
 direction. This idea leads to the polar form of complex numbers.
 
 ![Polar Form](https://upload.wikimedia.org/wikipedia/commons/7/7a/Complex_number_illustration_modarg.svg)
 
-The *absolute value* (or modulus or magnitude) of a complex number `z = x + yi` is:
+The _absolute value_ (or modulus or magnitude) of a complex number `z = x + yi` is:
 
 ![Radius](https://wikimedia.org/api/rest_v1/media/math/render/svg/b59629c801aa0ddcdf17ee489e028fb9f8d4ea75)
 
 The argument of `z` (in many applications referred to as the "phase") is the angle
-of the radius `OP` with the positive real axis, and is written as `arg(z)`. As 
+of the radius `OP` with the positive real axis, and is written as `arg(z)`. As
 with the modulus, the argument can be found from the rectangular form `x+yi`:
 
 ![Phase](https://wikimedia.org/api/rest_v1/media/math/render/svg/7cbbdd9bb1dd5df86dd2b820b20f82995023e566)
 
-Together, `r` and `φ` give another way of representing complex numbers, the 
-polar form, as the combination of modulus and argument fully specify the 
-position of a point on the plane. Recovering the original rectangular 
-co-ordinates from the polar form is done by the formula called trigonometric 
+Together, `r` and `φ` give another way of representing complex numbers, the
+polar form, as the combination of modulus and argument fully specify the
+position of a point on the plane. Recovering the original rectangular
+co-ordinates from the polar form is done by the formula called trigonometric
 form:
 
 ![Polar Form](https://wikimedia.org/api/rest_v1/media/math/render/svg/b03de1e1b7b049880b5e4870b68a57bc180ff6ce)
@@ -107,7 +110,7 @@ To subtract two complex numbers we subtract each part separately:
 
 ### Multiplying
 
-To multiply complex numbers each part of the first complex number gets multiplied 
+To multiply complex numbers each part of the first complex number gets multiplied
 by each part of the second complex number:
 
 Just use "FOIL", which stands for "**F**irsts, **O**uters, **I**nners, **L**asts" (
@@ -138,7 +141,7 @@ Use this rule:
 **Example**
 
 ```text
-(3 + 2i)(1 + 7i) 
+(3 + 2i)(1 + 7i)
 = 3×1 + 3×7i + 2i×1+ 2i×7i
 = 3 + 21i + 2i + 14i^2
 = 3 + 21i + 2i − 14   (because i^2 = −1)
@@ -164,7 +167,7 @@ ______
 5 − 3i   =   5 + 3i
 ```
 
-On the complex plane the conjugate number will be mirrored against real axes. 
+On the complex plane the conjugate number will be mirrored against real axes.
 
 ![Complex Conjugate](https://upload.wikimedia.org/wikipedia/commons/6/69/Complex_conjugate_picture.svg)
 
@@ -172,7 +175,7 @@ On the complex plane the conjugate number will be mirrored against real axes.
 
 The conjugate is used to help complex division.
 
-The trick is to *multiply both top and bottom by the conjugate of the bottom*.
+The trick is to _multiply both top and bottom by the conjugate of the bottom_.
 
 **Example**
 
@@ -207,7 +210,7 @@ In the previous example, what happened on the bottom was interesting:
 (4 − 5i)(4 + 5i) = 16 + 20i − 20i − 25i
 ```
 
-The middle terms `(20i − 20i)` cancel out!	Also `i^2 = −1` so we end up with this:
+The middle terms `(20i − 20i)` cancel out! Also `i^2 = −1` so we end up with this:
 
 ```text
 (4 − 5i)(4 + 5i) = 4^2 + 5^2
diff --git a/src/algorithms/math/euclidean-algorithm/README.fr-FR.md b/src/algorithms/math/euclidean-algorithm/README.fr-FR.md
new file mode 100644
index 0000000000..75b6a90b88
--- /dev/null
+++ b/src/algorithms/math/euclidean-algorithm/README.fr-FR.md
@@ -0,0 +1,49 @@
+# Algorithme d'Euclide
+
+_Read this in other languages:_
+[english](README.md).
+
+En mathématiques, l'algorithme d'Euclide est un algorithme qui calcule le plus grand commun diviseur (PGCD) de deux entiers, c'est-à-dire le plus grand entier qui divise les deux entiers, en laissant un reste nul. L'algorithme ne connaît pas la factorisation de ces deux nombres.
+
+Le PGCD de deux entiers relatifs est égal au PGCD de leurs valeurs absolues : de ce fait, on se restreint dans cette section aux entiers positifs. L'algorithme part du constat suivant : le PGCD de deux nombres n'est pas changé si on remplace le plus grand d'entre eux par leur différence. Autrement dit, `pgcd(a, b) = pgcd(b, a - b)`. Par exemple, le PGCD de `252` et `105` vaut `21` (en effet, `252 = 21 × 12` and `105 = 21 × 5`), mais c'est aussi le PGCD de `252 - 105 = 147` et `105`. Ainsi, comme le remplacement de ces nombres diminue strictement le plus grand d'entre eux, on peut continuer le processus, jusqu'à obtenir deux nombres égaux.
+
+En inversant les étapes, le PGCD peut être exprimé comme une somme de
+les deux nombres originaux, chacun étant multiplié
+par un entier positif ou négatif, par exemple `21 = 5 × 105 + (-2) × 252`.
+Le fait que le PGCD puisse toujours être exprimé de cette manière est
+connue sous le nom de Théorème de Bachet-Bézout.
+
+![GCD](https://upload.wikimedia.org/wikipedia/commons/3/37/Euclid%27s_algorithm_Book_VII_Proposition_2_3.png)
+
+La Méthode d'Euclide pour trouver le plus grand diviseur commun (PGCD)
+de deux longueurs de départ`BA` et `DC`, toutes deux définies comme étant
+multiples d'une longueur commune. La longueur `DC` étant
+plus courte, elle est utilisée pour « mesurer » `BA`, mais une seule fois car
+le reste `EA` est inférieur à `DC`. `EA` mesure maintenant (deux fois)
+la longueur la plus courte `DC`, le reste `FC` étant plus court que `EA`.
+Alors `FC` mesure (trois fois) la longueur `EA`. Parce qu'il y a
+pas de reste, le processus se termine par `FC` étant le « PGCD ».
+À droite, l'exemple de Nicomaque de Gérase avec les nombres `49` et `21`
+ayan un PGCD de `7` (dérivé de Heath 1908: 300).
+
+![GCD](https://upload.wikimedia.org/wikipedia/commons/7/74/24x60.svg)
+
+Un de rectangle de dimensions `24 par 60` peux se carreler en carrés de `12 par 12`,
+puisque `12` est le PGCD ed `24` et `60`. De façon générale,
+un rectangle de dimension `a par b` peut se carreler en carrés
+de côté `c`, seulement si `c` est un diviseur commun de `a` et `b`.
+
+![GCD](https://upload.wikimedia.org/wikipedia/commons/1/1c/Euclidean_algorithm_1071_462.gif)
+
+Animation basée sur la soustraction via l'algorithme euclidien.
+Le rectangle initial a les dimensions `a = 1071` et `b = 462`.
+Des carrés de taille `462 × 462` y sont placés en laissant un
+rectangle de `462 × 147`. Ce rectangle est carrelé avec des
+carrés de `147 × 147` jusqu'à ce qu'un rectangle de `21 × 147` soit laissé,
+qui à son tour estcarrelé avec des carrés `21 × 21`,
+ne laissant aucune zone non couverte.
+La plus petite taille carrée, `21`, est le PGCD de `1071` et `462`.
+
+## References
+
+[Wikipedia](https://fr.wikipedia.org/wiki/Algorithme_d%27Euclide)
diff --git a/src/algorithms/math/euclidean-algorithm/README.md b/src/algorithms/math/euclidean-algorithm/README.md
index a7276fd53c..89af03b15e 100644
--- a/src/algorithms/math/euclidean-algorithm/README.md
+++ b/src/algorithms/math/euclidean-algorithm/README.md
@@ -1,55 +1,58 @@
 # Euclidean algorithm
 
-In mathematics, the Euclidean algorithm, or Euclid's algorithm, 
-is an efficient method for computing the greatest common divisor 
-(GCD) of two numbers, the largest number that divides both of 
+_Read this in other languages:_
+[français](README.fr-FR.md).
+
+In mathematics, the Euclidean algorithm, or Euclid's algorithm,
+is an efficient method for computing the greatest common divisor
+(GCD) of two numbers, the largest number that divides both of
 them without leaving a remainder.
 
-The Euclidean algorithm is based on the principle that the 
-greatest common divisor of two numbers does not change if 
-the larger number is replaced by its difference with the 
-smaller number. For example, `21` is the GCD of `252` and 
-`105` (as `252 = 21 × 12` and `105 = 21 × 5`), and the same 
-number `21` is also the GCD of `105` and `252 − 105 = 147`. 
-Since this replacement reduces the larger of the two numbers, 
-repeating this process gives successively smaller pairs of 
-numbers until the two numbers become equal. 
-When that occurs, they are the GCD of the original two numbers. 
-
-By reversing the steps, the GCD can be expressed as a sum of 
-the two original numbers each multiplied by a positive or 
-negative integer, e.g., `21 = 5 × 105 + (−2) × 252`. 
-The fact that the GCD can always be expressed in this way is 
+The Euclidean algorithm is based on the principle that the
+greatest common divisor of two numbers does not change if
+the larger number is replaced by its difference with the
+smaller number. For example, `21` is the GCD of `252` and
+`105` (as `252 = 21 × 12` and `105 = 21 × 5`), and the same
+number `21` is also the GCD of `105` and `252 − 105 = 147`.
+Since this replacement reduces the larger of the two numbers,
+repeating this process gives successively smaller pairs of
+numbers until the two numbers become equal.
+When that occurs, they are the GCD of the original two numbers.
+
+By reversing the steps, the GCD can be expressed as a sum of
+the two original numbers each multiplied by a positive or
+negative integer, e.g., `21 = 5 × 105 + (−2) × 252`.
+The fact that the GCD can always be expressed in this way is
 known as Bézout's identity.
 
 ![GCD](https://upload.wikimedia.org/wikipedia/commons/3/37/Euclid%27s_algorithm_Book_VII_Proposition_2_3.png)
 
-Euclid's method for finding the greatest common divisor (GCD) 
-of two starting lengths `BA` and `DC`, both defined to be 
-multiples of a common "unit" length. The length `DC` being 
-shorter, it is used to "measure" `BA`, but only once because 
-remainder `EA` is less than `DC`. EA now measures (twice) 
-the shorter length `DC`, with remainder `FC` shorter than `EA`. 
-Then `FC` measures (three times) length `EA`. Because there is 
-no remainder, the process ends with `FC` being the `GCD`. 
-On the right Nicomachus' example with numbers `49` and `21` 
+Euclid's method for finding the greatest common divisor (GCD)
+of two starting lengths `BA` and `DC`, both defined to be
+multiples of a common "unit" length. The length `DC` being
+shorter, it is used to "measure" `BA`, but only once because
+remainder `EA` is less than `DC`. EA now measures (twice)
+the shorter length `DC`, with remainder `FC` shorter than `EA`.
+Then `FC` measures (three times) length `EA`. Because there is
+no remainder, the process ends with `FC` being the `GCD`.
+On the right Nicomachus' example with numbers `49` and `21`
 resulting in their GCD of `7` (derived from Heath 1908:300).
 
 ![GCD](https://upload.wikimedia.org/wikipedia/commons/7/74/24x60.svg)
 
-A `24-by-60` rectangle is covered with ten `12-by-12` square 
-tiles, where `12` is the GCD of `24` and `60`. More generally, 
-an `a-by-b` rectangle can be covered with square tiles of 
+A `24-by-60` rectangle is covered with ten `12-by-12` square
+tiles, where `12` is the GCD of `24` and `60`. More generally,
+an `a-by-b` rectangle can be covered with square tiles of
 side-length `c` only if `c` is a common divisor of `a` and `b`.
 
 ![GCD](https://upload.wikimedia.org/wikipedia/commons/1/1c/Euclidean_algorithm_1071_462.gif)
 
-Subtraction-based animation of the Euclidean algorithm. 
-The initial rectangle has dimensions `a = 1071` and `b = 462`. 
-Squares of size `462×462` are placed within it leaving a 
-`462×147` rectangle. This rectangle is tiled with `147×147` 
-squares until a `21×147` rectangle is left, which in turn is 
-tiled with `21×21` squares, leaving no uncovered area. 
+Subtraction-based animation of the Euclidean algorithm.
+The initial rectangle has dimensions `a = 1071` and `b = 462`.
+Squares of size `462×462` are placed within it leaving a
+`462×147` rectangle. This rectangle is tiled with `147×147`
+squares until a `21×147` rectangle is left, which in turn is
+tiled with `21×21` squares, leaving no uncovered area.
 The smallest square size, `21`, is the GCD of `1071` and `462`.
 
 ## References
diff --git a/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js b/src/algorithms/math/euclidean-algorithm/__test__/euclideanAlgorithm.test.js
similarity index 95%
rename from src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js
rename to src/algorithms/math/euclidean-algorithm/__test__/euclideanAlgorithm.test.js
index ab2e3d6c6a..bbf92b26a5 100644
--- a/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js
+++ b/src/algorithms/math/euclidean-algorithm/__test__/euclideanAlgorithm.test.js
@@ -1,7 +1,7 @@
 import euclideanAlgorithm from '../euclideanAlgorithm';
 
 describe('euclideanAlgorithm', () => {
-  it('should calculate GCD', () => {
+  it('should calculate GCD recursively', () => {
     expect(euclideanAlgorithm(0, 0)).toBe(0);
     expect(euclideanAlgorithm(2, 0)).toBe(2);
     expect(euclideanAlgorithm(0, 2)).toBe(2);
diff --git a/src/algorithms/math/euclidean-algorithm/__test__/euclideanAlgorithmIterative.test.js b/src/algorithms/math/euclidean-algorithm/__test__/euclideanAlgorithmIterative.test.js
new file mode 100644
index 0000000000..5fd0f846ea
--- /dev/null
+++ b/src/algorithms/math/euclidean-algorithm/__test__/euclideanAlgorithmIterative.test.js
@@ -0,0 +1,26 @@
+import euclideanAlgorithmIterative from '../euclideanAlgorithmIterative';
+
+describe('euclideanAlgorithmIterative', () => {
+  it('should calculate GCD iteratively', () => {
+    expect(euclideanAlgorithmIterative(0, 0)).toBe(0);
+    expect(euclideanAlgorithmIterative(2, 0)).toBe(2);
+    expect(euclideanAlgorithmIterative(0, 2)).toBe(2);
+    expect(euclideanAlgorithmIterative(1, 2)).toBe(1);
+    expect(euclideanAlgorithmIterative(2, 1)).toBe(1);
+    expect(euclideanAlgorithmIterative(6, 6)).toBe(6);
+    expect(euclideanAlgorithmIterative(2, 4)).toBe(2);
+    expect(euclideanAlgorithmIterative(4, 2)).toBe(2);
+    expect(euclideanAlgorithmIterative(12, 4)).toBe(4);
+    expect(euclideanAlgorithmIterative(4, 12)).toBe(4);
+    expect(euclideanAlgorithmIterative(5, 13)).toBe(1);
+    expect(euclideanAlgorithmIterative(27, 13)).toBe(1);
+    expect(euclideanAlgorithmIterative(24, 60)).toBe(12);
+    expect(euclideanAlgorithmIterative(60, 24)).toBe(12);
+    expect(euclideanAlgorithmIterative(252, 105)).toBe(21);
+    expect(euclideanAlgorithmIterative(105, 252)).toBe(21);
+    expect(euclideanAlgorithmIterative(1071, 462)).toBe(21);
+    expect(euclideanAlgorithmIterative(462, 1071)).toBe(21);
+    expect(euclideanAlgorithmIterative(462, -1071)).toBe(21);
+    expect(euclideanAlgorithmIterative(-462, -1071)).toBe(21);
+  });
+});
diff --git a/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js b/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js
index fe3b57dd60..b9cec97c19 100644
--- a/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js
+++ b/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js
@@ -1,11 +1,15 @@
 /**
+ * Recursive version of Euclidean Algorithm of finding greatest common divisor (GCD).
  * @param {number} originalA
  * @param {number} originalB
  * @return {number}
  */
 export default function euclideanAlgorithm(originalA, originalB) {
+  // Make input numbers positive.
   const a = Math.abs(originalA);
   const b = Math.abs(originalB);
 
+  // To make algorithm work faster instead of subtracting one number from the other
+  // we may use modulo operation.
   return (b === 0) ? a : euclideanAlgorithm(b, a % b);
 }
diff --git a/src/algorithms/math/euclidean-algorithm/euclideanAlgorithmIterative.js b/src/algorithms/math/euclidean-algorithm/euclideanAlgorithmIterative.js
new file mode 100644
index 0000000000..302b5bffe9
--- /dev/null
+++ b/src/algorithms/math/euclidean-algorithm/euclideanAlgorithmIterative.js
@@ -0,0 +1,20 @@
+/**
+ * Iterative version of Euclidean Algorithm of finding greatest common divisor (GCD).
+ * @param {number} originalA
+ * @param {number} originalB
+ * @return {number}
+ */
+export default function euclideanAlgorithmIterative(originalA, originalB) {
+  // Make input numbers positive.
+  let a = Math.abs(originalA);
+  let b = Math.abs(originalB);
+
+  // Subtract one number from another until both numbers would become the same.
+  // This will be out GCD. Also quit the loop if one of the numbers is zero.
+  while (a && b && a !== b) {
+    [a, b] = a > b ? [a - b, b] : [a, b - a];
+  }
+
+  // Return the number that is not equal to zero since the last subtraction (it will be a GCD).
+  return a || b;
+}
diff --git a/src/algorithms/math/euclidean-distance/README.md b/src/algorithms/math/euclidean-distance/README.md
new file mode 100644
index 0000000000..d67c217efe
--- /dev/null
+++ b/src/algorithms/math/euclidean-distance/README.md
@@ -0,0 +1,36 @@
+# Euclidean Distance
+
+In mathematics, the **Euclidean distance** between two points in Euclidean space is the length of a line segment between the two points. It can be calculated from the Cartesian coordinates of the points using the Pythagorean theorem, therefore occasionally being called the Pythagorean distance.
+
+![Euclidean distance between two points](https://upload.wikimedia.org/wikipedia/commons/5/55/Euclidean_distance_2d.svg)
+
+## Distance formulas
+
+### One dimension
+
+The distance between any two points on the real line is the absolute value of the numerical difference of their coordinates
+
+![One dimension formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/7d75418dbec9482dbcb70f9063ad66e9cf7b5db9)
+
+### Two dimensions
+
+![Two dimensions formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/9c0157084fd89f5f3d462efeedc47d3d7aa0b773)
+
+### Higher dimensions
+
+In three dimensions, for points given by their Cartesian coordinates, the distance is
+
+![Three dimensions formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/d1d13a40a7b203b455ae6d4be8b3cce898bda625)
+
+Example: the distance between the two points `(8,2,6)` and `(3,5,7)`:
+
+![3-dimension example](https://www.mathsisfun.com/algebra/images/dist-2-points-3d.svg)
+
+In general, for points given by Cartesian coordinates in `n`-dimensional Euclidean space, the distance is
+
+![n-dimensional formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/a0ef4fe055b2a51b4cca43a05e5d1cd93f758dcc)
+
+## References
+
+- [Euclidean Distance on MathIsFun](https://www.mathsisfun.com/algebra/distance-2-points.html)
+- [Euclidean Distance on Wikipedia](https://en.wikipedia.org/wiki/Euclidean_distance)
diff --git a/src/algorithms/math/euclidean-distance/__tests__/euclideanDistance.test.js b/src/algorithms/math/euclidean-distance/__tests__/euclideanDistance.test.js
new file mode 100644
index 0000000000..78d7d8dc86
--- /dev/null
+++ b/src/algorithms/math/euclidean-distance/__tests__/euclideanDistance.test.js
@@ -0,0 +1,23 @@
+import euclideanDistance from '../euclideanDistance';
+
+describe('euclideanDistance', () => {
+  it('should calculate euclidean distance between vectors', () => {
+    expect(euclideanDistance([[1]], [[2]])).toEqual(1);
+    expect(euclideanDistance([[2]], [[1]])).toEqual(1);
+    expect(euclideanDistance([[5, 8]], [[7, 3]])).toEqual(5.39);
+    expect(euclideanDistance([[5], [8]], [[7], [3]])).toEqual(5.39);
+    expect(euclideanDistance([[8, 2, 6]], [[3, 5, 7]])).toEqual(5.92);
+    expect(euclideanDistance([[8], [2], [6]], [[3], [5], [7]])).toEqual(5.92);
+    expect(euclideanDistance([[[8]], [[2]], [[6]]], [[[3]], [[5]], [[7]]])).toEqual(5.92);
+  });
+
+  it('should throw an error in case if two matrices are of different shapes', () => {
+    expect(() => euclideanDistance([[1]], [[[2]]])).toThrowError(
+      'Matrices have different dimensions',
+    );
+
+    expect(() => euclideanDistance([[1]], [[2, 3]])).toThrowError(
+      'Matrices have different shapes',
+    );
+  });
+});
diff --git a/src/algorithms/math/euclidean-distance/euclideanDistance.js b/src/algorithms/math/euclidean-distance/euclideanDistance.js
new file mode 100644
index 0000000000..afa5c2fece
--- /dev/null
+++ b/src/algorithms/math/euclidean-distance/euclideanDistance.js
@@ -0,0 +1,28 @@
+/**
+ * @typedef {import('../matrix/Matrix.js').Matrix} Matrix
+ */
+
+import * as mtrx from '../matrix/Matrix';
+
+/**
+ * Calculates the euclidean distance between 2 matrices.
+ *
+ * @param {Matrix} a
+ * @param {Matrix} b
+ * @returns {number}
+ * @trows {Error}
+ */
+const euclideanDistance = (a, b) => {
+  mtrx.validateSameShape(a, b);
+
+  let squaresTotal = 0;
+
+  mtrx.walk(a, (indices, aCellValue) => {
+    const bCellValue = mtrx.getCellAtIndex(b, indices);
+    squaresTotal += (aCellValue - bCellValue) ** 2;
+  });
+
+  return Number(Math.sqrt(squaresTotal).toFixed(2));
+};
+
+export default euclideanDistance;
diff --git a/src/algorithms/math/factorial/README.fr-FR.md b/src/algorithms/math/factorial/README.fr-FR.md
new file mode 100644
index 0000000000..aa092c1b78
--- /dev/null
+++ b/src/algorithms/math/factorial/README.fr-FR.md
@@ -0,0 +1,35 @@
+# Factorielle
+
+_Lisez ceci dans d'autres langues:_
+[english](README.md), [_简体中文_](README.zh-CN.md).
+
+En mathématiques, la factorielle d'un entier naturel `n`,
+notée avec un point d'exclamation `n!`, est le produit des nombres entiers
+strictement positifs inférieurs ou égaux à n. Par exemple:
+
+```
+5! = 5 * 4 * 3 * 2 * 1 = 120
+```
+
+| n   |                n! |
+| --- | ----------------: |
+| 0   |                 1 |
+| 1   |                 1 |
+| 2   |                 2 |
+| 3   |                 6 |
+| 4   |                24 |
+| 5   |               120 |
+| 6   |               720 |
+| 7   |             5 040 |
+| 8   |            40 320 |
+| 9   |           362 880 |
+| 10  |         3 628 800 |
+| 11  |        39 916 800 |
+| 12  |       479 001 600 |
+| 13  |     6 227 020 800 |
+| 14  |    87 178 291 200 |
+| 15  | 1 307 674 368 000 |
+
+## References
+
+[Wikipedia](https://fr.wikipedia.org/wiki/Factorielle)
diff --git a/src/algorithms/math/factorial/README.ka-GE.md b/src/algorithms/math/factorial/README.ka-GE.md
new file mode 100644
index 0000000000..1d02b9f7d3
--- /dev/null
+++ b/src/algorithms/math/factorial/README.ka-GE.md
@@ -0,0 +1,32 @@
+# ფაქტორიალი
+
+მათემატიკაში `n` ნატურალური რიცხვის ფაქტორიალი
+(აღინიშნება `n!` სიმბოლოთი)
+არის ყველა ნატურალური რიცხვის ნამრავლი 1-იდან `n`-ის ჩათვლით. მაგალითად:
+
+```
+5! = 5 * 4 * 3 * 2 * 1 = 120
+```
+
+| n   |                n! |
+| --- | ----------------: |
+| 0   |                 1 |
+| 1   |                 1 |
+| 2   |                 2 |
+| 3   |                 6 |
+| 4   |                24 |
+| 5   |               120 |
+| 6   |               720 |
+| 7   |             5 040 |
+| 8   |            40 320 |
+| 9   |           362 880 |
+| 10  |         3 628 800 |
+| 11  |        39 916 800 |
+| 12  |       479 001 600 |
+| 13  |     6 227 020 800 |
+| 14  |    87 178 291 200 |
+| 15  | 1 307 674 368 000 |
+
+## სქოლიო
+
+[Wikipedia](https://ka.wikipedia.org/wiki/%E1%83%9B%E1%83%90%E1%83%97%E1%83%94%E1%83%9B%E1%83%90%E1%83%A2%E1%83%98%E1%83%99%E1%83%A3%E1%83%A0%E1%83%98_%E1%83%A4%E1%83%90%E1%83%A5%E1%83%A2%E1%83%9D%E1%83%A0%E1%83%98%E1%83%90%E1%83%9A%E1%83%98)
diff --git a/src/algorithms/math/factorial/README.md b/src/algorithms/math/factorial/README.md
index 55c481a2fd..9c75b41aae 100644
--- a/src/algorithms/math/factorial/README.md
+++ b/src/algorithms/math/factorial/README.md
@@ -1,31 +1,34 @@
 # Factorial
 
-In mathematics, the factorial of a non-negative integer `n`, 
-denoted by `n!`, is the product of all positive integers less 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md), [_Français_](README.fr-FR.md), [_Türkçe_](README.tr-TR.md), [_ქართული_](README.ka-GE.md), [_Українська_](README.uk-UA.md).
+
+In mathematics, the factorial of a non-negative integer `n`,
+denoted by `n!`, is the product of all positive integers less
 than or equal to `n`. For example:
 
 ```
 5! = 5 * 4 * 3 * 2 * 1 = 120
 ```
 
-| n     | n!                          | 
-| ----- | --------------------------: |
-| 0     | 1                           |
-| 1     | 1                           |
-| 2     | 2                           |
-| 3     | 6                           |
-| 4     | 24                          |
-| 5     | 120                         |
-| 6     | 720                         |
-| 7     | 5 040                       |
-| 8     | 40 320                      |
-| 9     | 362 880                     |
-| 10    | 3 628 800                   |
-| 11    | 39 916 800                  |
-| 12    | 479 001 600                 |
-| 13    | 6 227 020 800               |
-| 14    | 87 178 291 200              |
-| 15    | 1 307 674 368 000           |
+| n   |                n! |
+| --- | ----------------: |
+| 0   |                 1 |
+| 1   |                 1 |
+| 2   |                 2 |
+| 3   |                 6 |
+| 4   |                24 |
+| 5   |               120 |
+| 6   |               720 |
+| 7   |             5 040 |
+| 8   |            40 320 |
+| 9   |           362 880 |
+| 10  |         3 628 800 |
+| 11  |        39 916 800 |
+| 12  |       479 001 600 |
+| 13  |     6 227 020 800 |
+| 14  |    87 178 291 200 |
+| 15  | 1 307 674 368 000 |
 
 ## References
 
diff --git a/src/algorithms/math/factorial/README.tr-TR.md b/src/algorithms/math/factorial/README.tr-TR.md
new file mode 100644
index 0000000000..68ce21a299
--- /dev/null
+++ b/src/algorithms/math/factorial/README.tr-TR.md
@@ -0,0 +1,38 @@
+# Faktöriyel
+
+_Bunu diğer dillerde okuyun:_
+[_简体中文_](README.zh-CN.md), [français](README.fr-FR.md).
+
+Faktöriyel, matematikte, sağına ünlem işareti konulmuş sayıya
+verilen isim, daha genel olan Gama fonksiyonunun tam sayılarla
+sınırlanmış özel bir durumudur. 1'den başlayarak belirli bir
+sayma sayısına kadar olan sayıların çarpımına o sayının
+faktöriyeli denir. Basit bir şekilde faktöriyel, n tane ayrık
+elemanın kaç farklı şekilde sıralanabileceğidir.
+
+```
+5! = 5 * 4 * 3 * 2 * 1 = 120
+```
+
+| n   |                n! |
+| --- | ----------------: |
+| 0   |                 1 |
+| 1   |                 1 |
+| 2   |                 2 |
+| 3   |                 6 |
+| 4   |                24 |
+| 5   |               120 |
+| 6   |               720 |
+| 7   |             5 040 |
+| 8   |            40 320 |
+| 9   |           362 880 |
+| 10  |         3 628 800 |
+| 11  |        39 916 800 |
+| 12  |       479 001 600 |
+| 13  |     6 227 020 800 |
+| 14  |    87 178 291 200 |
+| 15  | 1 307 674 368 000 |
+
+## Referanslar
+
+[Wikipedia](https://en.wikipedia.org/wiki/Factorial)
diff --git a/src/algorithms/math/factorial/README.uk-UA.md b/src/algorithms/math/factorial/README.uk-UA.md
new file mode 100644
index 0000000000..bf75963113
--- /dev/null
+++ b/src/algorithms/math/factorial/README.uk-UA.md
@@ -0,0 +1,33 @@
+# Факторіал
+
+_Прочитайте це іншими мовами:_
+[_English_](README.md), [_简体中文_](README.zh-CN.md), [_Français_](README.fr-FR.md), [_Türkçe_](README.tr-TR.md), [_ქართული_](README.ka-GE.md).
+
+У математиці факторіал невід'ємного цілого числа `n`, позначений `n!`, є добутком усіх натуральних чисел, менших або рівних `n`. Наприклад:
+
+```
+5! = 5 * 4 * 3 * 2 * 1 = 120
+```
+
+| n   |                n! |
+| --- | ----------------: |
+| 0   |                 1 |
+| 1   |                 1 |
+| 2   |                 2 |
+| 3   |                 6 |
+| 4   |                24 |
+| 5   |               120 |
+| 6   |               720 |
+| 7   |             5 040 |
+| 8   |            40 320 |
+| 9   |           362 880 |
+| 10  |         3 628 800 |
+| 11  |        39 916 800 |
+| 12  |       479 001 600 |
+| 13  |     6 227 020 800 |
+| 14  |    87 178 291 200 |
+| 15  | 1 307 674 368 000 |
+
+## Посилання
+
+[Wikipedia](https://uk.wikipedia.org/wiki/%D0%A4%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D1%96%D0%B0%D0%BB)
diff --git a/src/algorithms/math/factorial/README.zh-CN.md b/src/algorithms/math/factorial/README.zh-CN.md
new file mode 100644
index 0000000000..89bcff1ff2
--- /dev/null
+++ b/src/algorithms/math/factorial/README.zh-CN.md
@@ -0,0 +1,27 @@
+# 阶乘
+
+在数学上, 一个正整数 `n` 的阶乘 (写作 `n!`), 就是所有小于等于 `n` 的正整数的乘积. 比如:
+
+```
+5! = 5 * 4 * 3 * 2 * 1 = 120
+```
+
+| n     | n!                          | 
+| ----- | --------------------------: |
+| 0     | 1                           |
+| 1     | 1                           |
+| 2     | 2                           |
+| 3     | 6                           |
+| 4     | 24                          |
+| 5     | 120                         |
+| 6     | 720                         |
+| 7     | 5 040                       |
+| 8     | 40 320                      |
+| 9     | 362 880                     |
+| 10    | 3 628 800                   |
+| 11    | 39 916 800                  |
+| 12    | 479 001 600                 |
+| 13    | 6 227 020 800               |
+| 14    | 87 178 291 200              |
+| 15    | 1 307 674 368 000           |
+
diff --git a/src/algorithms/math/fast-powering/README.fr-FR.md b/src/algorithms/math/fast-powering/README.fr-FR.md
new file mode 100644
index 0000000000..ba44244cc8
--- /dev/null
+++ b/src/algorithms/math/fast-powering/README.fr-FR.md
@@ -0,0 +1,73 @@
+# Algorithme d'exponentiation rapide
+
+_Read this in other languages:_
+[english](README.md).
+
+En algèbre, une **puissance** d'un nombre est le résultat de la multiplication répétée de ce nombre avec lui-même.
+
+Elle est souvent notée en assortissant le nombre d'un entier, typographié en exposant, qui indique le nombre de fois qu'apparaît le nombre comme facteur dans cette multiplication.
+
+![Power](https://www.mathsisfun.com/algebra/images/exponent-8-2.svg)
+
+## Implémentation « naïve »
+
+Comment trouver `a` élevé à la puissance `b` ?
+
+On multiplie `a` avec lui-même, `b` nombre de fois.
+Ainsi, `a^b = a * a * a * ... * a` (`b` occurrences de `a`).
+
+Cette opération aura un complexité linéaire, notée `O(n)`,
+car la multiplication aura lieu exactement `n` fois.
+
+## Algorithme d'exponentiation rapide
+
+Peut-on faire mieux que cette implémentation naïve?
+Oui, on peut réduire le nombre de puissance à un complexité de `O(log(n))`.
+
+Cet algorithme utilise l'approche « diviser pour mieux régner »
+pour calculer cette puissance.
+En l'état, cet algorithme fonctionne pour deux entiers positifs `X` et `Y`.
+
+L'idée derrière cet algorithme est basée sur l'observation suivante.
+
+Lorsque `Y` est **pair**:
+
+```text
+X^Y = X^(Y/2) * X^(Y/2)
+```
+
+Lorsque `Y` est **impair**:
+
+```text
+X^Y = X^(Y//2) * X^(Y//2) * X
+où Y//2 est le résultat de la division entière de Y par 2.
+```
+
+**Par exemple**
+
+```text
+2^4 = (2 * 2) * (2 * 2) = (2^2) * (2^2)
+```
+
+```text
+2^5 = (2 * 2) * (2 * 2) * 2 = (2^2) * (2^2) * (2)
+```
+
+Ainsi, puisqu'à chaque étape on doits calculer
+deux fois la même puissance `X ^ (Y / 2)`,
+on peut optimiser en l'enregistrant dans une variable intermédiaire
+pour éviter son calcul en double.
+
+**Complexité en temps**
+
+Comme à chaque itération nous réduisons la puissance de moitié,
+nous appelons récursivement la fonction `log(n)` fois. Le complexité de temps de cet algorithme est donc réduite à:
+
+```text
+O(log(n))
+```
+
+## Références
+
+- [YouTube](https://www.youtube.com/watch?v=LUWavfN9zEo&index=80&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&t=0s)
+- [Wikipedia](https://fr.wikipedia.org/wiki/Exponentiation_rapide)
diff --git a/src/algorithms/math/fast-powering/README.md b/src/algorithms/math/fast-powering/README.md
new file mode 100644
index 0000000000..2c2619d2ef
--- /dev/null
+++ b/src/algorithms/math/fast-powering/README.md
@@ -0,0 +1,71 @@
+# Fast Powering Algorithm
+
+_Read this in other languages:_
+[français](README.fr-FR.md).
+
+**The power of a number** says how many times to use the number in a
+multiplication.
+
+It is written as a small number to the right and above the base number.
+
+![Power](https://www.mathsisfun.com/algebra/images/exponent-8-2.svg)
+
+## Naive Algorithm Complexity
+
+How to find `a` raised to the power `b`?
+
+We multiply `a` to itself, `b` times. That
+is, `a^b = a * a * a * ... * a` (`b` occurrences of `a`).
+
+This operation will take `O(n)` time since we need to do multiplication operation
+exactly `n` times.
+
+## Fast Power Algorithm
+
+Can we do better than naive algorithm does? Yes we may solve the task of
+powering in `O(log(n))` time.
+
+The algorithm uses divide and conquer approach to compute power. Currently the
+algorithm work for two positive integers `X` and `Y`.
+
+The idea behind the algorithm is based on the fact that:
+
+For **even** `Y`:
+
+```text
+X^Y = X^(Y/2) * X^(Y/2)
+```
+
+For **odd** `Y`:
+
+```text
+X^Y = X^(Y//2) * X^(Y//2) * X
+where Y//2 is result of division of Y by 2 without reminder.
+```
+
+**For example**
+
+```text
+2^4 = (2 * 2) * (2 * 2) = (2^2) * (2^2)
+```
+
+```text
+2^5 = (2 * 2) * (2 * 2) * 2 = (2^2) * (2^2) * (2)
+```
+
+Now, since on each step we need to compute the same `X^(Y/2)` power twice we may optimise
+it by saving it to some intermediate variable to avoid its duplicate calculation.
+
+**Time Complexity**
+
+Since each iteration we split the power by half then we will call function
+recursively `log(n)` times. This the time complexity of the algorithm is reduced to:
+
+```text
+O(log(n))
+```
+
+## References
+
+- [YouTube](https://www.youtube.com/watch?v=LUWavfN9zEo&index=80&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&t=0s)
+- [Wikipedia](https://en.wikipedia.org/wiki/Exponentiation_by_squaring)
diff --git a/src/algorithms/math/fast-powering/__test__/fastPowering.test.js b/src/algorithms/math/fast-powering/__test__/fastPowering.test.js
new file mode 100644
index 0000000000..0a5da7561a
--- /dev/null
+++ b/src/algorithms/math/fast-powering/__test__/fastPowering.test.js
@@ -0,0 +1,23 @@
+import fastPowering from '../fastPowering';
+
+describe('fastPowering', () => {
+  it('should compute power in log(n) time', () => {
+    expect(fastPowering(1, 1)).toBe(1);
+    expect(fastPowering(2, 0)).toBe(1);
+    expect(fastPowering(2, 2)).toBe(4);
+    expect(fastPowering(2, 3)).toBe(8);
+    expect(fastPowering(2, 4)).toBe(16);
+    expect(fastPowering(2, 5)).toBe(32);
+    expect(fastPowering(2, 6)).toBe(64);
+    expect(fastPowering(2, 7)).toBe(128);
+    expect(fastPowering(2, 8)).toBe(256);
+    expect(fastPowering(3, 4)).toBe(81);
+    expect(fastPowering(190, 2)).toBe(36100);
+    expect(fastPowering(11, 5)).toBe(161051);
+    expect(fastPowering(13, 11)).toBe(1792160394037);
+    expect(fastPowering(9, 16)).toBe(1853020188851841);
+    expect(fastPowering(16, 16)).toBe(18446744073709552000);
+    expect(fastPowering(7, 21)).toBe(558545864083284000);
+    expect(fastPowering(100, 9)).toBe(1000000000000000000);
+  });
+});
diff --git a/src/algorithms/math/fast-powering/fastPowering.js b/src/algorithms/math/fast-powering/fastPowering.js
new file mode 100644
index 0000000000..4f4a6b3571
--- /dev/null
+++ b/src/algorithms/math/fast-powering/fastPowering.js
@@ -0,0 +1,30 @@
+/**
+ * Fast Powering Algorithm.
+ * Recursive implementation to compute power.
+ *
+ * Complexity: log(n)
+ *
+ * @param {number} base - Number that will be raised to the power.
+ * @param {number} power - The power that number will be raised to.
+ * @return {number}
+ */
+export default function fastPowering(base, power) {
+  if (power === 0) {
+    // Anything that is raised to the power of zero is 1.
+    return 1;
+  }
+
+  if (power % 2 === 0) {
+    // If the power is even...
+    // we may recursively redefine the result via twice smaller powers:
+    // x^8 = x^4 * x^4.
+    const multiplier = fastPowering(base, power / 2);
+    return multiplier * multiplier;
+  }
+
+  // If the power is odd...
+  // we may recursively redefine the result via twice smaller powers:
+  // x^9 = x^4 * x^4 * x.
+  const multiplier = fastPowering(base, Math.floor(power / 2));
+  return multiplier * multiplier * base;
+}
diff --git a/src/algorithms/math/fibonacci/README.fr-FR.md b/src/algorithms/math/fibonacci/README.fr-FR.md
new file mode 100644
index 0000000000..81e3549b0d
--- /dev/null
+++ b/src/algorithms/math/fibonacci/README.fr-FR.md
@@ -0,0 +1,24 @@
+# Nombre de Fibonacci
+
+_Read this in other languages:_
+[english](README.md),
+[ქართული](README.ka-GE.md).
+
+En mathématiques, la suite de Fibonacci est une suite d'entiers
+dans laquelle chaque terme (après les deux premiers)
+est la somme des deux termes qui le précèdent.
+Les termes de cette suite sont appelés nombres de Fibonacci:
+
+`0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...`
+
+Les carrés de Fibonacci en spirale s'ajustent ensemble pour former une spirale d'or.
+
+![Fibonacci](https://upload.wikimedia.org/wikipedia/commons/d/db/34%2A21-FibonacciBlocks.png)
+
+La spirale de Fibonacci: approximation d'une spirale d'or créée en dessinant des arcs de cercle reliant les coins opposés de carrés dans un pavage Fibonacci[4] . Celui-ci utilise des carrés de tailles 1, 1, 2, 3, 5, 8, 13, 21, et 34.
+
+![Fibonacci Spiral](https://upload.wikimedia.org/wikipedia/commons/2/2e/FibonacciSpiral.svg)
+
+## References
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/Suite_de_Fibonacci)
diff --git a/src/algorithms/math/fibonacci/README.ka-GE.md b/src/algorithms/math/fibonacci/README.ka-GE.md
new file mode 100644
index 0000000000..ae7e11218b
--- /dev/null
+++ b/src/algorithms/math/fibonacci/README.ka-GE.md
@@ -0,0 +1,20 @@
+# ფიბონაჩის რიცხვი
+
+მათემატიკაში ფიბონაჩის მიმდევრობა წარმოადგენს მთელ რიცხვთა მიმდევრობას,
+რომელშიც ყოველი რიცხვი (პირველი ორი რიცხვის შემდეგ)
+მისი წინამორბედი ორი რიცხვის
+ჯამის ტოლია:
+
+`0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...`
+
+კვადრატების წყობა, სადაც ყოველი კვადრატის გვერდების სიგრძე, თანმიმდევრობით, ფიბონაჩის რიცხვებს შეესაბამება
+
+![Fibonacci](https://upload.wikimedia.org/wikipedia/commons/d/db/34%2A21-FibonacciBlocks.png)
+
+ფიბონაჩის სპირალი: ოქროს სპირალის აპროქსიმაცია, რომელიც შექმნილია კვადრატების მოპირდაპირე კუთხეებს შორის შემაერთებელი რკალების გავლებით;[4] ამ შემთხვევაში, გამოყენებულ კვადრატთა [გვერდების] ზომებია: 1, 1, 2, 3, 5, 8, 13 და 21.
+
+![Fibonacci Spiral](https://upload.wikimedia.org/wikipedia/commons/2/2e/FibonacciSpiral.svg)
+
+## სქოლიო
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Fibonacci_number)
diff --git a/src/algorithms/math/fibonacci/README.md b/src/algorithms/math/fibonacci/README.md
index f4a5df83d2..3d476c5020 100644
--- a/src/algorithms/math/fibonacci/README.md
+++ b/src/algorithms/math/fibonacci/README.md
@@ -1,8 +1,13 @@
 # Fibonacci Number
 
-In mathematics, the Fibonacci numbers are the numbers in the following 
-integer sequence, called the Fibonacci sequence, and characterized by 
-the fact that every number after the first two is the sum of the two 
+_Read this in other languages:_
+[français](README.fr-FR.md),
+[简体中文](README.zh-CN.md),
+[ქართული](README.ka-GE.md).
+
+In mathematics, the Fibonacci numbers are the numbers in the following
+integer sequence, called the Fibonacci sequence, and characterized by
+the fact that every number after the first two is the sum of the two
 preceding ones:
 
 `0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...`
@@ -17,4 +22,4 @@ The Fibonacci spiral: an approximation of the golden spiral created by drawing c
 
 ## References
 
-[Wikipedia](https://en.wikipedia.org/wiki/Fibonacci_number)
+- [Wikipedia](https://en.wikipedia.org/wiki/Fibonacci_number)
diff --git a/src/algorithms/math/fibonacci/README.zh-CN.md b/src/algorithms/math/fibonacci/README.zh-CN.md
new file mode 100644
index 0000000000..20378000d4
--- /dev/null
+++ b/src/algorithms/math/fibonacci/README.zh-CN.md
@@ -0,0 +1,23 @@
+# 斐波那契数
+
+_Read this in other languages:_
+[français](README.fr-FR.md),
+[english](README.md),
+[ქართული](README.ka-GE.md).
+
+在数学中，斐波那契数是以下整数序列（称为斐波那契数列）中的数字，其特征在于前两个数字之后的每个数字都是前两个数字的和：
+
+`0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...`
+
+边长为连续斐波纳契数的正方形平铺
+
+![Fibonacci](https://upload.wikimedia.org/wikipedia/commons/d/db/34%2A21-FibonacciBlocks.png)
+
+
+斐波那契螺旋：通过绘制连接斐波那契平铺中正方形的相对角的圆弧而创建的金色螺旋的近似值； [4]该三角形使用大小为1、1、2、3、5、8、13和21的正方形。
+
+![Fibonacci Spiral](https://upload.wikimedia.org/wikipedia/commons/2/2e/FibonacciSpiral.svg)
+
+## References
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Fibonacci_number)
diff --git a/src/algorithms/math/fibonacci/__test__/fibonacci.test.js b/src/algorithms/math/fibonacci/__test__/fibonacci.test.js
index 947c621b8a..ffbee8d9ea 100644
--- a/src/algorithms/math/fibonacci/__test__/fibonacci.test.js
+++ b/src/algorithms/math/fibonacci/__test__/fibonacci.test.js
@@ -10,5 +10,7 @@ describe('fibonacci', () => {
     expect(fibonacci(6)).toEqual([1, 1, 2, 3, 5, 8]);
     expect(fibonacci(7)).toEqual([1, 1, 2, 3, 5, 8, 13]);
     expect(fibonacci(8)).toEqual([1, 1, 2, 3, 5, 8, 13, 21]);
+    expect(fibonacci(9)).toEqual([1, 1, 2, 3, 5, 8, 13, 21, 34]);
+    expect(fibonacci(10)).toEqual([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]);
   });
 });
diff --git a/src/algorithms/math/fibonacci/__test__/fibonacciNth.test.js b/src/algorithms/math/fibonacci/__test__/fibonacciNth.test.js
index 8e702a5428..4a098eba70 100644
--- a/src/algorithms/math/fibonacci/__test__/fibonacciNth.test.js
+++ b/src/algorithms/math/fibonacci/__test__/fibonacciNth.test.js
@@ -11,5 +11,13 @@ describe('fibonacciNth', () => {
     expect(fibonacciNth(7)).toBe(13);
     expect(fibonacciNth(8)).toBe(21);
     expect(fibonacciNth(20)).toBe(6765);
+    expect(fibonacciNth(30)).toBe(832040);
+    expect(fibonacciNth(50)).toBe(12586269025);
+    expect(fibonacciNth(70)).toBe(190392490709135);
+    expect(fibonacciNth(71)).toBe(308061521170129);
+    expect(fibonacciNth(72)).toBe(498454011879264);
+    expect(fibonacciNth(73)).toBe(806515533049393);
+    expect(fibonacciNth(74)).toBe(1304969544928657);
+    expect(fibonacciNth(75)).toBe(2111485077978050);
   });
 });
diff --git a/src/algorithms/math/fibonacci/__test__/fibonacciNthClosedForm.test.js b/src/algorithms/math/fibonacci/__test__/fibonacciNthClosedForm.test.js
new file mode 100644
index 0000000000..4828ef5827
--- /dev/null
+++ b/src/algorithms/math/fibonacci/__test__/fibonacciNthClosedForm.test.js
@@ -0,0 +1,26 @@
+import fibonacciNthClosedForm from '../fibonacciNthClosedForm';
+
+describe('fibonacciClosedForm', () => {
+  it('should throw an error when trying to calculate fibonacci for not allowed positions', () => {
+    const calculateFibonacciForNotAllowedPosition = () => {
+      fibonacciNthClosedForm(76);
+    };
+
+    expect(calculateFibonacciForNotAllowedPosition).toThrow();
+  });
+
+  it('should calculate fibonacci correctly', () => {
+    expect(fibonacciNthClosedForm(1)).toBe(1);
+    expect(fibonacciNthClosedForm(2)).toBe(1);
+    expect(fibonacciNthClosedForm(3)).toBe(2);
+    expect(fibonacciNthClosedForm(4)).toBe(3);
+    expect(fibonacciNthClosedForm(5)).toBe(5);
+    expect(fibonacciNthClosedForm(6)).toBe(8);
+    expect(fibonacciNthClosedForm(7)).toBe(13);
+    expect(fibonacciNthClosedForm(8)).toBe(21);
+    expect(fibonacciNthClosedForm(20)).toBe(6765);
+    expect(fibonacciNthClosedForm(30)).toBe(832040);
+    expect(fibonacciNthClosedForm(50)).toBe(12586269025);
+    expect(fibonacciNthClosedForm(70)).toBe(190392490709135);
+  });
+});
diff --git a/src/algorithms/math/fibonacci/fibonacciNthClosedForm.js b/src/algorithms/math/fibonacci/fibonacciNthClosedForm.js
new file mode 100644
index 0000000000..e2a2a367cb
--- /dev/null
+++ b/src/algorithms/math/fibonacci/fibonacciNthClosedForm.js
@@ -0,0 +1,23 @@
+/**
+ * Calculate fibonacci number at specific position using closed form function (Binet's formula).
+ * @see: https://en.wikipedia.org/wiki/Fibonacci_number#Closed-form_expression
+ *
+ * @param {number} position - Position number of fibonacci sequence (must be number from 1 to 75).
+ * @return {number}
+ */
+export default function fibonacciClosedForm(position) {
+  const topMaxValidPosition = 70;
+
+  // Check that position is valid.
+  if (position < 1 || position > topMaxValidPosition) {
+    throw new Error(`Can't handle position smaller than 1 or greater than ${topMaxValidPosition}`);
+  }
+
+  // Calculate √5 to re-use it in further formulas.
+  const sqrt5 = Math.sqrt(5);
+  // Calculate φ constant (≈ 1.61803).
+  const phi = (1 + sqrt5) / 2;
+
+  // Calculate fibonacci number using Binet's formula.
+  return Math.floor((phi ** position) / sqrt5 + 0.5);
+}
diff --git a/src/algorithms/math/fourier-transform/README.fr-FR.md b/src/algorithms/math/fourier-transform/README.fr-FR.md
new file mode 100644
index 0000000000..0d345164c7
--- /dev/null
+++ b/src/algorithms/math/fourier-transform/README.fr-FR.md
@@ -0,0 +1,135 @@
+# Transformation de Fourier
+
+_Read this in other languages:_
+[english](README.md).
+
+## Définitions
+
+La transformation de Fourier (**ℱ**) est une opération qui transforme
+une fonction intégrable sur ℝ en une autre fonction,
+décrivant le spectre fréquentiel de cette dernière
+
+La **Transformée de Fourier Discrète** (**TFD**) convertit une séquence finie d'échantillons également espacés d'une fonction, dans une séquence de même longueur d'échantillons, également espacés de la Transformée de Fourier à temps discret (TFtd), qui est une fonction complexe de la fréquence.
+L'intervalle auquel le TFtd est échantillonné est l'inverse de la durée de la séquence d'entrée.
+Une TFD inverse est une série de Fourier, utilisant les échantillons TFtd comme coefficients de sinusoïdes complexes aux fréquences TFtd correspondantes. Elle a les mêmes valeurs d'échantillonnage que la
+séquence d'entrée originale. On dit donc que la TFD est une représentation du domaine fréquentiel
+de la séquence d'entrée d'origine. Si la séquence d'origine couvre toutes les
+valeurs non nulles d'une fonction, sa TFtd est continue (et périodique), et la TFD fournit
+les échantillons discrets d'une fenêtre. Si la séquence d'origine est un cycle d'une fonction périodique, la TFD fournit toutes les valeurs non nulles d'une fenêtre TFtd.
+
+Transformée de Fourier Discrète converti une séquence de `N` nombres complexes:
+
+{x<sub>n</sub>} = x<sub>0</sub>, x<sub>1</sub>, x<sub>2</sub> ..., x<sub>N-1</sub>
+
+en une atre séquence de nombres complexes::
+
+{X<sub>k</sub>} = X<sub>0</sub>, X<sub>1</sub>, X<sub>2</sub> ..., X<sub>N-1</sub>
+
+décrite par:
+
+![DFT](https://wikimedia.org/api/rest_v1/media/math/render/svg/1af0a78dc50bbf118ab6bd4c4dcc3c4ff8502223)
+
+The **Transformée de Fourier à temps discret** (**TFtd**) est une forme d'analyse de Fourier
+qui s'applique aux échantillons uniformément espacés d'une fonction continue. Le terme "temps discret" fait référence au fait que la transformée fonctionne sur des données discrètes
+(échantillons) dont l'intervalle a souvent des unités de temps.
+À partir des seuls échantillons, elle produit une fonction de fréquence qui est une somme périodique de la
+Transformée de Fourier continue de la fonction continue d'origine.
+
+A **Transformation de Fourier rapide** (**FFT** pour Fast Fourier Transform) est un algorithme de calcul de la transformation de Fourier discrète (TFD). Il est couramment utilisé en traitement numérique du signal pour transformer des données discrètes du domaine temporel dans le domaine fréquentiel, en particulier dans les oscilloscopes numériques (les analyseurs de spectre utilisant plutôt des filtres analogiques, plus précis). Son efficacité permet de réaliser des filtrages en modifiant le spectre et en utilisant la transformation inverse (filtre à réponse impulsionnelle finie).
+
+Cette transformation peut être illustée par la formule suivante. Sur la période de temps mesurée
+dans le diagramme, le signal contient 3 fréquences dominantes distinctes.
+
+Vue d'un signal dans le domaine temporel et fréquentiel:
+
+![FFT](https://upload.wikimedia.org/wikipedia/commons/6/61/FFT-Time-Frequency-View.png)
+
+An FFT algorithm computes the discrete Fourier transform (DFT) of a sequence, or
+its inverse (IFFT). Fourier analysis converts a signal from its original domain
+to a representation in the frequency domain and vice versa. An FFT rapidly
+computes such transformations by factorizing the DFT matrix into a product of
+sparse (mostly zero) factors. As a result, it manages to reduce the complexity of
+computing the DFT from O(n<sup>2</sup>), which arises if one simply applies the
+definition of DFT, to O(n log n), where n is the data size.
+
+Un algorithme FFT calcule la Transformée de Fourier discrète (TFD) d'une séquence, ou
+son inverse (IFFT). L'analyse de Fourier convertit un signal de son domaine d'origine
+en une représentation dans le domaine fréquentiel et vice versa. Une FFT
+calcule rapidement ces transformations en factorisant la matrice TFD en un produit de
+facteurs dispersés (généralement nuls). En conséquence, il parvient à réduire la complexité de
+calcul de la TFD de O (n <sup> 2 </sup>), qui survient si l'on applique simplement la
+définition de TFD, à O (n log n), où n est la taille des données.
+
+Voici une analyse de Fourier discrète d'une somme d'ondes cosinus à 10, 20, 30, 40,
+et 50 Hz:
+
+![FFT](https://upload.wikimedia.org/wikipedia/commons/6/64/FFT_of_Cosine_Summation_Function.png)
+
+## Explanation
+
+La Transformée de Fourier est l'une des connaissances les plus importante jamais découverte. Malheureusement, le
+son sens est enfoui dans des équations denses::
+
+![](https://betterexplained.com/wp-content/plugins/wp-latexrender/pictures/45c088dbb767150fc0bacfeb49dd49e5.png)
+
+et
+
+![](https://betterexplained.com/wp-content/plugins/wp-latexrender/pictures/faeb9c5bf2e60add63ae4a70b293c7b4.png)
+
+Plutôt que se noyer dans les symboles, faisons en premier lieu l'expérience de l'idée principale. Voici une métaphore en français simple:
+
+- _Que fait la transformée de Fourier ?_ A partir d'un smoothie, elle trouve sa recette.
+- _Comment ?_ Elle passe le smoothie dans un filtre pour en extraire chaque ingrédient.
+- _Pourquoi ?_ Les recettes sont plus faciles à analyser, comparer et modifier que le smoothie lui-même.
+- _Comment récupérer le smoothie ?_ Re-mélanger les ingrédients.
+
+**Pensez en cercles, pas seulement en sinusoïdes**
+
+La transformée de Fourier concerne des trajectoires circulaires (pas les sinusoïdes 1-d)
+et la formuled'Euler est une manière intelligente d'en générer une:
+
+![](https://betterexplained.com/wp-content/uploads/euler/equal_paths.png)
+
+Doit-on utiliser des exposants imaginaires pour se déplacer en cercle ? Non. Mais c'est pratique
+et compact. Et bien sûr, nous pouvons décrire notre chemin comme un mouvement coordonné en deux
+dimensions (réelle et imaginaire), mais n'oubliez pas la vue d'ensemble: nous sommes juste
+en déplacement dans un cercle.
+
+**À la découverte de la transformation complète**
+
+L'idée générale: notre signal n'est qu'un tas de pics de temps, d'instant T ! Si nous combinons les
+recettes pour chaque pic de temps, nous devrions obtenir la recette du signal complet.
+
+La transformée de Fourier construit cette recette fréquence par fréquence:
+
+![](https://betterexplained.com/wp-content/uploads/images/fourier-explained-20121219-224649.png)
+
+Quelques notes
+
+- N = nombre d'échantillons de temps dont nous disposons
+- n = échantillon actuellement étudié (0 ... N-1)
+- x<sub>n</sub> = valeur du signal au temps n
+- k = fréquence actuellement étudiée (0 Hertz up to N-1 Hertz)
+- X<sub>k</sub> = quantité de fréquence k dans le signal (amplitude et phase, un nombre complexe)
+- Le facteur 1 / N est généralement déplacé vers la transformée inverse (passant des fréquences au temps). Ceci est autorisé, bien que je préfère 1 / N dans la transformation directe car cela donne les tailles réelles des pics de temps. Vous pouvez être plus ambitieux et utiliser 1 / racine carrée de (N) sur les deux transformations (aller en avant et en arrière crée le facteur 1 / N).
+- n/N est le pourcentage du temps que nous avons passé. 2 _ pi _ k est notre vitesse en radians/s. e ^ -ix est notre chemin circulaire vers l'arrière. La combinaison est la distance parcourue, pour cette vitesse et ce temps.
+- Les équations brutes de la transformée de Fourier consiste simplement à "ajouter les nombres complexes". De nombreux langages de programmation ne peuvent pas gérer directement les nombres complexes, on converti donc tout en coordonnées rectangulaires, pour les ajouter.
+
+Stuart Riffle a une excellente interprétation de la transformée de Fourier:
+
+![](https://betterexplained.com/wp-content/uploads/images/DerivedDFT.png)
+
+## Références
+
+- Wikipedia
+
+  - [TF](https://fr.wikipedia.org/wiki/Transformation_de_Fourier)
+  - [TFD](https://fr.wikipedia.org/wiki/Transformation_de_Fourier_discr%C3%A8te)
+  - [FFT](https://fr.wikipedia.org/wiki/Transformation_de_Fourier_rapide)
+  - [TFtd (en anglais)](https://en.wikipedia.org/wiki/Discrete-time_Fourier_transform)
+
+- en Anglais
+  - [An Interactive Guide To The Fourier Transform](https://betterexplained.com/articles/an-interactive-guide-to-the-fourier-transform/)
+  - [DFT on YouTube by Better Explained](https://www.youtube.com/watch?v=iN0VG9N2q0U&t=0s&index=77&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+  - [FT on YouTube by 3Blue1Brown](https://www.youtube.com/watch?v=spUNpyF58BY&t=0s&index=76&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+  - [FFT on YouTube by Simon Xu](https://www.youtube.com/watch?v=htCj9exbGo0&index=78&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&t=0s)
diff --git a/src/algorithms/math/fourier-transform/README.md b/src/algorithms/math/fourier-transform/README.md
index b1785d725f..f7969ddd5c 100644
--- a/src/algorithms/math/fourier-transform/README.md
+++ b/src/algorithms/math/fourier-transform/README.md
@@ -1,26 +1,29 @@
 # Fourier Transform
 
+_Read this in other languages:_
+[français](README.fr-FR.md).
+
 ## Definitions
 
-The **Fourier Transform** (**FT**) decomposes a function of time (a signal) into 
-the frequencies that make it up, in a way similar to how a musical chord can be 
+The **Fourier Transform** (**FT**) decomposes a function of time (a signal) into
+the frequencies that make it up, in a way similar to how a musical chord can be
 expressed as the frequencies (or pitches) of its constituent notes.
 
-The **Discrete Fourier Transform** (**DFT**) converts a finite sequence of 
-equally-spaced samples of a function into a same-length sequence of 
-equally-spaced samples of the discrete-time Fourier transform (DTFT), which is a 
-complex-valued function of frequency. The interval at which the DTFT is sampled 
-is the reciprocal of the duration of the input sequence. An inverse DFT is a 
-Fourier series, using the DTFT samples as coefficients of complex sinusoids at 
-the corresponding DTFT frequencies. It has the same sample-values as the original 
-input sequence. The DFT is therefore said to be a frequency domain representation 
-of the original input sequence. If the original sequence spans all the non-zero 
-values of a function, its DTFT is continuous (and periodic), and the DFT provides 
-discrete samples of one cycle. If the original sequence is one cycle of a periodic 
+The **Discrete Fourier Transform** (**DFT**) converts a finite sequence of
+equally-spaced samples of a function into a same-length sequence of
+equally-spaced samples of the discrete-time Fourier transform (DTFT), which is a
+complex-valued function of frequency. The interval at which the DTFT is sampled
+is the reciprocal of the duration of the input sequence. An inverse DFT is a
+Fourier series, using the DTFT samples as coefficients of complex sinusoids at
+the corresponding DTFT frequencies. It has the same sample-values as the original
+input sequence. The DFT is therefore said to be a frequency domain representation
+of the original input sequence. If the original sequence spans all the non-zero
+values of a function, its DTFT is continuous (and periodic), and the DFT provides
+discrete samples of one cycle. If the original sequence is one cycle of a periodic
 function, the DFT provides all the non-zero values of one DTFT cycle.
 
 The Discrete Fourier transform transforms a sequence of `N` complex numbers:
-  
+
 {x<sub>n</sub>} = x<sub>0</sub>, x<sub>1</sub>, x<sub>2</sub> ..., x<sub>N-1</sub>
 
 into another sequence of complex numbers:
@@ -31,16 +34,16 @@ which is defined by:
 
 ![DFT](https://wikimedia.org/api/rest_v1/media/math/render/svg/1af0a78dc50bbf118ab6bd4c4dcc3c4ff8502223)
 
-The **Discrete-Time Fourier Transform** (**DTFT**) is a form of Fourier analysis 
-that is applicable to the uniformly-spaced samples of a continuous function. The 
+The **Discrete-Time Fourier Transform** (**DTFT**) is a form of Fourier analysis
+that is applicable to the uniformly-spaced samples of a continuous function. The
 term discrete-time refers to the fact that the transform operates on discrete data
-(samples) whose interval often has units of time. From only the samples, it 
-produces a function of frequency that is a periodic summation of the continuous 
+(samples) whose interval often has units of time. From only the samples, it
+produces a function of frequency that is a periodic summation of the continuous
 Fourier transform of the original continuous function.
 
 A **Fast Fourier Transform** (**FFT**) is an algorithm that samples a signal over
-a period of time (or space) and divides it into its frequency components. These 
-components are single sinusoidal oscillations at distinct frequencies each with 
+a period of time (or space) and divides it into its frequency components. These
+components are single sinusoidal oscillations at distinct frequencies each with
 their own amplitude and phase.
 
 This transformation is illustrated in Diagram below. Over the time period measured
@@ -50,22 +53,22 @@ View of a signal in the time and frequency domain:
 
 ![FFT](https://upload.wikimedia.org/wikipedia/commons/6/61/FFT-Time-Frequency-View.png)
 
-An FFT algorithm computes the discrete Fourier transform (DFT) of a sequence, or 
-its inverse (IFFT). Fourier analysis converts a signal from its original domain 
-to a representation in the frequency domain and vice versa. An FFT rapidly 
-computes such transformations by factorizing the DFT matrix into a product of 
-sparse (mostly zero) factors. As a result, it manages to reduce the complexity of 
-computing the DFT from O(n<sup>2</sup>), which arises if one simply applies the 
+An FFT algorithm computes the discrete Fourier transform (DFT) of a sequence, or
+its inverse (IFFT). Fourier analysis converts a signal from its original domain
+to a representation in the frequency domain and vice versa. An FFT rapidly
+computes such transformations by factorizing the DFT matrix into a product of
+sparse (mostly zero) factors. As a result, it manages to reduce the complexity of
+computing the DFT from O(n<sup>2</sup>), which arises if one simply applies the
 definition of DFT, to O(n log n), where n is the data size.
 
-Here a discrete Fourier analysis of a sum of cosine waves at 10, 20, 30, 40, 
+Here a discrete Fourier analysis of a sum of cosine waves at 10, 20, 30, 40,
 and 50 Hz:
 
 ![FFT](https://upload.wikimedia.org/wikipedia/commons/6/64/FFT_of_Cosine_Summation_Function.png)
 
 ## Explanation
 
-The Fourier Transform is one of deepest insights ever made. Unfortunately, the 
+The Fourier Transform is one of deepest insights ever made. Unfortunately, the
 meaning is buried within dense equations:
 
 ![](https://betterexplained.com/wp-content/plugins/wp-latexrender/pictures/45c088dbb767150fc0bacfeb49dd49e5.png)
@@ -76,26 +79,26 @@ and
 
 Rather than jumping into the symbols, let's experience the key idea firsthand. Here's a plain-English metaphor:
 
-- *What does the Fourier Transform do?* Given a smoothie, it finds the recipe.
-- *How?* Run the smoothie through filters to extract each ingredient.
-- *Why?* Recipes are easier to analyze, compare, and modify than the smoothie itself.
-- *How do we get the smoothie back?* Blend the ingredients.
+- _What does the Fourier Transform do?_ Given a smoothie, it finds the recipe.
+- _How?_ Run the smoothie through filters to extract each ingredient.
+- _Why?_ Recipes are easier to analyze, compare, and modify than the smoothie itself.
+- _How do we get the smoothie back?_ Blend the ingredients.
 
 **Think With Circles, Not Just Sinusoids**
 
-The Fourier Transform is about circular paths (not 1-d sinusoids) and Euler's 
+The Fourier Transform is about circular paths (not 1-d sinusoids) and Euler's
 formula is a clever way to generate one:
 
 ![](https://betterexplained.com/wp-content/uploads/euler/equal_paths.png)
 
 Must we use imaginary exponents to move in a circle? Nope. But it's convenient
-and compact. And sure, we can describe our path as coordinated motion in two 
-dimensions (real and imaginary), but don't forget the big picture: we're just 
+and compact. And sure, we can describe our path as coordinated motion in two
+dimensions (real and imaginary), but don't forget the big picture: we're just
 moving in a circle.
 
 **Discovering The Full Transform**
 
-The big insight: our signal is just a bunch of time spikes! If we merge the 
+The big insight: our signal is just a bunch of time spikes! If we merge the
 recipes for each time spike, we should get the recipe for the full signal.
 
 The Fourier Transform builds the recipe frequency-by-frequency:
@@ -110,7 +113,7 @@ A few notes:
 - k = current frequency we're considering (0 Hertz up to N-1 Hertz)
 - X<sub>k</sub> = amount of frequency k in the signal (amplitude and phase, a complex number)
 - The 1/N factor is usually moved to the reverse transform (going from frequencies back to time). This is allowed, though I prefer 1/N in the forward transform since it gives the actual sizes for the time spikes. You can get wild and even use 1/sqrt(N) on both transforms (going forward and back creates the 1/N factor).
-- n/N is the percent of the time we've gone through. 2 * pi * k is our speed in radians / sec. e^-ix is our backwards-moving circular path. The combination is how far we've moved, for this speed and time.
+- n/N is the percent of the time we've gone through. 2 _ pi _ k is our speed in radians / sec. e^-ix is our backwards-moving circular path. The combination is how far we've moved, for this speed and time.
 - The raw equations for the Fourier Transform just say "add the complex numbers". Many programming languages cannot handle complex numbers directly, so you convert everything to rectangular coordinates and add those.
 
 Stuart Riffle has a great interpretation of the Fourier Transform:
diff --git a/src/algorithms/math/fourier-transform/__test__/FourierTester.js b/src/algorithms/math/fourier-transform/__test__/FourierTester.js
index 84a16bf2bf..d2a2787d38 100644
--- a/src/algorithms/math/fourier-transform/__test__/FourierTester.js
+++ b/src/algorithms/math/fourier-transform/__test__/FourierTester.js
@@ -244,7 +244,7 @@ export default class FourierTester {
       const { input, output: expectedOutput } = testCase;
 
       // Try to split input signal into sequence of pure sinusoids.
-      const formattedInput = input.map(sample => sample.amplitude);
+      const formattedInput = input.map((sample) => sample.amplitude);
       const currentOutput = fourierTransform(formattedInput);
 
       // Check the signal has been split into proper amount of sub-signals.
diff --git a/src/algorithms/math/fourier-transform/fastFourierTransform.js b/src/algorithms/math/fourier-transform/fastFourierTransform.js
index 0a0f79d153..ab51cf26e4 100644
--- a/src/algorithms/math/fourier-transform/fastFourierTransform.js
+++ b/src/algorithms/math/fourier-transform/fastFourierTransform.js
@@ -46,8 +46,8 @@ export default function fastFourierTransform(inputData, inverse = false) {
   for (let blockLength = 2; blockLength <= N; blockLength *= 2) {
     const imaginarySign = inverse ? -1 : 1;
     const phaseStep = new ComplexNumber({
-      re: Math.cos(2 * Math.PI / blockLength),
-      im: imaginarySign * Math.sin(2 * Math.PI / blockLength),
+      re: Math.cos((2 * Math.PI) / blockLength),
+      im: imaginarySign * Math.sin((2 * Math.PI) / blockLength),
     });
 
     for (let blockStart = 0; blockStart < N; blockStart += blockLength) {
diff --git a/src/algorithms/math/horner-method/README.md b/src/algorithms/math/horner-method/README.md
new file mode 100644
index 0000000000..991a674deb
--- /dev/null
+++ b/src/algorithms/math/horner-method/README.md
@@ -0,0 +1,20 @@
+# Horner's Method
+
+In mathematics, Horner's method (or Horner's scheme) is an algorithm for polynomial evaluation. With this method, it is possible to evaluate a polynomial with only `n` additions and `n` multiplications. Hence, its storage requirements are `n` times the number of bits of `x`.
+
+Horner's method can be based on the following identity:
+
+![Horner's rule](https://wikimedia.org/api/rest_v1/media/math/render/svg/2a576e42d875496f8b0f0dda5ebff7c2415532e4)
+
+This identity is called _Horner's rule_.
+
+To solve the right part of the identity above, for a given `x`, we start by iterating through the polynomial from the inside out, accumulating each iteration result. After `n` iterations, with `n` being the order of the polynomial, the accumulated result gives us the polynomial evaluation. 
+
+**Using the polynomial:**
+`4 * x^4 + 2 * x^3 + 3 * x^2 + x^1 + 3`, a traditional approach to evaluate it at `x = 2`, could be representing it as an array `[3, 1, 3, 2, 4]` and iterate over it saving each iteration value at an accumulator, such as `acc += pow(x=2, index) * array[index]`. In essence, each power of a number (`pow`) operation is `n-1` multiplications. So, in this scenario, a total of `14` operations would have happened, composed of `4` additions, `5` multiplications, and `5` pows (we're assuming that each power is calculated by repeated multiplication).
+
+Now, **using the same scenario but with Horner's rule**, the polynomial can be re-written as `x * (x * (x * (4 * x + 2) + 3) + 1) + 3`, representing it as `[4, 2, 3, 1, 3]` it is possible to save the first iteration as `acc = arr[0] * (x=2) + arr[1]`, and then finish iterations for `acc *= (x=2) + arr[index]`. In the same scenario but using Horner's rule, a total of `10` operations would have happened, composed of only `4` additions and `4` multiplications.
+
+## References
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Horner%27s_method)
diff --git a/src/algorithms/math/horner-method/__test__/classicPolynome.test.js b/src/algorithms/math/horner-method/__test__/classicPolynome.test.js
new file mode 100644
index 0000000000..8cdf950d6e
--- /dev/null
+++ b/src/algorithms/math/horner-method/__test__/classicPolynome.test.js
@@ -0,0 +1,14 @@
+import classicPolynome from '../classicPolynome';
+
+describe('classicPolynome', () => {
+  it('should evaluate the polynomial for the specified value of x correctly', () => {
+    expect(classicPolynome([8], 0.1)).toBe(8);
+    expect(classicPolynome([2, 4, 2, 5], 0.555)).toBe(7.68400775);
+    expect(classicPolynome([2, 4, 2, 5], 0.75)).toBe(9.59375);
+    expect(classicPolynome([1, 1, 1, 1, 1], 1.75)).toBe(20.55078125);
+    expect(classicPolynome([15, 3.5, 0, 2, 1.42, 0.41], 0.315)).toBe(1.1367300651406251);
+    expect(classicPolynome([0, 0, 2.77, 1.42, 0.41], 1.35)).toBe(7.375325000000001);
+    expect(classicPolynome([0, 0, 2.77, 1.42, 2.3311], 1.35)).toBe(9.296425000000001);
+    expect(classicPolynome([2, 0, 0, 5.757, 5.31412, 12.3213], 3.141)).toBe(697.2731167035034);
+  });
+});
diff --git a/src/algorithms/math/horner-method/__test__/hornerMethod.test.js b/src/algorithms/math/horner-method/__test__/hornerMethod.test.js
new file mode 100644
index 0000000000..177ceb1aad
--- /dev/null
+++ b/src/algorithms/math/horner-method/__test__/hornerMethod.test.js
@@ -0,0 +1,21 @@
+import hornerMethod from '../hornerMethod';
+import classicPolynome from '../classicPolynome';
+
+describe('hornerMethod', () => {
+  it('should evaluate the polynomial for the specified value of x correctly', () => {
+    expect(hornerMethod([8], 0.1)).toBe(8);
+    expect(hornerMethod([2, 4, 2, 5], 0.555)).toBe(7.68400775);
+    expect(hornerMethod([2, 4, 2, 5], 0.75)).toBe(9.59375);
+    expect(hornerMethod([1, 1, 1, 1, 1], 1.75)).toBe(20.55078125);
+    expect(hornerMethod([15, 3.5, 0, 2, 1.42, 0.41], 0.315)).toBe(1.136730065140625);
+    expect(hornerMethod([0, 0, 2.77, 1.42, 0.41], 1.35)).toBe(7.375325000000001);
+    expect(hornerMethod([0, 0, 2.77, 1.42, 2.3311], 1.35)).toBe(9.296425000000001);
+    expect(hornerMethod([2, 0, 0, 5.757, 5.31412, 12.3213], 3.141)).toBe(697.2731167035034);
+  });
+
+  it('should evaluate the same polynomial value as classical approach', () => {
+    expect(hornerMethod([8], 0.1)).toBe(classicPolynome([8], 0.1));
+    expect(hornerMethod([2, 4, 2, 5], 0.555)).toBe(classicPolynome([2, 4, 2, 5], 0.555));
+    expect(hornerMethod([2, 4, 2, 5], 0.75)).toBe(classicPolynome([2, 4, 2, 5], 0.75));
+  });
+});
diff --git a/src/algorithms/math/horner-method/classicPolynome.js b/src/algorithms/math/horner-method/classicPolynome.js
new file mode 100644
index 0000000000..1f6aa8613c
--- /dev/null
+++ b/src/algorithms/math/horner-method/classicPolynome.js
@@ -0,0 +1,16 @@
+/**
+ * Returns the evaluation of a polynomial function at a certain point.
+ * Uses straightforward approach with powers.
+ *
+ * @param {number[]} coefficients - i.e. [4, 3, 2] for (4 * x^2 + 3 * x + 2)
+ * @param {number} xVal
+ * @return {number}
+ */
+export default function classicPolynome(coefficients, xVal) {
+  return coefficients.reverse().reduce(
+    (accumulator, currentCoefficient, index) => {
+      return accumulator + currentCoefficient * (xVal ** index);
+    },
+    0,
+  );
+}
diff --git a/src/algorithms/math/horner-method/hornerMethod.js b/src/algorithms/math/horner-method/hornerMethod.js
new file mode 100644
index 0000000000..2236170ed1
--- /dev/null
+++ b/src/algorithms/math/horner-method/hornerMethod.js
@@ -0,0 +1,16 @@
+/**
+ * Returns the evaluation of a polynomial function at a certain point.
+ * Uses Horner's rule.
+ *
+ * @param {number[]} coefficients - i.e. [4, 3, 2] for (4 * x^2 + 3 * x + 2)
+ * @param {number} xVal
+ * @return {number}
+ */
+export default function hornerMethod(coefficients, xVal) {
+  return coefficients.reduce(
+    (accumulator, currentCoefficient) => {
+      return accumulator * xVal + currentCoefficient;
+    },
+    0,
+  );
+}
diff --git a/src/algorithms/math/is-power-of-two/README.md b/src/algorithms/math/is-power-of-two/README.md
index 5b66b7c6cf..6ed1e92568 100644
--- a/src/algorithms/math/is-power-of-two/README.md
+++ b/src/algorithms/math/is-power-of-two/README.md
@@ -1,18 +1,17 @@
 # Is a power of two
 
-Given a positive integer, write a function to find if it is 
+Given a positive integer, write a function to find if it is
 a power of two or not.
 
 **Naive solution**
 
 In naive solution we just keep dividing the number by two
-unless the number becomes `1` and every time we do so we
-check that remainder after division is always `0`. Otherwise
-the number can't be a power of two.
+unless the number becomes `1` and every time we do so, we
+check that remainder after division is always `0`. Otherwise, the number can't be a power of two.
 
 **Bitwise solution**
 
-Powers of two in binary form always have just one bit.
+Powers of two in binary form always have just one bit set.
 The only exception is with a signed integer (e.g. an 8-bit
 signed integer with a value of -128 looks like: `10000000`)
 
@@ -23,8 +22,8 @@ signed integer with a value of -128 looks like: `10000000`)
 8: 1000
 ```
 
-So after checking that the number is greater than zero, 
-we can use a bitwise hack to test that one and only one 
+So after checking that the number is greater than zero,
+we can use a bitwise hack to test that one and only one
 bit is set.
 
 ```
@@ -38,11 +37,11 @@ For example for number `8` that operations will look like:
 - 0001
   ----
   0111
-  
+
   1000
 & 0111
   ----
-  0000    
+  0000
 ```
 
 ## References
diff --git a/src/algorithms/math/matrix/Matrix.js b/src/algorithms/math/matrix/Matrix.js
new file mode 100644
index 0000000000..2aee126ba2
--- /dev/null
+++ b/src/algorithms/math/matrix/Matrix.js
@@ -0,0 +1,309 @@
+/**
+ * @typedef {number} Cell
+ * @typedef {Cell[][]|Cell[][][]} Matrix
+ * @typedef {number[]} Shape
+ * @typedef {number[]} CellIndices
+ */
+
+/**
+ * Gets the matrix's shape.
+ *
+ * @param {Matrix} m
+ * @returns {Shape}
+ */
+export const shape = (m) => {
+  const shapes = [];
+  let dimension = m;
+  while (dimension && Array.isArray(dimension)) {
+    shapes.push(dimension.length);
+    dimension = (dimension.length && [...dimension][0]) || null;
+  }
+  return shapes;
+};
+
+/**
+ * Checks if matrix has a correct type.
+ *
+ * @param {Matrix} m
+ * @throws {Error}
+ */
+const validateType = (m) => {
+  if (
+    !m
+    || !Array.isArray(m)
+    || !Array.isArray(m[0])
+  ) {
+    throw new Error('Invalid matrix format');
+  }
+};
+
+/**
+ * Checks if matrix is two dimensional.
+ *
+ * @param {Matrix} m
+ * @throws {Error}
+ */
+const validate2D = (m) => {
+  validateType(m);
+  const aShape = shape(m);
+  if (aShape.length !== 2) {
+    throw new Error('Matrix is not of 2D shape');
+  }
+};
+
+/**
+ * Validates that matrices are of the same shape.
+ *
+ * @param {Matrix} a
+ * @param {Matrix} b
+ * @trows {Error}
+ */
+export const validateSameShape = (a, b) => {
+  validateType(a);
+  validateType(b);
+
+  const aShape = shape(a);
+  const bShape = shape(b);
+
+  if (aShape.length !== bShape.length) {
+    throw new Error('Matrices have different dimensions');
+  }
+
+  while (aShape.length && bShape.length) {
+    if (aShape.pop() !== bShape.pop()) {
+      throw new Error('Matrices have different shapes');
+    }
+  }
+};
+
+/**
+ * Generates the matrix of specific shape with specific values.
+ *
+ * @param {Shape} mShape - the shape of the matrix to generate
+ * @param {function({CellIndex}): Cell} fill - cell values of a generated matrix.
+ * @returns {Matrix}
+ */
+export const generate = (mShape, fill) => {
+  /**
+   * Generates the matrix recursively.
+   *
+   * @param {Shape} recShape - the shape of the matrix to generate
+   * @param {CellIndices} recIndices
+   * @returns {Matrix}
+   */
+  const generateRecursively = (recShape, recIndices) => {
+    if (recShape.length === 1) {
+      return Array(recShape[0])
+        .fill(null)
+        .map((cellValue, cellIndex) => fill([...recIndices, cellIndex]));
+    }
+    const m = [];
+    for (let i = 0; i < recShape[0]; i += 1) {
+      m.push(generateRecursively(recShape.slice(1), [...recIndices, i]));
+    }
+    return m;
+  };
+
+  return generateRecursively(mShape, []);
+};
+
+/**
+ * Generates the matrix of zeros of specified shape.
+ *
+ * @param {Shape} mShape - shape of the matrix
+ * @returns {Matrix}
+ */
+export const zeros = (mShape) => {
+  return generate(mShape, () => 0);
+};
+
+/**
+ * @param {Matrix} a
+ * @param {Matrix} b
+ * @return Matrix
+ * @throws {Error}
+ */
+export const dot = (a, b) => {
+  // Validate inputs.
+  validate2D(a);
+  validate2D(b);
+
+  // Check dimensions.
+  const aShape = shape(a);
+  const bShape = shape(b);
+  if (aShape[1] !== bShape[0]) {
+    throw new Error('Matrices have incompatible shape for multiplication');
+  }
+
+  // Perform matrix multiplication.
+  const outputShape = [aShape[0], bShape[1]];
+  const c = zeros(outputShape);
+
+  for (let bCol = 0; bCol < b[0].length; bCol += 1) {
+    for (let aRow = 0; aRow < a.length; aRow += 1) {
+      let cellSum = 0;
+      for (let aCol = 0; aCol < a[aRow].length; aCol += 1) {
+        cellSum += a[aRow][aCol] * b[aCol][bCol];
+      }
+      c[aRow][bCol] = cellSum;
+    }
+  }
+
+  return c;
+};
+
+/**
+ * Transposes the matrix.
+ *
+ * @param {Matrix} m
+ * @returns Matrix
+ * @throws {Error}
+ */
+export const t = (m) => {
+  validate2D(m);
+  const mShape = shape(m);
+  const transposed = zeros([mShape[1], mShape[0]]);
+  for (let row = 0; row < m.length; row += 1) {
+    for (let col = 0; col < m[0].length; col += 1) {
+      transposed[col][row] = m[row][col];
+    }
+  }
+  return transposed;
+};
+
+/**
+ * Traverses the matrix.
+ *
+ * @param {Matrix} m
+ * @param {function(indices: CellIndices, c: Cell)} visit
+ */
+export const walk = (m, visit) => {
+  /**
+   * Traverses the matrix recursively.
+   *
+   * @param {Matrix} recM
+   * @param {CellIndices} cellIndices
+   * @return {Matrix}
+   */
+  const recWalk = (recM, cellIndices) => {
+    const recMShape = shape(recM);
+
+    if (recMShape.length === 1) {
+      for (let i = 0; i < recM.length; i += 1) {
+        visit([...cellIndices, i], recM[i]);
+      }
+    }
+    for (let i = 0; i < recM.length; i += 1) {
+      recWalk(recM[i], [...cellIndices, i]);
+    }
+  };
+
+  recWalk(m, []);
+};
+
+/**
+ * Gets the matrix cell value at specific index.
+ *
+ * @param {Matrix} m - Matrix that contains the cell that needs to be updated
+ * @param {CellIndices} cellIndices - Array of cell indices
+ * @return {Cell}
+ */
+export const getCellAtIndex = (m, cellIndices) => {
+  // We start from the row at specific index.
+  let cell = m[cellIndices[0]];
+  // Going deeper into the next dimensions but not to the last one to preserve
+  // the pointer to the last dimension array.
+  for (let dimIdx = 1; dimIdx < cellIndices.length - 1; dimIdx += 1) {
+    cell = cell[cellIndices[dimIdx]];
+  }
+  // At this moment the cell variable points to the array at the last needed dimension.
+  return cell[cellIndices[cellIndices.length - 1]];
+};
+
+/**
+ * Update the matrix cell at specific index.
+ *
+ * @param {Matrix} m - Matrix that contains the cell that needs to be updated
+ * @param {CellIndices} cellIndices - Array of cell indices
+ * @param {Cell} cellValue - New cell value
+ */
+export const updateCellAtIndex = (m, cellIndices, cellValue) => {
+  // We start from the row at specific index.
+  let cell = m[cellIndices[0]];
+  // Going deeper into the next dimensions but not to the last one to preserve
+  // the pointer to the last dimension array.
+  for (let dimIdx = 1; dimIdx < cellIndices.length - 1; dimIdx += 1) {
+    cell = cell[cellIndices[dimIdx]];
+  }
+  // At this moment the cell variable points to the array at the last needed dimension.
+  cell[cellIndices[cellIndices.length - 1]] = cellValue;
+};
+
+/**
+ * Adds two matrices element-wise.
+ *
+ * @param {Matrix} a
+ * @param {Matrix} b
+ * @return {Matrix}
+ */
+export const add = (a, b) => {
+  validateSameShape(a, b);
+  const result = zeros(shape(a));
+
+  walk(a, (cellIndices, cellValue) => {
+    updateCellAtIndex(result, cellIndices, cellValue);
+  });
+
+  walk(b, (cellIndices, cellValue) => {
+    const currentCellValue = getCellAtIndex(result, cellIndices);
+    updateCellAtIndex(result, cellIndices, currentCellValue + cellValue);
+  });
+
+  return result;
+};
+
+/**
+ * Multiplies two matrices element-wise.
+ *
+ * @param {Matrix} a
+ * @param {Matrix} b
+ * @return {Matrix}
+ */
+export const mul = (a, b) => {
+  validateSameShape(a, b);
+  const result = zeros(shape(a));
+
+  walk(a, (cellIndices, cellValue) => {
+    updateCellAtIndex(result, cellIndices, cellValue);
+  });
+
+  walk(b, (cellIndices, cellValue) => {
+    const currentCellValue = getCellAtIndex(result, cellIndices);
+    updateCellAtIndex(result, cellIndices, currentCellValue * cellValue);
+  });
+
+  return result;
+};
+
+/**
+ * Subtract two matrices element-wise.
+ *
+ * @param {Matrix} a
+ * @param {Matrix} b
+ * @return {Matrix}
+ */
+export const sub = (a, b) => {
+  validateSameShape(a, b);
+  const result = zeros(shape(a));
+
+  walk(a, (cellIndices, cellValue) => {
+    updateCellAtIndex(result, cellIndices, cellValue);
+  });
+
+  walk(b, (cellIndices, cellValue) => {
+    const currentCellValue = getCellAtIndex(result, cellIndices);
+    updateCellAtIndex(result, cellIndices, currentCellValue - cellValue);
+  });
+
+  return result;
+};
diff --git a/src/algorithms/math/matrix/README.md b/src/algorithms/math/matrix/README.md
new file mode 100644
index 0000000000..8e084403b0
--- /dev/null
+++ b/src/algorithms/math/matrix/README.md
@@ -0,0 +1,63 @@
+# Matrices
+
+In mathematics, a **matrix** (plural **matrices**) is a rectangular array or table of numbers, symbols, or expressions, arranged in rows and columns. For example, the dimension of the matrix below is `2 × 3` (read "two by three"), because there are two rows and three columns:
+
+```
+| 1  9 -13 |
+| 20 5 -6  |
+```
+
+![An `m × n` matrix](https://upload.wikimedia.org/wikipedia/commons/b/bf/Matris.png)
+
+An `m × n` matrix: the `m` rows are horizontal, and the `n` columns are vertical. Each element of a matrix is often denoted by a variable with two subscripts. For example, <i>a<sub>2,1</sub></i> represents the element at the second row and first column of the matrix
+
+## Operations on matrices
+
+### Addition
+
+To add two matrices: add the numbers in the matching positions:
+
+![Matrices addition](https://www.mathsisfun.com/algebra/images/matrix-addition.gif)
+
+The two matrices must be the same size, i.e. the rows must match in size, and the columns must match in size.
+
+### Subtracting
+
+To subtract two matrices: subtract the numbers in the matching positions:
+
+![Matrices subtraction](https://www.mathsisfun.com/algebra/images/matrix-subtraction.gif)
+
+### Multiply by a Constant
+
+We can multiply a matrix by a constant (the value 2 in this case):
+
+![Matrices multiplication be a constant](https://www.mathsisfun.com/algebra/images/matrix-multiply-constant.gif)
+
+### Multiplying by Another Matrix
+
+To multiply a matrix by another matrix we need to do the [dot product](https://www.mathsisfun.com/algebra/vectors-dot-product.html) of rows and columns.
+
+To work out the answer for the **1st row** and **1st column**:
+
+![Matrices multiplication - 1st step](https://www.mathsisfun.com/algebra/images/matrix-multiply-a.svg)
+
+Here it is for the 1st row and 2nd column:
+
+![Matrices multiplication - 2st step](https://www.mathsisfun.com/algebra/images/matrix-multiply-b.svg)
+
+If we'll do the same for the rest of the rows and columns we'll get the following resulting matrix:
+
+![Matrices multiplication - Result](https://www.mathsisfun.com/algebra/images/matrix-multiply-c.svg)
+
+### Transposing
+
+To "transpose" a matrix, swap the rows and columns.
+
+We put a "T" in the top right-hand corner to mean transpose:
+
+![Transposing](https://www.mathsisfun.com/algebra/images/matrix-transpose.gif)
+
+## References
+
+- [Matrices on MathIsFun](https://www.mathsisfun.com/algebra/matrix-introduction.html)
+- [Matrix on Wikipedia](https://en.wikipedia.org/wiki/Matrix_(mathematics))
diff --git a/src/algorithms/math/matrix/__tests__/Matrix.test.js b/src/algorithms/math/matrix/__tests__/Matrix.test.js
new file mode 100644
index 0000000000..37dc892b0a
--- /dev/null
+++ b/src/algorithms/math/matrix/__tests__/Matrix.test.js
@@ -0,0 +1,455 @@
+import * as mtrx from '../Matrix';
+
+describe('Matrix', () => {
+  it('should throw when trying to add matrices of invalid shapes', () => {
+    expect(
+      () => mtrx.dot([0], [1]),
+    ).toThrowError('Invalid matrix format');
+    expect(
+      () => mtrx.dot([[0]], [1]),
+    ).toThrowError('Invalid matrix format');
+    expect(
+      () => mtrx.dot([[[0]]], [[1]]),
+    ).toThrowError('Matrix is not of 2D shape');
+    expect(
+      () => mtrx.dot([[0]], [[1], [2]]),
+    ).toThrowError('Matrices have incompatible shape for multiplication');
+  });
+
+  it('should calculate matrices dimensions', () => {
+    expect(mtrx.shape([])).toEqual([0]);
+
+    expect(mtrx.shape([
+      [],
+    ])).toEqual([1, 0]);
+
+    expect(mtrx.shape([
+      [0],
+    ])).toEqual([1, 1]);
+
+    expect(mtrx.shape([
+      [0, 0],
+    ])).toEqual([1, 2]);
+
+    expect(mtrx.shape([
+      [0, 0],
+      [0, 0],
+    ])).toEqual([2, 2]);
+
+    expect(mtrx.shape([
+      [0, 0, 0],
+      [0, 0, 0],
+    ])).toEqual([2, 3]);
+
+    expect(mtrx.shape([
+      [0, 0],
+      [0, 0],
+      [0, 0],
+    ])).toEqual([3, 2]);
+
+    expect(mtrx.shape([
+      [0, 0, 0],
+      [0, 0, 0],
+      [0, 0, 0],
+    ])).toEqual([3, 3]);
+
+    expect(mtrx.shape([
+      [0],
+      [0],
+      [0],
+    ])).toEqual([3, 1]);
+
+    expect(mtrx.shape([
+      [[0], [0], [0]],
+      [[0], [0], [0]],
+      [[0], [0], [0]],
+    ])).toEqual([3, 3, 1]);
+
+    expect(mtrx.shape([
+      [[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]],
+    ])).toEqual([3, 3, 3]);
+  });
+
+  it('should generate the matrix of zeros', () => {
+    expect(mtrx.zeros([1, 0])).toEqual([
+      [],
+    ]);
+
+    expect(mtrx.zeros([1, 1])).toEqual([
+      [0],
+    ]);
+
+    expect(mtrx.zeros([1, 3])).toEqual([
+      [0, 0, 0],
+    ]);
+
+    expect(mtrx.zeros([3, 3])).toEqual([
+      [0, 0, 0],
+      [0, 0, 0],
+      [0, 0, 0],
+    ]);
+
+    expect(mtrx.zeros([3, 3, 1])).toEqual([
+      [[0], [0], [0]],
+      [[0], [0], [0]],
+      [[0], [0], [0]],
+    ]);
+  });
+
+  it('should generate the matrix with custom values', () => {
+    expect(mtrx.generate([1, 0], () => 1)).toEqual([
+      [],
+    ]);
+
+    expect(mtrx.generate([1, 1], () => 1)).toEqual([
+      [1],
+    ]);
+
+    expect(mtrx.generate([1, 3], () => 1)).toEqual([
+      [1, 1, 1],
+    ]);
+
+    expect(mtrx.generate([3, 3], () => 1)).toEqual([
+      [1, 1, 1],
+      [1, 1, 1],
+      [1, 1, 1],
+    ]);
+
+    expect(mtrx.generate([3, 3, 1], () => 1)).toEqual([
+      [[1], [1], [1]],
+      [[1], [1], [1]],
+      [[1], [1], [1]],
+    ]);
+  });
+
+  it('should generate a custom matrix based on specific cell indices', () => {
+    const indicesCallback = jest.fn((indices) => {
+      return indices[0] * 10 + indices[1];
+    });
+    const m = mtrx.generate([3, 3], indicesCallback);
+
+    expect(indicesCallback).toHaveBeenCalledTimes(3 * 3);
+    expect(indicesCallback.mock.calls[0][0]).toEqual([0, 0]);
+    expect(indicesCallback.mock.calls[1][0]).toEqual([0, 1]);
+    expect(indicesCallback.mock.calls[2][0]).toEqual([0, 2]);
+    expect(indicesCallback.mock.calls[3][0]).toEqual([1, 0]);
+    expect(indicesCallback.mock.calls[4][0]).toEqual([1, 1]);
+    expect(indicesCallback.mock.calls[5][0]).toEqual([1, 2]);
+    expect(indicesCallback.mock.calls[6][0]).toEqual([2, 0]);
+    expect(indicesCallback.mock.calls[7][0]).toEqual([2, 1]);
+    expect(indicesCallback.mock.calls[8][0]).toEqual([2, 2]);
+    expect(m).toEqual([
+      [0, 1, 2],
+      [10, 11, 12],
+      [20, 21, 22],
+    ]);
+  });
+
+  it('should multiply two matrices', () => {
+    let c;
+    c = mtrx.dot(
+      [
+        [1, 2],
+        [3, 4],
+      ],
+      [
+        [5, 6],
+        [7, 8],
+      ],
+    );
+    expect(mtrx.shape(c)).toEqual([2, 2]);
+    expect(c).toEqual([
+      [19, 22],
+      [43, 50],
+    ]);
+
+    c = mtrx.dot(
+      [
+        [1, 2],
+        [3, 4],
+      ],
+      [
+        [5],
+        [6],
+      ],
+    );
+    expect(mtrx.shape(c)).toEqual([2, 1]);
+    expect(c).toEqual([
+      [17],
+      [39],
+    ]);
+
+    c = mtrx.dot(
+      [
+        [1, 2, 3],
+        [4, 5, 6],
+      ],
+      [
+        [7, 8],
+        [9, 10],
+        [11, 12],
+      ],
+    );
+    expect(mtrx.shape(c)).toEqual([2, 2]);
+    expect(c).toEqual([
+      [58, 64],
+      [139, 154],
+    ]);
+
+    c = mtrx.dot(
+      [
+        [3, 4, 2],
+      ],
+      [
+        [13, 9, 7, 5],
+        [8, 7, 4, 6],
+        [6, 4, 0, 3],
+      ],
+    );
+    expect(mtrx.shape(c)).toEqual([1, 4]);
+    expect(c).toEqual([
+      [83, 63, 37, 45],
+    ]);
+  });
+
+  it('should transpose matrices', () => {
+    expect(mtrx.t([[1, 2, 3]])).toEqual([
+      [1],
+      [2],
+      [3],
+    ]);
+
+    expect(mtrx.t([
+      [1],
+      [2],
+      [3],
+    ])).toEqual([
+      [1, 2, 3],
+    ]);
+
+    expect(mtrx.t([
+      [1, 2, 3],
+      [4, 5, 6],
+    ])).toEqual([
+      [1, 4],
+      [2, 5],
+      [3, 6],
+    ]);
+
+    expect(mtrx.t([
+      [1, 2, 3],
+      [4, 5, 6],
+      [7, 8, 9],
+    ])).toEqual([
+      [1, 4, 7],
+      [2, 5, 8],
+      [3, 6, 9],
+    ]);
+  });
+
+  it('should throw when trying to transpose non 2D matrix', () => {
+    expect(() => {
+      mtrx.t([[[1]]]);
+    }).toThrowError('Matrix is not of 2D shape');
+  });
+
+  it('should add two matrices', () => {
+    expect(mtrx.add([[1]], [[2]])).toEqual([[3]]);
+
+    expect(mtrx.add(
+      [[1, 2, 3]],
+      [[4, 5, 6]],
+    ))
+      .toEqual(
+        [[5, 7, 9]],
+      );
+
+    expect(mtrx.add(
+      [[1], [2], [3]],
+      [[4], [5], [6]],
+    ))
+      .toEqual(
+        [[5], [7], [9]],
+      );
+
+    expect(mtrx.add(
+      [
+        [1, 2, 3],
+        [4, 5, 6],
+        [7, 8, 9],
+      ],
+      [
+        [10, 11, 12],
+        [13, 14, 15],
+        [16, 17, 18],
+      ],
+    ))
+      .toEqual(
+        [
+          [11, 13, 15],
+          [17, 19, 21],
+          [23, 25, 27],
+        ],
+      );
+
+    expect(mtrx.add(
+      [
+        [[1], [2], [3]],
+        [[4], [5], [6]],
+        [[7], [8], [9]],
+      ],
+      [
+        [[10], [11], [12]],
+        [[13], [14], [15]],
+        [[16], [17], [18]],
+      ],
+    ))
+      .toEqual(
+        [
+          [[11], [13], [15]],
+          [[17], [19], [21]],
+          [[23], [25], [27]],
+        ],
+      );
+  });
+
+  it('should throw when trying to add matrices of different shape', () => {
+    expect(() => mtrx.add([[0]], [[[0]]])).toThrowError(
+      'Matrices have different dimensions',
+    );
+
+    expect(() => mtrx.add([[0]], [[0, 0]])).toThrowError(
+      'Matrices have different shapes',
+    );
+  });
+
+  it('should do element wise multiplication two matrices', () => {
+    expect(mtrx.mul([[2]], [[3]])).toEqual([[6]]);
+
+    expect(mtrx.mul(
+      [[1, 2, 3]],
+      [[4, 5, 6]],
+    ))
+      .toEqual(
+        [[4, 10, 18]],
+      );
+
+    expect(mtrx.mul(
+      [[1], [2], [3]],
+      [[4], [5], [6]],
+    ))
+      .toEqual(
+        [[4], [10], [18]],
+      );
+
+    expect(mtrx.mul(
+      [
+        [1, 2],
+        [3, 4],
+      ],
+      [
+        [5, 6],
+        [7, 8],
+      ],
+    ))
+      .toEqual(
+        [
+          [5, 12],
+          [21, 32],
+        ],
+      );
+
+    expect(mtrx.mul(
+      [
+        [[1], [2]],
+        [[3], [4]],
+      ],
+      [
+        [[5], [6]],
+        [[7], [8]],
+      ],
+    ))
+      .toEqual(
+        [
+          [[5], [12]],
+          [[21], [32]],
+        ],
+      );
+  });
+
+  it('should throw when trying to multiply matrices element-wise of different shape', () => {
+    expect(() => mtrx.mul([[0]], [[[0]]])).toThrowError(
+      'Matrices have different dimensions',
+    );
+
+    expect(() => mtrx.mul([[0]], [[0, 0]])).toThrowError(
+      'Matrices have different shapes',
+    );
+  });
+
+  it('should do element wise subtraction two matrices', () => {
+    expect(mtrx.sub([[3]], [[2]])).toEqual([[1]]);
+
+    expect(mtrx.sub(
+      [[10, 12, 14]],
+      [[4, 5, 6]],
+    ))
+      .toEqual(
+        [[6, 7, 8]],
+      );
+
+    expect(mtrx.sub(
+      [[[10], [12], [14]]],
+      [[[4], [5], [6]]],
+    ))
+      .toEqual(
+        [[[6], [7], [8]]],
+      );
+
+    expect(mtrx.sub(
+      [
+        [10, 20],
+        [30, 40],
+      ],
+      [
+        [5, 6],
+        [7, 8],
+      ],
+    ))
+      .toEqual(
+        [
+          [5, 14],
+          [23, 32],
+        ],
+      );
+
+    expect(mtrx.sub(
+      [
+        [[10], [20]],
+        [[30], [40]],
+      ],
+      [
+        [[5], [6]],
+        [[7], [8]],
+      ],
+    ))
+      .toEqual(
+        [
+          [[5], [14]],
+          [[23], [32]],
+        ],
+      );
+  });
+
+  it('should throw when trying to subtract matrices element-wise of different shape', () => {
+    expect(() => mtrx.sub([[0]], [[[0]]])).toThrowError(
+      'Matrices have different dimensions',
+    );
+
+    expect(() => mtrx.sub([[0]], [[0, 0]])).toThrowError(
+      'Matrices have different shapes',
+    );
+  });
+});
diff --git a/src/algorithms/math/pascal-triangle/pascalTriangle.js b/src/algorithms/math/pascal-triangle/pascalTriangle.js
index 5842e720e4..35686fa5b0 100644
--- a/src/algorithms/math/pascal-triangle/pascalTriangle.js
+++ b/src/algorithms/math/pascal-triangle/pascalTriangle.js
@@ -9,7 +9,7 @@ export default function pascalTriangle(lineNumber) {
 
   for (let numIndex = 1; numIndex < currentLineSize; numIndex += 1) {
     // See explanation of this formula in README.
-    currentLine[numIndex] = currentLine[numIndex - 1] * (lineNumber - numIndex + 1) / numIndex;
+    currentLine[numIndex] = (currentLine[numIndex - 1] * (lineNumber - numIndex + 1)) / numIndex;
   }
 
   return currentLine;
diff --git a/src/algorithms/math/prime-factors/README.md b/src/algorithms/math/prime-factors/README.md
new file mode 100644
index 0000000000..d8e0f96e3f
--- /dev/null
+++ b/src/algorithms/math/prime-factors/README.md
@@ -0,0 +1,37 @@
+# Prime Factors
+
+_Read this in other languages:_
+[简体中文](README.zh-CN.md).
+
+**Prime number** is a whole number greater than `1` that **cannot** be made by multiplying other whole numbers. The first few prime numbers are: `2`, `3`, `5`, `7`, `11`, `13`, `17`, `19` and so on.
+
+If we **can** make it by multiplying other whole numbers it is a **Composite Number**.
+
+![Composite numbers](https://www.mathsisfun.com/numbers/images/prime-composite.svg)
+
+_Image source: [Math is Fun](https://www.mathsisfun.com/prime-factorization.html)_
+
+**Prime factors** are those [prime numbers](https://en.wikipedia.org/wiki/Prime_number) which multiply together to give the original number. For example `39` will have prime factors of `3` and `13` which are also prime numbers. Another example is `15` whose prime factors are `3` and `5`.
+
+![Factors](https://www.mathsisfun.com/numbers/images/factor-2x3.svg)
+
+_Image source: [Math is Fun](https://www.mathsisfun.com/prime-factorization.html)_
+
+## Finding the prime factors and their count accurately
+
+The approach is to keep on dividing the natural number `n` by indexes from `i = 2` to `i = n` (by prime indexes only). The value of `n` is being overridden by `(n / i)` on each iteration.
+
+The time complexity till now is `O(n)` in the worst case scenario since the loop runs from index `i = 2` to `i = n`. This time complexity can be reduced from `O(n)` to `O(sqrt(n))`. The optimisation is achievable when loop runs from `i = 2` to `i = sqrt(n)`. Now, we go only till `O(sqrt(n))` because when `i` becomes greater than `sqrt(n)`, we have the confirmation that there is no index `i` left which can divide `n` completely other than `n` itself.
+
+## Hardy-Ramanujan formula for approximate calculation of prime-factor count
+
+In 1917, a theorem was formulated by G.H Hardy and Srinivasa Ramanujan which states that the normal order of the number `ω(n)` of distinct prime factors of a number `n` is `log(log(n))`.
+
+Roughly speaking, this means that most numbers have about this number of distinct prime factors.
+
+## References
+
+- [Prime numbers on Math is Fun](https://www.mathsisfun.com/prime-factorization.html)
+- [Prime numbers on Wikipedia](https://en.wikipedia.org/wiki/Prime_number)
+- [Hardy–Ramanujan theorem on Wikipedia](https://en.wikipedia.org/wiki/Hardy%E2%80%93Ramanujan_theorem)
+- [Prime factorization of a number on Youtube](https://www.youtube.com/watch?v=6PDtgHhpCHo&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=82)
diff --git a/src/algorithms/math/prime-factors/README.zh-CN.md b/src/algorithms/math/prime-factors/README.zh-CN.md
new file mode 100644
index 0000000000..c6ab82e39b
--- /dev/null
+++ b/src/algorithms/math/prime-factors/README.zh-CN.md
@@ -0,0 +1,38 @@
+# 质数因子
+
+_Read this in other languages:_
+[english](README.md).
+
+**质数** 是一个比 `1` 大的整数，且 **不能**由其它整数相乘得出。前几个质数是: `2`, `3`, `5`, `7`, `11`, `13`, `17`, `19`,依此类推。
+
+如果我们**能**通过其它整数相乘得出，我们则称它为**合数**
+
+![Composite numbers](https://www.mathsisfun.com/numbers/images/prime-composite.svg)
+
+_Image source: [Math is Fun](https://www.mathsisfun.com/prime-factorization.html)_
+
+
+**质数因子**是那些相乘得到原始数的[质数](https://en.wikipedia.org/wiki/Prime_number)。例如`39`的质数因子是`3`和`13`，`15`的质数因子是`3`和`5`。
+
+![Factors](https://www.mathsisfun.com/numbers/images/factor-2x3.svg)
+
+_Image source: [Math is Fun](https://www.mathsisfun.com/prime-factorization.html)_
+
+## 正确计算所有的质数因子及其数量
+
+这个方法将自然数`n`从`i = 2`除到`i = n`（仅按质数索引）。且每次循环后`n`的值被`(n / i)`的值替换。
+
+在最坏的情况下，即循环从`i = 2`执行到 `i = n`，上述方法的时间复杂度为`O(n)`。时间复杂度其实可以从`O(n)`减少到`O(sqrt(n))`，通过减少循环的执行次数，从`i = 2`执行到 `i = sqrt(n)`。因为可以确认，当`i`大于`sqrt(n)`时，除了`n`本身，再没有数可以被整除了。
+
+## Hardy-Ramanujan公式用于计算质数因子的个数
+
+1917年，G.H Hardy和Srinivasa Ramanujan提出了一个定理，该定理指出，自然数 `n` 的不同素数的数 `ω(n)` 的正态次序是`log(log(n))`。
+
+粗略地讲，这意味着大多数数字具有这个数量的质数因子。
+
+## References
+
+- [Prime numbers on Math is Fun](https://www.mathsisfun.com/prime-factorization.html)
+- [Prime numbers on Wikipedia](https://en.wikipedia.org/wiki/Prime_number)
+- [Hardy–Ramanujan theorem on Wikipedia](https://en.wikipedia.org/wiki/Hardy%E2%80%93Ramanujan_theorem)
+- [Prime factorization of a number on Youtube](https://www.youtube.com/watch?v=6PDtgHhpCHo&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=82)
diff --git a/src/algorithms/math/prime-factors/__test__/primeFactors.test.js b/src/algorithms/math/prime-factors/__test__/primeFactors.test.js
new file mode 100644
index 0000000000..92c94d1f6f
--- /dev/null
+++ b/src/algorithms/math/prime-factors/__test__/primeFactors.test.js
@@ -0,0 +1,87 @@
+import {
+  primeFactors,
+  hardyRamanujan,
+} from '../primeFactors';
+
+/**
+ * Calculates the error between exact and approximate prime factor counts.
+ * @param {number} exactCount
+ * @param {number} approximateCount
+ * @returns {number} - approximation error (percentage).
+ */
+function approximationError(exactCount, approximateCount) {
+  return (Math.abs((exactCount - approximateCount) / exactCount) * 100);
+}
+
+describe('primeFactors', () => {
+  it('should find prime factors', () => {
+    expect(primeFactors(1)).toEqual([]);
+    expect(primeFactors(2)).toEqual([2]);
+    expect(primeFactors(3)).toEqual([3]);
+    expect(primeFactors(4)).toEqual([2, 2]);
+    expect(primeFactors(14)).toEqual([2, 7]);
+    expect(primeFactors(40)).toEqual([2, 2, 2, 5]);
+    expect(primeFactors(54)).toEqual([2, 3, 3, 3]);
+    expect(primeFactors(100)).toEqual([2, 2, 5, 5]);
+    expect(primeFactors(156)).toEqual([2, 2, 3, 13]);
+    expect(primeFactors(273)).toEqual([3, 7, 13]);
+    expect(primeFactors(300)).toEqual([2, 2, 3, 5, 5]);
+    expect(primeFactors(980)).toEqual([2, 2, 5, 7, 7]);
+    expect(primeFactors(1000)).toEqual([2, 2, 2, 5, 5, 5]);
+    expect(primeFactors(52734)).toEqual([2, 3, 11, 17, 47]);
+    expect(primeFactors(343434)).toEqual([2, 3, 7, 13, 17, 37]);
+    expect(primeFactors(456745)).toEqual([5, 167, 547]);
+    expect(primeFactors(510510)).toEqual([2, 3, 5, 7, 11, 13, 17]);
+    expect(primeFactors(8735463)).toEqual([3, 3, 11, 88237]);
+    expect(primeFactors(873452453)).toEqual([149, 1637, 3581]);
+  });
+
+  it('should give approximate prime factors count using Hardy-Ramanujan theorem', () => {
+    expect(hardyRamanujan(2)).toBeCloseTo(-0.366, 2);
+    expect(hardyRamanujan(4)).toBeCloseTo(0.326, 2);
+    expect(hardyRamanujan(40)).toBeCloseTo(1.305, 2);
+    expect(hardyRamanujan(156)).toBeCloseTo(1.6193, 2);
+    expect(hardyRamanujan(980)).toBeCloseTo(1.929, 2);
+    expect(hardyRamanujan(52734)).toBeCloseTo(2.386, 2);
+    expect(hardyRamanujan(343434)).toBeCloseTo(2.545, 2);
+    expect(hardyRamanujan(456745)).toBeCloseTo(2.567, 2);
+    expect(hardyRamanujan(510510)).toBeCloseTo(2.575, 2);
+    expect(hardyRamanujan(8735463)).toBeCloseTo(2.771, 2);
+    expect(hardyRamanujan(873452453)).toBeCloseTo(3.024, 2);
+  });
+
+  it('should give correct deviation between exact and approx counts', () => {
+    expect(approximationError(primeFactors(2).length, hardyRamanujan(2)))
+      .toBeCloseTo(136.651, 2);
+
+    expect(approximationError(primeFactors(4).length, hardyRamanujan(2)))
+      .toBeCloseTo(118.325, 2);
+
+    expect(approximationError(primeFactors(40).length, hardyRamanujan(2)))
+      .toBeCloseTo(109.162, 2);
+
+    expect(approximationError(primeFactors(156).length, hardyRamanujan(2)))
+      .toBeCloseTo(109.162, 2);
+
+    expect(approximationError(primeFactors(980).length, hardyRamanujan(2)))
+      .toBeCloseTo(107.330, 2);
+
+    expect(approximationError(primeFactors(52734).length, hardyRamanujan(52734)))
+      .toBeCloseTo(52.274, 2);
+
+    expect(approximationError(primeFactors(343434).length, hardyRamanujan(343434)))
+      .toBeCloseTo(57.578, 2);
+
+    expect(approximationError(primeFactors(456745).length, hardyRamanujan(456745)))
+      .toBeCloseTo(14.420, 2);
+
+    expect(approximationError(primeFactors(510510).length, hardyRamanujan(510510)))
+      .toBeCloseTo(63.201, 2);
+
+    expect(approximationError(primeFactors(8735463).length, hardyRamanujan(8735463)))
+      .toBeCloseTo(30.712, 2);
+
+    expect(approximationError(primeFactors(873452453).length, hardyRamanujan(873452453)))
+      .toBeCloseTo(0.823, 2);
+  });
+});
diff --git a/src/algorithms/math/prime-factors/primeFactors.js b/src/algorithms/math/prime-factors/primeFactors.js
new file mode 100644
index 0000000000..691436c49e
--- /dev/null
+++ b/src/algorithms/math/prime-factors/primeFactors.js
@@ -0,0 +1,42 @@
+/**
+ * Finds prime factors of a number.
+ *
+ * @param {number} n - the number that is going to be split into prime factors.
+ * @returns {number[]} - array of prime factors.
+ */
+export function primeFactors(n) {
+  // Clone n to avoid function arguments override.
+  let nn = n;
+
+  // Array that stores the all the prime factors.
+  const factors = [];
+
+  // Running the loop till sqrt(n) instead of n to optimise time complexity from O(n) to O(sqrt(n)).
+  for (let factor = 2; factor <= Math.sqrt(nn); factor += 1) {
+    // Check that factor divides n without a reminder.
+    while (nn % factor === 0) {
+      // Overriding the value of n.
+      nn /= factor;
+      // Saving the factor.
+      factors.push(factor);
+    }
+  }
+
+  // The ultimate reminder should be a last prime factor,
+  // unless it is not 1 (since 1 is not a prime number).
+  if (nn !== 1) {
+    factors.push(nn);
+  }
+
+  return factors;
+}
+
+/**
+ * Hardy-Ramanujan approximation of prime factors count.
+ *
+ * @param {number} n
+ * @returns {number} - approximate number of prime factors.
+ */
+export function hardyRamanujan(n) {
+  return Math.log(Math.log(n));
+}
diff --git a/src/algorithms/math/radian/__test__/degreeToRadian.test.js b/src/algorithms/math/radian/__test__/degreeToRadian.test.js
index 71a14d0516..dbf775168f 100644
--- a/src/algorithms/math/radian/__test__/degreeToRadian.test.js
+++ b/src/algorithms/math/radian/__test__/degreeToRadian.test.js
@@ -6,7 +6,7 @@ describe('degreeToRadian', () => {
     expect(degreeToRadian(45)).toBe(Math.PI / 4);
     expect(degreeToRadian(90)).toBe(Math.PI / 2);
     expect(degreeToRadian(180)).toBe(Math.PI);
-    expect(degreeToRadian(270)).toBe(3 * Math.PI / 2);
+    expect(degreeToRadian(270)).toBe((3 * Math.PI) / 2);
     expect(degreeToRadian(360)).toBe(2 * Math.PI);
   });
 });
diff --git a/src/algorithms/math/radian/__test__/radianToDegree.test.js b/src/algorithms/math/radian/__test__/radianToDegree.test.js
index b6fe7a1210..92f8b906d3 100644
--- a/src/algorithms/math/radian/__test__/radianToDegree.test.js
+++ b/src/algorithms/math/radian/__test__/radianToDegree.test.js
@@ -6,7 +6,7 @@ describe('radianToDegree', () => {
     expect(radianToDegree(Math.PI / 4)).toBe(45);
     expect(radianToDegree(Math.PI / 2)).toBe(90);
     expect(radianToDegree(Math.PI)).toBe(180);
-    expect(radianToDegree(3 * Math.PI / 2)).toBe(270);
+    expect(radianToDegree((3 * Math.PI) / 2)).toBe(270);
     expect(radianToDegree(2 * Math.PI)).toBe(360);
   });
 });
diff --git a/src/algorithms/math/square-root/README.md b/src/algorithms/math/square-root/README.md
new file mode 100644
index 0000000000..7f9713b5f6
--- /dev/null
+++ b/src/algorithms/math/square-root/README.md
@@ -0,0 +1,62 @@
+# Square Root (Newton's Method)
+
+In numerical analysis, a branch of mathematics, there are several square root 
+algorithms or methods of computing the principal square root of a non-negative real 
+number. As, generally, the roots of a function cannot be computed exactly.
+The root-finding algorithms provide approximations to roots expressed as floating
+point numbers.
+
+Finding ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/bff86975b0e7944720b3e635c53c22c032a7a6f1) is
+the same as solving the equation ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6cf57722151ef19ba1ca918d702b95c335e21cad) for a
+positive `x`. Therefore, any general numerical root-finding algorithm can be used.
+
+**Newton's method** (also known as the Newton–Raphson method), named after 
+_Isaac Newton_ and _Joseph Raphson_, is one example of a root-finding algorithm. It is a 
+method for finding successively better approximations to the roots of a real-valued function.
+
+Let's start by explaining the general idea of Newton's method and then apply it to our particular
+case with finding a square root of the number.
+
+## Newton's Method General Idea
+
+The Newton–Raphson method in one variable is implemented as follows:
+
+The method starts with a function `f` defined over the real numbers `x`, the function's derivative `f'`, and an 
+initial guess `x0` for a root of the function `f`. If the function satisfies the assumptions made in the derivation 
+of the formula and the initial guess is close, then a better approximation `x1` is:
+
+![](https://wikimedia.org/api/rest_v1/media/math/render/svg/52c50eca0b7c4d64ef2fdca678665b73e944cb84)
+
+Geometrically, `(x1, 0)` is the intersection of the `x`-axis and the tangent of 
+the graph of `f` at `(x0, f (x0))`.
+
+The process is repeated as:
+
+![](https://wikimedia.org/api/rest_v1/media/math/render/svg/710c11b9ec4568d1cfff49b7c7d41e0a7829a736)
+
+until a sufficiently accurate value is reached.
+
+![](https://upload.wikimedia.org/wikipedia/commons/e/e0/NewtonIteration_Ani.gif)
+
+## Newton's Method of Finding a Square Root
+
+As it was mentioned above, finding ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/bff86975b0e7944720b3e635c53c22c032a7a6f1) is
+the same as solving the equation ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6cf57722151ef19ba1ca918d702b95c335e21cad) for a
+positive `x`.
+
+The derivative of the function `f(x)` in case of square root problem is `2x`.
+
+After applying the Newton's formula (see above) we get the following equation for our algorithm iterations:
+
+```text
+x := x - (x² - S) / (2x)
+```
+
+The `x² − S` above is how far away `x²` is from where it needs to be, and the 
+division by `2x` is the derivative of `x²`, to scale how much we adjust `x` by how 
+quickly `x²` is changing.
+
+## References
+
+- [Methods of computing square roots on Wikipedia](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots)
+- [Newton's method on Wikipedia](https://en.wikipedia.org/wiki/Newton%27s_method)
diff --git a/src/algorithms/math/square-root/__test__/squareRoot.test.js b/src/algorithms/math/square-root/__test__/squareRoot.test.js
new file mode 100644
index 0000000000..9679d1a1c5
--- /dev/null
+++ b/src/algorithms/math/square-root/__test__/squareRoot.test.js
@@ -0,0 +1,69 @@
+import squareRoot from '../squareRoot';
+
+describe('squareRoot', () => {
+  it('should throw for negative numbers', () => {
+    function failingSquareRoot() {
+      squareRoot(-5);
+    }
+    expect(failingSquareRoot).toThrow();
+  });
+
+  it('should correctly calculate square root with default tolerance', () => {
+    expect(squareRoot(0)).toBe(0);
+    expect(squareRoot(1)).toBe(1);
+    expect(squareRoot(2)).toBe(1);
+    expect(squareRoot(3)).toBe(2);
+    expect(squareRoot(4)).toBe(2);
+    expect(squareRoot(15)).toBe(4);
+    expect(squareRoot(16)).toBe(4);
+    expect(squareRoot(256)).toBe(16);
+    expect(squareRoot(473)).toBe(22);
+    expect(squareRoot(14723)).toBe(121);
+  });
+
+  it('should correctly calculate square root for integers with custom tolerance', () => {
+    let tolerance = 1;
+
+    expect(squareRoot(0, tolerance)).toBe(0);
+    expect(squareRoot(1, tolerance)).toBe(1);
+    expect(squareRoot(2, tolerance)).toBe(1.4);
+    expect(squareRoot(3, tolerance)).toBe(1.8);
+    expect(squareRoot(4, tolerance)).toBe(2);
+    expect(squareRoot(15, tolerance)).toBe(3.9);
+    expect(squareRoot(16, tolerance)).toBe(4);
+    expect(squareRoot(256, tolerance)).toBe(16);
+    expect(squareRoot(473, tolerance)).toBe(21.7);
+    expect(squareRoot(14723, tolerance)).toBe(121.3);
+
+    tolerance = 3;
+
+    expect(squareRoot(0, tolerance)).toBe(0);
+    expect(squareRoot(1, tolerance)).toBe(1);
+    expect(squareRoot(2, tolerance)).toBe(1.414);
+    expect(squareRoot(3, tolerance)).toBe(1.732);
+    expect(squareRoot(4, tolerance)).toBe(2);
+    expect(squareRoot(15, tolerance)).toBe(3.873);
+    expect(squareRoot(16, tolerance)).toBe(4);
+    expect(squareRoot(256, tolerance)).toBe(16);
+    expect(squareRoot(473, tolerance)).toBe(21.749);
+    expect(squareRoot(14723, tolerance)).toBe(121.338);
+
+    tolerance = 10;
+
+    expect(squareRoot(0, tolerance)).toBe(0);
+    expect(squareRoot(1, tolerance)).toBe(1);
+    expect(squareRoot(2, tolerance)).toBe(1.4142135624);
+    expect(squareRoot(3, tolerance)).toBe(1.7320508076);
+    expect(squareRoot(4, tolerance)).toBe(2);
+    expect(squareRoot(15, tolerance)).toBe(3.8729833462);
+    expect(squareRoot(16, tolerance)).toBe(4);
+    expect(squareRoot(256, tolerance)).toBe(16);
+    expect(squareRoot(473, tolerance)).toBe(21.7485631709);
+    expect(squareRoot(14723, tolerance)).toBe(121.3383698588);
+  });
+
+  it('should correctly calculate square root for integers with custom tolerance', () => {
+    expect(squareRoot(4.5, 10)).toBe(2.1213203436);
+    expect(squareRoot(217.534, 10)).toBe(14.7490338667);
+  });
+});
diff --git a/src/algorithms/math/square-root/squareRoot.js b/src/algorithms/math/square-root/squareRoot.js
new file mode 100644
index 0000000000..19779bf34c
--- /dev/null
+++ b/src/algorithms/math/square-root/squareRoot.js
@@ -0,0 +1,40 @@
+/**
+ * Calculates the square root of the number with given tolerance (precision)
+ * by using Newton's method.
+ *
+ * @param number - the number we want to find a square root for.
+ * @param [tolerance] - how many precise numbers after the floating point we want to get.
+ * @return {number}
+ */
+export default function squareRoot(number, tolerance = 0) {
+  // For now we won't support operations that involves manipulation with complex numbers.
+  if (number < 0) {
+    throw new Error('The method supports only positive integers');
+  }
+
+  // Handle edge case with finding the square root of zero.
+  if (number === 0) {
+    return 0;
+  }
+
+  // We will start approximation from value 1.
+  let root = 1;
+
+  // Delta is a desired distance between the number and the square of the root.
+  // - if tolerance=0 then delta=1
+  // - if tolerance=1 then delta=0.1
+  // - if tolerance=2 then delta=0.01
+  // - and so on...
+  const requiredDelta = 1 / (10 ** tolerance);
+
+  // Approximating the root value to the point when we get a desired precision.
+  while (Math.abs(number - (root ** 2)) > requiredDelta) {
+    // Newton's method reduces in this case to the so-called Babylonian method.
+    // These methods generally yield approximate results, but can be made arbitrarily
+    // precise by increasing the number of calculation steps.
+    root -= ((root ** 2) - number) / (2 * root);
+  }
+
+  // Cut off undesired floating digits and return the root value.
+  return Math.round(root * (10 ** tolerance)) / (10 ** tolerance);
+}
diff --git a/src/algorithms/ml/k-means/README.md b/src/algorithms/ml/k-means/README.md
new file mode 100644
index 0000000000..fb58c1bfa4
--- /dev/null
+++ b/src/algorithms/ml/k-means/README.md
@@ -0,0 +1,40 @@
+# k-Means Algorithm
+
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
+The **k-Means algorithm** is an unsupervised Machine Learning algorithm. It's a clustering algorithm, which groups the sample data on the basis of similarity between dimensions of vectors.
+
+In k-Means classification, the output is a set of classes assigned to each vector. Each cluster location is continuously optimized in order to get the accurate locations of each cluster such that they represent each group clearly.
+
+The idea is to calculate the similarity between cluster location and data vectors, and reassign clusters based on it. [Euclidean distance](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-distance) is used mostly for this task.
+
+![Euclidean distance between two points](https://upload.wikimedia.org/wikipedia/commons/5/55/Euclidean_distance_2d.svg)
+
+_Image source: [Wikipedia](https://en.wikipedia.org/wiki/Euclidean_distance)_
+
+The algorithm is as follows:
+
+1. Check for errors like invalid/inconsistent data
+2. Initialize the `k` cluster locations with initial/random `k` points
+3. Calculate the distance of each data point from each cluster
+4. Assign the cluster label of each data point equal to that of the cluster at its minimum distance
+5. Calculate the centroid of each cluster based on the data points it contains
+6. Repeat each of the above steps until the centroid locations are varying
+
+Here is a visualization of k-Means clustering for better understanding:
+
+![KNN Visualization 1](https://upload.wikimedia.org/wikipedia/commons/e/ea/K-means_convergence.gif)
+
+_Image source: [Wikipedia](https://en.wikipedia.org/wiki/K-means_clustering)_
+
+The centroids are moving continuously in order to create better distinction between the different set of data points. As we can see, after a few iterations, the difference in centroids is quite low between iterations. For example between iterations `13` and `14` the difference is quite small because there the optimizer is tuning boundary cases.
+
+## Code Examples
+
+- [kMeans.js](./kMeans.js)
+- [kMeans.test.js](./__test__/kMeans.test.js) (test cases)
+
+## References
+
+- [k-Means neighbors algorithm on Wikipedia](https://en.wikipedia.org/wiki/K-means_clustering)
diff --git a/src/algorithms/ml/k-means/README.pt-BR.md b/src/algorithms/ml/k-means/README.pt-BR.md
new file mode 100644
index 0000000000..d814ac19bc
--- /dev/null
+++ b/src/algorithms/ml/k-means/README.pt-BR.md
@@ -0,0 +1,35 @@
+# Algoritmo k-Means
+
+_Leia isso em outros idiomas:_
+[_English_](README.md)
+
+O **algoritmo k-Means** é um algoritmo de aprendizado de máquina não supervisionado. É um algoritmo de agrupamento, que agrupa os dados da amostra com base na semelhança entre as dimensões dos vetores.
+
+Na classificação k-Means, a saída é um conjunto de classes atribuídas a cada vetor. Cada localização de cluster é continuamente otimizada para obter as localizações precisas de cada cluster de forma que representem cada grupo claramente.
+
+A ideia é calcular a similaridade entre a localização do cluster e os vetores de dados e reatribuir os clusters com base nela. [Distância Euclidiana](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-distance) é usado principalmente para esta tarefa.
+
+![Distância Euclidiana entre dois pontos](https://upload.wikimedia.org/wikipedia/commons/5/55/Euclidean_distance_2d.svg)
+
+_Fonte: [Wikipedia](https://en.wikipedia.org/wiki/Euclidean_distance)_
+
+O algoritmo é o seguinte:
+
+1. Verifique se há erros como dados inválidos/inconsistentes
+2. Inicialize os locais do cluster `k` com pontos `k` iniciais/aleatórios
+3. Calcule a distância de cada ponto de dados de cada cluster
+4. Atribua o rótulo do cluster de cada ponto de dados igual ao do cluster em sua distância mínima
+5. Calcule o centroide de cada cluster com base nos pontos de dados que ele contém
+6. Repita cada uma das etapas acima até que as localizações do centroide estejam variando
+
+Aqui está uma visualização do agrupamento k-Means para melhor compreensão:
+
+![KNN Visualization 1](https://upload.wikimedia.org/wikipedia/commons/e/ea/K-means_convergence.gif)
+
+_Fonte: [Wikipedia](https://en.wikipedia.org/wiki/K-means_clustering)_
+
+Os centroides estão se movendo continuamente para criar uma melhor distinção entre os diferentes conjuntos de pontos de dados. Como podemos ver, após algumas iterações, a diferença de centroides é bastante baixa entre as iterações. Por exemplo, entre as iterações `13` e `14` a diferença é bem pequena porque o otimizador está ajustando os casos limite.
+
+## Referências
+
+- [k-Means neighbors algorithm on Wikipedia](https://en.wikipedia.org/wiki/K-means_clustering)
diff --git a/src/algorithms/ml/k-means/__test__/kMeans.test.js b/src/algorithms/ml/k-means/__test__/kMeans.test.js
new file mode 100644
index 0000000000..44a6f538e2
--- /dev/null
+++ b/src/algorithms/ml/k-means/__test__/kMeans.test.js
@@ -0,0 +1,40 @@
+import KMeans from '../kMeans';
+
+describe('kMeans', () => {
+  it('should throw an error on invalid data', () => {
+    expect(() => {
+      KMeans();
+    }).toThrowError('The data is empty');
+  });
+
+  it('should throw an error on inconsistent data', () => {
+    expect(() => {
+      KMeans([[1, 2], [1]], 2);
+    }).toThrowError('Matrices have different shapes');
+  });
+
+  it('should find the nearest neighbour', () => {
+    const data = [[1, 1], [6, 2], [3, 3], [4, 5], [9, 2], [2, 4], [8, 7]];
+    const k = 2;
+    const expectedClusters = [0, 1, 0, 1, 1, 0, 1];
+    expect(KMeans(data, k)).toEqual(expectedClusters);
+
+    expect(KMeans([[0, 0], [0, 1], [10, 10]], 2)).toEqual(
+      [0, 0, 1],
+    );
+  });
+
+  it('should find the clusters with equal distances', () => {
+    const dataSet = [[0, 0], [1, 1], [2, 2]];
+    const k = 3;
+    const expectedCluster = [0, 1, 2];
+    expect(KMeans(dataSet, k)).toEqual(expectedCluster);
+  });
+
+  it('should find the nearest neighbour in 3D space', () => {
+    const dataSet = [[0, 0, 0], [0, 1, 0], [2, 0, 2]];
+    const k = 2;
+    const expectedCluster = [1, 1, 0];
+    expect(KMeans(dataSet, k)).toEqual(expectedCluster);
+  });
+});
diff --git a/src/algorithms/ml/k-means/kMeans.js b/src/algorithms/ml/k-means/kMeans.js
new file mode 100644
index 0000000000..4361eed689
--- /dev/null
+++ b/src/algorithms/ml/k-means/kMeans.js
@@ -0,0 +1,85 @@
+import * as mtrx from '../../math/matrix/Matrix';
+import euclideanDistance from '../../math/euclidean-distance/euclideanDistance';
+
+/**
+ * Classifies the point in space based on k-Means algorithm.
+ *
+ * @param {number[][]} data - array of dataSet points, i.e. [[0, 1], [3, 4], [5, 7]]
+ * @param {number} k - number of clusters
+ * @return {number[]} - the class of the point
+ */
+export default function KMeans(
+  data,
+  k = 1,
+) {
+  if (!data) {
+    throw new Error('The data is empty');
+  }
+
+  // Assign k clusters locations equal to the location of initial k points.
+  const dataDim = data[0].length;
+  const clusterCenters = data.slice(0, k);
+
+  // Continue optimization till convergence.
+  // Centroids should not be moving once optimized.
+  // Calculate distance of each candidate vector from each cluster center.
+  // Assign cluster number to each data vector according to minimum distance.
+
+  // Matrix of distance from each data point to each cluster centroid.
+  const distances = mtrx.zeros([data.length, k]);
+
+  // Vector data points' classes. The value of -1 means that no class has bee assigned yet.
+  const classes = Array(data.length).fill(-1);
+
+  let iterate = true;
+  while (iterate) {
+    iterate = false;
+
+    // Calculate and store the distance of each data point from each cluster.
+    for (let dataIndex = 0; dataIndex < data.length; dataIndex += 1) {
+      for (let clusterIndex = 0; clusterIndex < k; clusterIndex += 1) {
+        distances[dataIndex][clusterIndex] = euclideanDistance(
+          [clusterCenters[clusterIndex]],
+          [data[dataIndex]],
+        );
+      }
+      // Assign the closest cluster number to each dataSet point.
+      const closestClusterIdx = distances[dataIndex].indexOf(
+        Math.min(...distances[dataIndex]),
+      );
+
+      // Check if data point class has been changed and we still need to re-iterate.
+      if (classes[dataIndex] !== closestClusterIdx) {
+        iterate = true;
+      }
+
+      classes[dataIndex] = closestClusterIdx;
+    }
+
+    // Recalculate cluster centroid values via all dimensions of the points under it.
+    for (let clusterIndex = 0; clusterIndex < k; clusterIndex += 1) {
+      // Reset cluster center coordinates since we need to recalculate them.
+      clusterCenters[clusterIndex] = Array(dataDim).fill(0);
+      let clusterSize = 0;
+      for (let dataIndex = 0; dataIndex < data.length; dataIndex += 1) {
+        if (classes[dataIndex] === clusterIndex) {
+          // Register one more data point of current cluster.
+          clusterSize += 1;
+          for (let dimensionIndex = 0; dimensionIndex < dataDim; dimensionIndex += 1) {
+            // Add data point coordinates to the cluster center coordinates.
+            clusterCenters[clusterIndex][dimensionIndex] += data[dataIndex][dimensionIndex];
+          }
+        }
+      }
+      // Calculate the average for each cluster center coordinate.
+      for (let dimensionIndex = 0; dimensionIndex < dataDim; dimensionIndex += 1) {
+        clusterCenters[clusterIndex][dimensionIndex] = parseFloat(Number(
+          clusterCenters[clusterIndex][dimensionIndex] / clusterSize,
+        ).toFixed(2));
+      }
+    }
+  }
+
+  // Return the clusters assigned.
+  return classes;
+}
diff --git a/src/algorithms/ml/knn/README.md b/src/algorithms/ml/knn/README.md
new file mode 100644
index 0000000000..1fc168eaf2
--- /dev/null
+++ b/src/algorithms/ml/knn/README.md
@@ -0,0 +1,44 @@
+# k-Nearest Neighbors Algorithm
+
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
+The **k-nearest neighbors algorithm (k-NN)** is a supervised Machine Learning algorithm. It's a classification algorithm, determining the class of a sample vector using a sample data.
+
+In k-NN classification, the output is a class membership. An object is classified by a plurality vote of its neighbors, with the object being assigned to the class most common among its `k` nearest neighbors (`k` is a positive integer, typically small). If `k = 1`, then the object is simply assigned to the class of that single nearest neighbor.
+
+The idea is to calculate the similarity between two data points on the basis of a distance metric. [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance) is used mostly for this task.
+
+![Euclidean distance between two points](https://upload.wikimedia.org/wikipedia/commons/5/55/Euclidean_distance_2d.svg)
+
+_Image source: [Wikipedia](https://en.wikipedia.org/wiki/Euclidean_distance)_
+
+The algorithm is as follows:
+
+1. Check for errors like invalid data/labels.
+2. Calculate the euclidean distance of all the data points in training data with the classification point
+3. Sort the distances of points along with their classes in ascending order
+4. Take the initial `K` classes and find the mode to get the most similar class
+5. Report the most similar class
+
+Here is a visualization of k-NN classification for better understanding:
+
+![KNN Visualization 1](https://upload.wikimedia.org/wikipedia/commons/e/e7/KnnClassification.svg)
+
+_Image source: [Wikipedia](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm)_
+
+The test sample (green dot) should be classified either to blue squares or to red triangles. If `k = 3` (solid line circle) it is assigned to the red triangles because there are `2` triangles and only `1` square inside the inner circle. If `k = 5` (dashed line circle) it is assigned to the blue squares (`3` squares vs. `2` triangles inside the outer circle).
+
+Another k-NN classification example:
+
+![KNN Visualization 2](https://media.geeksforgeeks.org/wp-content/uploads/graph2-2.png)
+
+_Image source: [GeeksForGeeks](https://media.geeksforgeeks.org/wp-content/uploads/graph2-2.png)_
+
+Here, as we can see, the classification of unknown points will be judged by their proximity to other points.
+
+It is important to note that `K` is preferred to have odd values in order to break ties. Usually `K` is taken as `3` or `5`.
+
+## References
+
+- [k-nearest neighbors algorithm on Wikipedia](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm)
diff --git a/src/algorithms/ml/knn/README.pt-BR.md b/src/algorithms/ml/knn/README.pt-BR.md
new file mode 100644
index 0000000000..b2abdd784f
--- /dev/null
+++ b/src/algorithms/ml/knn/README.pt-BR.md
@@ -0,0 +1,44 @@
+# Algoritmo de k-vizinhos mais próximos
+
+_Leia isso em outros idiomas:_
+[_English_](README.md)
+
+O **algoritmo de k-vizinhos mais próximos (k-NN)** é um algoritmo de aprendizado de máquina supervisionado. É um algoritmo de classificação, determinando a classe de um vetor de amostra usando dados de amostra.
+
+Na classificação k-NN, a saída é uma associação de classe. Um objeto é classificado por uma pluralidade de votos de seus vizinhos, com o objeto sendo atribuído à classe mais comum entre seus `k` vizinhos mais próximos (`k` é um inteiro positivo, tipicamente pequeno). Se `k = 1`, então o objeto é simplesmente atribuído à classe daquele único vizinho mais próximo.
+
+The idea is to calculate the similarity between two data points on the basis of a distance metric. [Distância Euclidiana](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-distance) é usado principalmente para esta tarefa.
+
+![Distância Euclidiana entre dois pontos](https://upload.wikimedia.org/wikipedia/commons/5/55/Euclidean_distance_2d.svg)
+
+_Fonte: [Wikipedia](https://en.wikipedia.org/wiki/Euclidean_distance)_
+
+O algoritmo é o seguinte:
+
+1. Verifique se há erros como dados/rótulos inválidos.
+2. Calcule a distância euclidiana de todos os pontos de dados nos dados de treinamento com o ponto de classificação
+3. Classifique as distâncias dos pontos junto com suas classes em ordem crescente
+4. Pegue as classes iniciais `K` e encontre o modo para obter a classe mais semelhante
+5. Informe a classe mais semelhante
+
+Aqui está uma visualização da classificação k-NN para melhor compreensão:
+
+![KNN Visualization 1](https://upload.wikimedia.org/wikipedia/commons/e/e7/KnnClassification.svg)
+
+_Fonte: [Wikipedia](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm)_
+
+A amostra de teste (ponto verde) deve ser classificada em quadrados azuis ou em triângulos vermelhos. Se `k = 3` (círculo de linha sólida) é atribuído aos triângulos vermelhos porque existem `2` triângulos e apenas `1` quadrado dentro do círculo interno. Se `k = 5` (círculo de linha tracejada) é atribuído aos quadrados azuis (`3` quadrados vs. `2` triângulos dentro do círculo externo).
+
+Outro exemplo de classificação k-NN:
+
+![KNN Visualization 2](https://media.geeksforgeeks.org/wp-content/uploads/graph2-2.png)
+
+_Fonte: [GeeksForGeeks](https://media.geeksforgeeks.org/wp-content/uploads/graph2-2.png)_
+
+Aqui, como podemos ver, a classificação dos pontos desconhecidos será julgada pela proximidade com outros pontos.
+
+É importante notar que `K` é preferível ter valores ímpares para desempate. Normalmente `K` é tomado como `3` ou `5`.
+
+## Referências
+
+- [k-nearest neighbors algorithm on Wikipedia](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm)
diff --git a/src/algorithms/ml/knn/__test__/knn.test.js b/src/algorithms/ml/knn/__test__/knn.test.js
new file mode 100644
index 0000000000..302bf3b7a9
--- /dev/null
+++ b/src/algorithms/ml/knn/__test__/knn.test.js
@@ -0,0 +1,71 @@
+import kNN from '../kNN';
+
+describe('kNN', () => {
+  it('should throw an error on invalid data', () => {
+    expect(() => {
+      kNN();
+    }).toThrowError('Either dataSet or labels or toClassify were not set');
+  });
+
+  it('should throw an error on invalid labels', () => {
+    const noLabels = () => {
+      kNN([[1, 1]]);
+    };
+    expect(noLabels).toThrowError('Either dataSet or labels or toClassify were not set');
+  });
+
+  it('should throw an error on not giving classification vector', () => {
+    const noClassification = () => {
+      kNN([[1, 1]], [1]);
+    };
+    expect(noClassification).toThrowError('Either dataSet or labels or toClassify were not set');
+  });
+
+  it('should throw an error on not giving classification vector', () => {
+    const inconsistent = () => {
+      kNN([[1, 1]], [1], [1]);
+    };
+    expect(inconsistent).toThrowError('Matrices have different shapes');
+  });
+
+  it('should find the nearest neighbour', () => {
+    let dataSet;
+    let labels;
+    let toClassify;
+    let expectedClass;
+
+    dataSet = [[1, 1], [2, 2]];
+    labels = [1, 2];
+    toClassify = [1, 1];
+    expectedClass = 1;
+    expect(kNN(dataSet, labels, toClassify)).toBe(expectedClass);
+
+    dataSet = [[1, 1], [6, 2], [3, 3], [4, 5], [9, 2], [2, 4], [8, 7]];
+    labels = [1, 2, 1, 2, 1, 2, 1];
+    toClassify = [1.25, 1.25];
+    expectedClass = 1;
+    expect(kNN(dataSet, labels, toClassify)).toBe(expectedClass);
+
+    dataSet = [[1, 1], [6, 2], [3, 3], [4, 5], [9, 2], [2, 4], [8, 7]];
+    labels = [1, 2, 1, 2, 1, 2, 1];
+    toClassify = [1.25, 1.25];
+    expectedClass = 2;
+    expect(kNN(dataSet, labels, toClassify, 5)).toBe(expectedClass);
+  });
+
+  it('should find the nearest neighbour with equal distances', () => {
+    const dataSet = [[0, 0], [1, 1], [0, 2]];
+    const labels = [1, 3, 3];
+    const toClassify = [0, 1];
+    const expectedClass = 3;
+    expect(kNN(dataSet, labels, toClassify)).toBe(expectedClass);
+  });
+
+  it('should find the nearest neighbour in 3D space', () => {
+    const dataSet = [[0, 0, 0], [0, 1, 1], [0, 0, 2]];
+    const labels = [1, 3, 3];
+    const toClassify = [0, 0, 1];
+    const expectedClass = 3;
+    expect(kNN(dataSet, labels, toClassify)).toBe(expectedClass);
+  });
+});
diff --git a/src/algorithms/ml/knn/kNN.js b/src/algorithms/ml/knn/kNN.js
new file mode 100644
index 0000000000..350e7ce176
--- /dev/null
+++ b/src/algorithms/ml/knn/kNN.js
@@ -0,0 +1,60 @@
+/**
+ * Classifies the point in space based on k-nearest neighbors algorithm.
+ *
+ * @param {number[][]} dataSet - array of data points, i.e. [[0, 1], [3, 4], [5, 7]]
+ * @param {number[]} labels - array of classes (labels), i.e. [1, 1, 2]
+ * @param {number[]} toClassify - the point in space that needs to be classified, i.e. [5, 4]
+ * @param {number} k - number of nearest neighbors which will be taken into account (preferably odd)
+ * @return {number} - the class of the point
+ */
+
+import euclideanDistance from '../../math/euclidean-distance/euclideanDistance';
+
+export default function kNN(
+  dataSet,
+  labels,
+  toClassify,
+  k = 3,
+) {
+  if (!dataSet || !labels || !toClassify) {
+    throw new Error('Either dataSet or labels or toClassify were not set');
+  }
+
+  // Calculate distance from toClassify to each point for all dimensions in dataSet.
+  // Store distance and point's label into distances list.
+  const distances = [];
+  for (let i = 0; i < dataSet.length; i += 1) {
+    distances.push({
+      dist: euclideanDistance([dataSet[i]], [toClassify]),
+      label: labels[i],
+    });
+  }
+
+  // Sort distances list (from closer point to further ones).
+  // Take initial k values, count with class index
+  const kNearest = distances.sort((a, b) => {
+    if (a.dist === b.dist) {
+      return 0;
+    }
+    return a.dist < b.dist ? -1 : 1;
+  }).slice(0, k);
+
+  // Count the number of instances of each class in top k members.
+  const labelsCounter = {};
+  let topClass = 0;
+  let topClassCount = 0;
+  for (let i = 0; i < kNearest.length; i += 1) {
+    if (kNearest[i].label in labelsCounter) {
+      labelsCounter[kNearest[i].label] += 1;
+    } else {
+      labelsCounter[kNearest[i].label] = 1;
+    }
+    if (labelsCounter[kNearest[i].label] > topClassCount) {
+      topClassCount = labelsCounter[kNearest[i].label];
+      topClass = kNearest[i].label;
+    }
+  }
+
+  // Return the class with highest count.
+  return topClass;
+}
diff --git a/src/algorithms/search/binary-search/README.es-ES.md b/src/algorithms/search/binary-search/README.es-ES.md
new file mode 100644
index 0000000000..f14aef985f
--- /dev/null
+++ b/src/algorithms/search/binary-search/README.es-ES.md
@@ -0,0 +1,27 @@
+# Búsqueda binaria
+
+_Lea esto en otros idiomas:_
+[English](README.md)
+[Português brasileiro](README.pt-BR.md).
+
+En informática, la búsqueda binaria, también conocida como búsqueda de medio intervalo
+búsqueda, búsqueda logarítmica, o corte binario, es un algoritmo de búsqueda
+que encuentra la posición de un valor objetivo dentro de una matriz
+ordenada. La búsqueda binaria compara el valor objetivo con el elemento central
+de la matriz; si son desiguales, se elimina la mitad en la que
+la mitad en la que no puede estar el objetivo se elimina y la búsqueda continúa
+en la mitad restante hasta que tenga éxito. Si la búsqueda
+termina con la mitad restante vacía, el objetivo no está
+en la matriz.
+
+![Búsqueda binaria](https://upload.wikimedia.org/wikipedia/commons/8/83/Binary_Search_Depiction.svg)
+
+## Complejidad
+
+**Complejidad de tiempo**: `O(log(n))` - ya que dividimos el área de búsqueda en dos para cada
+siguiente iteración.
+
+## Referencias
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_algorithm)
+- [YouTube](https://www.youtube.com/watch?v=P3YID7liBug&index=29&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/search/binary-search/README.md b/src/algorithms/search/binary-search/README.md
index 2259b94406..34d1bd7a40 100644
--- a/src/algorithms/search/binary-search/README.md
+++ b/src/algorithms/search/binary-search/README.md
@@ -1,13 +1,17 @@
 # Binary Search
 
-In computer science, binary search, also known as half-interval 
-search, logarithmic search, or binary chop, is a search algorithm 
-that finds the position of a target value within a sorted 
-array. Binary search compares the target value to the middle 
-element of the array; if they are unequal, the half in which 
-the target cannot lie is eliminated and the search continues 
-on the remaining half until it is successful. If the search 
-ends with the remaining half being empty, the target is not 
+_Read this in other languages:_
+[Português brasileiro](README.pt-BR.md).
+[Español](README.es-ES.md).
+
+In computer science, binary search, also known as half-interval
+search, logarithmic search, or binary chop, is a search algorithm
+that finds the position of a target value within a sorted
+array. Binary search compares the target value to the middle
+element of the array; if they are unequal, the half in which
+the target cannot lie is eliminated and the search continues
+on the remaining half until it is successful. If the search
+ends with the remaining half being empty, the target is not
 in the array.
 
 ![Binary Search](https://upload.wikimedia.org/wikipedia/commons/8/83/Binary_Search_Depiction.svg)
diff --git a/src/algorithms/search/binary-search/README.pt-BR.md b/src/algorithms/search/binary-search/README.pt-BR.md
new file mode 100644
index 0000000000..4f9de9f199
--- /dev/null
+++ b/src/algorithms/search/binary-search/README.pt-BR.md
@@ -0,0 +1,23 @@
+# Busca Binária
+
+_Leia isso em outras línguas:_
+[english](README.md).
+[Español](README.es-ES.md).
+
+Em ciência da computação, busca binária, também conhecida como busca de meio-intervalo, busca logarítmica ou corte binário, é um algoritmo de pesquisa
+que encontra a posição de um elemento alvo dentro de um
+vetor ordenado. O algoritmo compara o elemento alvo com o elemento central do vetor; se eles são diferentes, a metade em que
+o elemento alvo não pode estar é eliminada e a busca continua
+na metade remanescente até que o elemento alvo seja encontrado. Se a busca
+terminar com a metade remanescente vazia, o elemento alvo não está presente no vetor.
+
+![Busca Binária](https://upload.wikimedia.org/wikipedia/commons/8/83/Binary_Search_Depiction.svg)
+
+## Complexidade
+
+**Complexidade de Tempo**: `O(log(n))` - pois a área de busca é dividida por dois a cada iteração.
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_algorithm)
+- [YouTube](https://www.youtube.com/watch?v=P3YID7liBug&index=29&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/search/binary-search/binarySearch.js b/src/algorithms/search/binary-search/binarySearch.js
index 8fe622dbb7..5c8d48fbea 100644
--- a/src/algorithms/search/binary-search/binarySearch.js
+++ b/src/algorithms/search/binary-search/binarySearch.js
@@ -10,12 +10,18 @@ import Comparator from '../../../utils/comparator/Comparator';
  */
 
 export default function binarySearch(sortedArray, seekElement, comparatorCallback) {
+  // Let's create comparator from the comparatorCallback function.
+  // Comparator object will give us common comparison methods like equal() and lessThan().
   const comparator = new Comparator(comparatorCallback);
 
+  // These two indices will contain current array (sub-array) boundaries.
   let startIndex = 0;
   let endIndex = sortedArray.length - 1;
 
+  // Let's continue to split array until boundaries are collapsed
+  // and there is nothing to split anymore.
   while (startIndex <= endIndex) {
+    // Let's calculate the index of the middle element.
     const middleIndex = startIndex + Math.floor((endIndex - startIndex) / 2);
 
     // If we've found the element just return its position.
@@ -33,5 +39,6 @@ export default function binarySearch(sortedArray, seekElement, comparatorCallbac
     }
   }
 
+  // Return -1 if we have not found anything.
   return -1;
 }
diff --git a/src/algorithms/search/interpolation-search/interpolationSearch.js b/src/algorithms/search/interpolation-search/interpolationSearch.js
index a7bacc5393..8546c5be3b 100644
--- a/src/algorithms/search/interpolation-search/interpolationSearch.js
+++ b/src/algorithms/search/interpolation-search/interpolationSearch.js
@@ -31,7 +31,7 @@ export default function interpolationSearch(sortedArray, seekElement) {
     }
 
     // Do interpolation of the middle index.
-    const middleIndex = leftIndex + Math.floor(valueDelta * indexDelta / rangeDelta);
+    const middleIndex = leftIndex + Math.floor((valueDelta * indexDelta) / rangeDelta);
 
     // If we've found the element just return its position.
     if (sortedArray[middleIndex] === seekElement) {
diff --git a/src/algorithms/search/linear-search/README.md b/src/algorithms/search/linear-search/README.md
index 942058785f..8feb422eb2 100644
--- a/src/algorithms/search/linear-search/README.md
+++ b/src/algorithms/search/linear-search/README.md
@@ -1,4 +1,8 @@
 # Linear Search
+
+_Read this in other languages:_
+[Português brasileiro](README.pt-BR.md).
+
 In computer science, linear search or sequential search is a 
 method for finding a target value within a list. It sequentially 
 checks each element of the list for the target value until a 
diff --git a/src/algorithms/search/linear-search/README.pt-BR.md b/src/algorithms/search/linear-search/README.pt-BR.md
new file mode 100644
index 0000000000..e0af42f4ea
--- /dev/null
+++ b/src/algorithms/search/linear-search/README.pt-BR.md
@@ -0,0 +1,19 @@
+# Busca Linear
+
+_Leia isso em outras línguas:_
+[english](README.md).
+
+Na Ciência da Computação, busca linear ou busca sequencial é um método para encontrar um elemento alvo em uma lista. 
+O algoritmo verifica sequencialmente cada elemento da lista procurando o elemento alvo até ele ser encontrado ou até ter verificado todos os elementos.
+A Busca linear realiza no máximo `n` comparações, onde `n` é o tamanho da lista.
+
+![Busca Linear](https://www.tutorialspoint.com/data_structures_algorithms/images/linear_search.gif)
+
+## Complexidade
+
+**Complexidade de Tempo**: `O(n)` - pois no pior caso devemos verificar cada elemento exatamente uma vez.
+
+## Referências
+- [Wikipedia](https://en.wikipedia.org/wiki/Linear_search)
+- [TutorialsPoint](https://www.tutorialspoint.com/data_structures_algorithms/linear_search_algorithm.htm)
+- [Youtube](https://www.youtube.com/watch?v=SGU9duLE30w)
diff --git a/src/algorithms/sets/cartesian-product/cartesianProduct.js b/src/algorithms/sets/cartesian-product/cartesianProduct.js
index 6166681942..14924fae36 100644
--- a/src/algorithms/sets/cartesian-product/cartesianProduct.js
+++ b/src/algorithms/sets/cartesian-product/cartesianProduct.js
@@ -1,15 +1,27 @@
+/**
+ * Generates Cartesian Product of two sets.
+ * @param {*[]} setA
+ * @param {*[]} setB
+ * @return {*[]}
+ */
 export default function cartesianProduct(setA, setB) {
+  // Check if input sets are not empty.
+  // Otherwise return null since we can't generate Cartesian Product out of them.
   if (!setA || !setB || !setA.length || !setB.length) {
     return null;
   }
 
+  // Init product set.
   const product = [];
 
+  // Now, let's go through all elements of a first and second set and form all possible pairs.
   for (let indexA = 0; indexA < setA.length; indexA += 1) {
     for (let indexB = 0; indexB < setB.length; indexB += 1) {
+      // Add current product pair to the product set.
       product.push([setA[indexA], setB[indexB]]);
     }
   }
 
+  // Return cartesian product set.
   return product;
 }
diff --git a/src/algorithms/sets/combinations/README.md b/src/algorithms/sets/combinations/README.md
index 30243b8614..69fc0603f9 100644
--- a/src/algorithms/sets/combinations/README.md
+++ b/src/algorithms/sets/combinations/README.md
@@ -5,14 +5,14 @@ When the order doesn't matter, it is a **Combination**.
 When the order **does** matter it is a **Permutation**.
 
 **"My fruit salad is a combination of apples, grapes and bananas"**
-We don't care what order the fruits are in, they could also be 
-"bananas, grapes and apples" or "grapes, apples and bananas", 
+We don't care what order the fruits are in, they could also be
+"bananas, grapes and apples" or "grapes, apples and bananas",
 its the same fruit salad.
 
 ## Combinations without repetitions
 
-This is how lotteries work. The numbers are drawn one at a 
-time, and if we have the lucky numbers (no matter what order) 
+This is how lotteries work. The numbers are drawn one at a
+time, and if we have the lucky numbers (no matter what order)
 we win!
 
 No Repetition: such as lottery numbers `(2,14,15,27,30,33)`
@@ -30,12 +30,12 @@ It is often called "n choose r" (such as "16 choose 3"). And is also known as th
 
 Repetition is Allowed: such as coins in your pocket `(5,5,5,10,10)`
 
-Or let us say there are five flavours of ice cream: 
+Or let us say there are five flavours of ice cream:
 `banana`, `chocolate`, `lemon`, `strawberry` and `vanilla`.
 
 We can have three scoops. How many variations will there be?
 
-Let's use letters for the flavours: `{b, c, l, s, v}`. 
+Let's use letters for the flavours: `{b, c, l, s, v}`.
 Example selections include:
 
 - `{c, c, c}` (3 scoops of chocolate)
@@ -46,23 +46,21 @@ Example selections include:
 
 ![Formula](https://www.mathsisfun.com/combinatorics/images/combinations-repeat.gif)
 
-Where `n` is the number of things to choose from, and we 
-choose `r` of them. Repetition allowed, 
+Where `n` is the number of things to choose from, and we
+choose `r` of them. Repetition allowed,
 order doesn't matter.
 
-## Cheat Sheets
+## Cheatsheet
 
-Permutations cheat sheet
+![Permutations and Combinations Overview](./images/overview.png)
 
-![Permutations Cheat Sheet](https://cdn-images-1.medium.com/max/2000/1*JNK-n0Pt0Vbxk0lxVpgT5A.png)
+![Combinations overview](./images/combinations-overview.jpg)
 
-Combinations cheat sheet
+| | |
+| --- | --- |
+|![Combinations with repetition](./images/combinations-with-repetitions.jpg) | ![Combinations without repetition](./images/combinations-without-repetitions.jpg) |
 
-![Combinations Cheat Sheet](https://cdn-images-1.medium.com/max/2000/1*7cFRn8jW4g_91YgDAbmxRQ.png)
-
-Permutations/combinations algorithm ideas.
-
-![Algorithms Idea](https://cdn-images-1.medium.com/max/2000/1*vLsSsZMnesCFPCYTYMbxrQ.png)
+*Made with [okso.app](https://okso.app)*
 
 ## References
 
diff --git a/src/algorithms/sets/combinations/combineWithRepetitions.js b/src/algorithms/sets/combinations/combineWithRepetitions.js
index 8a743289f3..5e25635dac 100644
--- a/src/algorithms/sets/combinations/combineWithRepetitions.js
+++ b/src/algorithms/sets/combinations/combineWithRepetitions.js
@@ -4,21 +4,25 @@
  * @return {*[]}
  */
 export default function combineWithRepetitions(comboOptions, comboLength) {
+  // If the length of the combination is 1 then each element of the original array
+  // is a combination itself.
   if (comboLength === 1) {
-    return comboOptions.map(comboOption => [comboOption]);
+    return comboOptions.map((comboOption) => [comboOption]);
   }
 
   // Init combinations array.
   const combos = [];
 
-  // Eliminate characters one by one and concatenate them to
-  // combinations of smaller lengths.
+  // Remember characters one by one and concatenate them to combinations of smaller lengths.
+  // We don't extract elements here because the repetitions are allowed.
   comboOptions.forEach((currentOption, optionIndex) => {
+    // Generate combinations of smaller size.
     const smallerCombos = combineWithRepetitions(
       comboOptions.slice(optionIndex),
       comboLength - 1,
     );
 
+    // Concatenate currentOption with all combinations of smaller size.
     smallerCombos.forEach((smallerCombo) => {
       combos.push([currentOption].concat(smallerCombo));
     });
diff --git a/src/algorithms/sets/combinations/combineWithoutRepetitions.js b/src/algorithms/sets/combinations/combineWithoutRepetitions.js
index 56c1e8fe48..38fb8507f1 100644
--- a/src/algorithms/sets/combinations/combineWithoutRepetitions.js
+++ b/src/algorithms/sets/combinations/combineWithoutRepetitions.js
@@ -4,21 +4,25 @@
  * @return {*[]}
  */
 export default function combineWithoutRepetitions(comboOptions, comboLength) {
+  // If the length of the combination is 1 then each element of the original array
+  // is a combination itself.
   if (comboLength === 1) {
-    return comboOptions.map(comboOption => [comboOption]);
+    return comboOptions.map((comboOption) => [comboOption]);
   }
 
   // Init combinations array.
   const combos = [];
 
-  // Eliminate characters one by one and concatenate them to
-  // combinations of smaller lengths.
+  // Extract characters one by one and concatenate them to combinations of smaller lengths.
+  // We need to extract them because we don't want to have repetitions after concatenation.
   comboOptions.forEach((currentOption, optionIndex) => {
+    // Generate combinations of smaller size.
     const smallerCombos = combineWithoutRepetitions(
       comboOptions.slice(optionIndex + 1),
       comboLength - 1,
     );
 
+    // Concatenate currentOption with all combinations of smaller size.
     smallerCombos.forEach((smallerCombo) => {
       combos.push([currentOption].concat(smallerCombo));
     });
diff --git a/src/algorithms/sets/combinations/images/combinations-overview.jpg b/src/algorithms/sets/combinations/images/combinations-overview.jpg
new file mode 100644
index 0000000000..9f54477492
Binary files /dev/null and b/src/algorithms/sets/combinations/images/combinations-overview.jpg differ
diff --git a/src/algorithms/sets/combinations/images/combinations-with-repetitions.jpg b/src/algorithms/sets/combinations/images/combinations-with-repetitions.jpg
new file mode 100644
index 0000000000..6a80a91cd1
Binary files /dev/null and b/src/algorithms/sets/combinations/images/combinations-with-repetitions.jpg differ
diff --git a/src/algorithms/sets/combinations/images/combinations-without-repetitions.jpg b/src/algorithms/sets/combinations/images/combinations-without-repetitions.jpg
new file mode 100644
index 0000000000..9262f99c86
Binary files /dev/null and b/src/algorithms/sets/combinations/images/combinations-without-repetitions.jpg differ
diff --git a/src/algorithms/sets/combinations/images/overview.png b/src/algorithms/sets/combinations/images/overview.png
new file mode 100644
index 0000000000..fa8d6a66f5
Binary files /dev/null and b/src/algorithms/sets/combinations/images/overview.png differ
diff --git a/src/algorithms/sets/knapsack-problem/Knapsack.js b/src/algorithms/sets/knapsack-problem/Knapsack.js
index d93e51a3bd..5d14911102 100644
--- a/src/algorithms/sets/knapsack-problem/Knapsack.js
+++ b/src/algorithms/sets/knapsack-problem/Knapsack.js
@@ -150,7 +150,6 @@ export default class Knapsack {
     }
   }
 
-
   // Solve unbounded knapsack problem.
   // Greedy approach.
   solveUnboundedKnapsackProblem() {
diff --git a/src/algorithms/sets/longest-common-subsequence/__test__/longestCommonSubsequenceRecursive.test.js b/src/algorithms/sets/longest-common-subsequence/__test__/longestCommonSubsequenceRecursive.test.js
new file mode 100644
index 0000000000..3d5c9d802a
--- /dev/null
+++ b/src/algorithms/sets/longest-common-subsequence/__test__/longestCommonSubsequenceRecursive.test.js
@@ -0,0 +1,17 @@
+import longestCommonSubsequence from '../longestCommonSubsequenceRecursive';
+
+describe('longestCommonSubsequenceRecursive', () => {
+  it('should find longest common substring between two strings', () => {
+    expect(longestCommonSubsequence('', '')).toBe('');
+    expect(longestCommonSubsequence('ABC', '')).toBe('');
+    expect(longestCommonSubsequence('', 'ABC')).toBe('');
+    expect(longestCommonSubsequence('ABABC', 'BABCA')).toBe('BABC');
+    expect(longestCommonSubsequence('BABCA', 'ABCBA')).toBe('ABCA');
+    expect(longestCommonSubsequence('sea', 'eat')).toBe('ea');
+    expect(longestCommonSubsequence('algorithms', 'rithm')).toBe('rithm');
+    expect(longestCommonSubsequence(
+      'Algorithms and data structures implemented in JavaScript',
+      'Here you may find Algorithms and data structures that are implemented in JavaScript',
+    )).toBe('Algorithms and data structures implemented in JavaScript');
+  });
+});
diff --git a/src/algorithms/sets/longest-common-subsequence/longestCommonSubsequenceRecursive.js b/src/algorithms/sets/longest-common-subsequence/longestCommonSubsequenceRecursive.js
new file mode 100644
index 0000000000..580ba8c72f
--- /dev/null
+++ b/src/algorithms/sets/longest-common-subsequence/longestCommonSubsequenceRecursive.js
@@ -0,0 +1,36 @@
+/* eslint-disable no-param-reassign */
+/**
+ * Longest Common Subsequence (LCS) (Recursive Approach).
+ *
+ * @param {string} string1
+ * @param {string} string2
+ * @return {number}
+ */
+export default function longestCommonSubsequenceRecursive(string1, string2) {
+  /**
+   *
+   * @param {string} s1
+   * @param {string} s2
+   * @return {string} - returns the LCS (Longest Common Subsequence)
+   */
+  const lcs = (s1, s2, memo = {}) => {
+    if (!s1 || !s2) return '';
+
+    if (memo[`${s1}:${s2}`]) return memo[`${s1}:${s2}`];
+
+    if (s1[0] === s2[0]) {
+      return s1[0] + lcs(s1.substring(1), s2.substring(1), memo);
+    }
+
+    const nextLcs1 = lcs(s1.substring(1), s2, memo);
+    const nextLcs2 = lcs(s1, s2.substring(1), memo);
+
+    const nextLongest = nextLcs1.length >= nextLcs2.length ? nextLcs1 : nextLcs2;
+
+    memo[`${s1}:${s2}`] = nextLongest;
+
+    return nextLongest;
+  };
+
+  return lcs(string1, string2);
+}
diff --git a/src/algorithms/sets/maximum-subarray/README.md b/src/algorithms/sets/maximum-subarray/README.md
index c152f54edc..f06300f500 100644
--- a/src/algorithms/sets/maximum-subarray/README.md
+++ b/src/algorithms/sets/maximum-subarray/README.md
@@ -1,7 +1,7 @@
 # Maximum subarray problem
 
-The maximum subarray problem is the task of finding the contiguous 
-subarray within a one-dimensional array, `a[1...n]`, of numbers 
+The maximum subarray problem is the task of finding the contiguous
+subarray within a one-dimensional array, `a[1...n]`, of numbers
 which has the largest sum, where,
 
 ![Maximum subarray](https://wikimedia.org/api/rest_v1/media/math/render/svg/e8960f093107b71b21827e726e2bad8b023779b2)
@@ -10,13 +10,20 @@ which has the largest sum, where,
 
 ## Example
 
-The list usually contains both positive and negative numbers along 
-with `0`. For example, for the array of 
-values `−2, 1, −3, 4, −1, 2, 1, −5, 4` the contiguous subarray 
+The list usually contains both positive and negative numbers along
+with `0`. For example, for the array of
+values `−2, 1, −3, 4, −1, 2, 1, −5, 4` the contiguous subarray
 with the largest sum is `4, −1, 2, 1`, with sum `6`.
 
+## Solutions
+
+- Brute Force solution `O(n^2)`: [bfMaximumSubarray.js](./bfMaximumSubarray.js)
+- Divide and Conquer solution `O(n^2)`: [dcMaximumSubarraySum.js](./dcMaximumSubarraySum.js)
+- Dynamic Programming solution `O(n)`: [dpMaximumSubarray.js](./dpMaximumSubarray.js)
+
 ## References
 
 - [Wikipedia](https://en.wikipedia.org/wiki/Maximum_subarray_problem)
 - [YouTube](https://www.youtube.com/watch?v=ohHWQf1HDfU&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
 - [GeeksForGeeks](https://www.geeksforgeeks.org/largest-sum-contiguous-subarray/)
+- [LeetCode](https://leetcode.com/explore/interview/card/top-interview-questions-easy/97/dynamic-programming/566/discuss/1595195/C++Python-7-Simple-Solutions-w-Explanation-or-Brute-Force-+-DP-+-Kadane-+-Divide-and-Conquer)
diff --git a/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js b/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js
index 520b5c01c8..6bff427110 100644
--- a/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js
+++ b/src/algorithms/sets/maximum-subarray/__test__/bfMaximumSubarray.test.js
@@ -1,8 +1,12 @@
 import bfMaximumSubarray from '../bfMaximumSubarray';
 
 describe('bfMaximumSubarray', () => {
-  it('should find maximum subarray using brute force algorithm', () => {
+  it('should find maximum subarray using the brute force algorithm', () => {
     expect(bfMaximumSubarray([])).toEqual([]);
+    expect(bfMaximumSubarray([0, 0])).toEqual([0]);
+    expect(bfMaximumSubarray([0, 0, 1])).toEqual([0, 0, 1]);
+    expect(bfMaximumSubarray([0, 0, 1, 2])).toEqual([0, 0, 1, 2]);
+    expect(bfMaximumSubarray([0, 0, -1, 2])).toEqual([2]);
     expect(bfMaximumSubarray([-1, -2, -3, -4, -5])).toEqual([-1]);
     expect(bfMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual([1, 2, 3, 2, 3, 4, 5]);
     expect(bfMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual([4, -1, 2, 1]);
diff --git a/src/algorithms/sets/maximum-subarray/__test__/dcMaximumSubarraySum.test.js b/src/algorithms/sets/maximum-subarray/__test__/dcMaximumSubarraySum.test.js
new file mode 100644
index 0000000000..3b3ace08d3
--- /dev/null
+++ b/src/algorithms/sets/maximum-subarray/__test__/dcMaximumSubarraySum.test.js
@@ -0,0 +1,16 @@
+import dcMaximumSubarray from '../dcMaximumSubarraySum';
+
+describe('dcMaximumSubarraySum', () => {
+  it('should find maximum subarray sum using the divide and conquer algorithm', () => {
+    expect(dcMaximumSubarray([])).toEqual(-Infinity);
+    expect(dcMaximumSubarray([0, 0])).toEqual(0);
+    expect(dcMaximumSubarray([0, 0, 1])).toEqual(1);
+    expect(dcMaximumSubarray([0, 0, 1, 2])).toEqual(3);
+    expect(dcMaximumSubarray([0, 0, -1, 2])).toEqual(2);
+    expect(dcMaximumSubarray([-1, -2, -3, -4, -5])).toEqual(-1);
+    expect(dcMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual(20);
+    expect(dcMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual(6);
+    expect(dcMaximumSubarray([-2, -3, 4, -1, -2, 1, 5, -3])).toEqual(7);
+    expect(dcMaximumSubarray([1, -3, 2, -5, 7, 6, -1, 4, 11, -23])).toEqual(27);
+  });
+});
diff --git a/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js b/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js
index 4ce30db8b7..c00727cc66 100644
--- a/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js
+++ b/src/algorithms/sets/maximum-subarray/__test__/dpMaximumSubarray.test.js
@@ -1,8 +1,12 @@
 import dpMaximumSubarray from '../dpMaximumSubarray';
 
 describe('dpMaximumSubarray', () => {
-  it('should find maximum subarray using dynamic programming algorithm', () => {
+  it('should find maximum subarray using the dynamic programming algorithm', () => {
     expect(dpMaximumSubarray([])).toEqual([]);
+    expect(dpMaximumSubarray([0, 0])).toEqual([0]);
+    expect(dpMaximumSubarray([0, 0, 1])).toEqual([0, 0, 1]);
+    expect(dpMaximumSubarray([0, 0, 1, 2])).toEqual([0, 0, 1, 2]);
+    expect(dpMaximumSubarray([0, 0, -1, 2])).toEqual([2]);
     expect(dpMaximumSubarray([-1, -2, -3, -4, -5])).toEqual([-1]);
     expect(dpMaximumSubarray([1, 2, 3, 2, 3, 4, 5])).toEqual([1, 2, 3, 2, 3, 4, 5]);
     expect(dpMaximumSubarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toEqual([4, -1, 2, 1]);
diff --git a/src/algorithms/sets/maximum-subarray/dcMaximumSubarraySum.js b/src/algorithms/sets/maximum-subarray/dcMaximumSubarraySum.js
new file mode 100644
index 0000000000..3ed9ab3ee7
--- /dev/null
+++ b/src/algorithms/sets/maximum-subarray/dcMaximumSubarraySum.js
@@ -0,0 +1,33 @@
+/**
+ * Divide and Conquer solution.
+ * Complexity: O(n^2) in case if no memoization applied
+ *
+ * @param {Number[]} inputArray
+ * @return {Number[]}
+ */
+export default function dcMaximumSubarraySum(inputArray) {
+  /**
+   * We are going through the inputArray array and for each element we have two options:
+   * - to pick
+   * - not to pick
+   *
+   * Also keep in mind, that the maximum sub-array must be contiguous. It means if we picked
+   * the element, we need to continue picking the next elements or stop counting the max sum.
+   *
+   * @param {number} elementIndex - the index of the element we're deciding to pick or not
+   * @param {boolean} mustPick - to pick or not to pick the element
+   * @returns {number} - maximum subarray sum that we'll get
+   */
+  function solveRecursively(elementIndex, mustPick) {
+    if (elementIndex >= inputArray.length) {
+      return mustPick ? 0 : -Infinity;
+    }
+    return Math.max(
+      // Option #1: Pick the current element, and continue picking next one.
+      inputArray[elementIndex] + solveRecursively(elementIndex + 1, true),
+      // Option #2: Don't pick the current element.
+      mustPick ? 0 : solveRecursively(elementIndex + 1, false),
+    );
+  }
+  return solveRecursively(0, false);
+}
diff --git a/src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js b/src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js
index 12f919d737..ef1734b486 100644
--- a/src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js
+++ b/src/algorithms/sets/maximum-subarray/dpMaximumSubarray.js
@@ -6,57 +6,40 @@
  * @return {Number[]}
  */
 export default function dpMaximumSubarray(inputArray) {
-  // Check if all elements of inputArray are negative ones and return the highest
-  // one in this case.
-  let allNegative = true;
-  let highestElementValue = null;
-  for (let i = 0; i < inputArray.length; i += 1) {
-    if (inputArray[i] >= 0) {
-      allNegative = false;
-    }
-
-    if (highestElementValue === null || highestElementValue < inputArray[i]) {
-      highestElementValue = inputArray[i];
-    }
-  }
-
-  if (allNegative && highestElementValue !== null) {
-    return [highestElementValue];
-  }
-
-  // Let's assume that there is at list one positive integer exists in array.
-  // And thus the maximum sum will for sure be grater then 0. Thus we're able
-  // to always reset max sum to zero.
-  let maxSum = 0;
-
-  // This array will keep a combination that gave the highest sum.
-  let maxSubArray = [];
-
-  // Current sum and subarray that will memoize all previous computations.
+  // We iterate through the inputArray once, using a greedy approach to keep track of the maximum
+  // sum we've seen so far and the current sum.
+  //
+  // The currentSum variable gets reset to 0 every time it drops below 0.
+  //
+  // The maxSum variable is set to -Infinity so that if all numbers are negative, the highest
+  // negative number will constitute the maximum subarray.
+
+  let maxSum = -Infinity;
   let currentSum = 0;
-  let currentSubArray = [];
 
-  for (let i = 0; i < inputArray.length; i += 1) {
-    // Let's add current element value to the current sum.
-    currentSum += inputArray[i];
+  // We need to keep track of the starting and ending indices that contributed to our maxSum
+  // so that we can return the actual subarray. From the beginning let's assume that whole array
+  // is contributing to maxSum.
+  let maxStartIndex = 0;
+  let maxEndIndex = inputArray.length - 1;
+  let currentStartIndex = 0;
+
+  inputArray.forEach((currentNumber, currentIndex) => {
+    currentSum += currentNumber;
+
+    // Update maxSum and the corresponding indices if we have found a new max.
+    if (maxSum < currentSum) {
+      maxSum = currentSum;
+      maxStartIndex = currentStartIndex;
+      maxEndIndex = currentIndex;
+    }
 
+    // Reset currentSum and currentStartIndex if currentSum drops below 0.
     if (currentSum < 0) {
-      // If the sum went below zero then reset it and don't add current element to max subarray.
       currentSum = 0;
-      // Reset current subarray.
-      currentSubArray = [];
-    } else {
-      // If current sum stays positive then add current element to current sub array.
-      currentSubArray.push(inputArray[i]);
-
-      if (currentSum > maxSum) {
-        // If current sum became greater then max registered sum then update
-        // max sum and max subarray.
-        maxSum = currentSum;
-        maxSubArray = currentSubArray.slice();
-      }
+      currentStartIndex = currentIndex + 1;
     }
-  }
+  });
 
-  return maxSubArray;
+  return inputArray.slice(maxStartIndex, maxEndIndex + 1);
 }
diff --git a/src/algorithms/sets/permutations/README.md b/src/algorithms/sets/permutations/README.md
index 8864af7240..4b6a268adc 100644
--- a/src/algorithms/sets/permutations/README.md
+++ b/src/algorithms/sets/permutations/README.md
@@ -4,13 +4,13 @@ When the order doesn't matter, it is a **Combination**.
 
 When the order **does** matter it is a **Permutation**.
 
-**"The combination to the safe is 472"**. We do care about the order. `724` won't work, nor will `247`. 
+**"The combination to the safe is 472"**. We do care about the order. `724` won't work, nor will `247`.
 It has to be exactly `4-7-2`.
 
 ## Permutations without repetitions
 
-A permutation, also called an “arrangement number” or “order”, is a rearrangement of 
-the elements of an ordered list `S` into a one-to-one correspondence with `S` itself. 
+A permutation, also called an “arrangement number” or “order”, is a rearrangement of
+the elements of an ordered list `S` into a one-to-one correspondence with `S` itself.
 
 Below are the permutations of string `ABC`.
 
@@ -37,19 +37,17 @@ For example the the lock below: it could be `333`.
 n * n * n ... (r times) = n^r
 ```
 
-## Cheat Sheets
+## Cheatsheet
 
-Permutations cheat sheet
+![Permutations and Combinations Overview](./images/overview.png)
 
-![Permutations Cheat Sheet](https://cdn-images-1.medium.com/max/2000/1*JNK-n0Pt0Vbxk0lxVpgT5A.png)
+![Permutations overview](./images/permutations-overview.jpeg)
 
-Combinations cheat sheet
+| | |
+| --- | --- |
+|![Permutations with repetition](./images/permutations-with-repetitions.jpg) | ![Permutations without repetition](./images/permutations-without-repetitions.jpg) |
 
-![Combinations Cheat Sheet](https://cdn-images-1.medium.com/max/2000/1*7cFRn8jW4g_91YgDAbmxRQ.png)
-
-Permutations/combinations algorithm ideas.
-
-![Algorithms Idea](https://cdn-images-1.medium.com/max/2000/1*vLsSsZMnesCFPCYTYMbxrQ.png)
+*Made with [okso.app](https://okso.app)*
 
 ## References
 
diff --git a/src/algorithms/sets/permutations/images/overview.png b/src/algorithms/sets/permutations/images/overview.png
new file mode 100644
index 0000000000..fa8d6a66f5
Binary files /dev/null and b/src/algorithms/sets/permutations/images/overview.png differ
diff --git a/src/algorithms/sets/permutations/images/permutations-overview.jpeg b/src/algorithms/sets/permutations/images/permutations-overview.jpeg
new file mode 100644
index 0000000000..0df355d23d
Binary files /dev/null and b/src/algorithms/sets/permutations/images/permutations-overview.jpeg differ
diff --git a/src/algorithms/sets/permutations/images/permutations-with-repetitions.jpg b/src/algorithms/sets/permutations/images/permutations-with-repetitions.jpg
new file mode 100644
index 0000000000..3e5c0a7c95
Binary files /dev/null and b/src/algorithms/sets/permutations/images/permutations-with-repetitions.jpg differ
diff --git a/src/algorithms/sets/permutations/images/permutations-without-repetitions.jpg b/src/algorithms/sets/permutations/images/permutations-without-repetitions.jpg
new file mode 100644
index 0000000000..ca70b4d8c9
Binary files /dev/null and b/src/algorithms/sets/permutations/images/permutations-without-repetitions.jpg differ
diff --git a/src/algorithms/sets/permutations/permutateWithRepetitions.js b/src/algorithms/sets/permutations/permutateWithRepetitions.js
index 90502d29f6..08f918b558 100644
--- a/src/algorithms/sets/permutations/permutateWithRepetitions.js
+++ b/src/algorithms/sets/permutations/permutateWithRepetitions.js
@@ -8,19 +8,20 @@ export default function permutateWithRepetitions(
   permutationLength = permutationOptions.length,
 ) {
   if (permutationLength === 1) {
-    return permutationOptions.map(permutationOption => [permutationOption]);
+    return permutationOptions.map((permutationOption) => [permutationOption]);
   }
 
   // Init permutations array.
   const permutations = [];
 
+  // Get smaller permutations.
+  const smallerPermutations = permutateWithRepetitions(
+    permutationOptions,
+    permutationLength - 1,
+  );
+
   // Go through all options and join it to the smaller permutations.
   permutationOptions.forEach((currentOption) => {
-    const smallerPermutations = permutateWithRepetitions(
-      permutationOptions,
-      permutationLength - 1,
-    );
-
     smallerPermutations.forEach((smallerPermutation) => {
       permutations.push([currentOption].concat(smallerPermutation));
     });
diff --git a/src/algorithms/sets/power-set/README.md b/src/algorithms/sets/power-set/README.md
index 6681db0124..e96961dd8f 100644
--- a/src/algorithms/sets/power-set/README.md
+++ b/src/algorithms/sets/power-set/README.md
@@ -1,7 +1,7 @@
 # Power Set
 
 Power set of a set `S` is the set of all of the subsets of `S`, including the
-empty set and `S` itself. Power set of set `S` is denoted as `P(S)`. 
+empty set and `S` itself. Power set of set `S` is denoted as `P(S)`.
 
 For example for `{x, y, z}`, the subsets
 are:
@@ -21,32 +21,43 @@ are:
 
 ![Power Set](https://www.mathsisfun.com/sets/images/power-set.svg)
 
-Here is how we may illustrate the elements of the power set of the set `{x, y, z}` ordered with respect to 
+Here is how we may illustrate the elements of the power set of the set `{x, y, z}` ordered with respect to
 inclusion:
 
 ![](https://upload.wikimedia.org/wikipedia/commons/e/ea/Hasse_diagram_of_powerset_of_3.svg)
 
 **Number of Subsets**
 
-If `S` is a finite set with `|S| = n` elements, then the number of subsets 
-of `S` is `|P(S)| = 2^n`. This fact, which is the motivation for the 
+If `S` is a finite set with `|S| = n` elements, then the number of subsets
+of `S` is `|P(S)| = 2^n`. This fact, which is the motivation for the
 notation `2^S`, may be demonstrated simply as follows:
 
-> First, order the elements of `S` in any manner. We write any subset of `S` in 
-the format `{γ1, γ2, ..., γn}` where `γi , 1 ≤ i ≤ n`, can take the value 
+> First, order the elements of `S` in any manner. We write any subset of `S` in
+the format `{γ1, γ2, ..., γn}` where `γi , 1 ≤ i ≤ n`, can take the value
 of `0` or `1`. If `γi = 1`, the `i`-th element of `S` is in the subset;
-otherwise, the `i`-th element is not in the subset. Clearly the number of 
+otherwise, the `i`-th element is not in the subset. Clearly the number of
 distinct subsets that can be constructed this way is `2^n` as `γi ∈ {0, 1}`.
 
 ## Algorithms
 
 ### Bitwise Solution
 
-Each number in binary representation in a range from `0` to `2^n` does exactly 
-what we need: it shows by its bits (`0` or `1`) whether to include related 
-element from the set or not. For example, for the set `{1, 2, 3}` the binary 
+Each number in binary representation in a range from `0` to `2^n` does exactly
+what we need: it shows by its bits (`0` or `1`) whether to include related
+element from the set or not. For example, for the set `{1, 2, 3}` the binary
 number of `0b010` would mean that we need to include only `2` to the current set.
 
+|       | `abc` | Subset        |
+| :---: | :---: | :-----------: |
+| `0`   | `000` | `{}`          |
+| `1`   | `001` | `{c}`         |
+| `2`   | `010` | `{b}`         |
+| `3`   | `011` | `{c, b}`      |
+| `4`   | `100` | `{a}`         |
+| `5`   | `101` | `{a, c}`      |
+| `6`   | `110` | `{a, b}`      |
+| `7`   | `111` | `{a, b, c}`   |
+
 > See [bwPowerSet.js](./bwPowerSet.js) file for bitwise solution.
 
 ### Backtracking Solution
@@ -57,6 +68,44 @@ element.
 
 > See [btPowerSet.js](./btPowerSet.js) file for backtracking solution.
 
+### Cascading Solution
+
+This is, arguably, the simplest solution to generate a Power Set.
+
+We start with an empty set:
+
+```text
+powerSets = [[]]
+```
+
+Now, let's say:
+
+```text
+originalSet = [1, 2, 3]
+```
+
+Let's add the 1st element from the originalSet to all existing sets:
+
+```text
+[[]] ← 1 = [[], [1]]
+```
+
+Adding the 2nd element to all existing sets:
+
+```text
+[[], [1]] ← 2 = [[], [1], [2], [1, 2]]
+```
+
+Adding the 3nd element to all existing sets:
+
+```
+[[], [1], [2], [1, 2]] ← 3 = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
+```
+
+And so on, for the rest of the elements from the `originalSet`. On every iteration the number of sets is doubled, so we'll get `2^n` sets.
+
+> See [caPowerSet.js](./caPowerSet.js) file for cascading solution.
+
 ## References
 
 * [Wikipedia](https://en.wikipedia.org/wiki/Power_set)
diff --git a/src/algorithms/sets/power-set/__test__/caPowerSet.test.js b/src/algorithms/sets/power-set/__test__/caPowerSet.test.js
new file mode 100644
index 0000000000..4fad5efe20
--- /dev/null
+++ b/src/algorithms/sets/power-set/__test__/caPowerSet.test.js
@@ -0,0 +1,28 @@
+import caPowerSet from '../caPowerSet';
+
+describe('caPowerSet', () => {
+  it('should calculate power set of given set using cascading approach', () => {
+    expect(caPowerSet([1])).toEqual([
+      [],
+      [1],
+    ]);
+
+    expect(caPowerSet([1, 2])).toEqual([
+      [],
+      [1],
+      [2],
+      [1, 2],
+    ]);
+
+    expect(caPowerSet([1, 2, 3])).toEqual([
+      [],
+      [1],
+      [2],
+      [1, 2],
+      [3],
+      [1, 3],
+      [2, 3],
+      [1, 2, 3],
+    ]);
+  });
+});
diff --git a/src/algorithms/sets/power-set/btPowerSet.js b/src/algorithms/sets/power-set/btPowerSet.js
index 1cf2d19620..2007c124bf 100644
--- a/src/algorithms/sets/power-set/btPowerSet.js
+++ b/src/algorithms/sets/power-set/btPowerSet.js
@@ -6,16 +6,23 @@
  * @return {*[][]} - All subsets of original set.
  */
 function btPowerSetRecursive(originalSet, allSubsets = [[]], currentSubSet = [], startAt = 0) {
-  // In order to avoid duplication we need to start from next element every time we're forming a
-  // subset. If we will start from zero then we'll have duplicates like {3, 3, 3}.
+  // Let's iterate over originalSet elements that may be added to the subset
+  // without having duplicates. The value of startAt prevents adding the duplicates.
   for (let position = startAt; position < originalSet.length; position += 1) {
-    // Let's push current element to the subset.
+    // Let's push current element to the subset
     currentSubSet.push(originalSet[position]);
+
     // Current subset is already valid so let's memorize it.
+    // We do array destruction here to save the clone of the currentSubSet.
+    // We need to save a clone since the original currentSubSet is going to be
+    // mutated in further recursive calls.
     allSubsets.push([...currentSubSet]);
-    // Let's try to form all other subsets for the current subset.
+
+    // Let's try to generate all other subsets for the current subset.
+    // We're increasing the position by one to avoid duplicates in subset.
     btPowerSetRecursive(originalSet, allSubsets, currentSubSet, position + 1);
-    // BACKTRACK. Exclude last element from the subset and try the next one.
+
+    // BACKTRACK. Exclude last element from the subset and try the next valid one.
     currentSubSet.pop();
   }
 
diff --git a/src/algorithms/sets/power-set/bwPowerSet.js b/src/algorithms/sets/power-set/bwPowerSet.js
index 479cf98413..b8417bea4b 100644
--- a/src/algorithms/sets/power-set/bwPowerSet.js
+++ b/src/algorithms/sets/power-set/bwPowerSet.js
@@ -4,7 +4,7 @@
  * @param {*[]} originalSet
  * @return {*[][]}
  */
-export default function powerSet(originalSet) {
+export default function bwPowerSet(originalSet) {
   const subSets = [];
 
   // We will have 2^n possible combinations (where n is a length of original set).
diff --git a/src/algorithms/sets/power-set/caPowerSet.js b/src/algorithms/sets/power-set/caPowerSet.js
new file mode 100644
index 0000000000..45b9eb6169
--- /dev/null
+++ b/src/algorithms/sets/power-set/caPowerSet.js
@@ -0,0 +1,37 @@
+/**
+ * Find power-set of a set using CASCADING approach.
+ *
+ * @param {*[]} originalSet
+ * @return {*[][]}
+ */
+export default function caPowerSet(originalSet) {
+  // Let's start with an empty set.
+  const sets = [[]];
+
+  /*
+    Now, let's say:
+    originalSet = [1, 2, 3].
+
+    Let's add the first element from the originalSet to all existing sets:
+    [[]] ← 1 = [[], [1]]
+
+    Adding the 2nd element to all existing sets:
+    [[], [1]] ← 2 = [[], [1], [2], [1, 2]]
+
+    Adding the 3nd element to all existing sets:
+    [[], [1], [2], [1, 2]] ← 3 = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
+
+    And so on for the rest of the elements from originalSet.
+    On every iteration the number of sets is doubled, so we'll get 2^n sets.
+  */
+  for (let numIdx = 0; numIdx < originalSet.length; numIdx += 1) {
+    const existingSetsNum = sets.length;
+
+    for (let setIdx = 0; setIdx < existingSetsNum; setIdx += 1) {
+      const set = [...sets[setIdx], originalSet[numIdx]];
+      sets.push(set);
+    }
+  }
+
+  return sets;
+}
diff --git a/src/algorithms/sorting/bubble-sort/BubbleSort.js b/src/algorithms/sorting/bubble-sort/BubbleSort.js
index 4d3f156995..d78ebcbdc1 100644
--- a/src/algorithms/sorting/bubble-sort/BubbleSort.js
+++ b/src/algorithms/sorting/bubble-sort/BubbleSort.js
@@ -19,9 +19,7 @@ export default class BubbleSort extends Sort {
 
         // Swap elements if they are in wrong order.
         if (this.comparator.lessThan(array[j + 1], array[j])) {
-          const tmp = array[j + 1];
-          array[j + 1] = array[j];
-          array[j] = tmp;
+          [array[j], array[j + 1]] = [array[j + 1], array[j]];
 
           // Register the swap.
           swapped = true;
diff --git a/src/algorithms/sorting/bubble-sort/README.md b/src/algorithms/sorting/bubble-sort/README.md
index aed7169615..66347d86c7 100644
--- a/src/algorithms/sorting/bubble-sort/README.md
+++ b/src/algorithms/sorting/bubble-sort/README.md
@@ -1,11 +1,15 @@
 # Bubble Sort
 
-Bubble sort, sometimes referred to as sinking sort, is a 
-simple sorting algorithm that repeatedly steps through 
-the list to be sorted, compares each pair of adjacent 
-items and swaps them if they are in the wrong order. 
-The pass through the list is repeated until no swaps 
-are needed, which indicates that the list is sorted.
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
+Bubble sort, sometimes referred to as sinking sort, is a
+simple sorting algorithm that repeatedly steps through
+the list to be sorted, compares each pair of adjacent
+items and swaps them if they are in the wrong order
+(ascending or descending arrangement). The pass through
+the list is repeated until no swaps are needed, which
+indicates that the list is sorted.
 
 ![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/c/c8/Bubble-sort-example-300px.gif)
 
diff --git a/src/algorithms/sorting/bubble-sort/README.pt-BR.md b/src/algorithms/sorting/bubble-sort/README.pt-BR.md
new file mode 100644
index 0000000000..e650523d71
--- /dev/null
+++ b/src/algorithms/sorting/bubble-sort/README.pt-BR.md
@@ -0,0 +1,20 @@
+# Bubble Sort
+
+_Leia isso em outros idiomas:_
+[_English_](README.md)
+
+O bubble sort, ou ordenação por flutuação (literalmente "por bolha"), é um algoritmo de ordenação dos mais simples. A ideia é percorrer o vetor diversas vezes, e a cada passagem fazer flutuar para o topo o maior elemento da sequência. Essa movimentação lembra a forma como as bolhas em um tanque de água procuram seu próprio nível, e disso vem o nome do algoritmo.
+
+![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/c/c8/Bubble-sort-example-300px.gif)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Bubble sort**       | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Sim       |           |
+
+## Referências
+
+- [Wikipedia](https://pt.wikipedia.org/wiki/Bubble_sort)
+- [YouTube](https://www.youtube.com/watch?v=6Gv8vg0kcHc&index=27&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
diff --git a/src/algorithms/sorting/bucket-sort/BucketSort.js b/src/algorithms/sorting/bucket-sort/BucketSort.js
new file mode 100644
index 0000000000..a41e5892d2
--- /dev/null
+++ b/src/algorithms/sorting/bucket-sort/BucketSort.js
@@ -0,0 +1,46 @@
+import RadixSort from '../radix-sort/RadixSort';
+
+/**
+ * Bucket Sort
+ *
+ * @param {number[]} arr
+ * @param {number} bucketsNum
+ * @return {number[]}
+ */
+export default function BucketSort(arr, bucketsNum = 1) {
+  const buckets = new Array(bucketsNum).fill(null).map(() => []);
+
+  const minValue = Math.min(...arr);
+  const maxValue = Math.max(...arr);
+
+  const bucketSize = Math.ceil(Math.max(1, (maxValue - minValue) / bucketsNum));
+
+  // Place elements into buckets.
+  for (let i = 0; i < arr.length; i += 1) {
+    const currValue = arr[i];
+    const bucketIndex = Math.floor((currValue - minValue) / bucketSize);
+
+    // Edge case for max value.
+    if (bucketIndex === bucketsNum) {
+      buckets[bucketsNum - 1].push(currValue);
+    } else {
+      buckets[bucketIndex].push(currValue);
+    }
+  }
+
+  // Sort individual buckets.
+  for (let i = 0; i < buckets.length; i += 1) {
+    // Let's use the Radix Sorter here. This may give us
+    // the average O(n + k) time complexity to sort one bucket
+    // (where k is a number of digits in the longest number).
+    buckets[i] = new RadixSort().sort(buckets[i]);
+  }
+
+  // Merge sorted buckets into final output.
+  const sortedArr = [];
+  for (let i = 0; i < buckets.length; i += 1) {
+    sortedArr.push(...buckets[i]);
+  }
+
+  return sortedArr;
+}
diff --git a/src/algorithms/sorting/bucket-sort/README.md b/src/algorithms/sorting/bucket-sort/README.md
new file mode 100644
index 0000000000..673ba80b71
--- /dev/null
+++ b/src/algorithms/sorting/bucket-sort/README.md
@@ -0,0 +1,35 @@
+# Bucket Sort
+
+**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.
+
+## Algorithm
+
+Bucket sort works as follows:
+
+1. Set up an array of initially empty `buckets`.
+2. **Scatter:** Go over the original array, putting each object in its `bucket`.
+3. Sort each non-empty `bucket`.
+4. **Gather:** Visit the `buckets` in order and put all elements back into the original array.
+
+Elements are distributed among bins:
+
+![Elements are distributed among bins](./images/bucket_sort_1.png)
+
+Then, elements are sorted within each bin:
+
+![Elements are sorted within each bin](./images/bucket_sort_2.png)
+
+
+## Complexity
+
+The computational complexity depends on the algorithm used to sort each bucket, the number of buckets to use, and whether the input is uniformly distributed.
+
+The **worst-case** time complexity of bucket sort is
+`O(n^2)` if the sorting algorithm used on the bucket is *insertion sort*, which is the most common use case since the expectation is that buckets will not have too many elements relative to the entire list. In the worst case, all elements are placed in one bucket, causing the running time to reduce to the worst-case complexity of insertion sort (all elements are in reverse order). If the worst-case running time of the intermediate sort used is `O(n * log(n))`, then the worst-case running time of bucket sort will also be
+`O(n * log(n))`.
+
+On **average**, when the distribution of elements across buckets is reasonably uniform, it can be shown that bucket sort runs on average `O(n + k)` for `k` buckets.
+
+## References
+
+- [Bucket Sort on Wikipedia](https://en.wikipedia.org/wiki/Bucket_sort)
diff --git a/src/algorithms/sorting/bucket-sort/__test__/BucketSort.test.js b/src/algorithms/sorting/bucket-sort/__test__/BucketSort.test.js
new file mode 100644
index 0000000000..9cab525d9b
--- /dev/null
+++ b/src/algorithms/sorting/bucket-sort/__test__/BucketSort.test.js
@@ -0,0 +1,33 @@
+import BucketSort from '../BucketSort';
+import {
+  equalArr,
+  notSortedArr,
+  reverseArr,
+  sortedArr,
+} from '../../SortTester';
+
+describe('BucketSort', () => {
+  it('should sort the array of numbers with different buckets amounts', () => {
+    expect(BucketSort(notSortedArr, 4)).toEqual(sortedArr);
+    expect(BucketSort(equalArr, 4)).toEqual(equalArr);
+    expect(BucketSort(reverseArr, 4)).toEqual(sortedArr);
+    expect(BucketSort(sortedArr, 4)).toEqual(sortedArr);
+
+    expect(BucketSort(notSortedArr, 10)).toEqual(sortedArr);
+    expect(BucketSort(equalArr, 10)).toEqual(equalArr);
+    expect(BucketSort(reverseArr, 10)).toEqual(sortedArr);
+    expect(BucketSort(sortedArr, 10)).toEqual(sortedArr);
+
+    expect(BucketSort(notSortedArr, 50)).toEqual(sortedArr);
+    expect(BucketSort(equalArr, 50)).toEqual(equalArr);
+    expect(BucketSort(reverseArr, 50)).toEqual(sortedArr);
+    expect(BucketSort(sortedArr, 50)).toEqual(sortedArr);
+  });
+
+  it('should sort the array of numbers with the default buckets of 1', () => {
+    expect(BucketSort(notSortedArr)).toEqual(sortedArr);
+    expect(BucketSort(equalArr)).toEqual(equalArr);
+    expect(BucketSort(reverseArr)).toEqual(sortedArr);
+    expect(BucketSort(sortedArr)).toEqual(sortedArr);
+  });
+});
diff --git a/src/algorithms/sorting/bucket-sort/images/bucket_sort_1.png b/src/algorithms/sorting/bucket-sort/images/bucket_sort_1.png
new file mode 100644
index 0000000000..da0de9172f
Binary files /dev/null and b/src/algorithms/sorting/bucket-sort/images/bucket_sort_1.png differ
diff --git a/src/algorithms/sorting/bucket-sort/images/bucket_sort_2.png b/src/algorithms/sorting/bucket-sort/images/bucket_sort_2.png
new file mode 100644
index 0000000000..7b6f32c752
Binary files /dev/null and b/src/algorithms/sorting/bucket-sort/images/bucket_sort_2.png differ
diff --git a/src/algorithms/sorting/counting-sort/README.md b/src/algorithms/sorting/counting-sort/README.md
index 098961a0e0..40a9539a22 100644
--- a/src/algorithms/sorting/counting-sort/README.md
+++ b/src/algorithms/sorting/counting-sort/README.md
@@ -1,5 +1,8 @@
 # Counting Sort
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
 In computer science, **counting sort** is an algorithm for sorting 
 a collection of objects according to keys that are small integers; 
 that is, it is an integer sorting algorithm. It operates by 
diff --git a/src/algorithms/sorting/counting-sort/README.pt-br.md b/src/algorithms/sorting/counting-sort/README.pt-br.md
new file mode 100644
index 0000000000..af2c75f6af
--- /dev/null
+++ b/src/algorithms/sorting/counting-sort/README.pt-br.md
@@ -0,0 +1,70 @@
+# Counting Sort
+
+_Leia isso em outros idiomas:_
+[_English_](README.md)
+
+Em ciência da computação, **counting sort** é um algoritmo para ordenar
+uma coleção de objetos de acordo com chaves que são pequenos inteiros;
+ou seja, é um algoritmo de ordenação de inteiros. Ele opera por
+contando o número de objetos que têm cada valor de chave distinto,
+e usando aritmética nessas contagens para determinar as posições
+de cada valor de chave na sequência de saída. Seu tempo de execução é
+linear no número de itens e a diferença entre o
+valores de chave máximo e mínimo, portanto, é adequado apenas para
+uso em situações em que a variação de tonalidades não é significativamente
+maior que o número de itens. No entanto, muitas vezes é usado como
+sub-rotina em outro algoritmo de ordenação, radix sort, que pode
+lidar com chaves maiores de forma mais eficiente.
+
+Como a classificação por contagem usa valores-chave como índices em um vetor,
+não é uma ordenação por comparação, e o limite inferior `Ω(n log n)` para
+a ordenação por comparação não se aplica a ele. A classificação por bucket pode ser usada
+para muitas das mesmas tarefas que a ordenação por contagem, com um tempo semelhante
+análise; no entanto, em comparação com a classificação por contagem, a classificação por bucket requer
+listas vinculadas, arrays dinâmicos ou uma grande quantidade de pré-alocados
+memória para armazenar os conjuntos de itens dentro de cada bucket, enquanto
+A classificação por contagem armazena um único número (a contagem de itens)
+por balde.
+
+A classificação por contagem funciona melhor quando o intervalo de números para cada
+elemento do vetor é muito pequeno.
+
+## Algoritmo
+
+**Passo I**
+
+Na primeira etapa, calculamos a contagem de todos os elementos do
+vetor de entrada 'A'. Em seguida, armazene o resultado no vetor de contagem `C`.
+A maneira como contamos é descrita abaixo.
+
+![Counting Sort](https://3.bp.blogspot.com/-jJchly1BkTc/WLGqCFDdvCI/AAAAAAAAAHA/luljAlz2ptMndIZNH0KLTTuQMNsfzDeFQCLcB/s1600/CSortUpdatedStepI.gif)
+
+**Passo II**
+
+Na segunda etapa, calculamos quantos elementos existem na entrada
+do vetor `A` que são menores ou iguais para o índice fornecido.
+`Ci` = números de elementos menores ou iguais a `i` no vetor de entrada.
+
+![Counting Sort](https://1.bp.blogspot.com/-1vFu-VIRa9Y/WLHGuZkdF3I/AAAAAAAAAHs/8jKu2dbQee4ap9xlVcNsILrclqw0UxAVACLcB/s1600/Step-II.png)
+
+**Passo III**
+
+Nesta etapa, colocamos o elemento `A` do vetor de entrada em classificado
+posição usando a ajuda do vetor de contagem construída `C`, ou seja, o que
+construímos no passo dois. Usamos o vetor de resultados `B` para armazenar
+os elementos ordenados. Aqui nós lidamos com o índice de `B` começando de
+zero.
+ 
+![Counting Sort](https://1.bp.blogspot.com/-xPqylngqASY/WLGq3p9n9vI/AAAAAAAAAHM/JHdtXAkJY8wYzDMBXxqarjmhpPhM0u8MACLcB/s1600/ResultArrayCS.gif)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Counting sort**     | n + r           | n + r               | n + r               | n + r     | Sim       | r - Maior número no vetor |
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Counting_sort)
+- [YouTube](https://www.youtube.com/watch?v=OKd534EWcdk&index=61&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [EfficientAlgorithms](https://efficientalgorithms.blogspot.com/2016/09/lenear-sorting-counting-sort.html)
diff --git a/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js b/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js
index be15d03233..9bf5d5c513 100644
--- a/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js
+++ b/src/algorithms/sorting/counting-sort/__test__/CountingSort.test.js
@@ -27,14 +27,10 @@ describe('CountingSort', () => {
     const sorter = new CountingSort({ visitingCallback });
 
     // Detect biggest number in array in prior.
-    const biggestElement = notSortedArr.reduce((accumulator, element) => {
-      return element > accumulator ? element : accumulator;
-    }, 0);
+    const biggestElement = Math.max(...notSortedArr);
 
     // Detect smallest number in array in prior.
-    const smallestElement = notSortedArr.reduce((accumulator, element) => {
-      return element < accumulator ? element : accumulator;
-    }, 0);
+    const smallestElement = Math.min(...notSortedArr);
 
     const sortedArray = sorter.sort(notSortedArr, smallestElement, biggestElement);
 
diff --git a/src/algorithms/sorting/heap-sort/README.md b/src/algorithms/sorting/heap-sort/README.md
index 6ccc50b3b0..2370776f1b 100644
--- a/src/algorithms/sorting/heap-sort/README.md
+++ b/src/algorithms/sorting/heap-sort/README.md
@@ -1,5 +1,8 @@
 # Heap Sort
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
 Heapsort is a comparison-based sorting algorithm.
 Heapsort can be thought of as an improved selection
 sort: like that algorithm, it divides its input into
diff --git a/src/algorithms/sorting/heap-sort/README.pt-BR.md b/src/algorithms/sorting/heap-sort/README.pt-BR.md
new file mode 100644
index 0000000000..6d5a2d4849
--- /dev/null
+++ b/src/algorithms/sorting/heap-sort/README.pt-BR.md
@@ -0,0 +1,20 @@
+# Heap Sort
+
+_Leia isso em outros idiomas:_
+[_English_](README.md)
+
+Heapsort é um algoritmo de ordenação baseado em comparação. O Heapsort pode ser pensado como uma seleção aprimorada sort: como esse algoritmo, ele divide sua entrada em uma região classificada e uma região não classificada, e iterativamente encolhe a região não classificada extraindo o maior elemento e movendo-o para a região classificada. A melhoria consiste no uso de uma estrutura de dados heap em vez de uma busca em tempo linear para encontrar o máximo.
+
+![Visualização do Algoritmo](https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif)
+
+![Visualização do Algoritmo](https://upload.wikimedia.org/wikipedia/commons/4/4d/Heapsort-example.gif)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Heap sort**         | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | 1         | Não        |           |
+
+## Referências
+
+[Wikipedia](https://en.wikipedia.org/wiki/Heapsort)
diff --git a/src/algorithms/sorting/insertion-sort/InsertionSort.js b/src/algorithms/sorting/insertion-sort/InsertionSort.js
index 472625480a..7a80161379 100644
--- a/src/algorithms/sorting/insertion-sort/InsertionSort.js
+++ b/src/algorithms/sorting/insertion-sort/InsertionSort.js
@@ -5,14 +5,14 @@ export default class InsertionSort extends Sort {
     const array = [...originalArray];
 
     // Go through all array elements...
-    for (let i = 0; i < array.length; i += 1) {
+    for (let i = 1; i < array.length; i += 1) {
       let currentIndex = i;
 
       // Call visiting callback.
       this.callbacks.visitingCallback(array[i]);
 
-      // Go and check if previous elements and greater then current one.
-      // If this is the case then swap that elements.
+      // Check if previous element is greater than current element.
+      // If so, swap the two elements.
       while (
         array[currentIndex - 1] !== undefined
         && this.comparator.lessThan(array[currentIndex], array[currentIndex - 1])
@@ -21,9 +21,13 @@ export default class InsertionSort extends Sort {
         this.callbacks.visitingCallback(array[currentIndex - 1]);
 
         // Swap the elements.
-        const tmp = array[currentIndex - 1];
-        array[currentIndex - 1] = array[currentIndex];
-        array[currentIndex] = tmp;
+        [
+          array[currentIndex - 1],
+          array[currentIndex],
+        ] = [
+          array[currentIndex],
+          array[currentIndex - 1],
+        ];
 
         // Shift current index left.
         currentIndex -= 1;
diff --git a/src/algorithms/sorting/insertion-sort/README.md b/src/algorithms/sorting/insertion-sort/README.md
index cd3a74ef34..8bc922683f 100644
--- a/src/algorithms/sorting/insertion-sort/README.md
+++ b/src/algorithms/sorting/insertion-sort/README.md
@@ -1,5 +1,8 @@
 # Insertion Sort
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
 Insertion sort is a simple sorting algorithm that builds 
 the final sorted array (or list) one item at a time. 
 It is much less efficient on large lists than more 
diff --git a/src/algorithms/sorting/insertion-sort/README.pt-BR.md b/src/algorithms/sorting/insertion-sort/README.pt-BR.md
new file mode 100644
index 0000000000..a36d2742be
--- /dev/null
+++ b/src/algorithms/sorting/insertion-sort/README.pt-BR.md
@@ -0,0 +1,22 @@
+# Insertion Sort
+
+_Leia isso em outros idiomas:_
+[_English_](README.md)
+
+A ordenação por inserção é um algoritmo de ordenação simples que criaa matriz classificada final (ou lista) um item de cada vez.
+É muito menos eficiente em grandes listas do que mais algoritmos avançados, como quicksort, heapsort ou merge
+ordenar.
+
+![Visualização do Algoritmo](https://upload.wikimedia.org/wikipedia/commons/4/42/Insertion_sort.gif)
+
+![Visualização do Algoritmo](https://upload.wikimedia.org/wikipedia/commons/0/0f/Insertion-sort-example-300px.gif)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Insertion sort**    | n               | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Sim       |           |
+
+## Referências
+
+[Wikipedia](https://en.wikipedia.org/wiki/Insertion_sort)
diff --git a/src/algorithms/sorting/insertion-sort/__test__/InsertionSort.test.js b/src/algorithms/sorting/insertion-sort/__test__/InsertionSort.test.js
index 588472e222..8453b7db16 100644
--- a/src/algorithms/sorting/insertion-sort/__test__/InsertionSort.test.js
+++ b/src/algorithms/sorting/insertion-sort/__test__/InsertionSort.test.js
@@ -8,10 +8,10 @@ import {
 } from '../../SortTester';
 
 // Complexity constants.
-const SORTED_ARRAY_VISITING_COUNT = 20;
-const NOT_SORTED_ARRAY_VISITING_COUNT = 101;
-const REVERSE_SORTED_ARRAY_VISITING_COUNT = 210;
-const EQUAL_ARRAY_VISITING_COUNT = 20;
+const SORTED_ARRAY_VISITING_COUNT = 19;
+const NOT_SORTED_ARRAY_VISITING_COUNT = 100;
+const REVERSE_SORTED_ARRAY_VISITING_COUNT = 209;
+const EQUAL_ARRAY_VISITING_COUNT = 19;
 
 describe('InsertionSort', () => {
   it('should sort array', () => {
diff --git a/src/algorithms/sorting/merge-sort/MergeSort.js b/src/algorithms/sorting/merge-sort/MergeSort.js
index 6a2f8a843c..23fca74e49 100644
--- a/src/algorithms/sorting/merge-sort/MergeSort.js
+++ b/src/algorithms/sorting/merge-sort/MergeSort.js
@@ -24,36 +24,37 @@ export default class MergeSort extends Sort {
   }
 
   mergeSortedArrays(leftArray, rightArray) {
-    let sortedArray = [];
+    const sortedArray = [];
 
-    // In case if arrays are not of size 1.
-    while (leftArray.length && rightArray.length) {
-      let minimumElement = null;
+    // Use array pointers to exclude old elements after they have been added to the sorted array.
+    let leftIndex = 0;
+    let rightIndex = 0;
 
-      // Find minimum element of two arrays.
-      if (this.comparator.lessThanOrEqual(leftArray[0], rightArray[0])) {
-        minimumElement = leftArray.shift();
+    while (leftIndex < leftArray.length && rightIndex < rightArray.length) {
+      let minElement = null;
+
+      // Find the minimum element between the left and right array.
+      if (this.comparator.lessThanOrEqual(leftArray[leftIndex], rightArray[rightIndex])) {
+        minElement = leftArray[leftIndex];
+        // Increment index pointer to the right
+        leftIndex += 1;
       } else {
-        minimumElement = rightArray.shift();
+        minElement = rightArray[rightIndex];
+        // Increment index pointer to the right
+        rightIndex += 1;
       }
 
-      // Call visiting callback.
-      this.callbacks.visitingCallback(minimumElement);
-
-      // Push the minimum element of two arrays to the sorted array.
-      sortedArray.push(minimumElement);
-    }
+      // Add the minimum element to the sorted array.
+      sortedArray.push(minElement);
 
-    // If one of two array still have elements we need to just concatenate
-    // this element to the sorted array since it is already sorted.
-    if (leftArray.length) {
-      sortedArray = sortedArray.concat(leftArray);
-    }
-
-    if (rightArray.length) {
-      sortedArray = sortedArray.concat(rightArray);
+      // Call visiting callback.
+      this.callbacks.visitingCallback(minElement);
     }
 
-    return sortedArray;
+    // There will be elements remaining from either the left OR the right
+    // Concatenate the remaining elements into the sorted array
+    return sortedArray
+      .concat(leftArray.slice(leftIndex))
+      .concat(rightArray.slice(rightIndex));
   }
 }
diff --git a/src/algorithms/sorting/merge-sort/README.ko-KR.md b/src/algorithms/sorting/merge-sort/README.ko-KR.md
new file mode 100644
index 0000000000..5447fd217e
--- /dev/null
+++ b/src/algorithms/sorting/merge-sort/README.ko-KR.md
@@ -0,0 +1,22 @@
+# 병합 정렬
+
+컴퓨터과학에서, 병합 정렬(일반적으로 mergesort라고 쓰는)은 효율적이고, 범용적인, 비교 기반의 정렬 알고리즘입니다. 대부분의 구현들은 안정적인 정렬을 만들어내며, 이는 정렬된 산출물에서 동일한 요소들의 입력 순서가 유지된다는 것을 의미합니다. 병합 정렬은 1945년에 John von Neumann이 만든 분할 정복 알고리즘입니다.
+
+병합 정렬의 예시입니다. 우선 리스트를 가장 작은 단위로 나누고(한 개의 요소), 두 개의 인접한 리스트를 정렬하고 병합하기 위해 각 요소와 인접한 리스트를 비교합니다. 마지막으로 모든 요소들은 정렬되고 병합됩니다.
+
+![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif)
+
+재귀적인 병합 정렬 알고리즘은 7개의 정수값을 가진 배열을 정렬하는데 사용됩니다. 다음은 합병 정렬을 모방하기 위해 사람이 취하는 단계입니다.(하향식)
+
+![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/e/e6/Merge_sort_algorithm_diagram.svg)
+
+## 복잡도
+
+| Name                  | Best            | Average             | Worst               | Memory    | Stable    | Comments  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Merge sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Yes       |           |
+
+## 참조
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Merge_sort)
+- [YouTube](https://www.youtube.com/watch?v=KF2j-9iSf4Q&index=27&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/sorting/merge-sort/README.md b/src/algorithms/sorting/merge-sort/README.md
index f4ed837a22..2931329482 100644
--- a/src/algorithms/sorting/merge-sort/README.md
+++ b/src/algorithms/sorting/merge-sort/README.md
@@ -1,23 +1,27 @@
 # Merge Sort
 
-In computer science, merge sort (also commonly spelled 
-mergesort) is an efficient, general-purpose, 
-comparison-based sorting algorithm. Most implementations 
-produce a stable sort, which means that the implementation 
-preserves the input order of equal elements in the sorted 
-output. Mergesort is a divide and conquer algorithm that 
+_Read this in other languages:_
+[_한국어_](README.ko-KR.md),
+[_Português_](README.pt-BR.md)
+
+In computer science, merge sort (also commonly spelled
+mergesort) is an efficient, general-purpose,
+comparison-based sorting algorithm. Most implementations
+produce a stable sort, which means that the implementation
+preserves the input order of equal elements in the sorted
+output. Mergesort is a divide and conquer algorithm that
 was invented by John von Neumann in 1945.
 
-An example of merge sort. First divide the list into 
-the smallest unit (1 element), then compare each 
-element with the adjacent list to sort and merge the 
-two adjacent lists. Finally all the elements are sorted 
+An example of merge sort. First divide the list into
+the smallest unit (1 element), then compare each
+element with the adjacent list to sort and merge the
+two adjacent lists. Finally all the elements are sorted
 and merged.
 
 ![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif)
 
-A recursive merge sort algorithm used to sort an array of 7 
-integer values. These are the steps a human would take to 
+A recursive merge sort algorithm used to sort an array of 7
+integer values. These are the steps a human would take to
 emulate merge sort (top-down).
 
 ![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/e/e6/Merge_sort_algorithm_diagram.svg)
diff --git a/src/algorithms/sorting/merge-sort/README.pt-BR.md b/src/algorithms/sorting/merge-sort/README.pt-BR.md
new file mode 100644
index 0000000000..f7be449f04
--- /dev/null
+++ b/src/algorithms/sorting/merge-sort/README.pt-BR.md
@@ -0,0 +1,38 @@
+# Merge Sort
+
+_Leia isso em outros idiomas:_
+[_한국어_](README.ko-KR.md),
+[_English_](README.md)
+
+Em ciência da computação, merge sort (também comumente escrito
+mergesort) é uma ferramenta eficiente, de propósito geral,
+algoritmo de ordenação baseado em comparação. A maioria das implementações
+produzir uma classificação estável, o que significa que a implementação
+preserva a ordem de entrada de elementos iguais na ordenação
+resultado. Mergesort é um algoritmo de divisão e conquista que
+foi inventado por John von Neumann em 1945.
+
+Um exemplo de classificação de mesclagem. Primeiro divida a lista em
+a menor unidade (1 elemento), então compare cada
+elemento com a lista adjacente para classificar e mesclar o
+duas listas adjacentes. Finalmente todos os elementos são ordenados
+e mesclado.
+
+![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif)
+
+Um algoritmo de classificação de mesclagem recursivo usado para classificar uma matriz de 7
+valores inteiros. Estes são os passos que um ser humano daria para
+emular merge sort (top-down).
+
+![Merge Sort](https://upload.wikimedia.org/wikipedia/commons/e/e6/Merge_sort_algorithm_diagram.svg)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Merge sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n&nbsp;log(n)       | n         | Sim       |           |
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Merge_sort)
+- [YouTube](https://www.youtube.com/watch?v=KF2j-9iSf4Q&index=27&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/sorting/quick-sort/QuickSortInPlace.js b/src/algorithms/sorting/quick-sort/QuickSortInPlace.js
index 0fc6cf156f..d02298b230 100644
--- a/src/algorithms/sorting/quick-sort/QuickSortInPlace.js
+++ b/src/algorithms/sorting/quick-sort/QuickSortInPlace.js
@@ -4,71 +4,73 @@ export default class QuickSortInPlace extends Sort {
   /** Sorting in place avoids unnecessary use of additional memory, but modifies input array.
    *
    * This process is difficult to describe, but much clearer with a visualization:
-   * @see: http://www.algomation.com/algorithm/quick-sort-visualization
+   * @see: https://www.hackerearth.com/practice/algorithms/sorting/quick-sort/visualize/
    *
-   * @param {*[]} originalArray
+   * @param {*[]} originalArray - Not sorted array.
    * @param {number} inputLowIndex
    * @param {number} inputHighIndex
-   * @return {*[]}
+   * @param {boolean} recursiveCall
+   * @return {*[]} - Sorted array.
    */
-  sort(originalArray, inputLowIndex, inputHighIndex) {
-    // Destructures array on initial passthrough, and then sorts in place.
-    const array = inputLowIndex === undefined ? [...originalArray] : originalArray;
+  sort(
+    originalArray,
+    inputLowIndex = 0,
+    inputHighIndex = originalArray.length - 1,
+    recursiveCall = false,
+  ) {
+    // Copies array on initial call, and then sorts in place.
+    const array = recursiveCall ? originalArray : [...originalArray];
 
     /**
-     * Partition array segment and return index of last swap
+     * The partitionArray() operates on the subarray between lowIndex and highIndex, inclusive.
+     * It arbitrarily chooses the last element in the subarray as the pivot.
+     * Then, it partially sorts the subarray into elements than are less than the pivot,
+     * and elements that are greater than or equal to the pivot.
+     * Each time partitionArray() is executed, the pivot element is in its final sorted position.
      *
      * @param {number} lowIndex
      * @param {number} highIndex
      * @return {number}
      */
-    const partition = (lowIndex, highIndex) => {
+    const partitionArray = (lowIndex, highIndex) => {
       /**
+       * Swaps two elements in array.
        * @param {number} leftIndex
        * @param {number} rightIndex
        */
       const swap = (leftIndex, rightIndex) => {
-        const tempVariable = array[leftIndex];
+        const temp = array[leftIndex];
         array[leftIndex] = array[rightIndex];
-        array[rightIndex] = tempVariable;
+        array[rightIndex] = temp;
       };
 
       const pivot = array[highIndex];
-      this.callbacks.visitingCallback(array[pivot]);
+      // visitingCallback is used for time-complexity analysis.
+      this.callbacks.visitingCallback(pivot);
 
-      let firstRunner = lowIndex - 1;
-      for (let secondRunner = lowIndex; secondRunner < highIndex; secondRunner += 1) {
-        if (this.comparator.lessThan(array[secondRunner], pivot)) {
-          firstRunner += 1;
-          swap(firstRunner, secondRunner);
+      let partitionIndex = lowIndex;
+      for (let currentIndex = lowIndex; currentIndex < highIndex; currentIndex += 1) {
+        if (this.comparator.lessThan(array[currentIndex], pivot)) {
+          swap(partitionIndex, currentIndex);
+          partitionIndex += 1;
         }
       }
 
-      if (this.comparator.lessThan(pivot, array[firstRunner + 1])) {
-        swap(firstRunner + 1, highIndex);
-      }
+      // The element at the partitionIndex is guaranteed to be greater than or equal to pivot.
+      // All elements to the left of partitionIndex are guaranteed to be less than pivot.
+      // Swapping the pivot with the partitionIndex therefore places the pivot in its
+      // final sorted position.
+      swap(partitionIndex, highIndex);
 
-      return firstRunner + 1;
+      return partitionIndex;
     };
 
-    /*
-     * While we can use a default parameter to set `low` to 0, we would
-     * still have to set `high`'s default within the function as we
-     * don't have access to `array.length - 1` when declaring parameters
-     */
-    const lowIndex = inputLowIndex === undefined ? 0 : inputLowIndex;
-    const highIndex = inputHighIndex === undefined ? array.length - 1 : inputHighIndex;
-
-    // Base case is when low and high converge
-    if (lowIndex < highIndex) {
-      const partitionIndex = partition(lowIndex, highIndex);
-      /*
-       * `partition()` swaps elements of the array based on their comparison to the `hi` parameter,
-       * and then returns the index where swapping is no longer necessary, which can be best thought
-       * of as the pivot used to split an array in a non-in-place quicksort
-       */
-      this.sort(array, lowIndex, partitionIndex - 1);
-      this.sort(array, partitionIndex + 1, highIndex);
+    // Base case is when low and high converge.
+    if (inputLowIndex < inputHighIndex) {
+      const partitionIndex = partitionArray(inputLowIndex, inputHighIndex);
+      const RECURSIVE_CALL = true;
+      this.sort(array, inputLowIndex, partitionIndex - 1, RECURSIVE_CALL);
+      this.sort(array, partitionIndex + 1, inputHighIndex, RECURSIVE_CALL);
     }
 
     return array;
diff --git a/src/algorithms/sorting/quick-sort/README.md b/src/algorithms/sorting/quick-sort/README.md
index 88c768aa37..9dcec0f12f 100644
--- a/src/algorithms/sorting/quick-sort/README.md
+++ b/src/algorithms/sorting/quick-sort/README.md
@@ -1,5 +1,9 @@
 # Quicksort
 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Português_](README.pt-BR.md)
+
 Quicksort is a divide and conquer algorithm.
 Quicksort first divides a large array into two smaller 
 sub-arrays: the low elements and the high elements.
diff --git a/src/algorithms/sorting/quick-sort/README.pt-BR.md b/src/algorithms/sorting/quick-sort/README.pt-BR.md
new file mode 100644
index 0000000000..ca3a45699f
--- /dev/null
+++ b/src/algorithms/sorting/quick-sort/README.pt-BR.md
@@ -0,0 +1,39 @@
+# Quicksort
+
+_Leia isso em outros idiomas:_
+[_简体中文_](README.zh-CN.md),
+[_English_](README.md)
+
+Quicksort é um algoritmo de dividir para conquistar.
+Quicksort primeiro divide uma grande matriz em duas menores
+submatrizes: os elementos baixos e os elementos altos.
+O Quicksort pode então classificar recursivamente as submatrizes.
+
+As etapas são:
+
+1. Escolha um elemento, denominado pivô, na matriz.
+2. Particionamento: reordene a matriz para que todos os elementos com
+valores menores que o pivô estejam antes do pivô, enquanto todos
+elementos com valores maiores do que o pivô vêm depois dele
+(valores iguais podem ser usados em qualquer direção). Após este particionamento,
+o pivô está em sua posição final. Isso é chamado de
+operação de partição.
+3. Aplique recursivamente as etapas acima à submatriz de
+elementos com valores menores e separadamente para o
+submatriz de elementos com valores maiores.
+
+Visualização animada do algoritmo quicksort.
+As linhas horizontais são valores dinâmicos. 
+
+![Quicksort](https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Quick sort**        | n&nbsp;log(n)   | n&nbsp;log(n)       | n<sup>2</sup>       | log(n)    | Não        |  Quicksort geralmente é feito no local com espaço de pilha O(log(n)) |
+
+## Referências
+
+- [Wikipedia](https://pt.wikipedia.org/wiki/Quicksort)
+- [YouTube](https://www.youtube.com/watch?v=SLauY6PpjW4&index=28&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/sorting/quick-sort/README.zh-CN.md b/src/algorithms/sorting/quick-sort/README.zh-CN.md
new file mode 100644
index 0000000000..96b2e44de8
--- /dev/null
+++ b/src/algorithms/sorting/quick-sort/README.zh-CN.md
@@ -0,0 +1,27 @@
+# 快速排序
+
+快速排序是一种分而治之的算法。快速排序首先将一个大数组分成两个较小的子数组：比某个数小的元素和比某个数大的元素。然后快速排序可以递归地对子数组进行排序。
+
+步骤是：
+
+1. 从数组中选择一个元素，称为基点
+
+2. 分区：对数组重新排序，使所有值小于基点的元素都在它左边，而所有值大于基点的元素都在它右边（相等的值可以放在任何一边）。在此分区之后，基点处于其最终位置（左边和右边的中间位置）。这称为分区操作。
+
+3. 递归地将上述步骤应用于左边的数组和右边的数组。
+
+快速排序算法的动画可视化。水平线是基点值。
+
+![Quicksort](https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif)
+
+## 复杂度
+
+| Name           |     Best      |    Average    |     Worst     | Memory | Stable | Comments                                                      |
+| -------------- | :-----------: | :-----------: | :-----------: | :----: | :----: | :------------------------------------------------------------ |
+| **Quick sort** | n&nbsp;log(n) | n&nbsp;log(n) | n<sup>2</sup> | log(n) |   No   | Quicksort is usually done in-place with O(log(n)) stack space |
+
+## 引用
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Quicksort)
+
+- [YouTube](https://www.youtube.com/watch?v=SLauY6PpjW4&index=28&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/sorting/radix-sort/README.md b/src/algorithms/sorting/radix-sort/README.md
index 1d1fab1852..108d7d19b6 100644
--- a/src/algorithms/sorting/radix-sort/README.md
+++ b/src/algorithms/sorting/radix-sort/README.md
@@ -1,29 +1,39 @@
 # Radix Sort
 
-In computer science, **radix sort** is a non-comparative integer sorting 
-algorithm that sorts data with integer keys by grouping keys by the individual 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md),
+
+In computer science, **radix sort** is a non-comparative integer sorting
+algorithm that sorts data with integer keys by grouping keys by the individual
 digits which share the same significant position and value. A positional notation
-is required, but because integers can represent strings of characters 
-(e.g., names or dates) and specially formatted floating point numbers, radix 
+is required, but because integers can represent strings of characters
+(e.g., names or dates) and specially formatted floating point numbers, radix
 sort is not limited to integers.
 
+*Where does the name come from?*
+
+In mathematical numeral systems, the *radix* or base is the number of unique digits,
+including the digit zero, used to represent numbers in a positional numeral system.
+For example, a binary system (using numbers 0 and 1) has a radix of 2 and a decimal
+system (using numbers 0 to 9) has a radix of 10.
+
 ## Efficiency
 
-The topic of the efficiency of radix sort compared to other sorting algorithms is 
-somewhat tricky and subject to quite a lot of misunderstandings. Whether radix 
-sort is equally efficient, less efficient or more efficient than the best 
-comparison-based algorithms depends on the details of the assumptions made. 
-Radix sort complexity is `O(wn)` for `n` keys which are integers of word size `w`. 
-Sometimes `w` is presented as a constant, which would make radix sort better 
-(for sufficiently large `n`) than the best comparison-based sorting algorithms, 
-which all perform `O(n log n)` comparisons to sort `n` keys. However, in 
-general `w` cannot be considered a constant: if all `n` keys are distinct, 
-then `w` has to be at least `log n` for a random-access machine to be able to 
-store them in memory, which gives at best a time complexity `O(n log n)`. That 
-would seem to make radix sort at most equally efficient as the best 
+The topic of the efficiency of radix sort compared to other sorting algorithms is
+somewhat tricky and subject to quite a lot of misunderstandings. Whether radix
+sort is equally efficient, less efficient or more efficient than the best
+comparison-based algorithms depends on the details of the assumptions made.
+Radix sort complexity is `O(wn)` for `n` keys which are integers of word size `w`.
+Sometimes `w` is presented as a constant, which would make radix sort better
+(for sufficiently large `n`) than the best comparison-based sorting algorithms,
+which all perform `O(n log n)` comparisons to sort `n` keys. However, in
+general `w` cannot be considered a constant: if all `n` keys are distinct,
+then `w` has to be at least `log n` for a random-access machine to be able to
+store them in memory, which gives at best a time complexity `O(n log n)`. That
+would seem to make radix sort at most equally efficient as the best
 comparison-based sorts (and worse if keys are much longer than `log n`).
 
-![Radix Sort](https://www.researchgate.net/publication/291086231/figure/fig1/AS:614214452404240@1523451545568/Simplistic-illustration-of-the-steps-performed-in-a-radix-sort-In-this-example-the.png)
+![Radix Sort](./images/radix-sort.png)
 
 ## Complexity
 
diff --git a/src/algorithms/sorting/radix-sort/README.pt-BR.md b/src/algorithms/sorting/radix-sort/README.pt-BR.md
new file mode 100644
index 0000000000..2274440984
--- /dev/null
+++ b/src/algorithms/sorting/radix-sort/README.pt-BR.md
@@ -0,0 +1,48 @@
+# Radix Sort
+
+_Leia isso em outros idiomas:_
+[_English_](README.md)
+
+Em ciência da computação, **radix sort** é uma classificação inteira não comparativa
+algoritmo que classifica os dados com chaves inteiras agrupando as chaves pelo indivíduo
+dígitos que compartilham a mesma posição e valor significativos. Uma notação posicional
+é necessário, mas porque os números inteiros podem representar cadeias de caracteres
+(por exemplo, nomes ou datas) e números de ponto flutuante especialmente formatados, base
+sort não está limitado a inteiros.
+
+*De onde vem o nome?*
+
+Em sistemas numéricos matemáticos, a *radix* ou base é o número de dígitos únicos,
+incluindo o dígito zero, usado para representar números em um sistema de numeração posicional.
+Por exemplo, um sistema binário (usando números 0 e 1) tem uma raiz de 2 e um decimal
+sistema (usando números de 0 a 9) tem uma raiz de 10.
+
+## Eficiência
+
+O tópico da eficiência do radix sort comparado a outros algoritmos de ordenação é
+um pouco complicado e sujeito a muitos mal-entendidos. Se raiz
+sort é igualmente eficiente, menos eficiente ou mais eficiente do que o melhor
+algoritmos baseados em comparação depende dos detalhes das suposições feitas.
+A complexidade de classificação de raiz é `O(wn)` para chaves `n` que são inteiros de tamanho de palavra `w`.
+Às vezes, `w` é apresentado como uma constante, o que tornaria a classificação radix melhor
+(para `n` suficientemente grande) do que os melhores algoritmos de ordenação baseados em comparação,
+que todos realizam comparações `O(n log n)` para classificar chaves `n`. No entanto, em
+geral `w` não pode ser considerado uma constante: se todas as chaves `n` forem distintas,
+então `w` tem que ser pelo menos `log n` para que uma máquina de acesso aleatório seja capaz de
+armazená-los na memória, o que dá na melhor das hipóteses uma complexidade de tempo `O(n log n)`. Este
+parece tornar a ordenação radix no máximo tão eficiente quanto a melhor
+ordenações baseadas em comparação (e pior se as chaves forem muito mais longas que `log n`).
+
+![Radix Sort](https://www.researchgate.net/publication/291086231/figure/fig1/AS:614214452404240@1523451545568/Simplistic-illustration-of-the-steps-performed-in-a-radix-sort-In-this-example-the.png)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Radix sort**        | n * k           | n * k               | n * k               | n + k     | Sim       | k - comprimento da chave mais longa |
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Radix_sort)
+- [YouTube](https://www.youtube.com/watch?v=XiuSW_mEn7g&index=62&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [ResearchGate](https://www.researchgate.net/figure/Simplistic-illustration-of-the-steps-performed-in-a-radix-sort-In-this-example-the_fig1_291086231)
diff --git a/src/algorithms/sorting/radix-sort/images/radix-sort.png b/src/algorithms/sorting/radix-sort/images/radix-sort.png
new file mode 100644
index 0000000000..0cf9bf9e4d
Binary files /dev/null and b/src/algorithms/sorting/radix-sort/images/radix-sort.png differ
diff --git a/src/algorithms/sorting/selection-sort/README.md b/src/algorithms/sorting/selection-sort/README.md
index 6a708d2579..005c616131 100644
--- a/src/algorithms/sorting/selection-sort/README.md
+++ b/src/algorithms/sorting/selection-sort/README.md
@@ -1,5 +1,8 @@
 # Selection Sort
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md).
+
 Selection sort is a sorting algorithm, specifically an 
 in-place comparison sort. It has O(n2) time complexity, 
 making it inefficient on large lists, and generally 
diff --git a/src/algorithms/sorting/selection-sort/README.pt-BR.md b/src/algorithms/sorting/selection-sort/README.pt-BR.md
new file mode 100644
index 0000000000..0c45b73f61
--- /dev/null
+++ b/src/algorithms/sorting/selection-sort/README.pt-BR.md
@@ -0,0 +1,20 @@
+# Selection Sort
+
+_Leia isso em outros idiomas:_
+[_English_](README.md).
+
+Selection Sort é um algoritmo de ordenação, mais especificamente um algoritmo de ordenação por comparação in-place (requer uma quantidade constante de espaço de memória adicional). Tem complexidade O(n²), tornando-o ineficiente em listas grandes e, geralmente, tem desempenho inferior ao similar Insertion Sort. O Selection Sort é conhecido por sua simplicidade e tem vantagens de desempenho sobre algoritmos mais complexos em certas situações, particularmente quando a memória auxiliar é limitada.
+
+![Visualização do algoritmo](https://upload.wikimedia.org/wikipedia/commons/b/b0/Selection_sort_animation.gif)
+
+![Visualização do algoritmo](https://upload.wikimedia.org/wikipedia/commons/9/94/Selection-Sort-Animation.gif)
+
+## Complexidade
+
+| Nome                  | Melhor          | Médio               | Pior                | Memória   | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------:  | :----------: |
+| **Selection sort**    | n<sup>2</sup>   | n<sup>2</sup>       | n<sup>2</sup>       | 1         | Não        |              |
+
+## Referências
+
+[Wikipedia](https://en.wikipedia.org/wiki/Selection_sort)
diff --git a/src/algorithms/sorting/selection-sort/SelectionSort.js b/src/algorithms/sorting/selection-sort/SelectionSort.js
index 1b066affd3..fa4a720b2f 100644
--- a/src/algorithms/sorting/selection-sort/SelectionSort.js
+++ b/src/algorithms/sorting/selection-sort/SelectionSort.js
@@ -23,9 +23,7 @@ export default class SelectionSort extends Sort {
 
       // If new minimum element has been found then swap it with current i-th element.
       if (minIndex !== i) {
-        const tmp = array[i];
-        array[i] = array[minIndex];
-        array[minIndex] = tmp;
+        [array[i], array[minIndex]] = [array[minIndex], array[i]];
       }
     }
 
diff --git a/src/algorithms/sorting/shell-sort/README.md b/src/algorithms/sorting/shell-sort/README.md
index 5030333f79..f93338e2eb 100644
--- a/src/algorithms/sorting/shell-sort/README.md
+++ b/src/algorithms/sorting/shell-sort/README.md
@@ -1,5 +1,8 @@
 # Shellsort
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md).
+
 Shellsort, also known as Shell sort or Shell's method, 
 is an in-place comparison sort. It can be seen as either a 
 generalization of sorting by exchange (bubble sort) or sorting 
@@ -37,6 +40,8 @@ After this step, the array should look like this
 
 ![Shellsort](https://www.tutorialspoint.com/data_structures_algorithms/images/shell_sort_step_2.jpg)
 
+> UPD: On the picture below there is a typo and result array is supposed to be `[14, 10, 27, 19, 35, 33, 42, 44]`.
+
 Finally, we sort the rest of the array using interval of value 1. 
 Shell sort uses insertion sort to sort the array.
 
@@ -50,5 +55,6 @@ Shell sort uses insertion sort to sort the array.
 
 ## References
 
-* [Tutorials Point](https://www.tutorialspoint.com/data_structures_algorithms/shell_sort_algorithm.htm)
-* [Wikipedia](https://en.wikipedia.org/wiki/Shellsort)
+- [Tutorials Point](https://www.tutorialspoint.com/data_structures_algorithms/shell_sort_algorithm.htm)
+- [Wikipedia](https://en.wikipedia.org/wiki/Shellsort)
+- [YouTube by Rob Edwards](https://www.youtube.com/watch?v=ddeLSDsYVp8&index=79&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/sorting/shell-sort/README.pt-BR.md b/src/algorithms/sorting/shell-sort/README.pt-BR.md
new file mode 100644
index 0000000000..14de1a517e
--- /dev/null
+++ b/src/algorithms/sorting/shell-sort/README.pt-BR.md
@@ -0,0 +1,60 @@
+# Shellsort
+
+_Leia isso em outros idiomas:_
+[_English_](README.md).
+
+Shellsort, também conhecido como Shell sort ou método de Shell,
+é uma classificação de comparação in-loco. Pode ser visto tanto como um
+generalização da ordenação por troca (bubble sort) ou ordenação
+por inserção (ordenação por inserção). O método começa classificando
+pares de elementos distantes um do outro, então progressivamente
+reduzindo a distância entre os elementos a serem comparados. Iniciando
+com elementos distantes, pode mover alguns fora do lugar
+elementos em posição mais rápido do que um simples vizinho mais próximo
+intercâmbio
+
+![Shellsort](https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif)
+
+## Como o Shellsort funciona?
+
+Para nosso exemplo e facilidade de compreensão, tomamos o intervalo
+de `4`. Faça uma sub-lista virtual de todos os valores localizados no
+intervalo de 4 posições. Aqui esses valores são
+`{35, 14}`, `{33, 19}`, `{42, 27}` e `{10, 44}`
+
+![Shellsort](https://www.tutorialspoint.com/data_structures_algorithms/images/shell_sort_gap_4.jpg)
+
+Comparamos valores em cada sublista e os trocamos (se necessário)
+na matriz original. Após esta etapa, o novo array deve
+parece com isso
+
+![Shellsort](https://www.tutorialspoint.com/data_structures_algorithms/images/shell_sort_step_1.jpg)
+
+Então, pegamos o intervalo de 2 e essa lacuna gera duas sub-listas
+- `{14, 27, 35, 42}`, `{19, 10, 33, 44}`
+
+![Shellsort](https://www.tutorialspoint.com/data_structures_algorithms/images/shell_sort_gap_2.jpg)
+
+Comparamos e trocamos os valores, se necessário, no array original.
+Após esta etapa, a matriz deve ficar assim
+
+![Shellsort](https://www.tutorialspoint.com/data_structures_algorithms/images/shell_sort_step_2.jpg)
+
+> OBS: Na imagem abaixo há um erro de digitação e a matriz de resultados deve ser `[14, 10, 27, 19, 35, 33, 42, 44]`.
+
+Finalmente, ordenamos o resto do array usando o intervalo de valor 1.
+A classificação de shell usa a classificação por inserção para classificar a matriz.
+
+![Shellsort](https://www.tutorialspoint.com/data_structures_algorithms/images/shell_sort.jpg)
+
+## Complexidade
+
+| Nome                  | Melhor            | Média             | Pior               | Memória    | Estável    | Comentários  |
+| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
+| **Shell sort**        | n&nbsp;log(n)   | depends on gap sequence   | n&nbsp;(log(n))<sup>2</sup>  | 1         | Não         |           |
+
+## Referências
+
+- [Tutorials Point](https://www.tutorialspoint.com/data_structures_algorithms/shell_sort_algorithm.htm)
+- [Wikipedia](https://en.wikipedia.org/wiki/Shellsort)
+- [YouTube by Rob Edwards](https://www.youtube.com/watch?v=ddeLSDsYVp8&index=79&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/algorithms/stack/valid-parentheses/README.md b/src/algorithms/stack/valid-parentheses/README.md
new file mode 100644
index 0000000000..31d7fd60f3
--- /dev/null
+++ b/src/algorithms/stack/valid-parentheses/README.md
@@ -0,0 +1,44 @@
+# Valid Parentheses Problem
+
+Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
+
+An input string is valid if:
+
+Open brackets must be closed by the same type of brackets.
+Open brackets must be closed in the correct order.
+Every close bracket has a corresponding open bracket of the same type.
+ 
+
+Example 1:
+
+`Input: s = "()"`
+
+Output: true
+
+Example 2:
+
+`Input: s = "()[]{}"`
+
+Output: true
+
+Example 3:
+
+`Input: s = "(]"`
+
+Output: false
+
+This is actually a very common interview question and a very good example of how to use a stack data structure to solve problems. 
+
+## Solution
+The problem can be solved in two ways
+
+### Bruteforce Approach
+We can iterate through the string and then for each character in the string, we check for it's last closing character in the the string. Once we find the last closing character in the string, we remove both characters and then repeat the iteration, if we don't find a closing character for an opening character, then the string is invalid. The time complexity of this would be O(n^2) which is not so efficient.
+
+### Using a Stack
+We can use a hashtable to store all opening characters and the value would be the respective closing character. We can then iterate through the string and if we encounter an opening parantheses, we push it's closing character to the stack. If we ecounter a closing paraentheses, then we pop the stack and confirm that the popped element is equal to the current closing parentheses character. If it is not then the string is invalid. At the end of the iteration, we also need to check that the stack is empty. If it is not then the string is invalid. If it is, then the string is valid. This is a more efficient approach with a Time complexity and Space complexity of O(n).
+
+
+## References
+
+- [Leetcode](https://leetcode.com/problems/valid-parentheses/)
diff --git a/src/algorithms/stack/valid-parentheses/__test__/validParentheses.test.js b/src/algorithms/stack/valid-parentheses/__test__/validParentheses.test.js
new file mode 100644
index 0000000000..e286688dfd
--- /dev/null
+++ b/src/algorithms/stack/valid-parentheses/__test__/validParentheses.test.js
@@ -0,0 +1,23 @@
+import isValid from '../validParentheses';
+
+describe('validParentheses', () => {
+  it('should return false when string is empty', () => {
+    expect(isValid('')).toBe(false);
+  });
+
+  it('should return true when string contains valid parentheses in correct order', () => {
+    expect(isValid('()')).toBe(true);
+    expect(isValid('()[]{}')).toBe(true);
+    expect(isValid('((({[]})))')).toBe(true);
+  });
+
+  it('should return false when string contains invalid parentheses', () => {
+    expect(isValid('(]')).toBe(false);
+    expect(isValid('()[]{} }')).toBe(false);
+    expect(isValid('((({[(]})))')).toBe(false);
+  });
+
+  it('should return false when string contains valid parentheses in wrong order', () => {
+    expect(isValid('({)}')).toBe(false);
+  });
+});
diff --git a/src/algorithms/stack/valid-parentheses/validParentheses.js b/src/algorithms/stack/valid-parentheses/validParentheses.js
new file mode 100644
index 0000000000..a327780768
--- /dev/null
+++ b/src/algorithms/stack/valid-parentheses/validParentheses.js
@@ -0,0 +1,42 @@
+import Stack from '../../../data-structures/stack/Stack';
+import HashTable from '../../../data-structures/hash-table/HashTable';
+
+// Declare hashtable containg opening parentheses as key and it's closing parentheses as value.
+const hashTable = new HashTable(3);
+hashTable.set('{', '}');
+hashTable.set('(', ')');
+hashTable.set('[', ']');
+
+/**
+ * Check if string has valid parentheses.
+ *
+ * @param {string} parenthesesString
+ * @return {boolean}
+ */
+export default function isValid(parenthesesString) {
+  // If string is empty return false
+  if (parenthesesString.length === 0) {
+    return false;
+  }
+  // Create stack
+  const stack = new Stack();
+
+  // Loop through each character of string
+  for (let i = 0; i < parenthesesString.length; i += 1) {
+    const currentCharacter = parenthesesString[i];
+    // If character is opening parentheses push it's closing parentheses to stack
+    if (hashTable.has(currentCharacter)) {
+      stack.push(hashTable.get(currentCharacter));
+    } else {
+      /* If character is a closing parentheses then,:
+      check If stack is empty, if it is return false.
+      if stack is not empty, pop from stack and compare it with current character.
+      If they are not same return false. */
+      if (stack.isEmpty() || stack.pop() !== currentCharacter) {
+        return false;
+      }
+    }
+  }
+  // If stack is empty return true else return false
+  return stack.isEmpty();
+}
diff --git a/src/algorithms/statistics/weighted-random/README.md b/src/algorithms/statistics/weighted-random/README.md
new file mode 100644
index 0000000000..16e459704b
--- /dev/null
+++ b/src/algorithms/statistics/weighted-random/README.md
@@ -0,0 +1,143 @@
+# Weighted Random
+
+![Weighted Random](images/cover.png)
+
+## What is "Weighted Random"
+
+Let's say you have a list of **items**. Item could be anything. For example, we may have a list of fruits and vegetables that you like to eat: `[ '🍌', '🍎', '🥕' ]`.
+
+The list of **weights** represent the weight (or probability, or importance) of each item. Weights are numbers. For example, the weights like `[3, 7, 1]` would say that:
+
+- you would like to eat `🍎 apples` more often (`7` out of `3 + 7 + 1 = 11` times),
+- then you would like to eat `bananas 🍌` less often (only `3` out of `11` times),
+- and the `carrots 🥕` you really don't like (want to eat it only `1` out of `11` times).
+
+> If we speak in terms of probabilities than the weights list might be an array of floats that sum up to `1` (i.e. `[0.1, 0.5, 0.2, 0.2]`).
+
+The **Weighted Random** in this case will be the function that will randomly return you the item from the list, and it will take each item's weight into account, so that items with the higher weight will be picked more often.
+
+Example of the function interface:
+
+```javascript
+const items =   [ '🍌', '🍎', '🥕' ];
+const weights = [  3,    7,    1  ];
+
+function weightedRandom(items, weights) {
+  // implementation goes here ...
+}
+
+const nextSnackToEat = weightedRandom(items, weights); // Could be '🍎'
+```
+
+## Applications of Weighted Random
+
+- In [Genetic Algorithm](https://en.wikipedia.org/wiki/Genetic_algorithm) the weighted random is used during the "Selection" phase, when we need to select the fittest/strongest individuums based on their fitness score for mating and for producing the next stronger generation. You may find an **example** in the [Self-Parking Car in 500 Lines of Code](https://trekhleb.dev/blog/2021/self-parking-car-evolution/) article.
+- In [Recurrent Neural Networks (RNN)](https://en.wikipedia.org/wiki/Recurrent_neural_network) when trying to decide what letter to choose next (to form the sentence) based on the next letter probability. You may find an **example** in the [Recipe Generation using Recurrent Neural Network (RNN)](https://nbviewer.org/github/trekhleb/machine-learning-experiments/blob/master/experiments/recipe_generation_rnn/recipe_generation_rnn.ipynb) Jupyter notebook.
+- In [Nginx Load Balancing](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/) to send HTTP requests more often to the servers with the higher weights.
+- And more...
+
+## The Algorithm
+
+The **straightforward approach** would be to:
+
+1. Repeat each item in the list according to its weight.
+2. Pick the random item from the list.
+
+For example in our case with fruits and vegetables we could generate the following list of size `3 + 7 + 1 = 11`:
+
+```javascript
+const items =   [ '🍌', '🍎', '🥕' ];
+const weights = [  3,    7,    1  ];
+
+// Repeating the items based on weights.
+const weightedItems = [
+  '🍌', '🍌', '🍌',
+  '🍎', '🍎', '🍎', '🍎', '🍎', '🍎', '🍎',
+  '🥕',
+];
+
+// And now just pick the random item from weightedItems array.
+```
+
+However, as you may see, this approach may require a lot of memory, in case if we have a lot of items to repeat in `weightedItems` list. Think of it as if you would need to repeat a string like `"some-random-string"` (`18` bytes) a ten million times. You will need to allocate around `180Mb` of additional memory space just for this array.
+
+The **more efficient approach** would be to:
+
+1. Prepare the list of cumulative weights for each item (i.e. the `cumulativeWeights` list which will have the same number of elements as the original `weights` list). In our case it will look like this: `cumulativeWeights = [3, 3 + 7, 3 + 7 + 1] = [3, 10, 11]`
+2. Generate the random number `randomNumber` from `0` to the highest cumulative weight value. In our case the random number will be in a range of `[0..11]`. Let's say that we have `randomNumber = 8`.
+3. Go through the `cumulativeWeights` list from left to right and pick the first element which is higher or equal to the `randomNumber`. The index of such element we will use to pick the item from the `items` array.
+
+The idea behind this approach is that the higher weights will "occupy" more numeric space. Therefore, there is a higher chance that the random number will fall into the "higher weight numeric bucket".
+
+```javascript
+const weights =           [3, 7,  1 ];
+const cumulativeWeights = [3, 10, 11];
+
+// In a pseudo-representation we may think about the cumulativeWeights array like this.
+const pseudoCumulativeWeights = [
+  1, 2, 3,               // <-- [3] numbers
+  4, 5, 6, 7, 8, 9, 10,  // <-- [7] numbers
+  11,                    // <-- [1] number
+];
+```
+
+Here is an example of how the `weightedRandom` function might be implemented:
+
+```javascript
+/**
+ * Picks the random item based on its weight.
+ * The items with higher weight will be picked more often (with a higher probability).
+ *
+ * For example:
+ * - items = ['banana', 'orange', 'apple']
+ * - weights = [0, 0.2, 0.8]
+ * - weightedRandom(items, weights) in 80% of cases will return 'apple', in 20% of cases will return
+ * 'orange' and it will never return 'banana' (because probability of picking the banana is 0%)
+ *
+ * @param {any[]} items
+ * @param {number[]} weights
+ * @returns {{item: any, index: number}}
+ */
+export default function weightedRandom(items, weights) {
+  if (items.length !== weights.length) {
+    throw new Error('Items and weights must be of the same size');
+  }
+
+  if (!items.length) {
+    throw new Error('Items must not be empty');
+  }
+
+  // Preparing the cumulative weights array.
+  // For example:
+  // - weights = [1, 4, 3]
+  // - cumulativeWeights = [1, 5, 8]
+  const cumulativeWeights = [];
+  for (let i = 0; i < weights.length; i += 1) {
+    cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
+  }
+
+  // Getting the random number in a range of [0...sum(weights)]
+  // For example:
+  // - weights = [1, 4, 3]
+  // - maxCumulativeWeight = 8
+  // - range for the random number is [0...8]
+  const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
+  const randomNumber = maxCumulativeWeight * Math.random();
+
+  // Picking the random item based on its weight.
+  // The items with higher weight will be picked more often.
+  for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
+    if (cumulativeWeights[itemIndex] >= randomNumber) {
+      return {
+        item: items[itemIndex],
+        index: itemIndex,
+      };
+    }
+  }
+}
+```
+
+## Implementation
+
+- Check the [weightedRandom.js](weightedRandom.js) file for the implementation of the `weightedRandom()` function.
+- Check the [weightedRandom.test.js](__test__/weightedRandom.test.js) file for the tests-cases.
diff --git a/src/algorithms/statistics/weighted-random/__test__/weightedRandom.test.js b/src/algorithms/statistics/weighted-random/__test__/weightedRandom.test.js
new file mode 100644
index 0000000000..1b0b267627
--- /dev/null
+++ b/src/algorithms/statistics/weighted-random/__test__/weightedRandom.test.js
@@ -0,0 +1,64 @@
+import weightedRandom from '../weightedRandom';
+
+describe('weightedRandom', () => {
+  it('should throw an error when the number of weights does not match the number of items', () => {
+    const getWeightedRandomWithInvalidInputs = () => {
+      weightedRandom(['a', 'b', 'c'], [10, 0]);
+    };
+    expect(getWeightedRandomWithInvalidInputs).toThrow('Items and weights must be of the same size');
+  });
+
+  it('should throw an error when the number of weights or items are empty', () => {
+    const getWeightedRandomWithInvalidInputs = () => {
+      weightedRandom([], []);
+    };
+    expect(getWeightedRandomWithInvalidInputs).toThrow('Items must not be empty');
+  });
+
+  it('should correctly do random selection based on wights in straightforward cases', () => {
+    expect(weightedRandom(['a', 'b', 'c'], [1, 0, 0])).toEqual({ index: 0, item: 'a' });
+    expect(weightedRandom(['a', 'b', 'c'], [0, 1, 0])).toEqual({ index: 1, item: 'b' });
+    expect(weightedRandom(['a', 'b', 'c'], [0, 0, 1])).toEqual({ index: 2, item: 'c' });
+    expect(weightedRandom(['a', 'b', 'c'], [0, 1, 1])).not.toEqual({ index: 0, item: 'a' });
+    expect(weightedRandom(['a', 'b', 'c'], [1, 0, 1])).not.toEqual({ index: 1, item: 'b' });
+    expect(weightedRandom(['a', 'b', 'c'], [1, 1, 0])).not.toEqual({ index: 2, item: 'c' });
+  });
+
+  it('should correctly do random selection based on wights', () => {
+    // Number of times we're going to select the random items based on their weights.
+    const ATTEMPTS_NUM = 1000;
+    // The +/- delta in the number of times each item has been actually selected.
+    // I.e. if we want the item 'a' to be selected 300 times out of 1000 cases (30%)
+    // then 267 times is acceptable since it is bigger that 250 (which is 300 - 50)
+    // ans smaller than 350 (which is 300 + 50)
+    const THRESHOLD = 50;
+
+    const items = ['a', 'b', 'c']; // The actual items values don't matter.
+    const weights = [0.1, 0.3, 0.6];
+
+    const counter = [];
+    for (let i = 0; i < ATTEMPTS_NUM; i += 1) {
+      const randomItem = weightedRandom(items, weights);
+      if (!counter[randomItem.index]) {
+        counter[randomItem.index] = 1;
+      } else {
+        counter[randomItem.index] += 1;
+      }
+    }
+
+    for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
+      /*
+        i.e. item with the index of 0 must be selected 100 times (ideally)
+        or with the threshold of [100 - 50, 100 + 50] times.
+
+        i.e. item with the index of 1 must be selected 300 times (ideally)
+        or with the threshold of [300 - 50, 300 + 50] times.
+
+        i.e. item with the index of 2 must be selected 600 times (ideally)
+        or with the threshold of [600 - 50, 600 + 50] times.
+       */
+      expect(counter[itemIndex]).toBeGreaterThan(ATTEMPTS_NUM * weights[itemIndex] - THRESHOLD);
+      expect(counter[itemIndex]).toBeLessThan(ATTEMPTS_NUM * weights[itemIndex] + THRESHOLD);
+    }
+  });
+});
diff --git a/src/algorithms/statistics/weighted-random/images/cover.png b/src/algorithms/statistics/weighted-random/images/cover.png
new file mode 100644
index 0000000000..a4bf41b65f
Binary files /dev/null and b/src/algorithms/statistics/weighted-random/images/cover.png differ
diff --git a/src/algorithms/statistics/weighted-random/weightedRandom.js b/src/algorithms/statistics/weighted-random/weightedRandom.js
new file mode 100644
index 0000000000..0d58a504f8
--- /dev/null
+++ b/src/algorithms/statistics/weighted-random/weightedRandom.js
@@ -0,0 +1,52 @@
+/**
+ * Picks the random item based on its weight.
+ * The items with higher weight will be picked more often (with a higher probability).
+ *
+ * For example:
+ * - items = ['banana', 'orange', 'apple']
+ * - weights = [0, 0.2, 0.8]
+ * - weightedRandom(items, weights) in 80% of cases will return 'apple', in 20% of cases will return
+ * 'orange' and it will never return 'banana' (because probability of picking the banana is 0%)
+ *
+ * @param {any[]} items
+ * @param {number[]} weights
+ * @returns {{item: any, index: number}}
+ */
+/* eslint-disable consistent-return */
+export default function weightedRandom(items, weights) {
+  if (items.length !== weights.length) {
+    throw new Error('Items and weights must be of the same size');
+  }
+
+  if (!items.length) {
+    throw new Error('Items must not be empty');
+  }
+
+  // Preparing the cumulative weights array.
+  // For example:
+  // - weights = [1, 4, 3]
+  // - cumulativeWeights = [1, 5, 8]
+  const cumulativeWeights = [];
+  for (let i = 0; i < weights.length; i += 1) {
+    cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
+  }
+
+  // Getting the random number in a range of [0...sum(weights)]
+  // For example:
+  // - weights = [1, 4, 3]
+  // - maxCumulativeWeight = 8
+  // - range for the random number is [0...8]
+  const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
+  const randomNumber = maxCumulativeWeight * Math.random();
+
+  // Picking the random item based on its weight.
+  // The items with higher weight will be picked more often.
+  for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
+    if (cumulativeWeights[itemIndex] >= randomNumber) {
+      return {
+        item: items[itemIndex],
+        index: itemIndex,
+      };
+    }
+  }
+}
diff --git a/src/algorithms/string/knuth-morris-pratt/knuthMorrisPratt.js b/src/algorithms/string/knuth-morris-pratt/knuthMorrisPratt.js
index e4344b7730..a3a39904a4 100644
--- a/src/algorithms/string/knuth-morris-pratt/knuthMorrisPratt.js
+++ b/src/algorithms/string/knuth-morris-pratt/knuthMorrisPratt.js
@@ -50,7 +50,7 @@ export default function knuthMorrisPratt(text, word) {
     } else if (wordIndex > 0) {
       wordIndex = patternTable[wordIndex - 1];
     } else {
-      wordIndex = 0;
+      // wordIndex = 0;
       textIndex += 1;
     }
   }
diff --git a/src/algorithms/string/levenshtein-distance/README.md b/src/algorithms/string/levenshtein-distance/README.md
index cc5b5e9d7f..8876d6791c 100644
--- a/src/algorithms/string/levenshtein-distance/README.md
+++ b/src/algorithms/string/levenshtein-distance/README.md
@@ -50,7 +50,7 @@ to assist natural language translation based on translation memory.
 
 Let’s take a simple example of finding minimum edit distance between 
 strings `ME` and `MY`. Intuitively you already know that minimum edit distance 
-here is `1` operation and this operation. And it is a replacing `E` with `Y`. But 
+here is `1` operation, which is replacing `E` with `Y`. But 
 let’s try to formalize it in a form of the algorithm in order to be able to 
 do more complex examples like transforming `Saturday` into `Sunday`.
 
@@ -62,7 +62,7 @@ is being calculated based on three previously possible transformations.
 
 To explain this further let’s draw the following matrix:
 
-![Levenshtein Matrix](https://cdn-images-1.medium.com/max/1600/1*2d46ug_PL5LfeqztckoYGw.jpeg)
+![Levenshtein Matrix](https://cdn-images-1.medium.com/max/1600/1*aTunSUoy0BJyYBVn4tWGrA.png)
 
 - Cell `(0:1)` contains red number 1. It means that we need 1 operation to 
 transform `M` to an empty string. And it is by deleting `M`. This is why this number is red.
@@ -75,12 +75,12 @@ to transform an empty string to `MY`. And it is by inserting `Y` and  `M`.
 - Cell `(1:1)` contains number 0. It means that it costs nothing 
 to transform `M` into `M`.
 - Cell `(1:2)` contains red number 1. It means that we need 1 operation 
-to transform `ME` to `M`. And it is be deleting `E`.
+to transform `ME` to `M`. And it is by deleting `E`.
 - And so on...
 
 This looks easy for such small matrix as ours (it is only `3x3`). But here you
 may find basic concepts that may be applied to calculate all those numbers for
-bigger matrices (let’s say `9x7` one, for `Saturday → Sunday` transformation).
+bigger matrices (let’s say a `9x7` matrix for `Saturday → Sunday` transformation).
 
 According to the formula you only need three adjacent cells `(i-1:j)`, `(i-1:j-1)`, and `(i:j-1)` to
 calculate the number for current cell `(i:j)`. All we need to do is to find the 
@@ -89,24 +89,24 @@ letters in `i`'s row and `j`'s column.
 
 You may clearly see the recursive nature of the problem.
 
-![Levenshtein Matrix](https://cdn-images-1.medium.com/max/2000/1*JdHQ5TeKiDlE-iKK1s_2vw.jpeg)
+![Levenshtein Matrix](https://cdn-images-1.medium.com/max/1600/1*w8UB4DSvBnAK6mBXRGQDjw.png)
 
 Let's draw a decision graph for this problem.
 
-![Minimum Edit Distance Decision Graph](https://cdn-images-1.medium.com/max/1600/1*SGwYUpXH9H1xUeTvJk0e7Q.jpeg)
+![Minimum Edit Distance Decision Graph](https://cdn-images-1.medium.com/max/1600/1*8jD0qvr5B9PwRFM_9z7q9A.png)
 
 You may see a number of overlapping sub-problems on the picture that are marked 
 with red. Also there is no way to reduce the number of operations and make it 
-less then a minimum of those three adjacent cells from the formula. 
+less than a minimum of those three adjacent cells from the formula. 
 
 Also you may notice that each cell number in the matrix is being calculated 
 based on previous ones. Thus the tabulation technique (filling the cache in 
 bottom-up direction) is being applied here.
 
-Applying this principles further we may solve more complicated cases like 
+Applying this principle further we may solve more complicated cases like 
 with `Saturday → Sunday` transformation.
 
-![Levenshtein distance](https://cdn-images-1.medium.com/max/1600/1*fPEHiImYLKxSTUhrGbYq3g.jpeg)
+![Levenshtein distance](https://cdn-images-1.medium.com/max/2600/1*497gMaFErzJpCXG7kS_7dw.png)
 
 ## References
 
diff --git a/src/algorithms/string/longest-common-substring/__test__/longestCommonSubstring.test.js b/src/algorithms/string/longest-common-substring/__test__/longestCommonSubstring.test.js
index 55c7d4da91..8c1cb20703 100644
--- a/src/algorithms/string/longest-common-substring/__test__/longestCommonSubstring.test.js
+++ b/src/algorithms/string/longest-common-substring/__test__/longestCommonSubstring.test.js
@@ -7,5 +7,18 @@ describe('longestCommonSubstring', () => {
     expect(longestCommonSubstring('', 'ABC')).toBe('');
     expect(longestCommonSubstring('ABABC', 'BABCA')).toBe('BABC');
     expect(longestCommonSubstring('BABCA', 'ABCBA')).toBe('ABC');
+    expect(longestCommonSubstring('sea', 'eat')).toBe('ea');
+    expect(longestCommonSubstring('algorithms', 'rithm')).toBe('rithm');
+    expect(longestCommonSubstring(
+      'Algorithms and data structures implemented in JavaScript',
+      'Here you may find Algorithms and data structures that are implemented in JavaScript',
+    )).toBe('Algorithms and data structures ');
+  });
+
+  it('should handle unicode correctly', () => {
+    expect(longestCommonSubstring('𐌵𐌵**ABC', '𐌵𐌵--ABC')).toBe('ABC');
+    expect(longestCommonSubstring('𐌵𐌵**A', '𐌵𐌵--A')).toBe('𐌵𐌵');
+    expect(longestCommonSubstring('A买B时', '买B时GD')).toBe('买B时');
+    expect(longestCommonSubstring('After test买时 case', 'another_test买时')).toBe('test买时');
   });
 });
diff --git a/src/algorithms/string/longest-common-substring/longestCommonSubstring.js b/src/algorithms/string/longest-common-substring/longestCommonSubstring.js
index 10919bf6a1..6afeccf398 100644
--- a/src/algorithms/string/longest-common-substring/longestCommonSubstring.js
+++ b/src/algorithms/string/longest-common-substring/longestCommonSubstring.js
@@ -1,9 +1,18 @@
 /**
- * @param {string} s1
- * @param {string} s2
+ * Longest Common Substring (LCS) (Dynamic Programming Approach).
+ *
+ * @param {string} string1
+ * @param {string} string2
  * @return {string}
  */
-export default function longestCommonSubstring(s1, s2) {
+export default function longestCommonSubstring(string1, string2) {
+  // Convert strings to arrays to treat unicode symbols length correctly.
+  // For example:
+  // '𐌵'.length === 2
+  // [...'𐌵'].length === 1
+  const s1 = [...string1];
+  const s2 = [...string2];
+
   // Init the matrix of all substring lengths to use Dynamic Programming approach.
   const substringMatrix = Array(s2.length + 1).fill(null).map(() => {
     return Array(s1.length + 1).fill(null);
diff --git a/src/algorithms/string/palindrome/README.md b/src/algorithms/string/palindrome/README.md
new file mode 100644
index 0000000000..0ce77d42b6
--- /dev/null
+++ b/src/algorithms/string/palindrome/README.md
@@ -0,0 +1,29 @@
+# Palindrome Check
+
+A [Palindrome](https://en.wikipedia.org/wiki/Palindrome) is a string that reads the same forwards and backwards.
+This means that the second half of the string is the reverse of the
+first half.
+
+## Examples
+
+The following are palindromes (thus would return `TRUE`):
+
+```
+- "a"
+- "pop"     ->  p + o + p
+- "deed"    ->  de + ed
+- "kayak"   ->  ka + y + ak
+- "racecar" ->  rac + e + car
+```
+
+The following are NOT palindromes (thus would return `FALSE`):
+
+```
+- "rad"
+- "dodo"
+- "polo"
+```
+
+## References
+
+- [GeeksForGeeks - Check if a number is Palindrome](https://www.geeksforgeeks.org/check-if-a-number-is-palindrome/)
diff --git a/src/algorithms/string/palindrome/__test__/isPalindrome.test.js b/src/algorithms/string/palindrome/__test__/isPalindrome.test.js
new file mode 100644
index 0000000000..0a49d5be5d
--- /dev/null
+++ b/src/algorithms/string/palindrome/__test__/isPalindrome.test.js
@@ -0,0 +1,15 @@
+import isPalindrome from '../isPalindrome';
+
+describe('palindromeCheck', () => {
+  it('should return whether or not the string is a palindrome', () => {
+    expect(isPalindrome('a')).toBe(true);
+    expect(isPalindrome('pop')).toBe(true);
+    expect(isPalindrome('deed')).toBe(true);
+    expect(isPalindrome('kayak')).toBe(true);
+    expect(isPalindrome('racecar')).toBe(true);
+
+    expect(isPalindrome('rad')).toBe(false);
+    expect(isPalindrome('dodo')).toBe(false);
+    expect(isPalindrome('polo')).toBe(false);
+  });
+});
diff --git a/src/algorithms/string/palindrome/isPalindrome.js b/src/algorithms/string/palindrome/isPalindrome.js
new file mode 100644
index 0000000000..327fd6bbf2
--- /dev/null
+++ b/src/algorithms/string/palindrome/isPalindrome.js
@@ -0,0 +1,19 @@
+/**
+ * @param {string} string
+ * @return {boolean}
+ */
+
+export default function isPalindrome(string) {
+  let left = 0;
+  let right = string.length - 1;
+
+  while (left < right) {
+    if (string[left] !== string[right]) {
+      return false;
+    }
+    left += 1;
+    right -= 1;
+  }
+
+  return true;
+}
diff --git a/src/algorithms/tree/breadth-first-search/__test__/breadthFirstSearch.test.js b/src/algorithms/tree/breadth-first-search/__test__/breadthFirstSearch.test.js
index 4a77f0f310..2bf653a9cf 100644
--- a/src/algorithms/tree/breadth-first-search/__test__/breadthFirstSearch.test.js
+++ b/src/algorithms/tree/breadth-first-search/__test__/breadthFirstSearch.test.js
@@ -2,7 +2,7 @@ import BinaryTreeNode from '../../../../data-structures/tree/BinaryTreeNode';
 import breadthFirstSearch from '../breadthFirstSearch';
 
 describe('breadthFirstSearch', () => {
-  it('should perform DFS operation on tree', () => {
+  it('should perform BFS operation on tree', () => {
     const nodeA = new BinaryTreeNode('A');
     const nodeB = new BinaryTreeNode('B');
     const nodeC = new BinaryTreeNode('C');
diff --git a/src/algorithms/tree/breadth-first-search/breadthFirstSearch.js b/src/algorithms/tree/breadth-first-search/breadthFirstSearch.js
index 503ec34f96..e1c7b70e14 100644
--- a/src/algorithms/tree/breadth-first-search/breadthFirstSearch.js
+++ b/src/algorithms/tree/breadth-first-search/breadthFirstSearch.js
@@ -3,9 +3,9 @@ import Queue from '../../../data-structures/queue/Queue';
 /**
  * @typedef {Object} Callbacks
  * @property {function(node: BinaryTreeNode, child: BinaryTreeNode): boolean} allowTraversal -
- *   Determines whether DFS should traverse from the node to its child.
- * @property {function(node: BinaryTreeNode)} enterNode - Called when DFS enters the node.
- * @property {function(node: BinaryTreeNode)} leaveNode - Called when DFS leaves the node.
+ *   Determines whether BFS should traverse from the node to its child.
+ * @property {function(node: BinaryTreeNode)} enterNode - Called when BFS enters the node.
+ * @property {function(node: BinaryTreeNode)} leaveNode - Called when BFS leaves the node.
  */
 
 /**
diff --git a/src/algorithms/tree/depth-first-search/depthFirstSearch.js b/src/algorithms/tree/depth-first-search/depthFirstSearch.js
index 188e4a49f0..fade444996 100644
--- a/src/algorithms/tree/depth-first-search/depthFirstSearch.js
+++ b/src/algorithms/tree/depth-first-search/depthFirstSearch.js
@@ -1,52 +1,76 @@
 /**
- * @typedef {Object} Callbacks
- * @property {function(node: BinaryTreeNode, child: BinaryTreeNode): boolean} allowTraversal -
- *   Determines whether DFS should traverse from the node to its child.
+ * @typedef {Object} TraversalCallbacks
+ *
+ * @property {function(node: BinaryTreeNode, child: BinaryTreeNode): boolean} allowTraversal
+ * - Determines whether DFS should traverse from the node to its child.
+ *
  * @property {function(node: BinaryTreeNode)} enterNode - Called when DFS enters the node.
+ *
  * @property {function(node: BinaryTreeNode)} leaveNode - Called when DFS leaves the node.
  */
 
 /**
- * @param {Callbacks} [callbacks]
- * @returns {Callbacks}
+ * Extend missing traversal callbacks with default callbacks.
+ *
+ * @param {TraversalCallbacks} [callbacks] - The object that contains traversal callbacks.
+ * @returns {TraversalCallbacks} - Traversal callbacks extended with defaults callbacks.
  */
 function initCallbacks(callbacks = {}) {
-  const initiatedCallback = callbacks;
+  // Init empty callbacks object.
+  const initiatedCallbacks = {};
 
+  // Empty callback that we will use in case if user didn't provide real callback function.
   const stubCallback = () => {};
-  const defaultAllowTraversal = () => true;
+  // By default we will allow traversal of every node
+  // in case if user didn't provide a callback for that.
+  const defaultAllowTraversalCallback = () => true;
 
-  initiatedCallback.allowTraversal = callbacks.allowTraversal || defaultAllowTraversal;
-  initiatedCallback.enterNode = callbacks.enterNode || stubCallback;
-  initiatedCallback.leaveNode = callbacks.leaveNode || stubCallback;
+  // Copy original callbacks to our initiatedCallbacks object or use default callbacks instead.
+  initiatedCallbacks.allowTraversal = callbacks.allowTraversal || defaultAllowTraversalCallback;
+  initiatedCallbacks.enterNode = callbacks.enterNode || stubCallback;
+  initiatedCallbacks.leaveNode = callbacks.leaveNode || stubCallback;
 
-  return initiatedCallback;
+  // Returned processed list of callbacks.
+  return initiatedCallbacks;
 }
 
 /**
- * @param {BinaryTreeNode} node
- * @param {Callbacks} callbacks
+ * Recursive depth-first-search traversal for binary.
+ *
+ * @param {BinaryTreeNode} node - binary tree node that we will start traversal from.
+ * @param {TraversalCallbacks} callbacks - the object that contains traversal callbacks.
  */
 export function depthFirstSearchRecursive(node, callbacks) {
+  // Call the "enterNode" callback to notify that the node is going to be entered.
   callbacks.enterNode(node);
 
-  // Traverse left branch.
+  // Traverse left branch only if case if traversal of the left node is allowed.
   if (node.left && callbacks.allowTraversal(node, node.left)) {
     depthFirstSearchRecursive(node.left, callbacks);
   }
 
-  // Traverse right branch.
+  // Traverse right branch only if case if traversal of the right node is allowed.
   if (node.right && callbacks.allowTraversal(node, node.right)) {
     depthFirstSearchRecursive(node.right, callbacks);
   }
 
+  // Call the "leaveNode" callback to notify that traversal
+  // of the current node and its children is finished.
   callbacks.leaveNode(node);
 }
 
 /**
- * @param {BinaryTreeNode} rootNode
- * @param {Callbacks} [callbacks]
+ * Perform depth-first-search traversal of the rootNode.
+ * For every traversal step call "allowTraversal", "enterNode" and "leaveNode" callbacks.
+ * See TraversalCallbacks type definition for more details about the shape of callbacks object.
+ *
+ * @param {BinaryTreeNode} rootNode - The node from which we start traversing.
+ * @param {TraversalCallbacks} [callbacks] - Traversal callbacks.
  */
 export default function depthFirstSearch(rootNode, callbacks) {
-  depthFirstSearchRecursive(rootNode, initCallbacks(callbacks));
+  // In case if user didn't provide some callback we need to replace them with default ones.
+  const processedCallbacks = initCallbacks(callbacks);
+
+  // Now, when we have all necessary callbacks we may proceed to recursive traversal.
+  depthFirstSearchRecursive(rootNode, processedCallbacks);
 }
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/README.md b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/README.md
new file mode 100644
index 0000000000..975eade3a4
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/README.md
@@ -0,0 +1,107 @@
+# Best Time to Buy and Sell Stock
+
+## Task Description
+
+Say you have an array prices for which the `i`-th element is the price of a given stock on day `i`.
+
+Find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).
+
+> Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
+
+**Example #1**
+
+```
+Input: [7, 1, 5, 3, 6, 4]
+Output: 7
+```
+
+_Explanation:_ Buy on day `2` (`price = 1`) and sell on day `3` (`price = 5`), `profit = 5-1 = 4`. Then buy on day `4` (`price = 3`) and sell on day `5` (`price = 6`), `profit = 6-3 = 3`.
+
+**Example #2**
+
+```
+Input: [1, 2, 3, 4, 5]
+Output: 4
+```
+
+_Explanation:_ Buy on day `1` (`price = 1`) and sell on day `5` (`price = 5`), `profit = 5-1 = 4`. Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.
+
+**Example #3**
+
+```
+Input: [7, 6, 4, 3, 1]
+Output: 0
+```
+
+_Explanation:_ In this case, no transaction is done, i.e. max `profit = 0`.
+
+## Possible Solutions
+
+### Divide and conquer approach `O(2^n)`
+
+We may try **all** combinations of buying and selling and find out the most profitable one by applying _divide and conquer approach_.
+
+Let's say we have an array of prices `[7, 6, 4, 3, 1]` and we're on the _1st_ day of trading (at the very beginning of the array). At this point we may say that the overall maximum profit would be the _maximum_ of two following values:
+
+1. _Option 1: Keep the money_ → profit would equal to the profit from buying/selling the rest of the stocks → `keepProfit = profit([6, 4, 3, 1])`.
+2. _Option 2: Buy/sell at current price_ → profit in this case would equal to the profit from buying/selling the rest of the stocks plus (or minus, depending on whether we're selling or buying) the current stock price → `buySellProfit = -7 + profit([6, 4, 3, 1])`.
+
+The overall profit would be equal to → `overallProfit = Max(keepProfit, buySellProfit)`.
+
+As you can see the `profit([6, 4, 3, 1])` task is being solved in the same recursive manner.
+
+> See the full code example in [dqBestTimeToBuySellStocks.js](dqBestTimeToBuySellStocks.js)
+
+#### Time Complexity
+
+As you may see, every recursive call will produce _2_ more recursive branches. The depth of the recursion will be `n` (size of prices array) and thus, the time complexity will equal to `O(2^n)`.
+
+As you may see, this is very inefficient. For example for just `20` prices the number of recursive calls will be somewhere close to `2M`!
+
+#### Additional Space Complexity
+
+If we avoid cloning the prices array between recursive function calls and will use the array pointer then additional space complexity will be proportional to the depth of the recursion: `O(n)`
+
+## Peak Valley Approach `O(n)`
+
+If we plot the prices array (i.e. `[7, 1, 5, 3, 6, 4]`) we may notice that the points of interest are the consecutive valleys and peaks
+
+![Peak Valley Approach](https://leetcode.com/media/original_images/122_maxprofit_1.PNG)
+
+_Image source: [LeetCode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/solution/)_
+
+So, if we will track the growing price and will sell the stocks immediately _before_ the price goes down we'll get the maximum profit (remember, we bought the stock in the valley at its low price).
+
+> See the full code example in [peakvalleyBestTimeToBuySellStocks.js](peakvalleyBestTimeToBuySellStocks.js)
+
+#### Time Complexity
+
+Since the algorithm requires only one pass through the prices array, the time complexity would equal `O(n)`.
+
+#### Additional Space Complexity
+
+Except of the prices array itself the algorithm consumes the constant amount of memory. Thus, additional space complexity is `O(1)`.
+
+## Accumulator Approach `O(n)`
+
+There is even simpler approach exists. Let's say we have the prices array which looks like this `[1, 7, 2, 3, 6, 7, 6, 7]`:
+
+![Simple One Pass](https://leetcode.com/media/original_images/122_maxprofit_2.PNG)
+
+_Image source: [LeetCode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/solution/)_
+
+You may notice, that we don't even need to keep tracking of a constantly growing price. Instead, we may simply add the price difference for _all growing segments_ of the chart which eventually sums up to the highest possible profit,
+
+> See the full code example in [accumulatorBestTimeToBuySellStocks.js](accumulatorBestTimeToBuySellStocks.js)
+
+#### Time Complexity
+
+Since the algorithm requires only one pass through the prices array, the time complexity would equal `O(n)`.
+
+#### Additional Space Complexity
+
+Except of the prices array itself the algorithm consumes the constant amount of memory. Thus, additional space complexity is `O(1)`.
+
+## References
+
+- [Best Time to Buy and Sell Stock on LeetCode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/)
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/accumulatorBestTimeToBuySellStocks.test.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/accumulatorBestTimeToBuySellStocks.test.js
new file mode 100644
index 0000000000..40c1557770
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/accumulatorBestTimeToBuySellStocks.test.js
@@ -0,0 +1,48 @@
+import accumulatorBestTimeToBuySellStocks from '../accumulatorBestTimeToBuySellStocks';
+
+describe('accumulatorBestTimeToBuySellStocks', () => {
+  it('should find the best time to buy and sell stocks', () => {
+    let visit;
+
+    expect(accumulatorBestTimeToBuySellStocks([1, 5])).toEqual(4);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(1);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([1, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(2);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([5, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(2);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([1, 5, 10], visit)).toEqual(9);
+    expect(visit).toHaveBeenCalledTimes(3);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([10, 1, 5, 20, 15, 21], visit)).toEqual(25);
+    expect(visit).toHaveBeenCalledTimes(6);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([7, 1, 5, 3, 6, 4], visit)).toEqual(7);
+    expect(visit).toHaveBeenCalledTimes(6);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([1, 2, 3, 4, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(5);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks([7, 6, 4, 3, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(5);
+
+    visit = jest.fn();
+    expect(accumulatorBestTimeToBuySellStocks(
+      [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
+      visit,
+    )).toEqual(19);
+    expect(visit).toHaveBeenCalledTimes(20);
+  });
+});
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/dpBestTimeToBuySellStocks.test.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/dpBestTimeToBuySellStocks.test.js
new file mode 100644
index 0000000000..5904ee54f0
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/dpBestTimeToBuySellStocks.test.js
@@ -0,0 +1,48 @@
+import dpBestTimeToBuySellStocks from '../dpBestTimeToBuySellStocks';
+
+describe('dpBestTimeToBuySellStocks', () => {
+  it('should find the best time to buy and sell stocks', () => {
+    let visit;
+
+    expect(dpBestTimeToBuySellStocks([1, 5])).toEqual(4);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(1);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([1, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(2);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([5, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(2);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([1, 5, 10], visit)).toEqual(9);
+    expect(visit).toHaveBeenCalledTimes(3);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([10, 1, 5, 20, 15, 21], visit)).toEqual(25);
+    expect(visit).toHaveBeenCalledTimes(6);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([7, 1, 5, 3, 6, 4], visit)).toEqual(7);
+    expect(visit).toHaveBeenCalledTimes(6);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([1, 2, 3, 4, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(5);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks([7, 6, 4, 3, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(5);
+
+    visit = jest.fn();
+    expect(dpBestTimeToBuySellStocks(
+      [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
+      visit,
+    )).toEqual(19);
+    expect(visit).toHaveBeenCalledTimes(20);
+  });
+});
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/dqBestTimeToBuySellStocks.test.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/dqBestTimeToBuySellStocks.test.js
new file mode 100644
index 0000000000..f4fe9ad3f5
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/dqBestTimeToBuySellStocks.test.js
@@ -0,0 +1,48 @@
+import dqBestTimeToBuySellStocks from '../dqBestTimeToBuySellStocks';
+
+describe('dqBestTimeToBuySellStocks', () => {
+  it('should find the best time to buy and sell stocks', () => {
+    let visit;
+
+    expect(dqBestTimeToBuySellStocks([1, 5])).toEqual(4);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(3);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([1, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(7);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([5, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(7);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([1, 5, 10], visit)).toEqual(9);
+    expect(visit).toHaveBeenCalledTimes(15);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([10, 1, 5, 20, 15, 21], visit)).toEqual(25);
+    expect(visit).toHaveBeenCalledTimes(127);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([7, 1, 5, 3, 6, 4], visit)).toEqual(7);
+    expect(visit).toHaveBeenCalledTimes(127);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([1, 2, 3, 4, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(63);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks([7, 6, 4, 3, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(63);
+
+    visit = jest.fn();
+    expect(dqBestTimeToBuySellStocks(
+      [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
+      visit,
+    )).toEqual(19);
+    expect(visit).toHaveBeenCalledTimes(2097151);
+  });
+});
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/peakvalleyBestTimeToBuySellStocks.test.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/peakvalleyBestTimeToBuySellStocks.test.js
new file mode 100644
index 0000000000..4eeadf673c
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/__tests__/peakvalleyBestTimeToBuySellStocks.test.js
@@ -0,0 +1,48 @@
+import peakvalleyBestTimeToBuySellStocks from '../peakvalleyBestTimeToBuySellStocks';
+
+describe('peakvalleyBestTimeToBuySellStocks', () => {
+  it('should find the best time to buy and sell stocks', () => {
+    let visit;
+
+    expect(peakvalleyBestTimeToBuySellStocks([1, 5])).toEqual(4);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(1);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([1, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(2);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([5, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(2);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([1, 5, 10], visit)).toEqual(9);
+    expect(visit).toHaveBeenCalledTimes(3);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([10, 1, 5, 20, 15, 21], visit)).toEqual(25);
+    expect(visit).toHaveBeenCalledTimes(6);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([7, 1, 5, 3, 6, 4], visit)).toEqual(7);
+    expect(visit).toHaveBeenCalledTimes(6);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([1, 2, 3, 4, 5], visit)).toEqual(4);
+    expect(visit).toHaveBeenCalledTimes(5);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks([7, 6, 4, 3, 1], visit)).toEqual(0);
+    expect(visit).toHaveBeenCalledTimes(5);
+
+    visit = jest.fn();
+    expect(peakvalleyBestTimeToBuySellStocks(
+      [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
+      visit,
+    )).toEqual(19);
+    expect(visit).toHaveBeenCalledTimes(20);
+  });
+});
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/accumulatorBestTimeToBuySellStocks.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/accumulatorBestTimeToBuySellStocks.js
new file mode 100644
index 0000000000..9e244c89e1
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/accumulatorBestTimeToBuySellStocks.js
@@ -0,0 +1,20 @@
+/**
+ * Finds the maximum profit from selling and buying the stocks.
+ * ACCUMULATOR APPROACH.
+ *
+ * @param {number[]} prices - Array of stock prices, i.e. [7, 6, 4, 3, 1]
+ * @param {function(): void} visit - Visiting callback to calculate the number of iterations.
+ * @return {number} - The maximum profit
+ */
+const accumulatorBestTimeToBuySellStocks = (prices, visit = () => {}) => {
+  visit();
+  let profit = 0;
+  for (let day = 1; day < prices.length; day += 1) {
+    visit();
+    // Add the increase of the price from yesterday till today (if there was any) to the profit.
+    profit += Math.max(prices[day] - prices[day - 1], 0);
+  }
+  return profit;
+};
+
+export default accumulatorBestTimeToBuySellStocks;
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/dpBestTimeToBuySellStocks.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/dpBestTimeToBuySellStocks.js
new file mode 100644
index 0000000000..8bf1645f11
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/dpBestTimeToBuySellStocks.js
@@ -0,0 +1,25 @@
+/**
+ * Finds the maximum profit from selling and buying the stocks.
+ * DYNAMIC PROGRAMMING APPROACH.
+ *
+ * @param {number[]} prices - Array of stock prices, i.e. [7, 6, 4, 3, 1]
+ * @param {function(): void} visit - Visiting callback to calculate the number of iterations.
+ * @return {number} - The maximum profit
+ */
+const dpBestTimeToBuySellStocks = (prices, visit = () => {}) => {
+  visit();
+  let lastBuy = -prices[0];
+  let lastSold = 0;
+
+  for (let day = 1; day < prices.length; day += 1) {
+    visit();
+    const curBuy = Math.max(lastBuy, lastSold - prices[day]);
+    const curSold = Math.max(lastSold, lastBuy + prices[day]);
+    lastBuy = curBuy;
+    lastSold = curSold;
+  }
+
+  return lastSold;
+};
+
+export default dpBestTimeToBuySellStocks;
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/dqBestTimeToBuySellStocks.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/dqBestTimeToBuySellStocks.js
new file mode 100644
index 0000000000..188ad9fe1e
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/dqBestTimeToBuySellStocks.js
@@ -0,0 +1,42 @@
+/**
+ * Finds the maximum profit from selling and buying the stocks.
+ * DIVIDE & CONQUER APPROACH.
+ *
+ * @param {number[]} prices - Array of stock prices, i.e. [7, 6, 4, 3, 1]
+ * @param {function(): void} visit - Visiting callback to calculate the number of iterations.
+ * @return {number} - The maximum profit
+ */
+const dqBestTimeToBuySellStocks = (prices, visit = () => {}) => {
+  /**
+   * Recursive implementation of the main function. It is hidden from the users.
+   *
+   * @param {boolean} buy - Whether we're allow to sell or to buy now
+   * @param {number} day - Current day of trading (current index of prices array)
+   * @returns {number} - Max profit from buying/selling
+   */
+  const recursiveBuyerSeller = (buy, day) => {
+    // Registering the recursive call visit to calculate the complexity.
+    visit();
+
+    // Quitting the recursion if this is the last day of trading (prices array ended).
+    if (day === prices.length) {
+      return 0;
+    }
+
+    // If we're buying - we're loosing money (-1), if we're selling we're getting money (+1).
+    const operationSign = buy ? -1 : +1;
+    return Math.max(
+      // Option 1: Don't do anything.
+      recursiveBuyerSeller(buy, day + 1),
+      // Option 2: Sell or Buy at the current price.
+      operationSign * prices[day] + recursiveBuyerSeller(!buy, day + 1),
+    );
+  };
+
+  const buy = true;
+  const day = 0;
+
+  return recursiveBuyerSeller(buy, day);
+};
+
+export default dqBestTimeToBuySellStocks;
diff --git a/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/peakvalleyBestTimeToBuySellStocks.js b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/peakvalleyBestTimeToBuySellStocks.js
new file mode 100644
index 0000000000..44e8bcbd7d
--- /dev/null
+++ b/src/algorithms/uncategorized/best-time-to-buy-sell-stocks/peakvalleyBestTimeToBuySellStocks.js
@@ -0,0 +1,35 @@
+/**
+ * Finds the maximum profit from selling and buying the stocks.
+ * PEAK VALLEY APPROACH.
+ *
+ * @param {number[]} prices - Array of stock prices, i.e. [7, 6, 4, 3, 1]
+ * @param {function(): void} visit - Visiting callback to calculate the number of iterations.
+ * @return {number} - The maximum profit
+ */
+const peakvalleyBestTimeToBuySellStocks = (prices, visit = () => {}) => {
+  visit();
+  let profit = 0;
+  let low = prices[0];
+  let high = prices[0];
+
+  prices.slice(1).forEach((currentPrice) => {
+    visit();
+    if (currentPrice < high) {
+      // If price went down, we need to sell.
+      profit += high - low;
+      low = currentPrice;
+      high = currentPrice;
+    } else {
+      // If price went up, we don't need to do anything but increase a high record.
+      high = currentPrice;
+    }
+  });
+
+  // In case if price went up during the last day
+  // and we didn't have chance to sell inside the forEach loop.
+  profit += high - low;
+
+  return profit;
+};
+
+export default peakvalleyBestTimeToBuySellStocks;
diff --git a/src/algorithms/uncategorized/n-queens/README.md b/src/algorithms/uncategorized/n-queens/README.md
index 077e26d73d..399256768e 100644
--- a/src/algorithms/uncategorized/n-queens/README.md
+++ b/src/algorithms/uncategorized/n-queens/README.md
@@ -59,7 +59,7 @@ and return false.
         queen here leads to a solution.
     b) If placing queen in [row, column] leads to a solution then return 
         true.
-    c) If placing queen doesn't lead to a solution then umark this [row, 
+    c) If placing queen doesn't lead to a solution then unmark this [row, 
         column] (Backtrack) and go to step (a) to try other rows.
 3) If all rows have been tried and nothing worked, return false to trigger 
     backtracking.
diff --git a/src/algorithms/uncategorized/n-queens/nQueens.js b/src/algorithms/uncategorized/n-queens/nQueens.js
index 42a99995b7..d95d182cc6 100644
--- a/src/algorithms/uncategorized/n-queens/nQueens.js
+++ b/src/algorithms/uncategorized/n-queens/nQueens.js
@@ -81,7 +81,6 @@ function nQueensRecursive(solutions, previousQueensPositions, queensCount, rowIn
   return false;
 }
 
-
 /**
  * @param {number} queensCount
  * @return {QueenPosition[][]}
diff --git a/src/algorithms/uncategorized/rain-terraces/bfRainTerraces.js b/src/algorithms/uncategorized/rain-terraces/bfRainTerraces.js
index f136e6d4c6..0d3443a4aa 100644
--- a/src/algorithms/uncategorized/rain-terraces/bfRainTerraces.js
+++ b/src/algorithms/uncategorized/rain-terraces/bfRainTerraces.js
@@ -25,7 +25,7 @@ export default function bfRainTerraces(terraces) {
     if (terraceBoundaryLevel > terraces[terraceIndex]) {
       // Terrace will be able to store the water if the lowest of two left and right highest
       // terraces are still higher than the current one.
-      waterAmount += Math.min(leftHighestLevel, rightHighestLevel) - terraces[terraceIndex];
+      waterAmount += terraceBoundaryLevel - terraces[terraceIndex];
     }
   }
 
diff --git a/src/algorithms/uncategorized/recursive-staircase/README.md b/src/algorithms/uncategorized/recursive-staircase/README.md
new file mode 100644
index 0000000000..d7e6f81464
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/README.md
@@ -0,0 +1,21 @@
+# Recursive Staircase Problem
+
+## The Problem
+
+There are `n` stairs, a person standing at the bottom wants to reach the top. The person can climb either `1` or `2` stairs at a time. _Count the number of ways, the person can reach the top._
+
+![](https://cdncontribute.geeksforgeeks.org/wp-content/uploads/nth-stair.png)
+
+## The Solution
+
+This is an interesting problem because there are several ways of how it may be solved that illustrate different programming paradigms.
+
+- [Brute Force Recursive Solution](./recursiveStaircaseBF.js) - Time: `O(2^n)`; Space: `O(1)`
+- [Recursive Solution With Memoization](./recursiveStaircaseMEM.js) - Time: `O(n)`; Space: `O(n)`
+- [Dynamic Programming Solution](./recursiveStaircaseDP.js) - Time: `O(n)`; Space: `O(n)`
+- [Iterative Solution](./recursiveStaircaseIT.js) - Time: `O(n)`; Space: `O(1)` 
+
+## References
+
+- [On YouTube by Gayle Laakmann McDowell](https://www.youtube.com/watch?v=eREiwuvzaUM&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=81&t=0s)
+- [GeeksForGeeks](https://www.geeksforgeeks.org/count-ways-reach-nth-stair/)
diff --git a/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseBF.test.js b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseBF.test.js
new file mode 100644
index 0000000000..d1cccf4238
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseBF.test.js
@@ -0,0 +1,18 @@
+import recursiveStaircaseBF from '../recursiveStaircaseBF';
+
+describe('recursiveStaircaseBF', () => {
+  it('should calculate number of variants using Brute Force solution', () => {
+    expect(recursiveStaircaseBF(-1)).toBe(0);
+    expect(recursiveStaircaseBF(0)).toBe(0);
+    expect(recursiveStaircaseBF(1)).toBe(1);
+    expect(recursiveStaircaseBF(2)).toBe(2);
+    expect(recursiveStaircaseBF(3)).toBe(3);
+    expect(recursiveStaircaseBF(4)).toBe(5);
+    expect(recursiveStaircaseBF(5)).toBe(8);
+    expect(recursiveStaircaseBF(6)).toBe(13);
+    expect(recursiveStaircaseBF(7)).toBe(21);
+    expect(recursiveStaircaseBF(8)).toBe(34);
+    expect(recursiveStaircaseBF(9)).toBe(55);
+    expect(recursiveStaircaseBF(10)).toBe(89);
+  });
+});
diff --git a/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseDP.test.js b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseDP.test.js
new file mode 100644
index 0000000000..b8edd831c6
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseDP.test.js
@@ -0,0 +1,18 @@
+import recursiveStaircaseDP from '../recursiveStaircaseDP';
+
+describe('recursiveStaircaseDP', () => {
+  it('should calculate number of variants using Dynamic Programming solution', () => {
+    expect(recursiveStaircaseDP(-1)).toBe(0);
+    expect(recursiveStaircaseDP(0)).toBe(0);
+    expect(recursiveStaircaseDP(1)).toBe(1);
+    expect(recursiveStaircaseDP(2)).toBe(2);
+    expect(recursiveStaircaseDP(3)).toBe(3);
+    expect(recursiveStaircaseDP(4)).toBe(5);
+    expect(recursiveStaircaseDP(5)).toBe(8);
+    expect(recursiveStaircaseDP(6)).toBe(13);
+    expect(recursiveStaircaseDP(7)).toBe(21);
+    expect(recursiveStaircaseDP(8)).toBe(34);
+    expect(recursiveStaircaseDP(9)).toBe(55);
+    expect(recursiveStaircaseDP(10)).toBe(89);
+  });
+});
diff --git a/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseIT.test.js b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseIT.test.js
new file mode 100644
index 0000000000..2c0eb732cd
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseIT.test.js
@@ -0,0 +1,18 @@
+import recursiveStaircaseIT from '../recursiveStaircaseIT';
+
+describe('recursiveStaircaseIT', () => {
+  it('should calculate number of variants using Iterative solution', () => {
+    expect(recursiveStaircaseIT(-1)).toBe(0);
+    expect(recursiveStaircaseIT(0)).toBe(0);
+    expect(recursiveStaircaseIT(1)).toBe(1);
+    expect(recursiveStaircaseIT(2)).toBe(2);
+    expect(recursiveStaircaseIT(3)).toBe(3);
+    expect(recursiveStaircaseIT(4)).toBe(5);
+    expect(recursiveStaircaseIT(5)).toBe(8);
+    expect(recursiveStaircaseIT(6)).toBe(13);
+    expect(recursiveStaircaseIT(7)).toBe(21);
+    expect(recursiveStaircaseIT(8)).toBe(34);
+    expect(recursiveStaircaseIT(9)).toBe(55);
+    expect(recursiveStaircaseIT(10)).toBe(89);
+  });
+});
diff --git a/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseMEM.test.js b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseMEM.test.js
new file mode 100644
index 0000000000..3099873249
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/__test__/recursiveStaircaseMEM.test.js
@@ -0,0 +1,18 @@
+import recursiveStaircaseMEM from '../recursiveStaircaseMEM';
+
+describe('recursiveStaircaseMEM', () => {
+  it('should calculate number of variants using Brute Force with Memoization', () => {
+    expect(recursiveStaircaseMEM(-1)).toBe(0);
+    expect(recursiveStaircaseMEM(0)).toBe(0);
+    expect(recursiveStaircaseMEM(1)).toBe(1);
+    expect(recursiveStaircaseMEM(2)).toBe(2);
+    expect(recursiveStaircaseMEM(3)).toBe(3);
+    expect(recursiveStaircaseMEM(4)).toBe(5);
+    expect(recursiveStaircaseMEM(5)).toBe(8);
+    expect(recursiveStaircaseMEM(6)).toBe(13);
+    expect(recursiveStaircaseMEM(7)).toBe(21);
+    expect(recursiveStaircaseMEM(8)).toBe(34);
+    expect(recursiveStaircaseMEM(9)).toBe(55);
+    expect(recursiveStaircaseMEM(10)).toBe(89);
+  });
+});
diff --git a/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseBF.js b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseBF.js
new file mode 100644
index 0000000000..108aae2797
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseBF.js
@@ -0,0 +1,27 @@
+/**
+ * Recursive Staircase Problem (Brute Force Solution).
+ *
+ * @param {number} stairsNum - Number of stairs to climb on.
+ * @return {number} - Number of ways to climb a staircase.
+ */
+export default function recursiveStaircaseBF(stairsNum) {
+  if (stairsNum <= 0) {
+    // There is no way to go down - you climb the stairs only upwards.
+    // Also if you're standing on the ground floor that you don't need to do any further steps.
+    return 0;
+  }
+
+  if (stairsNum === 1) {
+    // There is only one way to go to the first step.
+    return 1;
+  }
+
+  if (stairsNum === 2) {
+    // There are two ways to get to the second steps: (1 + 1) or (2).
+    return 2;
+  }
+
+  // Sum up how many steps we need to take after doing one step up with the number of
+  // steps we need to take after doing two steps up.
+  return recursiveStaircaseBF(stairsNum - 1) + recursiveStaircaseBF(stairsNum - 2);
+}
diff --git a/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseDP.js b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseDP.js
new file mode 100644
index 0000000000..f8cffcfa2a
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseDP.js
@@ -0,0 +1,33 @@
+/**
+ * Recursive Staircase Problem (Dynamic Programming Solution).
+ *
+ * @param {number} stairsNum - Number of stairs to climb on.
+ * @return {number} - Number of ways to climb a staircase.
+ */
+export default function recursiveStaircaseDP(stairsNum) {
+  if (stairsNum < 0) {
+    // There is no way to go down - you climb the stairs only upwards.
+    return 0;
+  }
+
+  // Init the steps vector that will hold all possible ways to get to the corresponding step.
+  const steps = new Array(stairsNum + 1).fill(0);
+
+  // Init the number of ways to get to the 0th, 1st and 2nd steps.
+  steps[0] = 0;
+  steps[1] = 1;
+  steps[2] = 2;
+
+  if (stairsNum <= 2) {
+    // Return the number of ways to get to the 0th or 1st or 2nd steps.
+    return steps[stairsNum];
+  }
+
+  // Calculate every next step based on two previous ones.
+  for (let currentStep = 3; currentStep <= stairsNum; currentStep += 1) {
+    steps[currentStep] = steps[currentStep - 1] + steps[currentStep - 2];
+  }
+
+  // Return possible ways to get to the requested step.
+  return steps[stairsNum];
+}
diff --git a/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseIT.js b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseIT.js
new file mode 100644
index 0000000000..e46450475d
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseIT.js
@@ -0,0 +1,31 @@
+/**
+ * Recursive Staircase Problem (Iterative Solution).
+ *
+ * @param {number} stairsNum - Number of stairs to climb on.
+ * @return {number} - Number of ways to climb a staircase.
+ */
+export default function recursiveStaircaseIT(stairsNum) {
+  if (stairsNum <= 0) {
+    // There is no way to go down - you climb the stairs only upwards.
+    // Also you don't need to do anything to stay on the 0th step.
+    return 0;
+  }
+
+  // Init the number of ways to get to the 0th, 1st and 2nd steps.
+  const steps = [1, 2];
+
+  if (stairsNum <= 2) {
+    // Return the number of possible ways of how to get to the 1st or 2nd steps.
+    return steps[stairsNum - 1];
+  }
+
+  // Calculate the number of ways to get to the n'th step based on previous ones.
+  // Comparing to Dynamic Programming solution we don't store info for all the steps but
+  // rather for two previous ones only.
+  for (let currentStep = 3; currentStep <= stairsNum; currentStep += 1) {
+    [steps[0], steps[1]] = [steps[1], steps[0] + steps[1]];
+  }
+
+  // Return possible ways to get to the requested step.
+  return steps[1];
+}
diff --git a/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseMEM.js b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseMEM.js
new file mode 100644
index 0000000000..dcc9f9a40a
--- /dev/null
+++ b/src/algorithms/uncategorized/recursive-staircase/recursiveStaircaseMEM.js
@@ -0,0 +1,44 @@
+/**
+ * Recursive Staircase Problem (Recursive Solution With Memoization).
+ *
+ * @param {number} totalStairs - Number of stairs to climb on.
+ * @return {number} - Number of ways to climb a staircase.
+ */
+export default function recursiveStaircaseMEM(totalStairs) {
+  // Memo table that will hold all recursively calculated results to avoid calculating them
+  // over and over again.
+  const memo = [];
+
+  // Recursive closure.
+  const getSteps = (stairsNum) => {
+    if (stairsNum <= 0) {
+      // There is no way to go down - you climb the stairs only upwards.
+      // Also if you're standing on the ground floor that you don't need to do any further steps.
+      return 0;
+    }
+
+    if (stairsNum === 1) {
+      // There is only one way to go to the first step.
+      return 1;
+    }
+
+    if (stairsNum === 2) {
+      // There are two ways to get to the second steps: (1 + 1) or (2).
+      return 2;
+    }
+
+    // Avoid recursion for the steps that we've calculated recently.
+    if (memo[stairsNum]) {
+      return memo[stairsNum];
+    }
+
+    // Sum up how many steps we need to take after doing one step up with the number of
+    // steps we need to take after doing two steps up.
+    memo[stairsNum] = getSteps(stairsNum - 1) + getSteps(stairsNum - 2);
+
+    return memo[stairsNum];
+  };
+
+  // Return possible ways to get to the requested step.
+  return getSteps(totalStairs);
+}
diff --git a/src/data-structures/bloom-filter/BloomFilter.js b/src/data-structures/bloom-filter/BloomFilter.js
index 652c0d739b..dd264887c5 100644
--- a/src/data-structures/bloom-filter/BloomFilter.js
+++ b/src/data-structures/bloom-filter/BloomFilter.js
@@ -16,7 +16,7 @@ export default class BloomFilter {
     const hashValues = this.getHashValues(item);
 
     // Set each hashValue index to true.
-    hashValues.forEach(val => this.storage.setValue(val));
+    hashValues.forEach((val) => this.storage.setValue(val));
   }
 
   /**
diff --git a/src/data-structures/bloom-filter/README.md b/src/data-structures/bloom-filter/README.md
index 4e852dd823..7880a7f7be 100644
--- a/src/data-structures/bloom-filter/README.md
+++ b/src/data-structures/bloom-filter/README.md
@@ -1,35 +1,40 @@
 # Bloom Filter
 
-A **bloom filter** is a space-efficient probabilistic 
-data structure designed to test whether an element 
-is present in a set. It is designed to be blazingly 
+_Read this in other languages:_
+[_Русский_](README.ru-RU.md),
+[_Português_](README.pt-BR.md),
+[_Українська_](README.uk-UA.md)
+
+A **bloom filter** is a space-efficient probabilistic
+data structure designed to test whether an element
+is present in a set. It is designed to be blazingly
 fast and use minimal memory at the cost of potential
 false positives. False positive matches are possible,
 but false negatives are not – in other words, a query
 returns either "possibly in set" or "definitely not in set".
 
-Bloom proposed the technique for applications where the 
+Bloom proposed the technique for applications where the
 amount of source data would require an impractically large
-amount of memory if "conventional" error-free hashing 
+amount of memory if "conventional" error-free hashing
 techniques were applied.
 
 ## Algorithm description
 
-An empty Bloom filter is a bit array of `m` bits, all 
+An empty Bloom filter is a bit array of `m` bits, all
 set to `0`. There must also be `k` different hash functions
-defined, each of which maps or hashes some set element to 
-one of the `m` array positions, generating a uniform random 
-distribution. Typically, `k` is a constant, much smaller 
-than `m`, which is proportional to the number of elements 
-to be added; the precise choice of `k` and the constant of 
-proportionality of `m` are determined by the intended 
+defined, each of which maps or hashes some set element to
+one of the `m` array positions, generating a uniform random
+distribution. Typically, `k` is a constant, much smaller
+than `m`, which is proportional to the number of elements
+to be added; the precise choice of `k` and the constant of
+proportionality of `m` are determined by the intended
 false positive rate of the filter.
 
-Here is an example of a Bloom filter, representing the 
-set `{x, y, z}`. The colored arrows show the positions 
-in the bit array that each set element is mapped to. The 
-element `w` is not in the set `{x, y, z}`, because it 
-hashes to one bit-array position containing `0`. For 
+Here is an example of a Bloom filter, representing the
+set `{x, y, z}`. The colored arrows show the positions
+in the bit array that each set element is mapped to. The
+element `w` is not in the set `{x, y, z}`, because it
+hashes to one bit-array position containing `0`. For
 this figure, `m = 18` and `k = 3`.
 
 ![Bloom Filter](https://upload.wikimedia.org/wikipedia/commons/a/ac/Bloom_filter.svg)
@@ -88,7 +93,7 @@ three factors: the size of the bloom filter, the
 number of hash functions we use, and the number
 of items that have been inserted into the filter.
 
-The formula to calculate probablity of a false positive is:
+The formula to calculate probability of a false positive is:
 
 ( 1 - e <sup>-kn/m</sup> ) <sup>k</sup>
 
diff --git a/src/data-structures/bloom-filter/README.pt-BR.md b/src/data-structures/bloom-filter/README.pt-BR.md
new file mode 100644
index 0000000000..0c8522b958
--- /dev/null
+++ b/src/data-structures/bloom-filter/README.pt-BR.md
@@ -0,0 +1,129 @@
+# Filtro Bloom (Bloom Filter)
+
+O **bloom filter** é uma estrutura de dados probabilística
+espaço-eficiente designada para testar se um elemento está
+ou não presente em um conjunto de dados. Foi projetado para ser
+incrivelmente rápida e utilizar o mínimo de memória ao 
+potencial custo de um falso-positivo. Correspondências 
+_falsas positivas_ são possíveis, contudo _falsos negativos_ 
+não são - em outras palavras, a consulta retorna 
+"possivelmente no conjunto" ou "definitivamente não no conjunto".
+
+Bloom propôs a técnica para aplicações onde a quantidade 
+de entrada de dados exigiria uma alocação de memória
+impraticavelmente grande se as "convencionais" técnicas
+error-free hashing fossem aplicadas.
+
+## Descrição do algoritmo
+
+Um filtro Bloom vazio é um _bit array_ de `m` bits, todos
+definidos como `0`. Também deverá haver diferentes funções
+de hash `k` definidas, cada um dos quais mapeia e produz hash
+para um dos elementos definidos em uma das posições `m` da
+ _array_, gerando uma distribuição aleatória e uniforme.
+Normalmente, `k` é uma constante, muito menor do que `m`,
+pelo qual é proporcional ao número de elements a ser adicionado;
+a escolha precisa de `k` e a constante de proporcionalidade de `m`
+são determinadas pela taxa de falsos positivos planejado do filtro.
+
+Aqui está um exemplo de um filtro Bloom, representando o
+conjunto `{x, y, z}`. As flechas coloridas demonstram as
+posições no _bit array_ em que cada elemento é mapeado.
+O elemento `w` não está definido dentro de `{x, y, z}`,
+porque este produz hash para uma posição de array de bits
+contendo `0`. Para esta imagem: `m = 18` e `k = 3`.
+
+![Bloom Filter](https://upload.wikimedia.org/wikipedia/commons/a/ac/Bloom_filter.svg)
+
+## Operações
+
+Existem duas operações principais que o filtro Bloom pode operar:
+_inserção_ e _pesquisa_. A pesquisa pode resultar em falsos
+positivos. Remoção não é possível.
+
+Em outras palavras, o filtro pode receber itens. Quando
+vamos verificar se um item já foi anteriormente
+inserido, ele poderá nos dizer "não" ou "talvez".
+
+Ambas as inserções e pesquisas são operações `O(1)`.
+
+## Criando o filtro
+
+Um filtro Bloom é criado ao alocar um certo tamanho.
+No nosso exemplo, nós utilizamos `100` como tamanho padrão.
+Todas as posições são initializadas como `false`.
+
+### Inserção
+
+Durante a inserção, um número de função hash, no nosso caso `3`
+funções de hash, são utilizadas para criar hashes de uma entrada.
+Estas funções de hash emitem saída de índices. A cada índice
+recebido, nós simplismente trocamos o valor de nosso filtro
+Bloom para `true`.
+
+### Pesquisa
+
+Durante a pesquisa, a mesma função de hash é chamada
+e usada para emitir hash da entrada. Depois nós checamos
+se _todos_ os indices recebidos possuem o valor `true`
+dentro de nosso filtro Bloom. Caso _todos_ possuam o valor
+`true`, nós sabemos que o filtro Bloom pode ter tido
+o valor inserido anteriormente.
+
+Contudo, isto não é certeza, porque é possível que outros
+valores anteriormente inseridos trocaram o valor para `true`.
+Os valores não são necessariamente `true` devido ao ítem
+atualmente sendo pesquisado. A certeza absoluta é impossível,
+a não ser que apenas um item foi inserido anteriormente.
+
+Durante a checagem do filtro Bloom para índices retornados
+pela nossa função de hash, mesmo que apenas um deles possua
+valor como `false`, nós definitivamente sabemos que o ítem
+não foi anteriormente inserido.
+
+## Falso Positivos
+
+A probabilidade de falso positivos é determinado por
+três fatores: o tamanho do filtro de Bloom, o número de 
+funções de hash que utilizados, e o número de itens que
+foram inseridos dentro do filtro.
+
+A formula para calcular a probabilidade de um falso positivo é:
+
+( 1 - e <sup>-kn/m</sup> ) <sup>k</sup>
+
+`k` = número de funções de hash
+
+`m` = tamanho do filtro
+
+`n` = número de itens inserido
+
+Estas variáveis, `k`, `m` e `n`, devem ser escolhidas baseado
+em quanto aceitável são os falsos positivos. Se os valores
+escolhidos resultam em uma probabilidade muito alta, então
+os valores devem ser ajustados e a probabilidade recalculada.
+
+## Aplicações
+
+Um filtro Bloom pode ser utilizado em uma página de Blog.
+Se o objetivo é mostrar aos leitores somente os artigos
+em que eles nunca viram, então o filtro Bloom é perfeito 
+para isso. Ele pode armazenar hashes baseados nos artigos.
+Depois que um usuário lê alguns artigos, eles podem ser
+inseridos dentro do filtro. Na próxima vez que o usuário
+visitar o Blog, aqueles artigos poderão ser filtrados (eliminados) 
+do resultado.
+
+Alguns artigos serão inevitavelmente filtrados (eliminados) 
+por engano, mas o custo é aceitável. Tudo bem se um usuário nunca
+ver alguns poucos artigos, desde que tenham outros novos
+para ver toda vez que eles visitam o site. 
+
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Bloom_filter)
+- [Bloom Filters by Example](http://llimllib.github.io/bloomfilter-tutorial/)
+- [Calculating False Positive Probability](https://hur.st/bloomfilter/?n=4&p=&m=18&k=3)
+- [Bloom Filters on Medium](https://blog.medium.com/what-are-bloom-filters-1ec2a50c68ff)
+- [Bloom Filters on YouTube](https://www.youtube.com/watch?v=bEmBh1HtYrw)
diff --git a/src/data-structures/bloom-filter/README.ru-RU.md b/src/data-structures/bloom-filter/README.ru-RU.md
new file mode 100644
index 0000000000..b5c19c06c8
--- /dev/null
+++ b/src/data-structures/bloom-filter/README.ru-RU.md
@@ -0,0 +1,55 @@
+# Фильтр Блума
+
+**Фильтр Блума** - это пространственно-эффективная вероятностная структура данных, созданная для проверки наличия элемента
+в множестве. Он спроектирован невероятно быстрым при минимальном использовании памяти ценой потенциальных ложных срабатываний.
+Существует возможность получить ложноположительное срабатывание (элемента в множестве нет, но структура данных сообщает,
+что он есть), но не ложноотрицательное. Другими словами, очередь возвращает или "возможно в наборе", или "определённо не
+в наборе". Фильтр Блума может использовать любой объём памяти, однако чем он больше, тем меньше вероятность ложного
+срабатывания. 
+
+Блум предложил эту технику для применения в областях, где количество исходных данных потребовало бы непрактично много 
+памяти, в случае применения условно безошибочных техник хеширования.
+
+## Описание алгоритма
+
+Пустой фильтр Блума представлен битовым массивом из `m` битов, все биты которого обнулены. Должно быть определено `k`
+независимых хеш-функций, отображающих каждый элемент множества в одну из `m` позиций в массиве, генерируя единообразное
+случайное распределение. Обычно `k` задана константой, которая много меньше `m` и пропорциональна
+количеству добавляемых элементов; точный выбор `k` и постоянной пропорциональности `m` определяются уровнем ложных
+срабатываний фильтра.
+
+Вот пример Блум фильтра, представляющего набор `{x, y, z}`. Цветные стрелки показывают позиции в битовом массиве,
+которым привязан каждый элемент набора. Элемент `w` не в наборе `{x, y, z}`, потому что он привязан к позиции в битовом
+массиве, равной `0`. Для этой формы , `m = 18`, а `k = 3`.
+
+Фильтр Блума представляет собой битовый массив из `m` бит. Изначально, когда структура данных хранит пустое множество, все
+`m` бит обнулены. Пользователь должен определить `k` независимых хеш-функций `h1`, …, `hk`,
+отображающих каждый элемент в одну из `m` позиций битового массива достаточно равномерным образом.
+
+Для добавления элемента e необходимо записать единицы на каждую из позиций `h1(e)`, …, `hk(e)`
+битового массива.
+
+Для проверки принадлежности элемента `e` к множеству хранимых элементов, необходимо проверить состояние битов
+`h1(e)`, …, `hk(e)`. Если хотя бы один из них равен нулю, элемент не может принадлежать множеству
+(иначе бы при его добавлении все эти биты были установлены). Если все они равны единице, то структура данных сообщает,
+что `е` принадлежит множеству. При этом может возникнуть две ситуации: либо элемент действительно принадлежит множеству,
+либо все эти биты оказались установлены по случайности при добавлении других элементов, что и является источником ложных
+срабатываний в этой структуре данных.
+
+![Фильтр Блума](https://upload.wikimedia.org/wikipedia/commons/a/ac/Bloom_filter.svg)
+
+## Применения
+
+Фильтр Блума может быть использован для блогов. Если цель состоит в том, чтобы показать читателям только те статьи,
+которые они ещё не видели, фильтр блума идеален. Он может содержать хешированные значения, соответствующие статье. После
+того, как пользователь прочитал несколько статей, они могут быть помещены в фильтр. В следующий раз, когда пользователь
+посетит сайт, эти статьи могут быть убраны из результатов с помощью фильтра.
+
+Некоторые статьи неизбежно будут отфильтрованы по ошибке, но цена приемлема. То, что пользователь не увидит несколько
+статей в полне приемлемо, принимая во внимание тот факт, что ему всегда показываются другие новые статьи при каждом
+новом посещении.
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80_%D0%91%D0%BB%D1%83%D0%BC%D0%B0)
+- [Фильтр Блума на Хабре](https://habr.com/ru/post/112069/)
diff --git a/src/data-structures/bloom-filter/README.uk-UA.md b/src/data-structures/bloom-filter/README.uk-UA.md
new file mode 100644
index 0000000000..aa9075a094
--- /dev/null
+++ b/src/data-structures/bloom-filter/README.uk-UA.md
@@ -0,0 +1,54 @@
+# Фільтр Блума
+
+**Фільтр Блума** - це просторово-ефективна ймовірна структура даних, створена для перевірки наявності елемента
+у множині. Він спроектований неймовірно швидким за мінімального використання пам'яті ціною потенційних помилкових спрацьовувань.
+Існує можливість отримати хибнопозитивне спрацьовування (елемента в безлічі немає, але структура даних повідомляє,
+що він є), але не хибнонегативне. Іншими словами, черга повертає або "можливо в наборі", або "певно не
+у наборі". Фільтр Блума може використовувати будь-який обсяг пам'яті, проте чим він більший, тим менша вірогідність помилкового
+спрацьовування.
+
+Блум запропонував цю техніку для застосування в областях, де кількість вихідних даних потребувала б непрактично багато
+пам'яті, у разі застосування умовно безпомилкових технік хешування.
+
+## Опис алгоритму
+
+Порожній фільтр Блума представлений бітовим масивом з `m` бітів, всі біти якого обнулені. Має бути визначено `k`
+незалежних хеш-функцій, що відображають кожен елемент множини в одну з `m` позицій у масиві, генеруючи однакове
+випадковий розподіл. Зазвичай `k` задана константою, яка набагато менше `m` і пропорційна
+кількості елементів, що додаються; точний вибір `k` та постійної пропорційності `m` визначаються рівнем хибних
+спрацьовувань фільтра.
+
+Ось приклад Блум фільтра, що представляє набір `{x, y, z}`. Кольорові стрілки показують позиції в бітовому масиві,
+яким прив'язаний кожен елемент набору. Елемент `w` не в наборі `{x, y, z}`, тому що він прив'язаний до позиції в бітовому
+масиві, що дорівнює `0`. Для цієї форми, `m = 18`, а `k = 3`.
+
+Фільтр Блума є бітовий масив з `m` біт. Спочатку, коли структура даних зберігає порожню множину, всі
+m біт обнулені. Користувач повинен визначити `k` незалежних хеш-функцій `h1`, …, `hk`,
+що відображають кожен елемент в одну з m позицій бітового масиву досить рівномірним чином.
+
+Для додавання елемента e необхідно записати одиниці на кожну з позицій `h1(e)`, …, `hk(e)`
+бітового масиву.
+
+Для перевірки приналежності елемента `e` до безлічі елементів, що зберігаються, необхідно перевірити стан бітів
+`h1(e)`, …, `hk(e)`. Якщо хоча б один з них дорівнює нулю, елемент не може належати множині
+(інакше при його додаванні всі ці біти були встановлені). Якщо вони рівні одиниці, то структура даних повідомляє,
+що `е` належить безлічі. При цьому може виникнути дві ситуації: або елемент дійсно належить множині,
+або всі ці біти виявилися встановлені випадково при додаванні інших елементів, що і є джерелом помилкових
+спрацьовувань у цій структурі даних.
+
+![Фільтр Блума](https://upload.wikimedia.org/wikipedia/commons/a/ac/Bloom_filter.svg)
+
+## Застосування
+
+Фільтр Блума може бути використаний для блогів. Якщо мета полягає в тому, щоб показати читачам лише ті статті,
+які вони ще не бачили, фільтр блуму ідеальний. Він може містити значення, що хешуються, відповідні статті. Після
+того, як користувач прочитав кілька статей, вони можуть бути поміщені у фільтр. Наступного разу, коли користувач
+відвідає сайт, ці статті можуть бути вилучені з результатів за допомогою фільтра.
+
+Деякі статті неминуче будуть відфільтровані помилково, але ціна прийнятна. Те, що користувач не побачить дещо
+статей цілком прийнятно, беручи до уваги той факт, що йому завжди показуються інші нові статті при кожному
+новому відвідуванні.
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%A4%D1%96%D0%BB%D1%8C%D1%82%D1%80_%D0%91%D0%BB%D1%83%D0%BC%D0%B0)
diff --git a/src/data-structures/bloom-filter/__test__/BloomFilter.test.js b/src/data-structures/bloom-filter/__test__/BloomFilter.test.js
index 503ad695f7..cc5891e16f 100644
--- a/src/data-structures/bloom-filter/__test__/BloomFilter.test.js
+++ b/src/data-structures/bloom-filter/__test__/BloomFilter.test.js
@@ -51,7 +51,7 @@ describe('BloomFilter', () => {
   });
 
   it('should insert strings correctly and return true when checking for inserted values', () => {
-    people.forEach(person => bloomFilter.insert(person));
+    people.forEach((person) => bloomFilter.insert(person));
 
     expect(bloomFilter.mayContain('Bruce Wayne')).toBe(true);
     expect(bloomFilter.mayContain('Clark Kent')).toBe(true);
diff --git a/src/data-structures/disjoint-set/DisjointSetAdhoc.js b/src/data-structures/disjoint-set/DisjointSetAdhoc.js
new file mode 100644
index 0000000000..9653418042
--- /dev/null
+++ b/src/data-structures/disjoint-set/DisjointSetAdhoc.js
@@ -0,0 +1,78 @@
+/**
+ * The minimalistic (ad hoc) version of a DisjointSet (or a UnionFind) data structure
+ * that doesn't have external dependencies and that is easy to copy-paste and
+ * use during the coding interview if allowed by the interviewer (since many
+ * data structures in JS are missing).
+ *
+ * Time Complexity:
+ *
+ * - Constructor: O(N)
+ * - Find: O(α(N))
+ * - Union: O(α(N))
+ * - Connected: O(α(N))
+ *
+ * Where N is the number of vertices in the graph.
+ * α refers to the Inverse Ackermann function.
+ * In practice, we assume it's a constant.
+ * In other words, O(α(N)) is regarded as O(1) on average.
+ */
+class DisjointSetAdhoc {
+  /**
+   * Initializes the set of specified size.
+   * @param {number} size
+   */
+  constructor(size) {
+    // The index of a cell is an id of the node in a set.
+    // The value of a cell is an id (index) of the root node.
+    // By default, the node is a parent of itself.
+    this.roots = new Array(size).fill(0).map((_, i) => i);
+
+    // Using the heights array to record the height of each node.
+    // By default each node has a height of 1 because it has no children.
+    this.heights = new Array(size).fill(1);
+  }
+
+  /**
+   * Finds the root of node `a`
+   * @param {number} a
+   * @returns {number}
+   */
+  find(a) {
+    if (a === this.roots[a]) return a;
+    this.roots[a] = this.find(this.roots[a]);
+    return this.roots[a];
+  }
+
+  /**
+   * Joins the `a` and `b` nodes into same set.
+   * @param {number} a
+   * @param {number} b
+   * @returns {number}
+   */
+  union(a, b) {
+    const aRoot = this.find(a);
+    const bRoot = this.find(b);
+
+    if (aRoot === bRoot) return;
+
+    if (this.heights[aRoot] > this.heights[bRoot]) {
+      this.roots[bRoot] = aRoot;
+    } else if (this.heights[aRoot] < this.heights[bRoot]) {
+      this.roots[aRoot] = bRoot;
+    } else {
+      this.roots[bRoot] = aRoot;
+      this.heights[aRoot] += 1;
+    }
+  }
+
+  /**
+   * Checks if `a` and `b` belong to the same set.
+   * @param {number} a
+   * @param {number} b
+   */
+  connected(a, b) {
+    return this.find(a) === this.find(b);
+  }
+}
+
+export default DisjointSetAdhoc;
diff --git a/src/data-structures/disjoint-set/README.md b/src/data-structures/disjoint-set/README.md
index 5769275858..772f68191b 100644
--- a/src/data-structures/disjoint-set/README.md
+++ b/src/data-structures/disjoint-set/README.md
@@ -1,18 +1,28 @@
 # Disjoint Set
 
-**Disjoint-set** data structure (also called a union–find data structure or merge–find set) is a data 
-structure that tracks a set of elements partitioned into a number of disjoint (non-overlapping) subsets. 
-It provides near-constant-time operations (bounded by the inverse Ackermann function) to *add new sets*, 
-to *merge existing sets*, and to *determine whether elements are in the same set*. 
+_Read this in other languages:_
+[_Русский_](README.ru-RU.md),
+[_Português_](README.pt-BR.md),
+[_Українська_](README.uk-UA.md)
+
+**Disjoint-set** data structure (also called a union–find data structure or merge–find set) is a data
+structure that tracks a set of elements partitioned into a number of disjoint (non-overlapping) subsets.
+It provides near-constant-time operations (bounded by the inverse Ackermann function) to _add new sets_,
+to _merge existing sets_, and to _determine whether elements are in the same set_.
 In addition to many other uses (see the Applications section), disjoint-sets play a key role in Kruskal's algorithm for finding the minimum spanning tree of a graph.
 
 ![disjoint set](https://upload.wikimedia.org/wikipedia/commons/6/67/Dsu_disjoint_sets_init.svg)
 
-*MakeSet* creates 8 singletons.
+_MakeSet_ creates 8 singletons.
 
 ![disjoint set](https://upload.wikimedia.org/wikipedia/commons/a/ac/Dsu_disjoint_sets_final.svg)
 
-After some operations of *Union*, some sets are grouped together.
+After some operations of _Union_, some sets are grouped together.
+
+## Implementation
+
+- [DisjointSet.js](./DisjointSet.js)
+- [DisjointSetAdhoc.js](./DisjointSetAdhoc.js) - The minimalistic (ad hoc) version of a DisjointSet (or a UnionFind) data structure that doesn't have external dependencies and that is easy to copy-paste and use during the coding interview if allowed by the interviewer (since many data structures in JS are missing).
 
 ## References
 
diff --git a/src/data-structures/disjoint-set/README.pt-BR.md b/src/data-structures/disjoint-set/README.pt-BR.md
new file mode 100644
index 0000000000..a7d827a8a2
--- /dev/null
+++ b/src/data-structures/disjoint-set/README.pt-BR.md
@@ -0,0 +1,28 @@
+# Conjunto Disjunto (Disjoint Set)
+
+**Conjunto Disjunto**
+
+**Conjunto Disjunto** é uma estrutura de dados (também chamado de
+estrutura de dados de union–find ou merge–find) é uma estrutura de dados
+que rastreia um conjunto de elementos particionados em um número de
+subconjuntos separados (sem sobreposição).
+Ele fornece operações de tempo quase constante (limitadas pela função
+inversa de Ackermann) para *adicionar novos conjuntos*, para 
+*mesclar/fundir conjuntos existentes* e para *determinar se os elementos
+estão no mesmo conjunto*.
+Além de muitos outros usos (veja a seção Applications), conjuntos disjuntos
+desempenham um papel fundamental no algoritmo de Kruskal para encontrar a
+árvore geradora mínima de um grafo (graph).
+
+![disjoint set](https://upload.wikimedia.org/wikipedia/commons/6/67/Dsu_disjoint_sets_init.svg)
+
+*MakeSet* cria 8 singletons.
+
+![disjoint set](https://upload.wikimedia.org/wikipedia/commons/a/ac/Dsu_disjoint_sets_final.svg)
+
+Depois de algumas operações de *Uniões*, alguns conjuntos são agrupados juntos.
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
+- [By Abdul Bari on YouTube](https://www.youtube.com/watch?v=wU6udHRIkcc&index=14&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/disjoint-set/README.ru-RU.md b/src/data-structures/disjoint-set/README.ru-RU.md
new file mode 100644
index 0000000000..a9c234bf9c
--- /dev/null
+++ b/src/data-structures/disjoint-set/README.ru-RU.md
@@ -0,0 +1,21 @@
+# Система непересекающихся множеств
+
+**Система непересекающихся множеств** это структура данных (также называемая структурой данной поиска пересечения или
+множеством поиска слияния), которая управляет множеством элементов, разбитых на несколько непересекающихся подмножеств.
+Она предоставляет около-константное время выполнения операций (ограниченное обратной функцией Акерманна) по *добавлению
+новых множеств*, *слиянию существующих множеств* и *опеределению, относятся ли элементы к одному и тому же множеству*.
+
+Применяется для хранения компонент связности в графах, в частности, алгоритму Краскала необходима подобная структура
+данных для эффективной реализации.
+
+Основные операции:
+
+- *MakeSet(x)* - создаёт одноэлементное множество {x},
+- *Find(x)* - возвращает идентификатор множества, содержащего элемент x,
+- *Union(x,y)* - объединение множеств, содержащих x и y.
+
+После некоторых операций *объединения*, некоторые множества собраны вместе
+
+## Ссылки
+- [СНМ на Wikipedia](https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D0%BD%D0%B5%D0%BF%D0%B5%D1%80%D0%B5%D1%81%D0%B5%D0%BA%D0%B0%D1%8E%D1%89%D0%B8%D1%85%D1%81%D1%8F_%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2)
+- [СНМ на YouTube](https://www.youtube.com/watch?v=bXBHYqNeBLo)
diff --git a/src/data-structures/disjoint-set/README.uk-UA.md b/src/data-structures/disjoint-set/README.uk-UA.md
new file mode 100644
index 0000000000..c721b66420
--- /dev/null
+++ b/src/data-structures/disjoint-set/README.uk-UA.md
@@ -0,0 +1,22 @@
+# Система неперетинних множин
+
+**Система неперетинних множин** це структура даних (також звана структурою даної пошуку перетину або
+безліччю пошуку злиття), яка управляє безліччю елементів, розбитих на кілька підмножин, що не перетинаються.
+Вона надає близько-константний час виконання операцій (обмежений зворотною функцією Акерманна) за додаванням
+нових множин, *злиття існуючих множин і *випередження, чи відносяться елементи до одного і того ж безлічі.
+
+Застосовується для зберігання компонентів зв'язності в графах, зокрема, алгоритму Фарбала необхідна подібна структура
+даних для ефективної реалізації.
+
+Основні операції:
+
+- _MakeSet(x)_ - створює одноелементне безліч {x},
+- _Find(x)_ - повертає ідентифікатор множини, що містить елемент x,
+- _Union(x,y)_ - об'єднання множин, що містять x та y.
+
+Після деяких операцій _об'єднання_, деякі множини зібрані разом.
+
+## Посилання
+
+- [СНМ на Wikipedia](https://uk.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D0%BD%D0%B5%D0%BF%D0%B5%D1%80%D0%B5%D1%82%D0%B8%D0%BD%D0%BD%D0%B8%D1%85_%D0%BC%D0%BD%D0%BE%D0%B6%D0%B8%D0%BD)
+- [СНМ на YouTube](https://www.youtube.com/watch?v=5XwRPwLnK6I)
diff --git a/src/data-structures/disjoint-set/__test__/DisjointSet.test.js b/src/data-structures/disjoint-set/__test__/DisjointSet.test.js
index 5c67e8e6f2..db0d019b0a 100644
--- a/src/data-structures/disjoint-set/__test__/DisjointSet.test.js
+++ b/src/data-structures/disjoint-set/__test__/DisjointSet.test.js
@@ -94,7 +94,7 @@ describe('DisjointSet', () => {
   });
 
   it('should do basic manipulations on disjoint set with custom key extractor', () => {
-    const keyExtractor = value => value.key;
+    const keyExtractor = (value) => value.key;
 
     const disjointSet = new DisjointSet(keyExtractor);
 
diff --git a/src/data-structures/disjoint-set/__test__/DisjointSetAdhoc.test.js b/src/data-structures/disjoint-set/__test__/DisjointSetAdhoc.test.js
new file mode 100644
index 0000000000..f7be0fcde9
--- /dev/null
+++ b/src/data-structures/disjoint-set/__test__/DisjointSetAdhoc.test.js
@@ -0,0 +1,50 @@
+import DisjointSetAdhoc from '../DisjointSetAdhoc';
+
+describe('DisjointSetAdhoc', () => {
+  it('should create unions and find connected elements', () => {
+    const set = new DisjointSetAdhoc(10);
+
+    // 1-2-5-6-7 3-8-9 4
+    set.union(1, 2);
+    set.union(2, 5);
+    set.union(5, 6);
+    set.union(6, 7);
+
+    set.union(3, 8);
+    set.union(8, 9);
+
+    expect(set.connected(1, 5)).toBe(true);
+    expect(set.connected(5, 7)).toBe(true);
+    expect(set.connected(3, 8)).toBe(true);
+
+    expect(set.connected(4, 9)).toBe(false);
+    expect(set.connected(4, 7)).toBe(false);
+
+    // 1-2-5-6-7 3-8-9-4
+    set.union(9, 4);
+
+    expect(set.connected(4, 9)).toBe(true);
+    expect(set.connected(4, 3)).toBe(true);
+    expect(set.connected(8, 4)).toBe(true);
+
+    expect(set.connected(8, 7)).toBe(false);
+    expect(set.connected(2, 3)).toBe(false);
+  });
+
+  it('should keep the height of the tree small', () => {
+    const set = new DisjointSetAdhoc(10);
+
+    // 1-2-6-7-9 1 3 4 5
+    set.union(7, 6);
+    set.union(1, 2);
+    set.union(2, 6);
+    set.union(1, 7);
+    set.union(9, 1);
+
+    expect(set.connected(1, 7)).toBe(true);
+    expect(set.connected(6, 9)).toBe(true);
+    expect(set.connected(4, 9)).toBe(false);
+
+    expect(Math.max(...set.heights)).toBe(3);
+  });
+});
diff --git a/src/data-structures/doubly-linked-list/DoublyLinkedList.js b/src/data-structures/doubly-linked-list/DoublyLinkedList.js
index 4afb404bf6..6c3306b6dc 100644
--- a/src/data-structures/doubly-linked-list/DoublyLinkedList.js
+++ b/src/data-structures/doubly-linked-list/DoublyLinkedList.js
@@ -218,7 +218,7 @@ export default class DoublyLinkedList {
    * @return {DoublyLinkedList}
    */
   fromArray(values) {
-    values.forEach(value => this.append(value));
+    values.forEach((value) => this.append(value));
 
     return this;
   }
@@ -228,6 +228,36 @@ export default class DoublyLinkedList {
    * @return {string}
    */
   toString(callback) {
-    return this.toArray().map(node => node.toString(callback)).toString();
+    return this.toArray().map((node) => node.toString(callback)).toString();
+  }
+
+  /**
+   * Reverse a linked list.
+   * @returns {DoublyLinkedList}
+   */
+  reverse() {
+    let currNode = this.head;
+    let prevNode = null;
+    let nextNode = null;
+
+    while (currNode) {
+      // Store next node.
+      nextNode = currNode.next;
+      prevNode = currNode.previous;
+
+      // Change next node of the current node so it would link to previous node.
+      currNode.next = prevNode;
+      currNode.previous = nextNode;
+
+      // Move prevNode and currNode nodes one step forward.
+      prevNode = currNode;
+      currNode = nextNode;
+    }
+
+    // Reset head and tail.
+    this.tail = this.head;
+    this.head = prevNode;
+
+    return this;
   }
 }
diff --git a/src/data-structures/doubly-linked-list/README.es-ES.md b/src/data-structures/doubly-linked-list/README.es-ES.md
new file mode 100644
index 0000000000..490466d74b
--- /dev/null
+++ b/src/data-structures/doubly-linked-list/README.es-ES.md
@@ -0,0 +1,104 @@
+# Lista doblemente enlazada
+
+_Lea esto en otros idiomas:_
+[_Русский_](README.ru-RU.md),
+[_简体中文_](README.zh-CN.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md)
+[_한국어_](README.ko-KR.md)
+
+En informática, una **lista doblemente enlazada** es una estructura de datos relacionados que consta de un conjunto de registros conectados secuencialmente llamados nodos. Cada nodo contiene dos campos, llamados enlaces, que son referencias al nodo anterior y al siguiente en la secuencia de nodos. Los enlaces anterior y siguiente de los nodos inicial y final, apuntan respectivamente a algún tipo de terminador (normalmente un nodo centinela o nulo), facilitando así el recorrido de la lista. Si solo hay un nodo nulo, la lista se enlaza circularmente a través este. Puede conceptualizarse como dos listas enlazadas individualmente formadas a partir de los mismos elementos de datos, pero en órdenes secuenciales opuestos.
+
+![Lista doblemente enlazada](./images/doubly-linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Los dos enlaces de un nodo permiten recorrer la lista en cualquier dirección. Si bien agregar o eliminar un nodo en una lista doblemente enlazada requiere cambiar más enlaces que las mismas operaciones en una lista enlazada individualmente, las operaciones son más simples y potencialmente más eficientes (para nodos que no sean los primeros) porque no hay necesidad de realizar un seguimiento del nodo anterior durante el recorrido o no es necesario recorrer la lista para encontrar el nodo anterior, de modo que se pueda modificar su enlace.
+
+## Pseudocódigo para operaciones básicas
+
+### Insertar
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    n.previous ← tail
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+### Eliminar
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true; otherwise false
+  if head = ø
+    return false
+  end if
+  if value = head.value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+      head.previous ← ø
+    end if
+    return true
+  end if
+  n ← head.next
+  while n != ø and value !== n.value
+    n ← n.next
+  end while
+  if n = tail
+    tail ← tail.previous
+    tail.next ← ø
+    return true
+  else if n != ø
+    n.previous.next ← n.next
+    n.next.previous ← n.previous
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Recorrido Inverso
+
+```text
+ReverseTraversal(tail)
+  Pre: tail is the node of the list to traverse
+  Post: the list has been traversed in reverse order
+  n ← tail
+  while n != ø
+    yield n.value
+    n ← n.previous
+  end while
+end Reverse Traversal
+```
+
+## Complejidades
+
+## Complejidad del Tiempo
+
+| Acceso | Búsqueda | Inserción | Eliminación |
+| :----: | :------: | :-------: | :---------: |
+|  O(n)  |   O(n)   |   O(1)    |    O(n)     |
+
+### Complejidad del Espacio
+
+O(n)
+
+## Referencias
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
+- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/doubly-linked-list/README.ja-JP.md b/src/data-structures/doubly-linked-list/README.ja-JP.md
new file mode 100644
index 0000000000..0ff96628c9
--- /dev/null
+++ b/src/data-structures/doubly-linked-list/README.ja-JP.md
@@ -0,0 +1,97 @@
+# 双方向リスト
+
+コンピュータサイエンスにおいて、**双方向リスト**はノードと呼ばれる一連のリンクレコードからなる連結データ構造です。各ノードはリンクと呼ばれる2つのフィールドを持っていて、これらは一連のノード内における前のノードと次のノードを参照しています。最初のノードの前のリンクと最後のノードの次のリンクはある種の終端を示していて、一般的にはダミーノードやnullが格納され、リストのトラバースを容易に行えるようにしています。もしダミーノードが1つしかない場合、リストはその1つのノードを介して循環的にリンクされます。これは、それぞれ逆の順番の単方向のリンクリストが2つあるものとして考えることができます。
+
+![Doubly Linked List](./images/doubly-linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+2つのリンクにより、リストをどちらの方向にもトラバースすることができます。双方向リストはノードの追加や削除の際に、片方向リンクリストと比べてより多くのリンクを変更する必要があります。しかし、その操作は簡単で、より効率的な(最初のノード以外の場合)可能性があります。前のノードのリンクを更新する際に前のノードを保持したり、前のノードを見つけるためにリストをトラバースする必要がありません。
+
+## 基本操作の擬似コード
+
+### 挿入
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    n.previous ← tail
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+### 削除
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true; otherwise false
+  if head = ø
+    return false
+  end if
+  if value = head.value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+      head.previous ← ø
+    end if
+    return true
+  end if
+  n ← head.next
+  while n = ø and value !== n.value
+    n ← n.next
+  end while
+  if n = tail
+    tail ← tail.previous
+    tail.next ← ø
+    return true
+  else if n = ø
+    n.previous.next ← n.next
+    n.next.previous ← n.previous
+    return true
+  end if
+  return false
+end Remove
+```
+
+### 逆トラバース
+
+```text
+ReverseTraversal(tail)
+  Pre: tail is the node of the list to traverse
+  Post: the list has been traversed in reverse order
+  n ← tail
+  while n = ø
+    yield n.value
+    n ← n.previous
+  end while
+end Reverse Traversal
+```
+
+## 計算量
+
+## 時間計算量
+
+| Access    | Search    | Insertion | Deletion  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(n)      |
+
+### 空間計算量
+
+O(n)
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
+- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/doubly-linked-list/README.ko-KR.md b/src/data-structures/doubly-linked-list/README.ko-KR.md
new file mode 100644
index 0000000000..9f60e54f32
--- /dev/null
+++ b/src/data-structures/doubly-linked-list/README.ko-KR.md
@@ -0,0 +1,109 @@
+# Doubly Linked List
+
+_Read this in other languages:_
+[_Русский_](README.ru-RU.md),
+[_简体中文_](README.zh-CN.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md)
+
+컴퓨터공학에서 **이중 연결 리스트**는 순차적으로 링크된 노드라는 레코드 세트로 구성된 링크된 데이터 구조입니다.
+각 노드에는 링크라고 하는 두 개의 필드가 있으며, 노드 순서에서 이전 노드와 다음 노드에 대한 참조를 가집니다.
+시작 및 종료 노드의 이전 및 다음 링크는 각각 리스트의 순회를 용이하게 하기 위해서 일종의 종결자 (일반적으로 센티넬노드 또는 null)를 나타냅니다.
+센티넬 노드가 하나만 있으면, 목록이 센티넬 노드를 통해서 원형으로 연결됩니다.
+동일한 데이터 항목으로 구성되어 있지만, 반대 순서로 두 개의 단일 연결 리스트로 개념화 할 수 있습니다.
+
+![이중 연결 리스트](./images/doubly-linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+두 개의 노드 링크를 사용하면 어느 방향으로든 리스트를 순회할 수 있습니다.
+이중 연결 리스트에서 노드를 추가하거나 제거하려면, 단일 연결 리스트에서 동일한 작업보다 더 많은 링크를 변경해야 하지만, 첫 번째 노드 이외의 노드인 경우 작업을 추적할 필요가 없으므로 작업이 더 단순해져 잠재적으로 더 효율적입니다.
+리스트 순회 중 이전 노드 또는 링크를 수정할 수 있도록 이전 노드를 찾기 위해 리스트를 순회할 필요가 없습니다.
+
+## 기본 동작을 위한 Pseudocode
+
+### 삽입
+
+```text
+Add(value)
+  Pre: value는 리스트에 추가하고자 하는 값
+  Post: value는 목록의 끝에 배치됨
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    n.previous ← tail
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+### 삭제
+
+```text
+Remove(head, value)
+  Pre: head는 리스트의 앞단에 위치
+       value는 리스트에서 제거하고자 하는 값
+  Post: value가 리스트에서 제거되면 true; 아니라면 false;
+  if head = ø
+    return false
+  end if
+  if value = head.value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+      head.previous ← ø
+    end if
+    return true
+  end if
+  n ← head.next
+  while n = ø and value !== n.value
+    n ← n.next
+  end while
+  if n = tail
+    tail ← tail.previous
+    tail.next ← ø
+    return true
+  else if n = ø
+    n.previous.next ← n.next
+    n.next.previous ← n.previous
+    return true
+  end if
+  return false
+end Remove
+```
+
+### 역순회
+
+```text
+ReverseTraversal(tail)
+  Pre: tail은 리스트에서 순회하고자 하는 노드
+  Post: 리스트가 역순으로 순회됨
+  n ← tail
+  while n = ø
+    yield n.value
+    n ← n.previous
+  end while
+end Reverse Traversal
+```
+
+## 복잡도
+
+## 시간 복잡도
+
+| Access    | Search    | Insertion | Deletion  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(n)      |
+
+### 공간 복잡도
+
+O(n)
+
+## 참고
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
+- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/doubly-linked-list/README.md b/src/data-structures/doubly-linked-list/README.md
index da73f677a8..1610d1e497 100644
--- a/src/data-structures/doubly-linked-list/README.md
+++ b/src/data-structures/doubly-linked-list/README.md
@@ -1,22 +1,33 @@
 # Doubly Linked List
 
-In computer science, a **doubly linked list** is a linked data structure that 
-consists of a set of sequentially linked records called nodes. Each node contains 
-two fields, called links, that are references to the previous and to the next 
-node in the sequence of nodes. The beginning and ending nodes' previous and next 
-links, respectively, point to some kind of terminator, typically a sentinel 
-node or null, to facilitate traversal of the list. If there is only one 
-sentinel node, then the list is circularly linked via the sentinel node. It can 
-be conceptualized as two singly linked lists formed from the same data items, 
+_Read this in other languages:_
+[_Русский_](README.ru-RU.md),
+[_简体中文_](README.zh-CN.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Español_](README.es-ES.md),
+[_Українська_](README.uk-UA.md)
+
+In computer science, a **doubly linked list** is a linked data structure that
+consists of a set of sequentially linked records called nodes. Each node contains
+two fields, called links, that are references to the previous and to the next
+node in the sequence of nodes. The beginning and ending nodes' previous and next
+links, respectively, point to some kind of terminator, typically a sentinel
+node or null, to facilitate the traversal of the list. If there is only one
+sentinel node, then the list is circularly linked via the sentinel node. It can
+be conceptualized as two singly linked lists formed from the same data items,
 but in opposite sequential orders.
 
-![Doubly Linked List](https://upload.wikimedia.org/wikipedia/commons/5/5e/Doubly-linked-list.svg)
+![Doubly Linked List](./images/doubly-linked-list.jpeg)
+
+_Made with [okso.app](https://okso.app)_
 
-The two node links allow traversal of the list in either direction. While adding 
-or removing a node in a doubly linked list requires changing more links than the 
-same operations on a singly linked list, the operations are simpler and 
-potentially more efficient (for nodes other than first nodes) because there 
-is no need to keep track of the previous node during traversal or no need 
+The two node links allow traversal of the list in either direction. While adding
+or removing a node in a doubly linked list requires changing more links than the
+same operations on a singly linked list, the operations are simpler and
+potentially more efficient (for nodes other than first nodes) because there
+is no need to keep track of the previous node during traversal or no need
 to traverse the list to find the previous node, so that its link can be modified.
 
 ## Pseudocode for Basic Operations
@@ -38,7 +49,7 @@ Add(value)
   end if
 end Add
 ```
-    
+
 ### Delete
 
 ```text
@@ -54,20 +65,20 @@ Remove(head, value)
       head ← ø
       tail ← ø
     else
-      head ← head.Next
+      head ← head.next
       head.previous ← ø
     end if
     return true
   end if
   n ← head.next
-  while n = ø and value = n.value
+  while n != ø and value !== n.value
     n ← n.next
   end while
   if n = tail
     tail ← tail.previous
     tail.next ← ø
     return true
-  else if n = ø
+  else if n != ø
     n.previous.next ← n.next
     n.next.previous ← n.previous
     return true
@@ -75,7 +86,7 @@ Remove(head, value)
   return false
 end Remove
 ```
-    
+
 ### Reverse Traversal
 
 ```text
@@ -83,20 +94,20 @@ ReverseTraversal(tail)
   Pre: tail is the node of the list to traverse
   Post: the list has been traversed in reverse order
   n ← tail
-  while n = ø
+  while n != ø
     yield n.value
     n ← n.previous
   end while
 end Reverse Traversal
 ```
-    
+
 ## Complexities
 
 ## Time Complexity
 
-| Access    | Search    | Insertion | Deletion  |
-| :-------: | :-------: | :-------: | :-------: |
-| O(n)      | O(n)      | O(1)      | O(1)      |
+| Access | Search | Insertion | Deletion |
+| :----: | :----: | :-------: | :------: |
+|  O(n)  |  O(n)  |   O(1)    |   O(n)   |
 
 ### Space Complexity
 
diff --git a/src/data-structures/doubly-linked-list/README.pt-BR.md b/src/data-structures/doubly-linked-list/README.pt-BR.md
new file mode 100644
index 0000000000..c4294decf4
--- /dev/null
+++ b/src/data-structures/doubly-linked-list/README.pt-BR.md
@@ -0,0 +1,113 @@
+# Lista Duplamente Ligada (Doubly Linked List)
+
+Na ciência da computação, uma **lista duplamente conectada** é uma estrutura
+de dados vinculada que se consistem em um conjunto de registros
+sequencialmente vinculados chamados de nós (nodes). Em cada nó contém dois
+campos, chamados de ligações, que são referenciados ao nó anterior e posterior
+de uma sequência de nós. O começo e o fim dos nós anteriormente e posteriormente
+ligados, respectiviamente, apontam para algum tipo de terminação, normalmente
+um nó sentinela ou nulo, para facilitar a travessia da lista. Se existe
+somente um nó sentinela, então a lista é ligada circularmente através do nó
+sentinela. Ela pode ser conceitualizada como duas listas individualmente ligadas
+e formadas a partir dos mesmos itens, mas em ordem sequencial opostas.
+
+![Doubly Linked List](./images/doubly-linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Os dois nós ligados permitem a travessia da lista em qualquer direção.
+Enquanto adicionar ou remover um nó de uma lista duplamente vinculada requer
+alterar mais ligações (conexões) do que em uma lista encadeada individualmente
+(singly linked list), as operações são mais simples e potencialmente mais
+eficientes (para nós que não sejam nós iniciais) porque não há necessidade
+de manter um rastreamento do nó anterior durante a travessia ou não há
+necessidade de percorrer a lista para encontrar o nó anterior, para que
+então sua ligação/conexão possa ser modificada.
+
+## Pseudocódigo para Operações Básicas
+
+### Inserir
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    n.previous ← tail
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+### Remoção
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true; otherwise false
+  if head = ø
+    return false
+  end if
+  if value = head.value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+      head.previous ← ø
+    end if
+    return true
+  end if
+  n ← head.next
+  while n = ø and value !== n.value
+    n ← n.next
+  end while
+  if n = tail
+    tail ← tail.previous
+    tail.next ← ø
+    return true
+  else if n = ø
+    n.previous.next ← n.next
+    n.next.previous ← n.previous
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Travessia reversa
+
+```text
+ReverseTraversal(tail)
+  Pre: tail is the node of the list to traverse
+  Post: the list has been traversed in reverse order
+  n ← tail
+  while n = ø
+    yield n.value
+    n ← n.previous
+  end while
+end Reverse Traversal
+```
+
+## Complexidades
+
+## Complexidade de Tempo
+
+| Acesso    | Pesquisa    | Inserção | Remoção  |
+| :-------: | :---------: | :------: | :------: |
+| O(n)      | O(n)        | O(1)     | O(n)     |
+
+### Complexidade de Espaço
+
+O(n)
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
+- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/doubly-linked-list/README.ru-RU.md b/src/data-structures/doubly-linked-list/README.ru-RU.md
new file mode 100644
index 0000000000..b69d0da393
--- /dev/null
+++ b/src/data-structures/doubly-linked-list/README.ru-RU.md
@@ -0,0 +1,110 @@
+# Двусвязный список
+
+**Двусвязный список** — связная структура данных в информатике, состоящая из набора
+последовательно связанных записей, называемых узлами. Каждый узел содержит два поля,
+называемых ссылками, которые указывают на предыдущий и последующий элементы в
+последовательности узлов. Ссылка на предыдущий элемент корневого узла и ссылка на
+последующий элемент последнего узла указывают на некого рода прерыватель, обычно
+сторожевой узел или null, для облегчения обхода списка. Если в списке только один
+сторожевой узел, тогда список циклически связан через него.
+Двусвязный список можно представить, как два связных списка, которые образованы из
+одних и тех же данных, но расположенных в противоположном порядке.
+
+![Двусвязный список](./images/doubly-linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Две ссылки позволяют обходить список в обоих направлениях. Добавление и
+удаление узла в двусвязном списке требует изменения большего количества ссылок,
+чем аналогичные операции в связном списке. Однако данные операции проще и потенциально
+более эффективны (для некорневых узлов) - при обходе не нужно следить за предыдущим
+узлом или повторно обходить список в поиске предыдущего узла, плюс его ссылка
+может быть изменена.
+
+## Псевдокод основных операций
+
+### Вставка
+
+```text
+Add(value)
+  Pre: value - добавляемое значение
+  Post: value помещено в конец списка
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    n.previous ← tail
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+### Удаление
+
+```text
+Remove(head, value)
+  Pre: head - первый узел в списке
+       value - значение, которое следует удалить
+  Post: true - value удалено из списка, иначе false
+  if head = ø
+    return false
+  end if
+  if value = head.value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+      head.previous ← ø
+    end if
+    return true
+  end if
+  n ← head.next
+  while n = ø and value = n.value
+    n ← n.next
+  end while
+  if n = tail
+    tail ← tail.previous
+    tail.next ← ø
+    return true
+  else if n = ø
+    n.previous.next ← n.next
+    n.next.previous ← n.previous
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Обратный обход
+
+```text
+ReverseTraversal(tail)
+  Pre: tail - конечный элемент обходимого списка
+  Post: элементы списка пройдены в обратном порядке
+  n ← tail
+  while n = ø
+    yield n.value
+    n ← n.previous
+  end while
+end Reverse Traversal
+```
+
+## Сложность
+
+## Временная сложность
+
+| Чтение    | Поиск     | Вставка   | Удаление  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(n)      |
+
+### Пространственная сложность
+
+O(n)
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A1%D0%B2%D1%8F%D0%B7%D0%BD%D1%8B%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA#%D0%94%D0%B2%D1%83%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D1%8B%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_(%D0%B4%D0%B2%D1%83%D0%BD%D0%B0%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%81%D0%B2%D1%8F%D0%B7%D0%BD%D1%8B%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA))
+- [YouTube](https://www.youtube.com/watch?v=lQ-lPjbb9Ew)
diff --git a/src/data-structures/doubly-linked-list/README.uk-UA.md b/src/data-structures/doubly-linked-list/README.uk-UA.md
new file mode 100644
index 0000000000..a52a8015e0
--- /dev/null
+++ b/src/data-structures/doubly-linked-list/README.uk-UA.md
@@ -0,0 +1,109 @@
+# Двобічно зв'язаний список
+
+**Двобічно зв'язаний список** — зв'язкова структура даних в інформатиці, що складається з набору
+послідовно пов'язаних записів, званих вузлами. Кожен вузол містить два поля,
+званих посиланнями, які вказують на попередній і наступний елементи
+послідовність вузлів. Посилання на попередній елемент кореневого вузла та посилання на
+Наступний елемент останнього вузла вказують на деякого роду переривник, зазвичай
+сторожовий вузол або null для полегшення обходу списку. Якщо у списку лише один
+сторожовий вузол, тоді перелік циклічно пов'язаний через нього.
+Двобічно зв'язаний список можна уявити, як два зв'язкові списки, які утворені з
+одних і тих самих даних, але розташованих у протилежному порядку.
+
+![Двобічно зв'язаний список](./images/doubly-linked-list.jpeg)
+
+_Made with [okso.app](https://okso.app)_
+
+Два посилання дозволяють обходити список в обох напрямках. Додавання та
+видалення вузла у двозв'язному списку вимагає зміни більшої кількості посилань,
+ніж аналогічні операції у зв'язковому списку. Однак дані операції простіше та потенційно
+більш ефективні (для некореневих вузлів) – при обході не потрібно стежити за попереднім
+вузлом або повторно обходити список у пошуку попереднього вузла, плюс його посилання
+може бути змінено.
+
+## Псевдокод основних операцій
+
+### Вставка
+
+```text
+Add(value)
+  Pre: value - значення, що додається
+  Post: value поміщено в кінець списку
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    n.previous ← tail
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+### Видалення
+
+```text
+Remove(head, value)
+  Pre: head - перший вузол у списку
+       value - значення, яке слід видалити
+  Post: true - value видалено зі списку, інакше false
+  if head = ø
+    return false
+  end if
+  if value = head.value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+      head.previous ← ø
+    end if
+    return true
+  end if
+  n ← head.next
+  while n = ø and value = n.value
+    n ← n.next
+  end while
+  if n = tail
+    tail ← tail.previous
+    tail.next ← ø
+    return true
+  else if n = ø
+    n.previous.next ← n.next
+    n.next.previous ← n.previous
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Зворотний обхід
+
+```text
+ReverseTraversal(tail)
+  Pre: tail - кінцевий елемент обхідного списку
+  Post: елементи списку пройдено у зворотному порядку
+  n ← tail
+  while n = ø
+    yield n.value
+    n ← n.previous
+  end while
+end Reverse Traversal
+```
+
+## Складність
+
+## Часова складність
+
+| Читання | Пошук | Вставка | Видалення |
+| :-----: | :---: | :-----: | :-------: |
+|  O(n)   | O(n)  |  O(1)   |   O(n)    |
+
+### Просторова складність
+
+O(n)
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B1%D1%96%D1%87%D0%BD%D0%BE_%D0%B7%D0%B2%27%D1%8F%D0%B7%D0%B0%D0%BD%D0%B8%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA#:~:text=%D0%94%D0%B2%D0%BE%D0%B1%D1%96%D1%87%D0%BD%D0%BE%20%D0%B7%D0%B2'%D1%8F%D0%B7%D0%B0%D0%BD%D0%B8%D0%B9%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%E2%80%94%20%D0%B2%D0%B8%D0%B4,%D0%BD%D0%B0%20%D0%BF%D0%BE%D0%B4%D0%B0%D0%BB%D1%8C%D1%88%D0%B8%D0%B9%20%D0%B2%D1%83%D0%B7%D0%BE%D0%BB%20%D1%83%20%D1%81%D0%BF%D0%B8%D1%81%D0%BA%D1%83.)
diff --git a/src/data-structures/doubly-linked-list/README.zh-CN.md b/src/data-structures/doubly-linked-list/README.zh-CN.md
new file mode 100644
index 0000000000..3e669f4c45
--- /dev/null
+++ b/src/data-structures/doubly-linked-list/README.zh-CN.md
@@ -0,0 +1,101 @@
+# 双向链表
+
+在计算机科学中, 一个 **双向链表(doubly linked list)** 是由一组称为节点的顺序链接记录组成的链接数据结构。每个节点包含两个字段，称为链接，它们是对节点序列中上一个节点和下一个节点的引用。开始节点和结束节点的上一个链接和下一个链接分别指向某种终止节点，通常是前哨节点或null，以方便遍历列表。如果只有一个前哨节点，则列表通过前哨节点循环链接。它可以被概念化为两个由相同数据项组成的单链表，但顺序相反。
+
+![Doubly Linked List](./images/doubly-linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+两个节点链接允许在任一方向上遍历列表。
+
+在双向链表中进行添加或者删除节点时,需做的链接更改要比单向链表复杂得多。这种操作在单向链表中更简单高效,因为不需要关注一个节点（除第一个和最后一个节点以外的节点）的两个链接,而只需要关注一个链接即可。
+
+
+
+## 基础操作的伪代码
+
+### 插入
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head != ø
+    head ← n
+    tail ← n
+  else
+    n.previous ← tail
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+### 删除
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true; otherwise false
+  if head = ø
+    return false
+  end if
+  if value = head.value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+      head.previous ← ø
+    end if
+    return true
+  end if
+  n ← head.next
+  while n != ø and value !== n.value
+    n ← n.next
+  end while
+  if n = tail
+    tail ← tail.previous
+    tail.next ← ø
+    return true
+  else if n != ø
+    n.previous.next ← n.next
+    n.next.previous ← n.previous
+    return true
+  end if
+  return false
+end Remove
+```
+
+### 反向遍历
+
+```text
+ReverseTraversal(tail)
+  Pre: tail is the node of the list to traverse
+  Post: the list has been traversed in reverse order
+  n ← tail
+  while n != ø
+    yield n.value
+    n ← n.previous
+  end while
+end Reverse Traversal
+```
+
+## 复杂度
+
+## 时间复杂度
+
+| Access    | Search    | Insertion | Deletion  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(1)      |
+
+### 空间复杂度
+
+O(n)
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
+- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ&t=7s&index=72&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js b/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js
index 5e4e81eaee..b6060d177c 100644
--- a/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js
+++ b/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js
@@ -164,7 +164,7 @@ describe('DoublyLinkedList', () => {
       .append(nodeValue1)
       .prepend(nodeValue2);
 
-    const nodeStringifier = value => `${value.key}:${value.value}`;
+    const nodeStringifier = (value) => `${value.key}:${value.value}`;
 
     expect(linkedList.toString(nodeStringifier)).toBe('key2:2,key1:1');
   });
@@ -195,12 +195,12 @@ describe('DoublyLinkedList', () => {
       .append({ value: 2, key: 'test2' })
       .append({ value: 3, key: 'test3' });
 
-    const node = linkedList.find({ callback: value => value.key === 'test2' });
+    const node = linkedList.find({ callback: (value) => value.key === 'test2' });
 
     expect(node).toBeDefined();
     expect(node.value.value).toBe(2);
     expect(node.value.key).toBe('test2');
-    expect(linkedList.find({ callback: value => value.key === 'test5' })).toBeNull();
+    expect(linkedList.find({ callback: (value) => value.key === 'test5' })).toBeNull();
   });
 
   it('should find node by means of custom compare function', () => {
@@ -228,4 +228,53 @@ describe('DoublyLinkedList', () => {
     expect(node.value.customValue).toBe('test2');
     expect(linkedList.find({ value: 2, customValue: 'test5' })).toBeNull();
   });
+
+  it('should reverse linked list', () => {
+    const linkedList = new DoublyLinkedList();
+
+    // Add test values to linked list.
+    linkedList
+      .append(1)
+      .append(2)
+      .append(3)
+      .append(4);
+
+    expect(linkedList.toString()).toBe('1,2,3,4');
+    expect(linkedList.head.value).toBe(1);
+    expect(linkedList.tail.value).toBe(4);
+
+    // Reverse linked list.
+    linkedList.reverse();
+
+    expect(linkedList.toString()).toBe('4,3,2,1');
+
+    expect(linkedList.head.previous).toBeNull();
+    expect(linkedList.head.value).toBe(4);
+    expect(linkedList.head.next.value).toBe(3);
+    expect(linkedList.head.next.next.value).toBe(2);
+    expect(linkedList.head.next.next.next.value).toBe(1);
+
+    expect(linkedList.tail.next).toBeNull();
+    expect(linkedList.tail.value).toBe(1);
+    expect(linkedList.tail.previous.value).toBe(2);
+    expect(linkedList.tail.previous.previous.value).toBe(3);
+    expect(linkedList.tail.previous.previous.previous.value).toBe(4);
+
+    // Reverse linked list back to initial state.
+    linkedList.reverse();
+
+    expect(linkedList.toString()).toBe('1,2,3,4');
+
+    expect(linkedList.head.previous).toBeNull();
+    expect(linkedList.head.value).toBe(1);
+    expect(linkedList.head.next.value).toBe(2);
+    expect(linkedList.head.next.next.value).toBe(3);
+    expect(linkedList.head.next.next.next.value).toBe(4);
+
+    expect(linkedList.tail.next).toBeNull();
+    expect(linkedList.tail.value).toBe(4);
+    expect(linkedList.tail.previous.value).toBe(3);
+    expect(linkedList.tail.previous.previous.value).toBe(2);
+    expect(linkedList.tail.previous.previous.previous.value).toBe(1);
+  });
 });
diff --git a/src/data-structures/doubly-linked-list/__test__/DoublyLinkedListNode.test.js b/src/data-structures/doubly-linked-list/__test__/DoublyLinkedListNode.test.js
index d32a86811a..fdcf65aeec 100644
--- a/src/data-structures/doubly-linked-list/__test__/DoublyLinkedListNode.test.js
+++ b/src/data-structures/doubly-linked-list/__test__/DoublyLinkedListNode.test.js
@@ -48,7 +48,7 @@ describe('DoublyLinkedListNode', () => {
   it('should convert node to string with custom stringifier', () => {
     const nodeValue = { value: 1, key: 'test' };
     const node = new DoublyLinkedListNode(nodeValue);
-    const toStringCallback = value => `value: ${value.value}, key: ${value.key}`;
+    const toStringCallback = (value) => `value: ${value.value}, key: ${value.key}`;
 
     expect(node.toString(toStringCallback)).toBe('value: 1, key: test');
   });
diff --git a/src/data-structures/doubly-linked-list/images/doubly-linked-list.jpeg b/src/data-structures/doubly-linked-list/images/doubly-linked-list.jpeg
new file mode 100644
index 0000000000..3483e69fb8
Binary files /dev/null and b/src/data-structures/doubly-linked-list/images/doubly-linked-list.jpeg differ
diff --git a/src/data-structures/graph/Graph.js b/src/data-structures/graph/Graph.js
index ae123ca15c..ba0b6d0125 100644
--- a/src/data-structures/graph/Graph.js
+++ b/src/data-structures/graph/Graph.js
@@ -123,18 +123,6 @@ export default class Graph {
     return vertex.findEdge(endVertex);
   }
 
-  /**
-   * @param {string} vertexKey
-   * @returns {GraphVertex}
-   */
-  findVertexByKey(vertexKey) {
-    if (this.vertices[vertexKey]) {
-      return this.vertices[vertexKey];
-    }
-
-    return null;
-  }
-
   /**
    * @return {number}
    */
diff --git a/src/data-structures/graph/GraphVertex.js b/src/data-structures/graph/GraphVertex.js
index b28ced323b..f11dd2c161 100644
--- a/src/data-structures/graph/GraphVertex.js
+++ b/src/data-structures/graph/GraphVertex.js
@@ -64,7 +64,7 @@ export default class GraphVertex {
    * @return {GraphEdge[]}
    */
   getEdges() {
-    return this.edges.toArray().map(linkedListNode => linkedListNode.value);
+    return this.edges.toArray().map((linkedListNode) => linkedListNode.value);
   }
 
   /**
@@ -80,7 +80,7 @@ export default class GraphVertex {
    */
   hasEdge(requiredEdge) {
     const edgeNode = this.edges.find({
-      callback: edge => edge === requiredEdge,
+      callback: (edge) => edge === requiredEdge,
     });
 
     return !!edgeNode;
@@ -92,7 +92,7 @@ export default class GraphVertex {
    */
   hasNeighbor(vertex) {
     const vertexNode = this.edges.find({
-      callback: edge => edge.startVertex === vertex || edge.endVertex === vertex,
+      callback: (edge) => edge.startVertex === vertex || edge.endVertex === vertex,
     });
 
     return !!vertexNode;
@@ -123,7 +123,7 @@ export default class GraphVertex {
    * @return {GraphVertex}
    */
   deleteAllEdges() {
-    this.getEdges().forEach(edge => this.deleteEdge(edge));
+    this.getEdges().forEach((edge) => this.deleteEdge(edge));
 
     return this;
   }
diff --git a/src/data-structures/graph/README.fr-FR.md b/src/data-structures/graph/README.fr-FR.md
new file mode 100644
index 0000000000..c6a6c90892
--- /dev/null
+++ b/src/data-structures/graph/README.fr-FR.md
@@ -0,0 +1,25 @@
+# Graph
+
+En informatique, un **graphe** est une structure de
+données abstraite qui implémente les concepts de
+graphe orienté et de graphe non-orienté venant
+des mathématiques, plus précisément du domaine de
+la théorie des graphes.
+
+La structure de données abstraite de graphe consiste
+en un ensemble fini, éventuellement mutable de sommets
+ou nœuds ou points, avec un ensemble de paires ordonnées
+ou non de tels éléments. Ces paires sont des arêtes, arcs
+non orientés, ou lignes pour un graphe non orienté, et
+flèches, arêtes orientées , arcs, ou lignes orientées
+dans le cas orienté. Les sommets peuvent faire partie
+de la structure, ou être des entités extérieures,
+représentées par des entiers ou des références.
+
+![Graph](./images/graph.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## References
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/Graphe_(type_abstrait))
diff --git a/src/data-structures/graph/README.md b/src/data-structures/graph/README.md
index c9b8ec4fb8..790602b536 100644
--- a/src/data-structures/graph/README.md
+++ b/src/data-structures/graph/README.md
@@ -1,22 +1,32 @@
 # Graph
 
-In computer science, a **graph** is an abstract data type 
-that is meant to implement the undirected graph and 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md),
+[_Українська_](README.uk-UA.md)
+
+
+In computer science, a **graph** is an abstract data type
+that is meant to implement the undirected graph and
 directed graph concepts from mathematics, specifically
 the field of graph theory
 
-A graph data structure consists of a finite (and possibly 
-mutable) set of vertices or nodes or points, together 
-with a set of unordered pairs of these vertices for an 
-undirected graph or a set of ordered pairs for a 
-directed graph. These pairs are known as edges, arcs, 
-or lines for an undirected graph and as arrows, 
-directed edges, directed arcs, or directed lines 
-for a directed graph. The vertices may be part of 
-the graph structure, or may be external entities 
+A graph data structure consists of a finite (and possibly
+mutable) set of vertices or nodes or points, together
+with a set of unordered pairs of these vertices for an
+undirected graph or a set of ordered pairs for a
+directed graph. These pairs are known as edges, arcs,
+or lines for an undirected graph and as arrows,
+directed edges, directed arcs, or directed lines
+for a directed graph. The vertices may be part of
+the graph structure, or may be external entities
 represented by integer indices or references.
 
-![Graph](https://www.tutorialspoint.com/data_structures_algorithms/images/graph.jpg)
+![Graph](./images/graph.jpeg)
+
+*Made with [okso.app](https://okso.app)*
 
 ## References
 
diff --git a/src/data-structures/graph/README.pt-BR.md b/src/data-structures/graph/README.pt-BR.md
new file mode 100644
index 0000000000..de7947cba8
--- /dev/null
+++ b/src/data-structures/graph/README.pt-BR.md
@@ -0,0 +1,28 @@
+# Grafo (Graph)
+
+Na ciência da computação, um **grafo** é uma abstração de estrutura
+de dados que se destina a implementar os conceitos da matemática de
+grafos direcionados e não direcionados, especificamente o campo da
+teoria dos grafos.
+
+Uma estrutura de dados grafos consiste em um finito (e possivelmente
+mutável) conjunto de vértices, nós ou pontos, juntos com um
+conjunto de pares não ordenados desses vértices para um grafo não
+direcionado ou para um conjunto de pares ordenados para um grafo
+direcionado. Esses pares são conhecidos como arestas, arcos
+ou linhas diretas para um grafo não direcionado e como setas,
+arestas direcionadas, arcos direcionados ou linhas direcionadas
+para um grafo direcionado.
+
+Os vértices podem fazer parte a estrutura do grafo, ou podem
+ser entidades externas representadas por índices inteiros ou referências.
+
+![Graph](./images/graph.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Graph_(abstract_data_type))
+- [Introduction to Graphs on YouTube](https://www.youtube.com/watch?v=gXgEDyodOJU&index=9&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [Graphs representation on YouTube](https://www.youtube.com/watch?v=k1wraWzqtvQ&index=10&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/graph/README.ru-RU.md b/src/data-structures/graph/README.ru-RU.md
new file mode 100644
index 0000000000..950198451b
--- /dev/null
+++ b/src/data-structures/graph/README.ru-RU.md
@@ -0,0 +1,26 @@
+# Граф
+
+**Граф** в информатике - абстрактный тип данных, который должен реализовывать концепции направленного и ненаправленного
+графа в математике, особенно в области теории графов.
+
+Структура данных графа состоит из конечного (и возможно изменяющегося) набора вершин или узлов, или точек, совместно с
+набором ненаправленных пар этих вершин для ненаправленного графа или с набором направленных пар для направленного графа.
+Эти пары известны как рёбра, арки или линии для ненаправленного графа и как стрелки, направленные рёбра, направленные
+арки или направленные линии для направленного графа. Эти вершины могут быть частью структуры графа, или внешними
+сущностями, представленными целочисленными индексами или ссылками.
+
+Для разных областей применения виды графов могут различаться направленностью, ограничениями на количество связей и
+дополнительными данными о вершинах или рёбрах. Многие структуры, представляющие практический интерес в математике и
+информатике, могут быть представлены графами. Например, строение Википедии можно смоделировать при помощи
+ориентированного графа, в котором вершины — это статьи, а дуги (ориентированные рёбра) — гиперссылки.
+
+![Граф](./images/graph.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Ссылки
+
+- [Граф в математике на Wikipedia](https://ru.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D1%84_(%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0))
+- [Графы на YouTube. Часть 1(~10 мин)](https://www.youtube.com/watch?v=GOUuhJPLG3s)
+- [Графы на YouTube. Часть 2(~10 мин)](https://www.youtube.com/watch?v=N-kCJJkTk7g)
+- [Графы на YouTube. Часть 3(~10 мин)](https://www.youtube.com/watch?v=2o3TINew0b8)
diff --git a/src/data-structures/graph/README.uk-UA.md b/src/data-structures/graph/README.uk-UA.md
new file mode 100644
index 0000000000..9886cd904c
--- /dev/null
+++ b/src/data-structures/graph/README.uk-UA.md
@@ -0,0 +1,24 @@
+# Граф
+
+**Граф** в інформатиці - абстрактний тип даних, який має реалізовувати концепції спрямованого та неспрямованого
+графа у математиці, особливо у галузі теорії графів.
+
+Структура даних графа складається з кінцевого (і можливо, що змінюється) набору вершин або вузлів, або точок, спільно з
+набором ненаправлених пар цих вершин для ненаправленого графа або набором спрямованих пар для спрямованого графа.
+Ці пари відомі як ребра, арки або лінії для ненаправленого графа та як стрілки, спрямовані ребра, спрямовані
+арки чи спрямовані лінії для спрямованого графа. Ці вершини можуть бути частиною структури графа, або зовнішніми
+сутностями, представленими цілими індексами або посиланнями.
+
+Для різних областей застосування види графів можуть відрізнятися спрямованістю, обмеженнями на кількість зв'язків та
+додатковими даними про вершини або ребра. Багато структур, що становлять практичний інтерес у математиці та
+інформатики можуть бути представлені графами. Наприклад, будову Вікіпедії можна змоделювати за допомогою
+орієнтованого графа, в якому вершини – це статті, а дуги (орієнтовані ребра) – гіперпосилання.
+
+![Граф](./images/graph.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Посилання
+
+- [Граф у математиці на Wikipedia](https://uk.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D1%84_(%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0))
+- [Структура даних Graph / Граф](https://www.youtube.com/watch?v=D0U8aFEhgKQ)
diff --git a/src/data-structures/graph/README.zh-CN.md b/src/data-structures/graph/README.zh-CN.md
new file mode 100644
index 0000000000..1f885a43b9
--- /dev/null
+++ b/src/data-structures/graph/README.zh-CN.md
@@ -0,0 +1,22 @@
+# 图
+
+在计算机科学中, **图(graph)** 是一种抽象数据类型,
+旨在实现数学中的无向图和有向图概念，特别是图论领域。
+
+一个图数据结构是一个(由有限个或者可变数量的)顶点/节点/点和边构成的有限集。
+
+如果顶点对之间是无序的,称为无序图,否则称为有序图;
+
+如果顶点对之间的边是没有方向的,称为无向图,否则称为有向图;
+
+如果顶点对之间的边是有权重的,该图可称为加权图。
+
+![Graph](./images/graph.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Graph_(abstract_data_type))
+- [Introduction to Graphs on YouTube](https://www.youtube.com/watch?v=gXgEDyodOJU&index=9&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [Graphs representation on YouTube](https://www.youtube.com/watch?v=k1wraWzqtvQ&index=10&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/graph/__test__/Graph.test.js b/src/data-structures/graph/__test__/Graph.test.js
index be6e9d7fb2..936a69b805 100644
--- a/src/data-structures/graph/__test__/Graph.test.js
+++ b/src/data-structures/graph/__test__/Graph.test.js
@@ -32,14 +32,14 @@ describe('Graph', () => {
     expect(graph.getAllVertices()[0]).toEqual(vertexA);
     expect(graph.getAllVertices()[1]).toEqual(vertexB);
 
-    const graphVertexA = graph.findVertexByKey(vertexA.getKey());
-    const graphVertexB = graph.findVertexByKey(vertexB.getKey());
+    const graphVertexA = graph.getVertexByKey(vertexA.getKey());
+    const graphVertexB = graph.getVertexByKey(vertexB.getKey());
 
     expect(graph.toString()).toBe('A,B');
     expect(graphVertexA).toBeDefined();
     expect(graphVertexB).toBeDefined();
 
-    expect(graph.findVertexByKey('not existing')).toBeNull();
+    expect(graph.getVertexByKey('not existing')).toBeUndefined();
 
     expect(graphVertexA.getNeighbors().length).toBe(1);
     expect(graphVertexA.getNeighbors()[0]).toEqual(vertexB);
@@ -60,8 +60,8 @@ describe('Graph', () => {
 
     graph.addEdge(edgeAB);
 
-    const graphVertexA = graph.findVertexByKey(vertexA.getKey());
-    const graphVertexB = graph.findVertexByKey(vertexB.getKey());
+    const graphVertexA = graph.getVertexByKey(vertexA.getKey());
+    const graphVertexB = graph.getVertexByKey(vertexB.getKey());
 
     expect(graph.toString()).toBe('A,B');
     expect(graphVertexA).toBeDefined();
diff --git a/src/data-structures/graph/images/graph.jpeg b/src/data-structures/graph/images/graph.jpeg
new file mode 100644
index 0000000000..e49ecc0b9c
Binary files /dev/null and b/src/data-structures/graph/images/graph.jpeg differ
diff --git a/src/data-structures/hash-table/HashTable.js b/src/data-structures/hash-table/HashTable.js
index 1ae63a7f1e..b8b523ea85 100644
--- a/src/data-structures/hash-table/HashTable.js
+++ b/src/data-structures/hash-table/HashTable.js
@@ -52,7 +52,7 @@ export default class HashTable {
     const keyHash = this.hash(key);
     this.keys[key] = keyHash;
     const bucketLinkedList = this.buckets[keyHash];
-    const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
+    const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
 
     if (!node) {
       // Insert new node.
@@ -71,7 +71,7 @@ export default class HashTable {
     const keyHash = this.hash(key);
     delete this.keys[key];
     const bucketLinkedList = this.buckets[keyHash];
-    const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
+    const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
 
     if (node) {
       return bucketLinkedList.delete(node.value);
@@ -86,7 +86,7 @@ export default class HashTable {
    */
   get(key) {
     const bucketLinkedList = this.buckets[this.hash(key)];
-    const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
+    const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
 
     return node ? node.value.value : undefined;
   }
@@ -105,4 +105,17 @@ export default class HashTable {
   getKeys() {
     return Object.keys(this.keys);
   }
+
+  /**
+   * Gets the list of all the stored values in the hash table.
+   *
+   * @return {*[]}
+   */
+  getValues() {
+    return this.buckets.reduce((values, bucket) => {
+      const bucketValues = bucket.toArray()
+        .map((linkedListNode) => linkedListNode.value.value);
+      return values.concat(bucketValues);
+    }, []);
+  }
 }
diff --git a/src/data-structures/hash-table/README.fr-FR.md b/src/data-structures/hash-table/README.fr-FR.md
new file mode 100644
index 0000000000..2fd41c8838
--- /dev/null
+++ b/src/data-structures/hash-table/README.fr-FR.md
@@ -0,0 +1,31 @@
+# Table de hachage
+
+En informatique, une **table de hachage** (carte de
+hachage) est une structure de données qui implémente
+un type de données abstrait *tableau nassociatif*,
+une structure qui permet de *mapper des clés sur des
+valeurs*. Une table de hachage utilise une *fonction
+de hachage* pour calculer un index dans un tableau
+d'alvéoles (en anglais, buckets ou slots), à partir
+duquel la valeur souhaitée peut être trouvée.
+
+Idéalement, la fonction de hachage affectera chaque clé
+à une alvéole unique, mais la plupart des tables de
+hachage conçues emploient une fonction de hachage
+imparfaite, ce qui peut provoquer des collisions de
+hachage où la fonction de hachage génère le même index
+pour plusieurs clés. De telles collisions doivent être
+accommodées d'une manière ou d'une autre.
+
+![Hash Table](./images/hash-table.jpeg)
+
+Collision de hachage résolue par chaînage séparé.
+
+![Hash Collision](./images/collision-resolution.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Références
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/Table_de_hachage)
+- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/hash-table/README.ja-JP.md b/src/data-structures/hash-table/README.ja-JP.md
new file mode 100644
index 0000000000..d71cb273b9
--- /dev/null
+++ b/src/data-structures/hash-table/README.ja-JP.md
@@ -0,0 +1,18 @@
+# ハッシュテーブル
+
+コンピュータサイエンスにおいて、**ハッシュテーブル**（ハッシュマップ)は*キーを値にマッピング*できる*連想配列*の機能を持ったデータ構造です。ハッシュテーブルは*ハッシュ関数*を使ってバケットやスロットの配列へのインデックスを計算し、そこから目的の値を見つけることができます。
+
+理想的には、ハッシュ関数は各キーを一意のバケットに割り当てますが、ほとんどのハッシュテーブルは不完全なハッシュ関数を採用しているため、複数のキーに対して同じインデックスを生成した時にハッシュの衝突が起こります。このような衝突は何らかの方法で対処する必要があります。
+
+![Hash Table](./images/hash-table.jpeg)
+
+チェイン法によるハッシュの衝突の解決例
+
+![Hash Collision](./images/collision-resolution.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Hash_table)
+- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/hash-table/README.ko-KR.md b/src/data-structures/hash-table/README.ko-KR.md
new file mode 100644
index 0000000000..b3a8c94066
--- /dev/null
+++ b/src/data-structures/hash-table/README.ko-KR.md
@@ -0,0 +1,27 @@
+# Hash Table
+
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md)
+
+컴퓨팅에서, **해시 테이블**(해시 맵)은 키를 값에 매핑할 수 있는 구조인 *연관 배열*을 구현하는 자료 구조입니다. 해시 테이블은 *해시 함수*를 사용해 원하는 값을 담을 수 있는 버킷 또는 슬롯 배열의 인덱스를 계산합니다.
+
+이상적으로, 해시 함수는 각 키들을 고유 버킷에 할당하지만 대부분의 해시 테이블은 불완전한 해시 함수를 사용하기 때문에 해시 함수를 통해 두 개 이상의 키에 대해 동일한 인덱스를 생성하는 해시 충돌이 발생할 수 있습니다. 이러한 해시 충돌은 어떠한 방법으로든 해결되어야 합니다.
+
+![Hash Table](./images/hash-table.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+다음은 분리 연결법을 통해 해시 충돌을 해결한 예시입니다.
+
+![Hash Collision](./images/collision-resolution.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 참고
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Hash_table)
+- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/hash-table/README.md b/src/data-structures/hash-table/README.md
index cdcad78b80..f0d0c9932d 100644
--- a/src/data-structures/hash-table/README.md
+++ b/src/data-structures/hash-table/README.md
@@ -1,24 +1,35 @@
 # Hash Table
 
-In computing, a **hash table** (hash map) is a data 
-structure which implements an *associative array* 
-abstract data type, a structure that can *map keys 
-to values*. A hash table uses a *hash function* to 
-compute an index into an array of buckets or slots, 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Українська_](README.uk-UA.md)
+
+In computing, a **hash table** (hash map) is a data
+structure which implements an _associative array_
+abstract data type, a structure that can _map keys
+to values_. A hash table uses a _hash function_ to
+compute an index into an array of buckets or slots,
 from which the desired value can be found
 
-Ideally, the hash function will assign each key to a 
-unique bucket, but most hash table designs employ an 
-imperfect hash function, which might cause hash 
+Ideally, the hash function will assign each key to a
+unique bucket, but most hash table designs employ an
+imperfect hash function, which might cause hash
 collisions where the hash function generates the same
 index for more than one key. Such collisions must be
 accommodated in some way.
 
-![Hash Table](https://upload.wikimedia.org/wikipedia/commons/7/7d/Hash_table_3_1_1_0_1_0_0_SP.svg)
+![Hash Table](./images/hash-table.jpeg)
 
 Hash collision resolved by separate chaining.
 
-![Hash Collision](https://upload.wikimedia.org/wikipedia/commons/d/d0/Hash_table_5_0_1_1_1_1_1_LL.svg)
+![Hash Collision](./images/collision-resolution.jpeg)
+
+_Made with [okso.app](https://okso.app)_
 
 ## References
 
diff --git a/src/data-structures/hash-table/README.pt-BR.md b/src/data-structures/hash-table/README.pt-BR.md
new file mode 100644
index 0000000000..c89faf6ca1
--- /dev/null
+++ b/src/data-structures/hash-table/README.pt-BR.md
@@ -0,0 +1,27 @@
+# Tabela de Hash (Hash Table)
+
+Na ciência da computação, uma **tabela de hash** (hash table) é uma
+estrutura de dados pela qual implementa um tipo de dado abstrado de
+*array associativo*, uma estrutura que pode *mapear chaves para valores*.
+Uma tabela de hash utiliza uma *função de hash* para calcular um índice
+em um _array_ de buckets ou slots, a partir do qual o valor desejado
+pode ser encontrado.
+
+Idealmente, a função de hash irá atribuir a cada chave a um bucket único,
+mas a maioria dos designs de tabela de hash emprega uma função de hash
+imperfeita, pela qual poderá causar colisões de hashes onde a função de hash
+gera o mesmo índice para mais de uma chave. Tais colisões devem ser
+acomodados de alguma forma.
+
+![Hash Table](./images/hash-table.jpeg)
+
+Colisão de hash resolvida por encadeamento separado.
+
+![Hash Collision](./images/collision-resolution.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Hash_table)
+- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/hash-table/README.ru-RU.md b/src/data-structures/hash-table/README.ru-RU.md
new file mode 100644
index 0000000000..e1e21129da
--- /dev/null
+++ b/src/data-structures/hash-table/README.ru-RU.md
@@ -0,0 +1,29 @@
+# Хэш таблица
+
+**Хеш-таблица** - структура данных, реализующая абстрактный тип данных *ассоциативный массив*, т.е. структура, которая
+*связывает ключи со значениями*. Хеш-таблица использует *хеш-функцию* для вычисления индекса в массиве, в котором может
+быть найдено желаемое значение. Ниже представлена хеш-таблица, в которой ключом выступает имя человека, а значениями
+являются телефонные номера. Хеш-функция преобразует ключ-имя в индекс массива с телефонными номерами.
+
+![Hash Table](./images/hash-table.jpeg)
+
+В идеале хеш-функция будет присваивать элементу массива уникальный ключ. Однако большинство реальных хеш-таблиц
+используют несовершенные хеш-функции. Это может привести к ситуациям, когда хеш-функция генерирует одинаковый индекс для
+нескольких ключей. Данные ситуации называются коллизиями и должны быть как-то разрешены.
+
+Существует два варианта решения коллизий - хеш-таблица с цепочками и с открытой адресацией.
+
+Метод цепочек подразумевает хранение значений, соответствующих одному и тому же индексу в виде связного списка(цепочки).
+
+![Hash Collision](./images/collision-resolution.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Метод открытой адресации помещает значение, для которого получен дублирующий индекс, в первую свободную ячейку.
+
+![Хеш открытая адресация](https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Hash_table_5_0_1_1_1_1_0_SP.svg/380px-Hash_table_5_0_1_1_1_1_0_SP.svg.png)
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0)
+- [YouTube](https://www.youtube.com/watch?v=rVr1y32fDI0)
diff --git a/src/data-structures/hash-table/README.uk-UA.md b/src/data-structures/hash-table/README.uk-UA.md
new file mode 100644
index 0000000000..2ab3183599
--- /dev/null
+++ b/src/data-structures/hash-table/README.uk-UA.md
@@ -0,0 +1,29 @@
+# Геш таблиця
+
+**Геш таблиця** - структура даних, що реалізує абстрактний тип даних асоціативний масив, тобто. структура, яка
+_зв'язує ключі зі значеннями_. Геш-таблиця використовує _геш-функцію_ для обчислення індексу в масиві, в якому може
+бути знайдено бажане значення. Нижче представлена геш-таблиця, у якій ключем виступає ім'я людини, а значеннями
+телефонні номери. Геш-функція перетворює ключ-ім'я на індекс масиву з телефонними номерами.
+
+![Hash Table](./images/hash-table.jpeg)
+
+В ідеалі геш-функція присвоюватиме елементу масиву унікальний ключ. Проте більшість реальних геш-таблиць
+використовують недосконалі геш-функції. Це може призвести до ситуацій, коли геш-функція генерує однаковий індекс для
+кількох ключів. Ці ситуації називаються колізіями і мають бути якось вирішені.
+
+Існує два варіанти вирішення колізій - геш-таблиця з ланцюжками та з відкритою адресацією.
+
+Метод ланцюжків передбачає зберігання значень, відповідних одному й тому індексу як зв'язкового списку(ланцюжка).
+
+![Hash Collision](./images/collision-resolution.jpeg)
+
+_Made with [okso.app](https://okso.app)_
+
+Метод відкритої адресації поміщає значення, для якого отримано дублюючий індекс, в першу вільну комірку.
+
+![Геш відкрита адресація](https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Hash_table_5_0_1_1_1_1_0_SP.svg/380px-Hash_table_5_0_1_1_1_1_0_SP.svg.png)
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%93%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8F)
+- [YouTube](https://www.youtube.com/watch?v=WTYaboK-NMk)
diff --git a/src/data-structures/hash-table/README.zh-CN.md b/src/data-structures/hash-table/README.zh-CN.md
new file mode 100644
index 0000000000..c53214aa88
--- /dev/null
+++ b/src/data-structures/hash-table/README.zh-CN.md
@@ -0,0 +1,25 @@
+# 哈希表
+
+在计算中, 一个  **哈希表(hash table 或hash map)**  是一种实现 *关联数组(associative array)*
+的抽象数据类型, 该结构可以将 *键映射到值*。
+
+哈希表使用 *哈希函数/散列函数* 来计算一个值在数组或桶(buckets)中或槽(slots)中对应的索引,可使用该索引找到所需的值。
+
+理想情况下,散列函数将为每个键分配给一个唯一的桶(bucket),但是大多数哈希表设计采用不完美的散列函数,这可能会导致"哈希冲突(hash collisions)",也就是散列函数为多个键(key)生成了相同的索引,这种碰撞必须
+以某种方式进行处理。
+
+
+![Hash Table](./images/hash-table.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+通过单独的链接解决哈希冲突
+
+![Hash Collision](./images/collision-resolution.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Hash_table)
+- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/hash-table/__test__/HashTable.test.js b/src/data-structures/hash-table/__test__/HashTable.test.js
index d6618c4aa0..86bbf3adbd 100644
--- a/src/data-structures/hash-table/__test__/HashTable.test.js
+++ b/src/data-structures/hash-table/__test__/HashTable.test.js
@@ -35,7 +35,7 @@ describe('HashTable', () => {
     expect(hashTable.has('b')).toBe(true);
     expect(hashTable.has('c')).toBe(true);
 
-    const stringifier = value => `${value.key}:${value.value}`;
+    const stringifier = (value) => `${value.key}:${value.value}`;
 
     expect(hashTable.buckets[0].toString(stringifier)).toBe('c:earth');
     expect(hashTable.buckets[1].toString(stringifier)).toBe('a:sky,d:ocean');
@@ -86,4 +86,32 @@ describe('HashTable', () => {
     expect(hashTable.has('b')).toBe(true);
     expect(hashTable.has('x')).toBe(false);
   });
+
+  it('should get all the values', () => {
+    const hashTable = new HashTable(3);
+
+    hashTable.set('a', 'alpha');
+    hashTable.set('b', 'beta');
+    hashTable.set('c', 'gamma');
+
+    expect(hashTable.getValues()).toEqual(['gamma', 'alpha', 'beta']);
+  });
+
+  it('should get all the values from empty hash table', () => {
+    const hashTable = new HashTable();
+    expect(hashTable.getValues()).toEqual([]);
+  });
+
+  it('should get all the values in case of hash collision', () => {
+    const hashTable = new HashTable(3);
+
+    // Keys `ab` and `ba` in current implementation should result in one hash (one bucket).
+    // We need to make sure that several items from one bucket will be serialized.
+    hashTable.set('ab', 'one');
+    hashTable.set('ba', 'two');
+
+    hashTable.set('ac', 'three');
+
+    expect(hashTable.getValues()).toEqual(['one', 'two', 'three']);
+  });
 });
diff --git a/src/data-structures/hash-table/images/collision-resolution.jpeg b/src/data-structures/hash-table/images/collision-resolution.jpeg
new file mode 100644
index 0000000000..1e0c979289
Binary files /dev/null and b/src/data-structures/hash-table/images/collision-resolution.jpeg differ
diff --git a/src/data-structures/hash-table/images/hash-table.jpeg b/src/data-structures/hash-table/images/hash-table.jpeg
new file mode 100644
index 0000000000..f350453c10
Binary files /dev/null and b/src/data-structures/hash-table/images/hash-table.jpeg differ
diff --git a/src/data-structures/heap/Heap.js b/src/data-structures/heap/Heap.js
index 59ed5f0f7d..b978739ec8 100644
--- a/src/data-structures/heap/Heap.js
+++ b/src/data-structures/heap/Heap.js
@@ -144,18 +144,17 @@ export default class Heap {
 
   /**
    * @param {*} item
-   * @param {Comparator} [customFindingComparator]
+   * @param {Comparator} [comparator]
    * @return {Heap}
    */
-  remove(item, customFindingComparator) {
+  remove(item, comparator = this.compare) {
     // Find number of items to remove.
-    const customComparator = customFindingComparator || this.compare;
-    const numberOfItemsToRemove = this.find(item, customComparator).length;
+    const numberOfItemsToRemove = this.find(item, comparator).length;
 
     for (let iteration = 0; iteration < numberOfItemsToRemove; iteration += 1) {
       // We need to find item index to remove each time after removal since
-      // indices are being change after each heapify process.
-      const indexToRemove = this.find(item, customComparator).pop();
+      // indices are being changed after each heapify process.
+      const indexToRemove = this.find(item, comparator).pop();
 
       // If we need to remove last child in the heap then just remove it.
       // There is no need to heapify the heap afterwards.
@@ -166,16 +165,15 @@ export default class Heap {
         this.heapContainer[indexToRemove] = this.heapContainer.pop();
 
         // Get parent.
-        const parentItem = this.hasParent(indexToRemove) ? this.parent(indexToRemove) : null;
-        const leftChild = this.hasLeftChild(indexToRemove) ? this.leftChild(indexToRemove) : null;
+        const parentItem = this.parent(indexToRemove);
 
-        // If there is no parent or parent is in incorrect order with the node
+        // If there is no parent or parent is in correct order with the node
         // we're going to delete then heapify down. Otherwise heapify up.
         if (
-          leftChild !== null
+          this.hasLeftChild(indexToRemove)
           && (
-            parentItem === null
-            || !this.pairIsInCorrectOrder(parentItem, this.heapContainer[indexToRemove])
+            !parentItem
+            || this.pairIsInCorrectOrder(parentItem, this.heapContainer[indexToRemove])
           )
         ) {
           this.heapifyDown(indexToRemove);
@@ -190,12 +188,11 @@ export default class Heap {
 
   /**
    * @param {*} item
-   * @param {Comparator} [customComparator]
+   * @param {Comparator} [comparator]
    * @return {Number[]}
    */
-  find(item, customComparator) {
+  find(item, comparator = this.compare) {
     const foundItemIndices = [];
-    const comparator = customComparator || this.compare;
 
     for (let itemIndex = 0; itemIndex < this.heapContainer.length; itemIndex += 1) {
       if (comparator.equal(item, this.heapContainer[itemIndex])) {
@@ -224,9 +221,9 @@ export default class Heap {
    * @param {number} [customStartIndex]
    */
   heapifyUp(customStartIndex) {
-    // Take last element (last in array or the bottom left in a tree) in
-    // a heap container and lift him up until we find the parent element
-    // that is less then the current new one.
+    // Take the last element (last in array or the bottom left in a tree)
+    // in the heap container and lift it up until it is in the correct
+    // order with respect to its parent element.
     let currentIndex = customStartIndex || this.heapContainer.length - 1;
 
     while (
@@ -241,10 +238,11 @@ export default class Heap {
   /**
    * @param {number} [customStartIndex]
    */
-  heapifyDown(customStartIndex) {
-    // Compare the root element to its children and swap root with the smallest
-    // of children. Do the same for next children after swap.
-    let currentIndex = customStartIndex || 0;
+  heapifyDown(customStartIndex = 0) {
+    // Compare the parent element to its children and swap parent with the appropriate
+    // child (smallest child for MinHeap, largest child for MaxHeap).
+    // Do the same for next children after swap.
+    let currentIndex = customStartIndex;
     let nextIndex = null;
 
     while (this.hasLeftChild(currentIndex)) {
@@ -281,7 +279,7 @@ export default class Heap {
   /* istanbul ignore next */
   pairIsInCorrectOrder(firstElement, secondElement) {
     throw new Error(`
-      You have to implement heap pair comparision method
+      You have to implement heap pair comparison method
       for ${firstElement} and ${secondElement} values.
     `);
   }
diff --git a/src/data-structures/heap/MaxHeapAdhoc.js b/src/data-structures/heap/MaxHeapAdhoc.js
new file mode 100644
index 0000000000..b9d69c59e0
--- /dev/null
+++ b/src/data-structures/heap/MaxHeapAdhoc.js
@@ -0,0 +1,115 @@
+/**
+ * The minimalistic (ad hoc) version of a MaxHeap data structure that doesn't have
+ * external dependencies and that is easy to copy-paste and use during the
+ * coding interview if allowed by the interviewer (since many data
+ * structures in JS are missing).
+ */
+class MaxHeapAdhoc {
+  constructor(heap = []) {
+    this.heap = [];
+    heap.forEach(this.add);
+  }
+
+  add(num) {
+    this.heap.push(num);
+    this.heapifyUp();
+  }
+
+  peek() {
+    return this.heap[0];
+  }
+
+  poll() {
+    if (this.heap.length === 0) return undefined;
+    const top = this.heap[0];
+    this.heap[0] = this.heap[this.heap.length - 1];
+    this.heap.pop();
+    this.heapifyDown();
+    return top;
+  }
+
+  isEmpty() {
+    return this.heap.length === 0;
+  }
+
+  toString() {
+    return this.heap.join(',');
+  }
+
+  heapifyUp() {
+    let nodeIndex = this.heap.length - 1;
+    while (nodeIndex > 0) {
+      const parentIndex = this.getParentIndex(nodeIndex);
+      if (this.heap[parentIndex] >= this.heap[nodeIndex]) break;
+      this.swap(parentIndex, nodeIndex);
+      nodeIndex = parentIndex;
+    }
+  }
+
+  heapifyDown() {
+    let nodeIndex = 0;
+
+    while (
+      (
+        this.hasLeftChild(nodeIndex) && this.heap[nodeIndex] < this.leftChild(nodeIndex)
+      )
+      || (
+        this.hasRightChild(nodeIndex) && this.heap[nodeIndex] < this.rightChild(nodeIndex)
+      )
+    ) {
+      const leftIndex = this.getLeftChildIndex(nodeIndex);
+      const rightIndex = this.getRightChildIndex(nodeIndex);
+      const left = this.leftChild(nodeIndex);
+      const right = this.rightChild(nodeIndex);
+
+      if (this.hasLeftChild(nodeIndex) && this.hasRightChild(nodeIndex)) {
+        if (left >= right) {
+          this.swap(leftIndex, nodeIndex);
+          nodeIndex = leftIndex;
+        } else {
+          this.swap(rightIndex, nodeIndex);
+          nodeIndex = rightIndex;
+        }
+      } else if (this.hasLeftChild(nodeIndex)) {
+        this.swap(leftIndex, nodeIndex);
+        nodeIndex = leftIndex;
+      }
+    }
+  }
+
+  getLeftChildIndex(parentIndex) {
+    return (2 * parentIndex) + 1;
+  }
+
+  getRightChildIndex(parentIndex) {
+    return (2 * parentIndex) + 2;
+  }
+
+  getParentIndex(childIndex) {
+    return Math.floor((childIndex - 1) / 2);
+  }
+
+  hasLeftChild(parentIndex) {
+    return this.getLeftChildIndex(parentIndex) < this.heap.length;
+  }
+
+  hasRightChild(parentIndex) {
+    return this.getRightChildIndex(parentIndex) < this.heap.length;
+  }
+
+  leftChild(parentIndex) {
+    return this.heap[this.getLeftChildIndex(parentIndex)];
+  }
+
+  rightChild(parentIndex) {
+    return this.heap[this.getRightChildIndex(parentIndex)];
+  }
+
+  swap(indexOne, indexTwo) {
+    const tmp = this.heap[indexTwo];
+    this.heap[indexTwo] = this.heap[indexOne];
+    this.heap[indexOne] = tmp;
+  }
+}
+
+export default MaxHeapAdhoc;
diff --git a/src/data-structures/heap/MinHeapAdhoc.js b/src/data-structures/heap/MinHeapAdhoc.js
new file mode 100644
index 0000000000..c70692f3de
--- /dev/null
+++ b/src/data-structures/heap/MinHeapAdhoc.js
@@ -0,0 +1,117 @@
+/**
+ * The minimalistic (ad hoc) version of a MinHeap data structure that doesn't have
+ * external dependencies and that is easy to copy-paste and use during the
+ * coding interview if allowed by the interviewer (since many data
+ * structures in JS are missing).
+ */
+class MinHeapAdhoc {
+  constructor(heap = []) {
+    this.heap = [];
+    heap.forEach(this.add);
+  }
+
+  add(num) {
+    this.heap.push(num);
+    this.heapifyUp();
+  }
+
+  peek() {
+    return this.heap[0];
+  }
+
+  poll() {
+    if (this.heap.length === 0) return undefined;
+    const top = this.heap[0];
+    this.heap[0] = this.heap[this.heap.length - 1];
+    this.heap.pop();
+    this.heapifyDown();
+    return top;
+  }
+
+  isEmpty() {
+    return this.heap.length === 0;
+  }
+
+  toString() {
+    return this.heap.join(',');
+  }
+
+  heapifyUp() {
+    let nodeIndex = this.heap.length - 1;
+    while (nodeIndex > 0) {
+      const parentIndex = this.getParentIndex(nodeIndex);
+      if (this.heap[parentIndex] <= this.heap[nodeIndex]) break;
+      this.swap(parentIndex, nodeIndex);
+      nodeIndex = parentIndex;
+    }
+  }
+
+  heapifyDown() {
+    let nodeIndex = 0;
+
+    while (
+      (
+        this.hasLeftChild(nodeIndex)
+        && this.heap[nodeIndex] > this.leftChild(nodeIndex)
+      )
+      || (
+        this.hasRightChild(nodeIndex)
+        && this.heap[nodeIndex] > this.rightChild(nodeIndex)
+      )
+    ) {
+      const leftIndex = this.getLeftChildIndex(nodeIndex);
+      const rightIndex = this.getRightChildIndex(nodeIndex);
+      const left = this.leftChild(nodeIndex);
+      const right = this.rightChild(nodeIndex);
+
+      if (this.hasLeftChild(nodeIndex) && this.hasRightChild(nodeIndex)) {
+        if (left <= right) {
+          this.swap(leftIndex, nodeIndex);
+          nodeIndex = leftIndex;
+        } else {
+          this.swap(rightIndex, nodeIndex);
+          nodeIndex = rightIndex;
+        }
+      } else if (this.hasLeftChild(nodeIndex)) {
+        this.swap(leftIndex, nodeIndex);
+        nodeIndex = leftIndex;
+      }
+    }
+  }
+
+  getLeftChildIndex(parentIndex) {
+    return 2 * parentIndex + 1;
+  }
+
+  getRightChildIndex(parentIndex) {
+    return 2 * parentIndex + 2;
+  }
+
+  getParentIndex(childIndex) {
+    return Math.floor((childIndex - 1) / 2);
+  }
+
+  hasLeftChild(parentIndex) {
+    return this.getLeftChildIndex(parentIndex) < this.heap.length;
+  }
+
+  hasRightChild(parentIndex) {
+    return this.getRightChildIndex(parentIndex) < this.heap.length;
+  }
+
+  leftChild(parentIndex) {
+    return this.heap[this.getLeftChildIndex(parentIndex)];
+  }
+
+  rightChild(parentIndex) {
+    return this.heap[this.getRightChildIndex(parentIndex)];
+  }
+
+  swap(indexOne, indexTwo) {
+    const tmp = this.heap[indexTwo];
+    this.heap[indexTwo] = this.heap[indexOne];
+    this.heap[indexOne] = tmp;
+  }
+}
+
+export default MinHeapAdhoc;
diff --git a/src/data-structures/heap/README.fr-FR.md b/src/data-structures/heap/README.fr-FR.md
new file mode 100644
index 0000000000..56f184b593
--- /dev/null
+++ b/src/data-structures/heap/README.fr-FR.md
@@ -0,0 +1,22 @@
+# Tas (structure de données)
+
+En informatique, un **tas** est une structure de données arborescente spécialisée qui satisfait la propriété de tas décrite ci-dessous.
+
+Dans un *tas minimal* (en anglais *min heap*), si `P` est un nœud parent de `C`, alors la clé (la valeur) de `P` est inférieure ou égale à la clé de `C`.
+
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Dans un *tas maximal* (en anglais *max heap*), la clé de `P` est supérieure ou égale à la clé de `C`.
+
+![MaxHeap](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+Le nœud au «sommet» du tas sans parents est appelé le nœud racine.
+
+## Références
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/Tas_(informatique))
+- [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/heap/README.ja-JP.md b/src/data-structures/heap/README.ja-JP.md
new file mode 100644
index 0000000000..13d3e36338
--- /dev/null
+++ b/src/data-structures/heap/README.ja-JP.md
@@ -0,0 +1,22 @@
+# ヒープ (データ構造)
+
+コンピュータサイエンスにおいて、*ヒープ*は特殊な木構造のデータ構造で、後述するヒープの特性を持っています。
+
+*最小ヒープ*では、もし`P`が`C`の親ノードの場合、`P`のキー(値)は`C`のキーより小さい、または等しくなります。
+
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+*最大ヒープ*では、`P`のキーは`C`のキーより大きい、もしくは等しくなります。
+
+![MaxHeap](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+ヒープの「トップ」のノードには親ノードが存在せず、ルートノードと呼ばれます。
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Heap_(data_structure))
+- [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/heap/README.ko-KR.md b/src/data-structures/heap/README.ko-KR.md
new file mode 100644
index 0000000000..136a87b420
--- /dev/null
+++ b/src/data-structures/heap/README.ko-KR.md
@@ -0,0 +1,23 @@
+# 힙 (자료구조)
+
+컴퓨터 과학에서의 **힙**은 아래에 설명된 힙 속성을 만족하는 전문화된 트리 기반 데이터구조입니다.
+
+*최소 힙*에서 `P`가 `C`의 상위 노드라면 `P`의 키(값)는 `C`의 키보다 작거나 같습니다.
+
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+*최대 힙*에서 `P`의 키는 `C`의 키보다 크거나 같습니다.
+
+![MaxHeap](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+상위 노드가 없는 힙의 "상단"에 있는 노드를 루트 노드라고 합니다.
+
+## 참조
+
+- [Wikipedia](<https://en.wikipedia.org/wiki/Heap_(data_structure)>)
+- [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+
diff --git a/src/data-structures/heap/README.md b/src/data-structures/heap/README.md
index 204bb6769b..392c5c96bc 100644
--- a/src/data-structures/heap/README.md
+++ b/src/data-structures/heap/README.md
@@ -1,6 +1,17 @@
 # Heap (data-structure)
 
-In computer science, a **heap** is a specialized tree-based 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md),
+[_Türkçe_](README.tr-TR.md),
+[_한국어_](README.ko-KR.md),
+[_Українська_](README.uk-UA.md)
+
+
+In computer science, a **heap** is a specialized tree-based
 data structure that satisfies the heap property described
 below.
 
@@ -8,16 +19,50 @@ In a *min heap*, if `P` is a parent node of `C`, then the
 key (the value) of `P` is less than or equal to the
 key of `C`.
 
-![MinHeap](https://upload.wikimedia.org/wikipedia/commons/6/69/Min-heap.png)
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
 
 In a *max heap*, the key of `P` is greater than or equal
 to the key of `C`
 
-![Heap](https://upload.wikimedia.org/wikipedia/commons/3/38/Max-Heap.svg)
+![MaxHeap](./images/max-heap.jpeg)
 
-The node at the "top" of the heap with no parents is 
+![Array Representation](./images/array-representation.jpeg)
+
+The node at the "top" of the heap with no parents is
 called the root node.
 
+## Time Complexities
+
+Here are time complexities of various heap data structures. Function names assume a max-heap.
+
+| Operation | find-max | delete-max | insert| increase-key| meld |
+| --------- | -------- | ---------- | ----- | ----------- | ---- |
+| [Binary](https://en.wikipedia.org/wiki/Binary_heap) | `Θ(1)` | `Θ(log n)` | `O(log n)` | `O(log n)` | `Θ(n)` |
+| [Leftist](https://en.wikipedia.org/wiki/Leftist_tree) | `Θ(1)` | `Θ(log n)` | `Θ(log n)` | `O(log n)` | `Θ(log n)` |
+| [Binomial](https://en.wikipedia.org/wiki/Binomial_heap) | `Θ(1)` | `Θ(log n)` | `Θ(1)` | `O(log n)` | `O(log n)` |
+| [Fibonacci](https://en.wikipedia.org/wiki/Fibonacci_heap) | `Θ(1)` | `Θ(log n)` | `Θ(1)` | `Θ(1)` | `Θ(1)` |
+| [Pairing](https://en.wikipedia.org/wiki/Pairing_heap) | `Θ(1)` | `Θ(log n)` | `Θ(1)` | `o(log n)` | `Θ(1)` |
+| [Brodal](https://en.wikipedia.org/wiki/Brodal_queue) | `Θ(1)` | `Θ(log n)` | `Θ(1)` | `Θ(1)` | `Θ(1)` |
+| [Rank-pairing](https://en.wikipedia.org/w/index.php?title=Rank-pairing_heap&action=edit&redlink=1) | `Θ(1)` | `Θ(log n)` | `Θ(1)` | `Θ(1)` | `Θ(1)` |
+| [Strict Fibonacci](https://en.wikipedia.org/wiki/Fibonacci_heap) | `Θ(1)` | `Θ(log n)` | `Θ(1)` | `Θ(1)` | `Θ(1)` |
+| [2-3 heap](https://en.wikipedia.org/wiki/2%E2%80%933_heap) | `O(log n)` | `O(log n)` | `O(log n)` | `Θ(1)` | `?` |
+
+Where:
+- **find-max (or find-min):** find a maximum item of a max-heap, or a minimum item of a min-heap, respectively (a.k.a. *peek*)
+- **delete-max (or delete-min):** removing the root node of a max heap (or min heap), respectively
+- **insert:** adding a new key to the heap (a.k.a., *push*)
+- **increase-key or decrease-key:** updating a key within a max- or min-heap, respectively
+- **meld:** joining two heaps to form a valid new heap containing all the elements of both, destroying the original heaps.
+
+> In this repository, the [MaxHeap.js](./MaxHeap.js) and [MinHeap.js](./MinHeap.js) are examples of the **Binary** heap.
+
+## Implementation
+
+- [MaxHeap.js](./MaxHeap.js) and [MinHeap.js](./MinHeap.js)
+- [MaxHeapAdhoc.js](./MaxHeapAdhoc.js) and [MinHeapAdhoc.js](./MinHeapAdhoc.js) - The minimalistic (ad hoc) version of a MinHeap/MaxHeap data structure that doesn't have external dependencies and that is easy to copy-paste and use during the coding interview if allowed by the interviewer (since many data structures in JS are missing).
+
 ## References
 
 - [Wikipedia](https://en.wikipedia.org/wiki/Heap_(data_structure))
diff --git a/src/data-structures/heap/README.pt-BR.md b/src/data-structures/heap/README.pt-BR.md
new file mode 100644
index 0000000000..f229048d3a
--- /dev/null
+++ b/src/data-structures/heap/README.pt-BR.md
@@ -0,0 +1,25 @@
+# Heap (estrutura de dados)
+
+Na ciência da computação, um **heap** é uma estrutura de dados
+baseada em uma árvore especializada que satisfaz a propriedade _heap_ descrita abaixo.
+
+Em um *heap mínimo* (min heap), caso `P` é um nó pai de `C`, então a chave
+(o valor) de `P` é menor ou igual a chave de `C`.
+
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Em uma *heap máximo* (max heap), a chave de `P` é maior ou igual
+a chave de `C`.
+
+![MaxHeap](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+O nó no "topo" do _heap_, cujo não possui pais, é chamado de nó raiz.
+
+## References
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Heap_(data_structure))
+- [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/heap/README.ru-RU.md b/src/data-structures/heap/README.ru-RU.md
new file mode 100644
index 0000000000..4c074d1f7c
--- /dev/null
+++ b/src/data-structures/heap/README.ru-RU.md
@@ -0,0 +1,26 @@
+# Куча (структура данных)
+
+В компьютерных науках куча — это специализированная структура данных типа дерево, которая удовлетворяет свойству кучи:
+если B является узлом-потомком узла A, то ключ(A) ≥ ключ(B). Из этого следует, что элемент с наибольшим ключом всегда
+является корневым узлом кучи, поэтому иногда такие кучи называют max-кучами.
+
+![MaxHeap](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+Если сравнение перевернуть, то наименьший элемент будет всегда корневым узлом, такие кучи называют min-кучами.
+
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Не существует никаких ограничений относительно того, сколько узлов-потомков имеет каждый узел кучи. На практике их
+число обычно не более двух. Куча является максимально эффективной реализацией абстрактного типа данных, который
+называется очередью с приоритетом.
+
+Узел на вершине кучи, у которого нет родителей, называется корневым узлом.
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/Куча_(структура_данных))
+- [YouTube](https://www.youtube.com/watch?v=noQ4SUoqrQA)
diff --git a/src/data-structures/heap/README.tr-TR.md b/src/data-structures/heap/README.tr-TR.md
new file mode 100644
index 0000000000..05906391e8
--- /dev/null
+++ b/src/data-structures/heap/README.tr-TR.md
@@ -0,0 +1,22 @@
+# Heap (data-structure)
+
+Bilgisayar biliminde, **yığın (heap)** aşağıda açıklanan özellikleri karşılayan ağaç tabanlı(tree-based) özel bir veri yapısıdır.
+
+*min heap*, Eğer `P`, `C`'nin üst düğümü ise, `P`'nin anahtarı (değeri)  `C`'nin anahtarından (değerinden) küçük veya ona eşittir.
+
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+*max heap*, `P`'nin anahtarı `C`'nin anahtarından büyük veya eşittir.
+
+![MaxHeap](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+Yığının (Heap) "en üstündeki" ebeveyni olmayan düğüme kök düğüm (root node) denir.
+
+## Referanslar
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Heap_(data_structure))
+- [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/heap/README.uk-UA.md b/src/data-structures/heap/README.uk-UA.md
new file mode 100644
index 0000000000..f35995d8ae
--- /dev/null
+++ b/src/data-structures/heap/README.uk-UA.md
@@ -0,0 +1,25 @@
+# Купа (структура даних)
+
+У комп'ютерних науках купа - це спеціалізована структура даних на кшталт дерева, яка задовольняє властивості купи:
+якщо B є вузлом-нащадком вузла A, то ключ (A) ≥ ключ (B). З цього випливає, що елемент із найбільшим ключем завжди
+є кореневим вузлом купи, тому іноді такі купи називають max-купами.
+
+![MaxHeap](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+Якщо порівняння перевернути, то найменший елемент завжди буде кореневим вузлом, такі купи називають min-купами.
+
+![MinHeap](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+Не існує жодних обмежень щодо того, скільки вузлів-нащадків має кожен вузол купи. На практиці їх
+число зазвичай трохи більше двох. Купа є максимально ефективною реалізацією абстрактного типу даних, який
+називається чергою із пріоритетом.
+
+Вузол на вершині купи, який не має батьків, називається кореневим вузлом.
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%9A%D1%83%D0%BF%D0%B0_(%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B4%D0%B0%D0%BD%D0%B8%D1%85))
diff --git a/src/data-structures/heap/README.zh-CN.md b/src/data-structures/heap/README.zh-CN.md
new file mode 100644
index 0000000000..8ba42dfb05
--- /dev/null
+++ b/src/data-structures/heap/README.zh-CN.md
@@ -0,0 +1,23 @@
+# 堆 (数据结构)
+
+在计算机科学中, 一个 **堆(heap)** 是一种特殊的基于树的数据结构，它满足下面描述的堆属性。
+
+在一个 *最小堆(min heap)* 中, 如果 `P` 是 `C` 的一个父级节点, 那么 `P`  的key(或value)应小于或等于 `C` 的对应值.
+
+![M最小堆](./images/min-heap.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+在一个  *最大堆(max heap)* 中,  `P` 的key(或value)大于 `C` 的对应值。
+
+![堆](./images/max-heap.jpeg)
+
+![Array Representation](./images/array-representation.jpeg)
+
+
+在堆“顶部”的没有父级节点的节点,被称之为根节点。
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Heap_(data_structure))
+- [YouTube](https://www.youtube.com/watch?v=t0Cq6tVNRBA&index=5&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/heap/__test__/MaxHeapAdhoc.test.js b/src/data-structures/heap/__test__/MaxHeapAdhoc.test.js
new file mode 100644
index 0000000000..0217a98ddf
--- /dev/null
+++ b/src/data-structures/heap/__test__/MaxHeapAdhoc.test.js
@@ -0,0 +1,91 @@
+import MaxHeap from '../MaxHeapAdhoc';
+
+describe('MaxHeapAdhoc', () => {
+  it('should create an empty max heap', () => {
+    const maxHeap = new MaxHeap();
+
+    expect(maxHeap).toBeDefined();
+    expect(maxHeap.peek()).toBe(undefined);
+    expect(maxHeap.isEmpty()).toBe(true);
+  });
+
+  it('should add items to the heap and heapify it up', () => {
+    const maxHeap = new MaxHeap();
+
+    maxHeap.add(5);
+    expect(maxHeap.isEmpty()).toBe(false);
+    expect(maxHeap.peek()).toBe(5);
+    expect(maxHeap.toString()).toBe('5');
+
+    maxHeap.add(3);
+    expect(maxHeap.peek()).toBe(5);
+    expect(maxHeap.toString()).toBe('5,3');
+
+    maxHeap.add(10);
+    expect(maxHeap.peek()).toBe(10);
+    expect(maxHeap.toString()).toBe('10,3,5');
+
+    maxHeap.add(1);
+    expect(maxHeap.peek()).toBe(10);
+    expect(maxHeap.toString()).toBe('10,3,5,1');
+
+    maxHeap.add(1);
+    expect(maxHeap.peek()).toBe(10);
+    expect(maxHeap.toString()).toBe('10,3,5,1,1');
+
+    expect(maxHeap.poll()).toBe(10);
+    expect(maxHeap.toString()).toBe('5,3,1,1');
+
+    expect(maxHeap.poll()).toBe(5);
+    expect(maxHeap.toString()).toBe('3,1,1');
+
+    expect(maxHeap.poll()).toBe(3);
+    expect(maxHeap.toString()).toBe('1,1');
+  });
+
+  it('should poll items from the heap and heapify it down', () => {
+    const maxHeap = new MaxHeap();
+
+    maxHeap.add(5);
+    maxHeap.add(3);
+    maxHeap.add(10);
+    maxHeap.add(11);
+    maxHeap.add(1);
+
+    expect(maxHeap.toString()).toBe('11,10,5,3,1');
+
+    expect(maxHeap.poll()).toBe(11);
+    expect(maxHeap.toString()).toBe('10,3,5,1');
+
+    expect(maxHeap.poll()).toBe(10);
+    expect(maxHeap.toString()).toBe('5,3,1');
+
+    expect(maxHeap.poll()).toBe(5);
+    expect(maxHeap.toString()).toBe('3,1');
+
+    expect(maxHeap.poll()).toBe(3);
+    expect(maxHeap.toString()).toBe('1');
+
+    expect(maxHeap.poll()).toBe(1);
+    expect(maxHeap.toString()).toBe('');
+
+    expect(maxHeap.poll()).toBe(undefined);
+    expect(maxHeap.toString()).toBe('');
+  });
+
+  it('should heapify down through the right branch as well', () => {
+    const maxHeap = new MaxHeap();
+
+    maxHeap.add(3);
+    maxHeap.add(12);
+    maxHeap.add(10);
+
+    expect(maxHeap.toString()).toBe('12,3,10');
+
+    maxHeap.add(11);
+    expect(maxHeap.toString()).toBe('12,11,10,3');
+
+    expect(maxHeap.poll()).toBe(12);
+    expect(maxHeap.toString()).toBe('11,3,10');
+  });
+});
diff --git a/src/data-structures/heap/__test__/MinHeap.test.js b/src/data-structures/heap/__test__/MinHeap.test.js
index 99914855be..b963a41ff8 100644
--- a/src/data-structures/heap/__test__/MinHeap.test.js
+++ b/src/data-structures/heap/__test__/MinHeap.test.js
@@ -169,4 +169,26 @@ describe('MinHeap', () => {
     minHeap.remove('hey', comparator);
     expect(minHeap.toString()).toBe('a,bb,dddd');
   });
+
+  it('should remove values from heap and correctly re-order the tree', () => {
+    const minHeap = new MinHeap();
+
+    minHeap.add(1);
+    minHeap.add(2);
+    minHeap.add(3);
+    minHeap.add(4);
+    minHeap.add(5);
+    minHeap.add(6);
+    minHeap.add(7);
+    minHeap.add(8);
+    minHeap.add(9);
+
+    expect(minHeap.toString()).toBe('1,2,3,4,5,6,7,8,9');
+
+    minHeap.remove(2);
+    expect(minHeap.toString()).toBe('1,4,3,8,5,6,7,9');
+
+    minHeap.remove(4);
+    expect(minHeap.toString()).toBe('1,5,3,8,9,6,7');
+  });
 });
diff --git a/src/data-structures/heap/__test__/MinHeapAdhoc.test.js b/src/data-structures/heap/__test__/MinHeapAdhoc.test.js
new file mode 100644
index 0000000000..766b307ff0
--- /dev/null
+++ b/src/data-structures/heap/__test__/MinHeapAdhoc.test.js
@@ -0,0 +1,91 @@
+import MinHeapAdhoc from '../MinHeapAdhoc';
+
+describe('MinHeapAdhoc', () => {
+  it('should create an empty min heap', () => {
+    const minHeap = new MinHeapAdhoc();
+
+    expect(minHeap).toBeDefined();
+    expect(minHeap.peek()).toBe(undefined);
+    expect(minHeap.isEmpty()).toBe(true);
+  });
+
+  it('should add items to the heap and heapify it up', () => {
+    const minHeap = new MinHeapAdhoc();
+
+    minHeap.add(5);
+    expect(minHeap.isEmpty()).toBe(false);
+    expect(minHeap.peek()).toBe(5);
+    expect(minHeap.toString()).toBe('5');
+
+    minHeap.add(3);
+    expect(minHeap.peek()).toBe(3);
+    expect(minHeap.toString()).toBe('3,5');
+
+    minHeap.add(10);
+    expect(minHeap.peek()).toBe(3);
+    expect(minHeap.toString()).toBe('3,5,10');
+
+    minHeap.add(1);
+    expect(minHeap.peek()).toBe(1);
+    expect(minHeap.toString()).toBe('1,3,10,5');
+
+    minHeap.add(1);
+    expect(minHeap.peek()).toBe(1);
+    expect(minHeap.toString()).toBe('1,1,10,5,3');
+
+    expect(minHeap.poll()).toBe(1);
+    expect(minHeap.toString()).toBe('1,3,10,5');
+
+    expect(minHeap.poll()).toBe(1);
+    expect(minHeap.toString()).toBe('3,5,10');
+
+    expect(minHeap.poll()).toBe(3);
+    expect(minHeap.toString()).toBe('5,10');
+  });
+
+  it('should poll items from the heap and heapify it down', () => {
+    const minHeap = new MinHeapAdhoc();
+
+    minHeap.add(5);
+    minHeap.add(3);
+    minHeap.add(10);
+    minHeap.add(11);
+    minHeap.add(1);
+
+    expect(minHeap.toString()).toBe('1,3,10,11,5');
+
+    expect(minHeap.poll()).toBe(1);
+    expect(minHeap.toString()).toBe('3,5,10,11');
+
+    expect(minHeap.poll()).toBe(3);
+    expect(minHeap.toString()).toBe('5,11,10');
+
+    expect(minHeap.poll()).toBe(5);
+    expect(minHeap.toString()).toBe('10,11');
+
+    expect(minHeap.poll()).toBe(10);
+    expect(minHeap.toString()).toBe('11');
+
+    expect(minHeap.poll()).toBe(11);
+    expect(minHeap.toString()).toBe('');
+
+    expect(minHeap.poll()).toBe(undefined);
+    expect(minHeap.toString()).toBe('');
+  });
+
+  it('should heapify down through the right branch as well', () => {
+    const minHeap = new MinHeapAdhoc();
+
+    minHeap.add(3);
+    minHeap.add(12);
+    minHeap.add(10);
+
+    expect(minHeap.toString()).toBe('3,12,10');
+
+    minHeap.add(11);
+    expect(minHeap.toString()).toBe('3,11,10,12');
+
+    expect(minHeap.poll()).toBe(3);
+    expect(minHeap.toString()).toBe('10,11,12');
+  });
+});
diff --git a/src/data-structures/heap/images/array-representation.jpeg b/src/data-structures/heap/images/array-representation.jpeg
new file mode 100644
index 0000000000..8914e7102e
Binary files /dev/null and b/src/data-structures/heap/images/array-representation.jpeg differ
diff --git a/src/data-structures/heap/images/max-heap.jpeg b/src/data-structures/heap/images/max-heap.jpeg
new file mode 100644
index 0000000000..c1585d0302
Binary files /dev/null and b/src/data-structures/heap/images/max-heap.jpeg differ
diff --git a/src/data-structures/heap/images/min-heap.jpeg b/src/data-structures/heap/images/min-heap.jpeg
new file mode 100644
index 0000000000..646923b2df
Binary files /dev/null and b/src/data-structures/heap/images/min-heap.jpeg differ
diff --git a/src/data-structures/linked-list/LinkedList.js b/src/data-structures/linked-list/LinkedList.js
index dfb17aaf96..ba7d0e3ee1 100644
--- a/src/data-structures/linked-list/LinkedList.js
+++ b/src/data-structures/linked-list/LinkedList.js
@@ -54,6 +54,40 @@ export default class LinkedList {
     return this;
   }
 
+  /**
+   * @param {*} value
+   * @param {number} index
+   * @return {LinkedList}
+   */
+  insert(value, rawIndex) {
+    const index = rawIndex < 0 ? 0 : rawIndex;
+    if (index === 0) {
+      this.prepend(value);
+    } else {
+      let count = 1;
+      let currentNode = this.head;
+      const newNode = new LinkedListNode(value);
+      while (currentNode) {
+        if (count === index) break;
+        currentNode = currentNode.next;
+        count += 1;
+      }
+      if (currentNode) {
+        newNode.next = currentNode.next;
+        currentNode.next = newNode;
+      } else {
+        if (this.tail) {
+          this.tail.next = newNode;
+          this.tail = newNode;
+        } else {
+          this.head = newNode;
+          this.tail = newNode;
+        }
+      }
+    }
+    return this;
+  }
+
   /**
    * @param {*} value
    * @return {LinkedListNode}
@@ -65,7 +99,7 @@ export default class LinkedList {
 
     let deletedNode = null;
 
-    // If the head must be deleted then make next node that is differ
+    // If the head must be deleted then make next node that is different
     // from the head to be a new head.
     while (this.head && this.compare.equal(this.head.value, value)) {
       deletedNode = this.head;
@@ -180,7 +214,7 @@ export default class LinkedList {
    * @return {LinkedList}
    */
   fromArray(values) {
-    values.forEach(value => this.append(value));
+    values.forEach((value) => this.append(value));
 
     return this;
   }
@@ -205,6 +239,34 @@ export default class LinkedList {
    * @return {string}
    */
   toString(callback) {
-    return this.toArray().map(node => node.toString(callback)).toString();
+    return this.toArray().map((node) => node.toString(callback)).toString();
+  }
+
+  /**
+   * Reverse a linked list.
+   * @returns {LinkedList}
+   */
+  reverse() {
+    let currNode = this.head;
+    let prevNode = null;
+    let nextNode = null;
+
+    while (currNode) {
+      // Store next node.
+      nextNode = currNode.next;
+
+      // Change next node of the current node so it would link to previous node.
+      currNode.next = prevNode;
+
+      // Move prevNode and currNode nodes one step forward.
+      prevNode = currNode;
+      currNode = nextNode;
+    }
+
+    // Reset head and tail.
+    this.tail = this.head;
+    this.head = prevNode;
+
+    return this;
   }
 }
diff --git a/src/data-structures/linked-list/README.es-ES.md b/src/data-structures/linked-list/README.es-ES.md
new file mode 100644
index 0000000000..6c6ba7ee87
--- /dev/null
+++ b/src/data-structures/linked-list/README.es-ES.md
@@ -0,0 +1,165 @@
+# Lista Enlazada (Linked List)
+
+_Lee este artículo en otros idiomas:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md)
+[_English_](README.md)
+
+En ciencias de la computación una **lista enlazada** es una  colección lineal 
+de elementos, en los cuales el orden lineal no es dado por
+su posición física en memoria. En cambio, cada 
+elemento señala al siguiente. Es una estructura de datos
+que consiste en un grupo de nodos los cuales juntos representan
+una secuencia. En su forma más sencilla, cada nodo está
+compuesto de datos y una referencia (en otras palabras,
+un enlace) al siguiente nodo en la secuencia. Esta estructura
+permite la inserción o eliminación de elementos
+desde cualquier posición en la secuencia durante la iteración.
+Las variantes más complejas agregan enlaces adicionales, permitiendo
+una eficiente inserción o eliminación desde referencias arbitrarias
+del elemento. Una desventaja de las listas enlazadas es que el tiempo de
+acceso es lineal (y difícil de canalizar). Un acceso
+más rápido, como un acceso aleatorio, no es factible. Los arreglos
+tienen una mejor localización en caché comparados con las listas enlazadas.
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Pseudocódigo para operaciones básicas
+
+### Insertar
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```text
+Prepend(value)
+ Pre: value is the value to add to the list
+ Post: value has been placed at the head of the list
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### Buscar
+
+```text
+Contains(head, value)
+  Pre: head is the head node in the list
+       value is the value to search for
+  Post: the item is either in the linked list, true; otherwise false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### Borrar
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true, otherwise false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Atravesar
+
+```text
+Traverse(head)
+  Pre: head is the head node in the list
+  Post: the items in the list have been traversed
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### Atravesar en Reversa
+
+```text
+ReverseTraversal(head, tail)
+  Pre: head and tail belong to the same list
+  Post: the items in the list have been traversed in reverse order
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## Complejidades
+
+### Complejidad de Tiempo
+
+| Acceso | Búsqueda | Inserción | Eliminación |
+| :----: | :------: | :-------: | :---------: |
+|  O(n)  |   O(n)   |   O(1)    |    O(n)     |
+
+### Complejidad Espacial
+
+O(n)
+
+## Referencias
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/linked-list/README.ja-JP.md b/src/data-structures/linked-list/README.ja-JP.md
new file mode 100644
index 0000000000..c78997042b
--- /dev/null
+++ b/src/data-structures/linked-list/README.ja-JP.md
@@ -0,0 +1,143 @@
+# リンクリスト
+
+コンピュータサイエンスにおいて、**リンクリスト**はデータ要素の線形コレクションです。要素の順番はメモリ内の物理的な配置によっては決まりません。代わりに、各要素が次の要素を指しています。リンクリストはノードのグループからなるデータ構造です。最も単純な形式では、各ノードはデータとシーケンス内における次のノードへの参照(つまり、リンク)で構成されています。この構造はイテレーションにおいて任意の位置へ要素を効率的に挿入、削除することを可能にしています。より複雑なリンクリストではリンクをさらに追加することで、任意の要素の参照から要素を効率的に挿入、削除することを可能にしています。リンクリストの欠点はアクセスタイムが線形である(そして、パイプライン処理が難しい)ことです。ランダムアクセスのような高速なアクセスは実現不可能です。配列の方がリンクリストと比較して参照の局所性が優れています。
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 基本操作の擬似コード
+
+### 挿入
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```text
+Prepend(value)
+ Pre: value is the value to add to the list
+ Post: value has been placed at the head of the list
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### 検索
+
+```text
+Contains(head, value)
+  Pre: head is the head node in the list
+       value is the value to search for
+  Post: the item is either in the linked list, true; otherwise false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### 削除
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true, otherwise false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### トラバース
+
+```text
+Traverse(head)
+  Pre: head is the head node in the list
+  Post: the items in the list have been traversed
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### 逆トラバース
+
+```text
+ReverseTraversal(head, tail)
+  Pre: head and tail belong to the same list
+  Post: the items in the list have been traversed in reverse order
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## 計算量
+
+### 時間計算量
+
+| Access    | Search    | Insertion | Deletion  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(n)      |
+
+### 空間計算量
+
+O(n)
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/linked-list/README.ko-KR.md b/src/data-structures/linked-list/README.ko-KR.md
new file mode 100644
index 0000000000..f68d8a464d
--- /dev/null
+++ b/src/data-structures/linked-list/README.ko-KR.md
@@ -0,0 +1,151 @@
+# 링크드 리스트
+
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md)
+
+컴퓨터과학에서, **링크드 리스트**는 데이터 요소의 선형 집합이며, 이 집합에서 논리적 저장 순서는 메모리의 물리적 저장 순서와 일치하지 않습니다. 그 대신, 각각의 원소들은 자기 자신 다음의 원소를 가리킵니다. **링크드 리스트**는 순서를 표현하는 노드들의 집합으로 이루어져 있습니다. 간단하게, 각각의 노드들은 데이터와 다음 순서의 노드를 가리키는 레퍼런스로 이루어져 있습니다. (링크라고 부릅니다.) 이 자료구조는 순회하는 동안 순서에 상관없이 효율적인 삽입이나 삭제가 가능합니다. 더 복잡한 변형은 추가적인 링크를 더해, 임의의 원소 참조로부터 효율적인 삽입과 삭제를 가능하게 합니다. 링크드 리스트의 단점은 접근 시간이 선형이라는 것이고, 병렬처리도 하지 못합니다. 임의 접근처럼 빠른 접근은 불가능합니다. 링크드 리스트에 비해 배열이 더 나은 캐시 지역성을 가지고 있습니다.
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 기본 연산에 대한 수도코드
+
+### 삽입
+
+```text
+Add(value)
+  Pre: 리스트에 추가할 값
+  Post: 리스트의 맨 마지막에 있는 값
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```text
+Prepend(value)
+ Pre: 리스트에 추가할 값
+ Post: 리스트의 맨 앞에 있는 값
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### 탐색
+
+```text
+Contains(head, value)
+  Pre: head는 리스트에서 맨 앞 노드
+       value는 찾고자 하는 값
+  Post: 항목이 링크드 리스트에 있으면 true;
+        없으면 false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### 삭제
+
+```text
+Remove(head, value)
+  Pre: head는 리스트에서 맨 앞 노드
+       value는 삭제하고자 하는 값
+  Post: 항목이 링크드 리스트에서 삭제되면 true;
+        없으면 false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### 순회
+
+```text
+Traverse(head)
+  Pre: head는 리스트에서 맨 앞 노드
+  Post: 순회된 항목들
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### 역순회
+
+```text
+ReverseTraversal(head, tail)
+  Pre: 같은 리스트에 들어 있는 맨 앞, 맨 뒤 노드
+  Post: 역순회된 항목들
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## 복잡도
+
+### 시간 복잡도
+
+|  접근   |  탐색   |  삽입   |  삭제   |
+| :---: | :---: | :---: | :---: |
+| O(n)  | O(n)  | O(1)  | O(n)  |
+
+### 공간 복잡도
+
+O(n)
+
+## 참조
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/linked-list/README.md b/src/data-structures/linked-list/README.md
index b0e5eaf934..825a0272f4 100644
--- a/src/data-structures/linked-list/README.md
+++ b/src/data-structures/linked-list/README.md
@@ -1,23 +1,35 @@
 # Linked List
 
-In computer science, a **linked list** is a linear collection 
-of data elements, in which linear order is not given by 
-their physical placement in memory. Instead, each 
-element points to the next. It is a data structure 
-consisting of a group of nodes which together represent 
-a sequence. Under the simplest form, each node is 
-composed of data and a reference (in other words, 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Español_](README.es-ES.md),
+[_Türkçe_](README.tr-TR.md),
+[_Українська_](README.uk-UA.md)
+
+In computer science, a **linked list** is a linear collection
+of data elements, in which linear order is not given by
+their physical placement in memory. Instead, each
+element points to the next. It is a data structure
+consisting of a group of nodes which together represent
+a sequence. Under the simplest form, each node is
+composed of data and a reference (in other words,
 a link) to the next node in the sequence. This structure
-allows for efficient insertion or removal of elements 
-from any position in the sequence during iteration. 
-More complex variants add additional links, allowing 
-efficient insertion or removal from arbitrary element 
-references. A drawback of linked lists is that access 
-time is linear (and difficult to pipeline). Faster 
-access, such as random access, is not feasible. Arrays 
+allows for efficient insertion or removal of elements
+from any position in the sequence during iteration.
+More complex variants add additional links, allowing
+efficient insertion or removal from arbitrary element
+references. A drawback of linked lists is that access
+time is linear (and difficult to pipeline). Faster
+access, such as random access, is not feasible. Arrays
 have better cache locality as compared to linked lists.
 
-![Linked List](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
 
 ## Pseudocode for Basic Operations
 
@@ -37,7 +49,20 @@ Add(value)
   end if
 end Add
 ```
-    
+
+```text
+Prepend(value)
+ Pre: value is the value to add to the list
+ Post: value has been placed at the head of the list
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
 ### Search
 
 ```text
@@ -46,7 +71,7 @@ Contains(head, value)
        value is the value to search for
   Post: the item is either in the linked list, true; otherwise false
   n ← head
-  while n = ø and n.value = value
+  while n != ø and n.value != value
     n ← n.next
   end while
   if n = ø
@@ -55,7 +80,7 @@ Contains(head, value)
   return true
 end Contains
 ```
-    
+
 ### Delete
 
 ```text
@@ -76,14 +101,16 @@ Remove(head, value)
     end if
     return true
   end if
-  while n.next = ø and n.next.value = value
+  while n.next != ø and n.next.value != value
     n ← n.next
   end while
-  if n.next = ø
+  if n.next != ø
     if n.next = tail
       tail ← n
+      tail.next = null
+    else
+      n.next ← n.next.next
     end if
-    n.next ← n.next.next
     return true
   end if
   return false
@@ -97,30 +124,30 @@ Traverse(head)
   Pre: head is the head node in the list
   Post: the items in the list have been traversed
   n ← head
-  while n = 0
+  while n != ø
     yield n.value
     n ← n.next
   end while
 end Traverse
 ```
-    
+
 ### Traverse in Reverse
 
 ```text
 ReverseTraversal(head, tail)
   Pre: head and tail belong to the same list
   Post: the items in the list have been traversed in reverse order
-  if tail = ø
+  if tail != ø
     curr ← tail
-    while curr = head
+    while curr != head
       prev ← head
-      while prev.next = curr
+      while prev.next != curr
         prev ← prev.next
       end while
       yield curr.value
       curr ← prev
     end while
-   yeild curr.value
+   yield curr.value
   end if
 end ReverseTraversal
 ```
@@ -131,7 +158,7 @@ end ReverseTraversal
 
 | Access    | Search    | Insertion | Deletion  |
 | :-------: | :-------: | :-------: | :-------: |
-| O(n)      | O(n)      | O(1)      | O(1)      |
+| O(n)      | O(n)      | O(1)      | O(n)      |
 
 ### Space Complexity
 
diff --git a/src/data-structures/linked-list/README.pt-BR.md b/src/data-structures/linked-list/README.pt-BR.md
new file mode 100644
index 0000000000..579a4fce8c
--- /dev/null
+++ b/src/data-structures/linked-list/README.pt-BR.md
@@ -0,0 +1,159 @@
+# Lista Encadeada (Linked List)
+
+Na ciência da computação, uma **lista encadeada** é uma coleção linear de
+elementos de dados, em que a ordem linear não é dada por sua locação
+física na memória. Em vez disso, cada elemento aponta para o próximo.
+É uma estrutura de dados consistindo em um grupo de nós
+que juntos representam uma sequência. Sob a forma mais simples,
+cada nó é composto de dados e uma referência (em outras palavras,
+uma ligação/conexão) para o próximo nó na sequência. Esta estrutura
+permite inserção ou remoção eficiente de elementos de qualquer
+posição na sequência durante a iteração.
+
+Variantes mais complexas adicionam ligações adicionais, permitindo
+uma inserção ou remoção mais eficiente a partir de referências
+de elementos arbitrárias. Uma desvantagem das listas encadeadas
+é que o tempo de acesso é linear (e difícil de inserir em uma
+pipeline). Acesso mais rápido, como acesso aleatório, não é viável.
+Arrays possuem uma melhor localização de cache em comparação
+com listas encadeadas (linked lists).
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Pseudo código para Operações Básicas
+
+### Inserção
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```text
+Prepend(value)
+ Pre: value is the value to add to the list
+ Post: value has been placed at the head of the list
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### Pesquisa
+
+```text
+Contains(head, value)
+  Pre: head is the head node in the list
+       value is the value to search for
+  Post: the item is either in the linked list, true; otherwise false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### Remoção
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true, otherwise false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Travessia
+
+```text
+Traverse(head)
+  Pre: head is the head node in the list
+  Post: the items in the list have been traversed
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### Travessia Reversa
+
+```text
+ReverseTraversal(head, tail)
+  Pre: head and tail belong to the same list
+  Post: the items in the list have been traversed in reverse order
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## Complexidades
+
+### Complexidade de Tempo
+
+| Acesso | Pesquisa | Inserção | Remoção |
+| :----: | :------: | :------: | :-----: |
+|  O(n)  |  O(n)    |  O(1)    |  O(n)   |
+
+### Complexidade de Espaçø
+
+O(n)
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/linked-list/README.ru-RU.md b/src/data-structures/linked-list/README.ru-RU.md
new file mode 100644
index 0000000000..98ad6c4727
--- /dev/null
+++ b/src/data-structures/linked-list/README.ru-RU.md
@@ -0,0 +1,147 @@
+# Связный список
+
+Связный список — базовая динамическая структура данных в информатике, состоящая из узлов, каждый из которых содержит как собственно данные,так ссылку («связку») на следующий узел списка. Данная структура позволяет эффективно добавлять и удалять элементы на произвольной позиции в последовательности в процессе итерации. Более сложные варианты включают дополнительные ссылки, позволяющие эффективно добавлять и удалять произвольные элементы.
+
+Принципиальным преимуществом перед массивом является структурная гибкость: порядок элементов связного списка может не совпадать с порядком расположения элементов данных в памяти компьютера, а порядок обхода списка всегда явно задаётся его внутренними связями. Суть преимущества состоит в том, что во многих языках создание массива требует указать его размер заранее. Связный список позволяет обойти это ограничение.
+
+Недостатком связных списков является то, что время доступа линейно (и затруднительно для реализации конвейеров). Быстрый доступ(случайный) невозможен.
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Псевдокод основных операций
+
+### Вставка
+
+```text
+Add(value)
+  Pre: value - добавляемое значение
+  Post: value помещено в конец списка
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```text
+Prepend(value)
+ Pre: value - добавляемое значение
+ Post: value помещено в начало списка
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### Поиск
+
+```text
+Contains(head, value)
+  Pre: head - первый узел в списке
+         value - значение, которое следует найти
+  Post: true - value найдено в списке, иначе false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### Удаление
+
+```text
+Remove(head, value)
+  Pre: head - первый узел в списке
+       value - значение, которое следует удалить
+  Post: true - value удалено из списка, иначе false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Обход
+
+```text
+Traverse(head)
+  Pre: head - первый узел в списке
+  Post: элементы списка пройдены
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### Обратный обход
+
+```text
+ReverseTraversal(head, tail)
+  Pre: head и tail относятся к одному списку
+  Post: элементы списка пройдены в обратном порядке
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## Сложность
+
+### Временная сложность
+
+| Чтение    | Поиск     | Вставка   | Удаление  |
+| :--------: | :-------: | :--------: | :-------: |
+| O(n)       | O(n)      | O(1)       | O(n)      |
+
+### Пространственная сложность
+
+O(n)
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A1%D0%B2%D1%8F%D0%B7%D0%BD%D1%8B%D0%B9_%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA)
+- [YouTube](https://www.youtube.com/watch?v=KTpOalDwBjg)
diff --git a/src/data-structures/linked-list/README.tr-TR.md b/src/data-structures/linked-list/README.tr-TR.md
new file mode 100644
index 0000000000..e5ed05623d
--- /dev/null
+++ b/src/data-structures/linked-list/README.tr-TR.md
@@ -0,0 +1,161 @@
+# Bağlantılı Liste
+
+_Bunu diğer dillerde okuyun:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Español_](README.es-ES.md),
+
+Bilgisayar bilimlerinde, **Bağlantılı liste**, her biri hem gerçek verileri
+hem de listedeki bir sonraki düğümün bir bağlantısını içeren dinamik bir
+veri yapısıdır. Bu yapı, yineleme sırasında rastgele bir konumda
+öğeleri verimli bir şekilde eklemenize ve kaldırmanıza olanak tanır.
+Daha karmaşık seçenekler için, isteğe bağlı öğeleri verimli bir şekilde
+eklemek ve kaldırmak için ek bağlantılar içerir.
+
+Bağlantılı listelerin bir dezavantajı, erişim süresinin doğrusal olmasıdır
+(ve ardışık düzene geçirilmesi zordur). Rastgele erişim gibi daha hızlı erişim
+mümkün değildir. Diziler, bağlantılı listelere kıyasla daha iyi önbellek konumuna sahiptir.
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Temel İşlemler için Sözde Kod
+
+### Ekleme
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```text
+Prepend(value)
+ Pre: value is the value to add to the list
+ Post: value has been placed at the head of the list
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### Arama
+
+```text
+Contains(head, value)
+  Pre: head is the head node in the list
+       value is the value to search for
+  Post: the item is either in the linked list, true; otherwise false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### Silme
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true, otherwise false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+      tail.next = null
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Geçiş
+
+```text
+Traverse(head)
+  Pre: head is the head node in the list
+  Post: the items in the list have been traversed
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### Ters Geçiş
+
+```text
+ReverseTraversal(head, tail)
+  Pre: head and tail belong to the same list
+  Post: the items in the list have been traversed in reverse order
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## Karmaşıklıklar
+
+### Zaman Karmaşıklığı
+
+| Erişim    | Arama     | Ekleme    | Silme     |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(n)      |
+
+### Uzay Karmaşıklığı
+
+O(n)
+
+## Referanslar
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/linked-list/README.uk-UA.md b/src/data-structures/linked-list/README.uk-UA.md
new file mode 100644
index 0000000000..9b34a6d828
--- /dev/null
+++ b/src/data-structures/linked-list/README.uk-UA.md
@@ -0,0 +1,147 @@
+# Зв'язаний список
+
+Зв'язаний список — базова динамічна структура даних в інформатиці, що складається з вузлів, кожен з яких містить як дані, так і посилання («зв'язку») на наступний вузол списку. Ця структура даних дозволяє ефективно додавати та видаляти елементи на довільній позиції у послідовності у процесі ітерації. Більш складні варіанти включають додаткові посилання, що дозволяють ефективно додавати та видаляти довільні елементи.
+
+Принциповою перевагою перед масивом є структурна гнучкість: порядок елементів зв'язаного списку може збігатися з порядком розташування елементів даних у пам'яті комп'ютера, а порядок обходу списку завжди явно задається його внутрішніми зв'язками. Це важливо, бо у багатьох мовах створення масиву вимагає вказати його розмір заздалегідь. Зв'язаний список дозволяє обійти це обмеження.
+
+Недоліком зв'язаних списків є те, що час доступу є лінійним (і важко для реалізації конвеєрів). Неможливий швидкий доступ (випадковий).
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Псевдокод основних операцій
+
+### Вставка
+
+```text
+Add(value)
+  Pre: value - значення, що додається
+  Post: value додано в кінець списку
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```text
+Prepend(value)
+ Pre: value - значення, що додається
+ Post: value додано на початку списку
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### Пошук
+
+```text
+Contains(head, value)
+  Pre: head - перший вузол у списку
+         value - значення, яке слід знайти
+  Post: true - value знайдено у списку, інакше false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### Видалення
+
+```text
+Remove(head, value)
+  Pre: head - перший вузол у списку
+       value - значення, яке слід видалити
+  Post: true - value видалено зі списку, інакше false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Обхід
+
+```text
+Traverse(head)
+  Pre: head - перший вузол у списку
+  Post: елементи списку пройдені
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### Зворотній обхід
+
+```text
+ReverseTraversal(head, tail)
+  Pre: head і tail відносяться до одного списку
+  Post: елементи списку пройдено у зворотньому порядку
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## Складність
+
+### Часова складність
+
+| Читання    | Пошук     | Вставка    | Вилучення |
+| :--------: | :-------: | :--------: | :-------: |
+| O(n)       | O(n)      | O(1)       | O(n)      |
+
+### Просторова складність
+
+O(n)
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/Зв'язаний_список)
+- [YouTube](https://www.youtube.com/watch?v=6snsMa4E1Os)
diff --git a/src/data-structures/linked-list/README.vi-VN.md b/src/data-structures/linked-list/README.vi-VN.md
new file mode 100644
index 0000000000..005838c6c1
--- /dev/null
+++ b/src/data-structures/linked-list/README.vi-VN.md
@@ -0,0 +1,155 @@
+# Danh sách liên kết (Linked List)
+
+_Đọc bằng ngôn ngữ khác:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Español_](README.es-ES.md),
+[_Türkçe_](README.tr-TR.md),
+[_Українська_](README.uk-UA.md)
+
+
+Trong khoa học máy tính, một danh sách liên kết là một bộ sưu tập tuyến tính
+các phần tử dữ liệu, trong đó thứ tự tuyến tính không được xác định bởi
+vị trí vật lý của chúng trong bộ nhớ. Thay vào đó, mỗi
+phần tử trỏ đến phần tử tiếp theo. Đây là một cấu trúc dữ liệu
+bao gồm một nhóm các nút cùng đại diện cho
+một chuỗi. Dưới dạng đơn giản nhất, mỗi nút
+bao gồm dữ liệu và một tham chiếu (nói cách khác,
+một liên kết) đến nút tiếp theo trong chuỗi. Cấu trúc này
+cho phép việc chèn hoặc loại bỏ các phần tử một cách hiệu quả
+từ bất kỳ vị trí nào trong chuỗi trong quá trình lặp.
+Các biến thể phức tạp hơn thêm các liên kết bổ sung, cho phép
+việc chèn hoặc loại bỏ một cách hiệu quả từ bất kỳ phần tử nào
+trong chuỗi dựa trên tham chiếu. Một nhược điểm của danh sách liên kết
+là thời gian truy cập tuyến tính (và khó điều chỉnh). Truy cập nhanh hơn,
+như truy cập ngẫu nhiên, là không khả thi. Mảng
+có độ tương phản cache tốt hơn so với danh sách liên kết.
+
+![Linked List](./images/linked-list.jpeg)
+*Được làm từ [okso.app](https://okso.app)*
+
+## Mã giải (Pseudocode) cho Các Hoạt Động Cơ Bản
+  *head = đầu,
+  *tail = đuôi,
+  *next = kế tiếp,
+  *node = nút,
+  *value = giá trị
+
+### Chèn (Insert)
+
+```
+ThêmGiáTrị(giá trị) (Add(value))
+  Trước(Pre): giá trị là giá trị muốn thêm vào danh sách
+  Sau(Post): giá trị đã được đặt ở cuối danh sách
+
+ n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end ThêmGiáTrị(Add)
+```
+
+```
+ChènVàoĐầu(giá trị)
+  Trước(Pre): giá trị là giá trị muốn thêm vào danh sách
+  Sau(Post): giá trị đã được đặt ở đầu danh sách
+
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end ChènVàoĐầu
+```
+
+### Tìm Kiếm (Search)
+```
+Chứa(đầu, giá trị)
+  Trước: đầu là nút đầu trong danh sách
+       giá trị là giá trị cần tìm kiếm
+  Sau: mục đó có thể ở trong danh sách liên kết, true; nếu không, là false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### Xóa (Delete)
+```
+Xóa(đầu, giá trị)
+  Trước: đầu là nút đầu trong danh sách
+       giá trị là giá trị cần xóa khỏi danh sách
+  Sau: giá trị đã được xóa khỏi danh sách, true; nếu không, là false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+      tail.next = null
+    else
+      n.next ← n.next.next
+    end if
+    return true
+  end if
+  return false
+end Remove
+```
+
+### Duyệt(raverse)
+Duyệt(đầu)
+  Trước: đầu là nút đầu trong danh sách
+  Sau: các mục trong danh sách đã được duyệt
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+
+### Duyệt Ngược (Traverse in Reverse)
+DuyệtNgược(đầu, đuôi)
+  Trước: đầu và đuôi thuộc cùng một danh sách
+  Sau: các mục trong danh sách đã được duyệt theo thứ tự ngược lại
+
+## Độ Phức Tạp
+
+### Độ Phức Tạp Thời Gian (Time Complexity)
+
+| Access    | Search    | Insertion | Deletion  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(n)      |
+
+## Độ Phức Tạp Không Gian (Space Complexity)
+O(n)
+
+## Tham Khảo
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/linked-list/README.zh-CN.md b/src/data-structures/linked-list/README.zh-CN.md
new file mode 100644
index 0000000000..f0b75121e7
--- /dev/null
+++ b/src/data-structures/linked-list/README.zh-CN.md
@@ -0,0 +1,149 @@
+# 链表
+
+在计算机科学中, 一个 **链表** 是数据元素的线性集合, 元素的线性顺序不是由它们在内存中的物理位置给出的。 相反, 每个元素指向下一个元素。它是由一组节点组成的数据结构,这些节点一起,表示序列。
+
+在最简单的形式下，每个节点由数据和到序列中下一个节点的引用(换句话说，链接)组成。这种结构允许在迭代期间有效地从序列中的任何位置插入或删除元素。
+
+更复杂的变体添加额外的链接，允许有效地插入或删除任意元素引用。链表的一个缺点是访问时间是线性的(而且难以管道化)。
+
+更快的访问，如随机访问，是不可行的。与链表相比，数组具有更好的缓存位置。
+
+![Linked List](./images/linked-list.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 基本操作的伪代码
+
+### 插入
+
+```text
+Add(value)
+  Pre: value is the value to add to the list
+  Post: value has been placed at the tail of the list
+  n ← node(value)
+  if head = ø
+    head ← n
+    tail ← n
+  else
+    tail.next ← n
+    tail ← n
+  end if
+end Add
+```
+
+```
+Prepend(value)
+ Pre: value is the value to add to the list
+ Post: value has been placed at the head of the list
+ n ← node(value)
+ n.next ← head
+ head ← n
+ if tail = ø
+   tail ← n
+ end
+end Prepend
+```
+
+### 搜索
+
+```text
+Contains(head, value)
+  Pre: head is the head node in the list
+       value is the value to search for
+  Post: the item is either in the linked list, true; otherwise false
+  n ← head
+  while n != ø and n.value != value
+    n ← n.next
+  end while
+  if n = ø
+    return false
+  end if
+  return true
+end Contains
+```
+
+### 删除
+
+```text
+Remove(head, value)
+  Pre: head is the head node in the list
+       value is the value to remove from the list
+  Post: value is removed from the list, true, otherwise false
+  if head = ø
+    return false
+  end if
+  n ← head
+  if n.value = value
+    if head = tail
+      head ← ø
+      tail ← ø
+    else
+      head ← head.next
+    end if
+    return true
+  end if
+  while n.next != ø and n.next.value != value
+    n ← n.next
+  end while
+  if n.next != ø
+    if n.next = tail
+      tail ← n
+    end if
+    n.next ← n.next.next
+    return true
+  end if
+  return false
+end Remove
+```
+
+### 遍历
+
+```text
+Traverse(head)
+  Pre: head is the head node in the list
+  Post: the items in the list have been traversed
+  n ← head
+  while n != ø
+    yield n.value
+    n ← n.next
+  end while
+end Traverse
+```
+
+### 反向遍历
+
+```text
+ReverseTraversal(head, tail)
+  Pre: head and tail belong to the same list
+  Post: the items in the list have been traversed in reverse order
+  if tail != ø
+    curr ← tail
+    while curr != head
+      prev ← head
+      while prev.next != curr
+        prev ← prev.next
+      end while
+      yield curr.value
+      curr ← prev
+    end while
+   yield curr.value
+  end if
+end ReverseTraversal
+```
+
+## 复杂度
+
+### 时间复杂度
+
+| Access    | Search    | Insertion | Deletion  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(n)      | O(n)      | O(1)      | O(1)      |
+
+### 空间复杂度
+
+O(n)
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/linked-list/__test__/LinkedList.test.js b/src/data-structures/linked-list/__test__/LinkedList.test.js
index 376a2e019b..6ac41ddf05 100644
--- a/src/data-structures/linked-list/__test__/LinkedList.test.js
+++ b/src/data-structures/linked-list/__test__/LinkedList.test.js
@@ -16,6 +16,7 @@ describe('LinkedList', () => {
     linkedList.append(2);
 
     expect(linkedList.toString()).toBe('1,2');
+    expect(linkedList.tail.next).toBeNull();
   });
 
   it('should prepend node to linked list', () => {
@@ -31,6 +32,21 @@ describe('LinkedList', () => {
     expect(linkedList.toString()).toBe('3,2,1');
   });
 
+  it('should insert node to linked list', () => {
+    const linkedList = new LinkedList();
+
+    linkedList.insert(4, 3);
+    expect(linkedList.head.toString()).toBe('4');
+    expect(linkedList.tail.toString()).toBe('4');
+
+    linkedList.insert(3, 2);
+    linkedList.insert(2, 1);
+    linkedList.insert(1, -7);
+    linkedList.insert(10, 9);
+
+    expect(linkedList.toString()).toBe('1,4,2,3,10');
+  });
+
   it('should delete node by value from linked list', () => {
     const linkedList = new LinkedList();
 
@@ -145,7 +161,7 @@ describe('LinkedList', () => {
       .append(nodeValue1)
       .prepend(nodeValue2);
 
-    const nodeStringifier = value => `${value.key}:${value.value}`;
+    const nodeStringifier = (value) => `${value.key}:${value.value}`;
 
     expect(linkedList.toString(nodeStringifier)).toBe('key2:2,key1:1');
   });
@@ -176,12 +192,12 @@ describe('LinkedList', () => {
       .append({ value: 2, key: 'test2' })
       .append({ value: 3, key: 'test3' });
 
-    const node = linkedList.find({ callback: value => value.key === 'test2' });
+    const node = linkedList.find({ callback: (value) => value.key === 'test2' });
 
     expect(node).toBeDefined();
     expect(node.value.value).toBe(2);
     expect(node.value.key).toBe('test2');
-    expect(linkedList.find({ callback: value => value.key === 'test5' })).toBeNull();
+    expect(linkedList.find({ callback: (value) => value.key === 'test5' })).toBeNull();
   });
 
   it('should create linked list from array', () => {
@@ -214,6 +230,53 @@ describe('LinkedList', () => {
     expect(node).toBeDefined();
     expect(node.value.value).toBe(2);
     expect(node.value.customValue).toBe('test2');
-    expect(linkedList.find({ value: 2, customValue: 'test5' })).toBeNull();
+    expect(linkedList.find({ value: { value: 2, customValue: 'test5' } })).toBeNull();
+  });
+
+  it('should find preferring callback over compare function', () => {
+    const greaterThan = (value, compareTo) => (value > compareTo ? 0 : 1);
+
+    const linkedList = new LinkedList(greaterThan);
+    linkedList.fromArray([1, 2, 3, 4, 5]);
+
+    let node = linkedList.find({ value: 3 });
+    expect(node.value).toBe(4);
+
+    node = linkedList.find({ callback: (value) => value < 3 });
+    expect(node.value).toBe(1);
+  });
+
+  it('should convert to array', () => {
+    const linkedList = new LinkedList();
+    linkedList.append(1);
+    linkedList.append(2);
+    linkedList.append(3);
+    expect(linkedList.toArray().join(',')).toBe('1,2,3');
+  });
+
+  it('should reverse linked list', () => {
+    const linkedList = new LinkedList();
+
+    // Add test values to linked list.
+    linkedList
+      .append(1)
+      .append(2)
+      .append(3);
+
+    expect(linkedList.toString()).toBe('1,2,3');
+    expect(linkedList.head.value).toBe(1);
+    expect(linkedList.tail.value).toBe(3);
+
+    // Reverse linked list.
+    linkedList.reverse();
+    expect(linkedList.toString()).toBe('3,2,1');
+    expect(linkedList.head.value).toBe(3);
+    expect(linkedList.tail.value).toBe(1);
+
+    // Reverse linked list back to initial state.
+    linkedList.reverse();
+    expect(linkedList.toString()).toBe('1,2,3');
+    expect(linkedList.head.value).toBe(1);
+    expect(linkedList.tail.value).toBe(3);
   });
 });
diff --git a/src/data-structures/linked-list/__test__/LinkedListNode.test.js b/src/data-structures/linked-list/__test__/LinkedListNode.test.js
index 0a989fc8c8..71b2d284b5 100644
--- a/src/data-structures/linked-list/__test__/LinkedListNode.test.js
+++ b/src/data-structures/linked-list/__test__/LinkedListNode.test.js
@@ -39,7 +39,7 @@ describe('LinkedListNode', () => {
   it('should convert node to string with custom stringifier', () => {
     const nodeValue = { value: 1, key: 'test' };
     const node = new LinkedListNode(nodeValue);
-    const toStringCallback = value => `value: ${value.value}, key: ${value.key}`;
+    const toStringCallback = (value) => `value: ${value.value}, key: ${value.key}`;
 
     expect(node.toString(toStringCallback)).toBe('value: 1, key: test');
   });
diff --git a/src/data-structures/linked-list/images/linked-list.jpeg b/src/data-structures/linked-list/images/linked-list.jpeg
new file mode 100644
index 0000000000..61a0712066
Binary files /dev/null and b/src/data-structures/linked-list/images/linked-list.jpeg differ
diff --git a/src/data-structures/lru-cache/LRUCache.js b/src/data-structures/lru-cache/LRUCache.js
new file mode 100644
index 0000000000..d6b4b3c6be
--- /dev/null
+++ b/src/data-structures/lru-cache/LRUCache.js
@@ -0,0 +1,153 @@
+/* eslint-disable no-param-reassign, max-classes-per-file */
+
+/**
+ * Simple implementation of the Doubly-Linked List Node
+ * that is used in LRUCache class below.
+ */
+class LinkedListNode {
+  /**
+   * Creates a doubly-linked list node.
+   * @param {string} key
+   * @param {any} val
+   * @param {LinkedListNode} prev
+   * @param {LinkedListNode} next
+   */
+  constructor(key, val, prev = null, next = null) {
+    this.key = key;
+    this.val = val;
+    this.prev = prev;
+    this.next = next;
+  }
+}
+
+/**
+ * Implementation of the LRU (Least Recently Used) Cache
+ * based on the HashMap and Doubly Linked List data-structures.
+ *
+ * Current implementation allows to have fast O(1) (in average) read and write operations.
+ *
+ * At any moment in time the LRU Cache holds not more that "capacity" number of items in it.
+ */
+class LRUCache {
+  /**
+   * Creates a cache instance of a specific capacity.
+   * @param {number} capacity
+   */
+  constructor(capacity) {
+    this.capacity = capacity; // How many items to store in cache at max.
+    this.nodesMap = {}; // The quick links to each linked list node in cache.
+    this.size = 0; // The number of items that is currently stored in the cache.
+    this.head = new LinkedListNode(); // The Head (first) linked list node.
+    this.tail = new LinkedListNode(); // The Tail (last) linked list node.
+  }
+
+  /**
+   * Returns the cached value by its key.
+   * Time complexity: O(1) in average.
+   * @param {string} key
+   * @returns {any}
+   */
+  get(key) {
+    if (this.nodesMap[key] === undefined) return undefined;
+    const node = this.nodesMap[key];
+    this.promote(node);
+    return node.val;
+  }
+
+  /**
+   * Sets the value to cache by its key.
+   * Time complexity: O(1) in average.
+   * @param {string} key
+   * @param {any} val
+   */
+  set(key, val) {
+    if (this.nodesMap[key]) {
+      const node = this.nodesMap[key];
+      node.val = val;
+      this.promote(node);
+    } else {
+      const node = new LinkedListNode(key, val);
+      this.append(node);
+    }
+  }
+
+  /**
+   * Promotes the node to the end of the linked list.
+   * It means that the node is most frequently used.
+   * It also reduces the chance for such node to get evicted from cache.
+   * @param {LinkedListNode} node
+   */
+  promote(node) {
+    this.evict(node);
+    this.append(node);
+  }
+
+  /**
+   * Appends a new node to the end of the cache linked list.
+   * @param {LinkedListNode} node
+   */
+  append(node) {
+    this.nodesMap[node.key] = node;
+
+    if (!this.head.next) {
+      // First node to append.
+      this.head.next = node;
+      this.tail.prev = node;
+      node.prev = this.head;
+      node.next = this.tail;
+    } else {
+      // Append to an existing tail.
+      const oldTail = this.tail.prev;
+      oldTail.next = node;
+      node.prev = oldTail;
+      node.next = this.tail;
+      this.tail.prev = node;
+    }
+
+    this.size += 1;
+
+    if (this.size > this.capacity) {
+      this.evict(this.head.next);
+    }
+  }
+
+  /**
+   * Evicts (removes) the node from cache linked list.
+   * @param {LinkedListNode} node
+   */
+  evict(node) {
+    delete this.nodesMap[node.key];
+    this.size -= 1;
+
+    const prevNode = node.prev;
+    const nextNode = node.next;
+
+    // If one and only node.
+    if (prevNode === this.head && nextNode === this.tail) {
+      this.head.next = null;
+      this.tail.prev = null;
+      this.size = 0;
+      return;
+    }
+
+    // If this is a Head node.
+    if (prevNode === this.head) {
+      nextNode.prev = this.head;
+      this.head.next = nextNode;
+      return;
+    }
+
+    // If this is a Tail node.
+    if (nextNode === this.tail) {
+      prevNode.next = this.tail;
+      this.tail.prev = prevNode;
+      return;
+    }
+
+    // If the node is in the middle.
+    prevNode.next = nextNode;
+    nextNode.prev = prevNode;
+  }
+}
+
+export default LRUCache;
diff --git a/src/data-structures/lru-cache/LRUCacheOnMap.js b/src/data-structures/lru-cache/LRUCacheOnMap.js
new file mode 100644
index 0000000000..8e4e226129
--- /dev/null
+++ b/src/data-structures/lru-cache/LRUCacheOnMap.js
@@ -0,0 +1,53 @@
+/* eslint-disable no-restricted-syntax, no-unreachable-loop */
+
+/**
+ * Implementation of the LRU (Least Recently Used) Cache
+ * based on the (ordered) Map data-structure.
+ *
+ * Current implementation allows to have fast O(1) (in average) read and write operations.
+ *
+ * At any moment in time the LRU Cache holds not more that "capacity" number of items in it.
+ */
+class LRUCacheOnMap {
+  /**
+   * Creates a cache instance of a specific capacity.
+   * @param {number} capacity
+   */
+  constructor(capacity) {
+    this.capacity = capacity; // How many items to store in cache at max.
+    this.items = new Map(); // The ordered hash map of all cached items.
+  }
+
+  /**
+   * Returns the cached value by its key.
+   * Time complexity: O(1) in average.
+   * @param {string} key
+   * @returns {any}
+   */
+  get(key) {
+    if (!this.items.has(key)) return undefined;
+    const val = this.items.get(key);
+    this.items.delete(key);
+    this.items.set(key, val);
+    return val;
+  }
+
+  /**
+   * Sets the value to cache by its key.
+   * Time complexity: O(1).
+   * @param {string} key
+   * @param {any} val
+   */
+  set(key, val) {
+    this.items.delete(key);
+    this.items.set(key, val);
+    if (this.items.size > this.capacity) {
+      for (const headKey of this.items.keys()) {
+        this.items.delete(headKey);
+        break;
+      }
+    }
+  }
+}
+
+export default LRUCacheOnMap;
diff --git a/src/data-structures/lru-cache/README.ko-KR.md b/src/data-structures/lru-cache/README.ko-KR.md
new file mode 100644
index 0000000000..93957bc0ed
--- /dev/null
+++ b/src/data-structures/lru-cache/README.ko-KR.md
@@ -0,0 +1,51 @@
+# LRU 캐시 알고리즘
+
+**LRU 캐시 알고리즘** 은 사용된 순서대로 아이템을 정리함으로써, 오랜 시간 동안 사용되지 않은 아이템을 빠르게 찾아낼 수 있도록 한다.
+
+한방향으로만 옷을 걸 수 있는 옷걸이 행거를 생각해봅시다. 가장 오랫동안 입지 않은 옷을 찾기 위해서는, 행거의 반대쪽 끝을 보면 됩니다.
+
+## 문제 정의
+
+LRUCache 클래스를 구현해봅시다:
+
+- `LRUCache(int capacity)` LRU 캐시를 **양수** 의 `capacity` 로 초기화합니다.
+- `int get(int key)` `key` 가 존재할 경우 `key` 값을 반환하고, 그렇지 않으면 `undefined` 를 반환합니다.
+- `void set(int key, int value)` `key` 가 존재할 경우 `key` 값을 업데이트 하고, 그렇지 않으면 `key-value` 쌍을 캐시에 추가합니다. 만약 이 동작으로 인해 키 개수가 `capacity` 를 넘는 경우, 가장 오래된 키 값을 **제거** 합니다.
+
+`get()` 과 `set()` 함수는 무조건 평균 `O(1)` 의 시간 복잡도 내에 실행되어야 합니다.
+
+## 구현
+
+### 버전 1: 더블 링크드 리스트 + 해시맵
+
+[LRUCache.js](./LRUCache.js) 에서 `LRUCache` 구현체 예시를 확인할 수 있습니다. 예시에서는 (평균적으로) 빠른 `O(1)` 캐시 아이템 접근을 위해 `HashMap` 을 사용했고, (평균적으로) 빠른 `O(1)` 캐시 아이템 수정과 제거를 위해 `DoublyLinkedList` 를 사용했습니다. (허용된 최대의 캐시 용량을 유지하기 위해)
+
+![Linked List](./images/lru-cache.jpg)
+
+_[okso.app](https://okso.app) 으로 만듦_
+
+LRU 캐시가 어떻게 작동하는지 더 많은 예시로 확인하고 싶다면 LRUCache.test.js](./**test**/LRUCache.test.js) 파일을 참고하세요.
+
+### 버전 2: 정렬된 맵
+
+더블 링크드 리스트로 구현한 첫번째 예시는 어떻게 평균 `O(1)` 시간 복잡도가 `set()` 과 `get()` 으로 나올 수 있는지 학습 목적과 이해를 돕기 위해 좋은 예시입니다.
+
+그러나, 더 쉬운 방법은 자바스크립트의 [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 객체를 사용하는 것입니다. 이 `Map` 객체는 키-값 쌍과 키를 **추가하는 순서 원본** 을 지닙니다. 우리는 이걸 아이템을 제거하거나 다시 추가하면서 맵의 "가장 마지막" 동작에서 최근에 사용된 아이템을 유지하기 위해 사용할 수 있습니다. `Map` 의 시작점에 있는 아이템은 캐시 용량이 넘칠 경우 가장 먼저 제거되는 대상입니다. 아이템의 순서는 `map.keys()` 와 같은 `IterableIterator` 을 사용해 확인할 수 있습니다.
+
+해당 구현체는 [LRUCacheOnMap.js](./LRUCacheOnMap.js) 의 `LRUCacheOnMap` 예시에서 확인할 수 있습니다.
+
+이 LRU 캐시 방식이 어떻게 작동하는지 더 많은 테스트 케이스를 확인하고 싶다면 [LRUCacheOnMap.test.js](./__test__/LRUCacheOnMap.test.js) 파일을 참고하세요.
+
+## 복잡도
+
+|                 | 평균   |
+| --------------- | ------ |
+| 공간            | `O(n)` |
+| 아이템 찾기     | `O(1)` |
+| 아이템 설정하기 | `O(1)` |
+
+## 참조
+
+- [LRU Cache on LeetCode](https://leetcode.com/problems/lru-cache/solutions/244744/lru-cache/)
+- [LRU Cache on InterviewCake](https://www.interviewcake.com/concept/java/lru-cache)
+- [LRU Cache on Wiki](https://en.wikipedia.org/wiki/Cache_replacement_policies)
diff --git a/src/data-structures/lru-cache/README.md b/src/data-structures/lru-cache/README.md
new file mode 100644
index 0000000000..eaa075f809
--- /dev/null
+++ b/src/data-structures/lru-cache/README.md
@@ -0,0 +1,54 @@
+# Least Recently Used (LRU) Cache
+
+_Read this in other languages:_
+[한국어](README.ko-KR.md),
+
+A **Least Recently Used (LRU) Cache** organizes items in order of use, allowing you to quickly identify which item hasn't been used for the longest amount of time.
+
+Picture a clothes rack, where clothes are always hung up on one side. To find the least-recently used item, look at the item on the other end of the rack.
+
+## The problem statement
+
+Implement the LRUCache class:
+
+- `LRUCache(int capacity)` Initialize the LRU cache with **positive** size `capacity`.
+- `int get(int key)` Return the value of the `key` if the `key` exists, otherwise return `undefined`.
+- `void set(int key, int value)` Update the value of the `key` if the `key` exists. Otherwise, add the `key-value` pair to the cache. If the number of keys exceeds the `capacity` from this operation, **evict** the least recently used key.
+
+The functions `get()` and `set()` must each run in `O(1)` average time complexity.
+
+## Implementation
+
+### Version 1: Doubly Linked List + Hash Map
+
+See the `LRUCache` implementation example in [LRUCache.js](./LRUCache.js). The solution uses a `HashMap` for fast `O(1)` (in average) cache items access, and a `DoublyLinkedList` for fast `O(1)` (in average) cache items promotions and eviction (to keep the maximum allowed cache capacity).
+
+![Linked List](./images/lru-cache.jpg)
+
+_Made with [okso.app](https://okso.app)_
+
+You may also find more test-case examples of how the LRU Cache works in [LRUCache.test.js](./__test__/LRUCache.test.js) file.
+
+### Version 2: Ordered Map
+
+The first implementation that uses doubly linked list is good for learning purposes and for better understanding of how the average `O(1)` time complexity is achievable while doing `set()` and `get()`.
+
+However, the simpler approach might be to use a JavaScript [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object. The `Map` object holds key-value pairs and **remembers the original insertion order** of the keys. We can use this fact in order to keep the recently-used items in the "end" of the map by removing and re-adding items. The item at the beginning of the `Map` is the first one to be evicted if cache capacity overflows. The order of the items may checked by using the `IterableIterator` like `map.keys()`.
+
+See the `LRUCacheOnMap` implementation example in [LRUCacheOnMap.js](./LRUCacheOnMap.js).
+
+You may also find more test-case examples of how the LRU Cache works in [LRUCacheOnMap.test.js](./__test__/LRUCacheOnMap.test.js) file.
+
+## Complexities
+
+|          | Average |
+| -------- | ------- |
+| Space    | `O(n)`  |
+| Get item | `O(1)`  |
+| Set item | `O(1)`  |
+
+## References
+
+- [LRU Cache on LeetCode](https://leetcode.com/problems/lru-cache/solutions/244744/lru-cache/)
+- [LRU Cache on InterviewCake](https://www.interviewcake.com/concept/java/lru-cache)
+- [LRU Cache on Wiki](https://en.wikipedia.org/wiki/Cache_replacement_policies)
diff --git a/src/data-structures/lru-cache/__test__/LRUCache.test.js b/src/data-structures/lru-cache/__test__/LRUCache.test.js
new file mode 100644
index 0000000000..438fd50378
--- /dev/null
+++ b/src/data-structures/lru-cache/__test__/LRUCache.test.js
@@ -0,0 +1,150 @@
+import LRUCache from '../LRUCache';
+
+describe('LRUCache', () => {
+  it('should set and get values to and from the cache', () => {
+    const cache = new LRUCache(100);
+    expect(cache.get('key-1')).toBeUndefined();
+
+    cache.set('key-1', 15);
+    cache.set('key-2', 16);
+    cache.set('key-3', 17);
+    expect(cache.get('key-1')).toBe(15);
+    expect(cache.get('key-2')).toBe(16);
+    expect(cache.get('key-3')).toBe(17);
+    expect(cache.get('key-3')).toBe(17);
+    expect(cache.get('key-2')).toBe(16);
+    expect(cache.get('key-1')).toBe(15);
+
+    cache.set('key-1', 5);
+    cache.set('key-2', 6);
+    cache.set('key-3', 7);
+    expect(cache.get('key-1')).toBe(5);
+    expect(cache.get('key-2')).toBe(6);
+    expect(cache.get('key-3')).toBe(7);
+  });
+
+  it('should evict least recently used items from cache with cache size of 1', () => {
+    const cache = new LRUCache(1);
+    expect(cache.get('key-1')).toBeUndefined();
+
+    cache.set('key-1', 15);
+    expect(cache.get('key-1')).toBe(15);
+
+    cache.set('key-2', 16);
+    expect(cache.get('key-1')).toBeUndefined();
+    expect(cache.get('key-2')).toBe(16);
+
+    cache.set('key-2', 17);
+    expect(cache.get('key-2')).toBe(17);
+
+    cache.set('key-3', 18);
+    cache.set('key-4', 19);
+    expect(cache.get('key-2')).toBeUndefined();
+    expect(cache.get('key-3')).toBeUndefined();
+    expect(cache.get('key-4')).toBe(19);
+  });
+
+  it('should evict least recently used items from cache with cache size of 2', () => {
+    const cache = new LRUCache(2);
+    expect(cache.get('key-21')).toBeUndefined();
+
+    cache.set('key-21', 15);
+    expect(cache.get('key-21')).toBe(15);
+
+    cache.set('key-22', 16);
+    expect(cache.get('key-21')).toBe(15);
+    expect(cache.get('key-22')).toBe(16);
+
+    cache.set('key-22', 17);
+    expect(cache.get('key-22')).toBe(17);
+
+    cache.set('key-23', 18);
+    expect(cache.size).toBe(2);
+    expect(cache.get('key-21')).toBeUndefined();
+    expect(cache.get('key-22')).toBe(17);
+    expect(cache.get('key-23')).toBe(18);
+
+    cache.set('key-24', 19);
+    expect(cache.size).toBe(2);
+    expect(cache.get('key-21')).toBeUndefined();
+    expect(cache.get('key-22')).toBeUndefined();
+    expect(cache.get('key-23')).toBe(18);
+    expect(cache.get('key-24')).toBe(19);
+  });
+
+  it('should evict least recently used items from cache with cache size of 3', () => {
+    const cache = new LRUCache(3);
+
+    cache.set('key-1', 1);
+    cache.set('key-2', 2);
+    cache.set('key-3', 3);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(3);
+
+    cache.set('key-3', 4);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(4);
+
+    cache.set('key-4', 5);
+    expect(cache.get('key-1')).toBeUndefined();
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(4);
+    expect(cache.get('key-4')).toBe(5);
+  });
+
+  it('should promote the node while calling set() method', () => {
+    const cache = new LRUCache(2);
+
+    cache.set('2', 1);
+    cache.set('1', 1);
+    cache.set('2', 3);
+    cache.set('4', 1);
+    expect(cache.get('1')).toBeUndefined();
+    expect(cache.get('2')).toBe(3);
+  });
+
+  it('should promote the recently accessed item with cache size of 3', () => {
+    const cache = new LRUCache(3);
+
+    cache.set('key-1', 1);
+    cache.set('key-2', 2);
+    cache.set('key-3', 3);
+    expect(cache.get('key-1')).toBe(1);
+
+    cache.set('key-4', 4);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-4')).toBe(4);
+    expect(cache.get('key-2')).toBeUndefined();
+  });
+
+  it('should promote the recently accessed item with cache size of 4', () => {
+    const cache = new LRUCache(4);
+
+    cache.set('key-1', 1);
+    cache.set('key-2', 2);
+    cache.set('key-3', 3);
+    cache.set('key-4', 4);
+    expect(cache.get('key-4')).toBe(4);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-1')).toBe(1);
+
+    cache.set('key-5', 5);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-4')).toBeUndefined();
+    expect(cache.get('key-5')).toBe(5);
+
+    cache.set('key-6', 6);
+    expect(cache.get('key-1')).toBeUndefined();
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-4')).toBeUndefined();
+    expect(cache.get('key-5')).toBe(5);
+    expect(cache.get('key-6')).toBe(6);
+  });
+});
diff --git a/src/data-structures/lru-cache/__test__/LRUCacheOnMap.test.js b/src/data-structures/lru-cache/__test__/LRUCacheOnMap.test.js
new file mode 100644
index 0000000000..8912a1199a
--- /dev/null
+++ b/src/data-structures/lru-cache/__test__/LRUCacheOnMap.test.js
@@ -0,0 +1,148 @@
+import LRUCache from '../LRUCacheOnMap';
+
+describe('LRUCacheOnMap', () => {
+  it('should set and get values to and from the cache', () => {
+    const cache = new LRUCache(100);
+    expect(cache.get('key-1')).toBeUndefined();
+
+    cache.set('key-1', 15);
+    cache.set('key-2', 16);
+    cache.set('key-3', 17);
+    expect(cache.get('key-1')).toBe(15);
+    expect(cache.get('key-2')).toBe(16);
+    expect(cache.get('key-3')).toBe(17);
+    expect(cache.get('key-3')).toBe(17);
+    expect(cache.get('key-2')).toBe(16);
+    expect(cache.get('key-1')).toBe(15);
+
+    cache.set('key-1', 5);
+    cache.set('key-2', 6);
+    cache.set('key-3', 7);
+    expect(cache.get('key-1')).toBe(5);
+    expect(cache.get('key-2')).toBe(6);
+    expect(cache.get('key-3')).toBe(7);
+  });
+
+  it('should evict least recently used items from cache with cache size of 1', () => {
+    const cache = new LRUCache(1);
+    expect(cache.get('key-1')).toBeUndefined();
+
+    cache.set('key-1', 15);
+    expect(cache.get('key-1')).toBe(15);
+
+    cache.set('key-2', 16);
+    expect(cache.get('key-1')).toBeUndefined();
+    expect(cache.get('key-2')).toBe(16);
+
+    cache.set('key-2', 17);
+    expect(cache.get('key-2')).toBe(17);
+
+    cache.set('key-3', 18);
+    cache.set('key-4', 19);
+    expect(cache.get('key-2')).toBeUndefined();
+    expect(cache.get('key-3')).toBeUndefined();
+    expect(cache.get('key-4')).toBe(19);
+  });
+
+  it('should evict least recently used items from cache with cache size of 2', () => {
+    const cache = new LRUCache(2);
+    expect(cache.get('key-21')).toBeUndefined();
+
+    cache.set('key-21', 15);
+    expect(cache.get('key-21')).toBe(15);
+
+    cache.set('key-22', 16);
+    expect(cache.get('key-21')).toBe(15);
+    expect(cache.get('key-22')).toBe(16);
+
+    cache.set('key-22', 17);
+    expect(cache.get('key-22')).toBe(17);
+
+    cache.set('key-23', 18);
+    expect(cache.get('key-21')).toBeUndefined();
+    expect(cache.get('key-22')).toBe(17);
+    expect(cache.get('key-23')).toBe(18);
+
+    cache.set('key-24', 19);
+    expect(cache.get('key-21')).toBeUndefined();
+    expect(cache.get('key-22')).toBeUndefined();
+    expect(cache.get('key-23')).toBe(18);
+    expect(cache.get('key-24')).toBe(19);
+  });
+
+  it('should evict least recently used items from cache with cache size of 3', () => {
+    const cache = new LRUCache(3);
+
+    cache.set('key-1', 1);
+    cache.set('key-2', 2);
+    cache.set('key-3', 3);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(3);
+
+    cache.set('key-3', 4);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(4);
+
+    cache.set('key-4', 5);
+    expect(cache.get('key-1')).toBeUndefined();
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(4);
+    expect(cache.get('key-4')).toBe(5);
+  });
+
+  it('should promote the node while calling set() method', () => {
+    const cache = new LRUCache(2);
+
+    cache.set('2', 1);
+    cache.set('1', 1);
+    cache.set('2', 3);
+    cache.set('4', 1);
+    expect(cache.get('1')).toBeUndefined();
+    expect(cache.get('2')).toBe(3);
+  });
+
+  it('should promote the recently accessed item with cache size of 3', () => {
+    const cache = new LRUCache(3);
+
+    cache.set('key-1', 1);
+    cache.set('key-2', 2);
+    cache.set('key-3', 3);
+    expect(cache.get('key-1')).toBe(1);
+
+    cache.set('key-4', 4);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-4')).toBe(4);
+    expect(cache.get('key-2')).toBeUndefined();
+  });
+
+  it('should promote the recently accessed item with cache size of 4', () => {
+    const cache = new LRUCache(4);
+
+    cache.set('key-1', 1);
+    cache.set('key-2', 2);
+    cache.set('key-3', 3);
+    cache.set('key-4', 4);
+    expect(cache.get('key-4')).toBe(4);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-1')).toBe(1);
+
+    cache.set('key-5', 5);
+    expect(cache.get('key-1')).toBe(1);
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-4')).toBeUndefined();
+    expect(cache.get('key-5')).toBe(5);
+
+    cache.set('key-6', 6);
+    expect(cache.get('key-1')).toBeUndefined();
+    expect(cache.get('key-2')).toBe(2);
+    expect(cache.get('key-3')).toBe(3);
+    expect(cache.get('key-4')).toBeUndefined();
+    expect(cache.get('key-5')).toBe(5);
+    expect(cache.get('key-6')).toBe(6);
+  });
+});
diff --git a/src/data-structures/lru-cache/images/lru-cache.jpg b/src/data-structures/lru-cache/images/lru-cache.jpg
new file mode 100644
index 0000000000..6a2fcfceb4
Binary files /dev/null and b/src/data-structures/lru-cache/images/lru-cache.jpg differ
diff --git a/src/data-structures/priority-queue/PriorityQueue.js b/src/data-structures/priority-queue/PriorityQueue.js
index 2bf27bb33a..0b283c2c7a 100644
--- a/src/data-structures/priority-queue/PriorityQueue.js
+++ b/src/data-structures/priority-queue/PriorityQueue.js
@@ -1,52 +1,59 @@
 import MinHeap from '../heap/MinHeap';
 import Comparator from '../../utils/comparator/Comparator';
 
-// It is the same as min heap except that when comparing to elements
-// we take into account not element's value but rather its priority.
+// It is the same as min heap except that when comparing two elements
+// we take into account its priority instead of the element's value.
 export default class PriorityQueue extends MinHeap {
   constructor() {
+    // Call MinHip constructor first.
     super();
-    this.priorities = {};
+
+    // Setup priorities map.
+    this.priorities = new Map();
+
+    // Use custom comparator for heap elements that will take element priority
+    // instead of element value into account.
     this.compare = new Comparator(this.comparePriority.bind(this));
   }
 
   /**
-   * @param {*} item
-   * @param {number} [priority]
+   * Add item to the priority queue.
+   * @param {*} item - item we're going to add to the queue.
+   * @param {number} [priority] - items priority.
    * @return {PriorityQueue}
    */
   add(item, priority = 0) {
-    this.priorities[item] = priority;
+    this.priorities.set(item, priority);
     super.add(item);
-
     return this;
   }
 
   /**
-   * @param {*} item
-   * @param {Comparator} [customFindingComparator]
+   * Remove item from priority queue.
+   * @param {*} item - item we're going to remove.
+   * @param {Comparator} [customFindingComparator] - custom function for finding the item to remove
    * @return {PriorityQueue}
    */
   remove(item, customFindingComparator) {
     super.remove(item, customFindingComparator);
-    delete this.priorities[item];
-
+    this.priorities.delete(item);
     return this;
   }
 
   /**
-   * @param {*} item
-   * @param {number} priority
+   * Change priority of the item in a queue.
+   * @param {*} item - item we're going to re-prioritize.
+   * @param {number} priority - new item's priority.
    * @return {PriorityQueue}
    */
   changePriority(item, priority) {
     this.remove(item, new Comparator(this.compareValue));
     this.add(item, priority);
-
     return this;
   }
 
   /**
+   * Find item by ite value.
    * @param {*} item
    * @return {Number[]}
    */
@@ -55,6 +62,7 @@ export default class PriorityQueue extends MinHeap {
   }
 
   /**
+   * Check if item already exists in a queue.
    * @param {*} item
    * @return {boolean}
    */
@@ -63,19 +71,20 @@ export default class PriorityQueue extends MinHeap {
   }
 
   /**
+   * Compares priorities of two items.
    * @param {*} a
    * @param {*} b
    * @return {number}
    */
   comparePriority(a, b) {
-    if (this.priorities[a] === this.priorities[b]) {
+    if (this.priorities.get(a) === this.priorities.get(b)) {
       return 0;
     }
-
-    return this.priorities[a] < this.priorities[b] ? -1 : 1;
+    return this.priorities.get(a) < this.priorities.get(b) ? -1 : 1;
   }
 
   /**
+   * Compares values of two items.
    * @param {*} a
    * @param {*} b
    * @return {number}
@@ -84,7 +93,6 @@ export default class PriorityQueue extends MinHeap {
     if (a === b) {
       return 0;
     }
-
     return a < b ? -1 : 1;
   }
 }
diff --git a/src/data-structures/priority-queue/README.fr-FR.md b/src/data-structures/priority-queue/README.fr-FR.md
new file mode 100644
index 0000000000..76a5f895dd
--- /dev/null
+++ b/src/data-structures/priority-queue/README.fr-FR.md
@@ -0,0 +1,22 @@
+# File de priorité
+
+En informatique, une **file de priorité** est un type
+de données abstrait qui s'apparente à une file d'attente normale
+ou une structure de données empilées, mais où chaque élément est
+en plus associé à une "priorité".
+Dans une file de priorité, un élément avec une priorité élevée
+est servi avant un élément à faible priorité. Si deux éléments ont
+la même priorité, ils sont servis selon leur ordre dans la file
+d'attente.
+
+Alors que les files de priorité sont souvent implémentées avec des tas,
+elles sont conceptuellement distinctes des tas. Une file de priorité
+est un concept abstrait comme "une liste" ou "une carte"; tout comme
+une liste peut être implémentée avec une liste chaînée ou un tableau,
+une file de priorité peut être implémentée avec un tas ou une variété
+d'autres méthodes telles qu'un tableau non ordonné.
+
+## Références
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/File_de_priorit%C3%A9)
+- [YouTube](https://www.youtube.com/watch?v=wptevk0bshY&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=6)
diff --git a/src/data-structures/priority-queue/README.ja-JP.md b/src/data-structures/priority-queue/README.ja-JP.md
new file mode 100644
index 0000000000..0154034871
--- /dev/null
+++ b/src/data-structures/priority-queue/README.ja-JP.md
@@ -0,0 +1,10 @@
+# 優先度付きキュー
+
+コンピュータサイエンスにおいて、**優先度付きキュー**は通常のキューやスタックのデータ構造と似た抽象データ型ですが、各要素に「優先度」が関連づけられています。優先度付きキューでは優先度の高い要素が優先度の低い要素よりも先に処理されます。もし2つの要素が同じ優先度だった場合、それらはキュー内の順序に従って処理されます。
+
+優先度付きキューは多くの場合ヒープによって実装されていますが、概念的にはヒープとは異なります。優先度付きキューは「リスト」や「マップ」のような抽象的な概念です。リストがリンクリストや配列で実装できるのと同様に、優先度付きキューはヒープや未ソート配列のような様々な方法で実装することができます。
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Priority_queue)
+- [YouTube](https://www.youtube.com/watch?v=wptevk0bshY&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=6)
diff --git a/src/data-structures/priority-queue/README.ko-KR.md b/src/data-structures/priority-queue/README.ko-KR.md
new file mode 100644
index 0000000000..50a064d05a
--- /dev/null
+++ b/src/data-structures/priority-queue/README.ko-KR.md
@@ -0,0 +1,12 @@
+# 우선 순위 큐 
+
+컴퓨터 과학에서 **우선 순위 큐**는 일반 큐 또는 스택 데이터 구조와 같은 추상 데이터 유형이지만, 여기서 각 요소에는 "우선 순위"가 연결됩니다. 
+우선 순위 큐에서는 우선 순위가 높은 요소가 낮은 요소 앞에 제공됩니다. 두 요소가 동일한 우선 순위를 가질 경우 큐의 순서에 따라 제공됩니다.
+
+우선 순위 큐는 종종 힙을 사용하여 구현되지만 개념적으로는 힙과 구별됩니다. 우선 순위 대기열은 "리스트(list)" 또는 "맵(map)"과 같은 추상적인 개념입니다; 
+리스트가 링크드 리스트나 배열로 구현될 수 있는 것처럼 우선 순위 큐는 힙이나 정렬되지 않은 배열과 같은 다양한 다른 방법으로 구현될 수 있습니다.
+
+## 참조
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Priority_queue)
+- [YouTube](https://www.youtube.com/watch?v=wptevk0bshY&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=6)
diff --git a/src/data-structures/priority-queue/README.md b/src/data-structures/priority-queue/README.md
index 9704f7b225..6d0901960d 100644
--- a/src/data-structures/priority-queue/README.md
+++ b/src/data-structures/priority-queue/README.md
@@ -1,17 +1,26 @@
 # Priority Queue
 
-In computer science, a **priority queue** is an abstract data type 
-which is like a regular queue or stack data structure, but where 
-additionally each element has a "priority" associated with it. 
-In a priority queue, an element with high priority is served before 
-an element with low priority. If two elements have the same 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Українська_](README.uk-UA.md)
+
+In computer science, a **priority queue** is an abstract data type
+which is like a regular queue or stack data structure, but where
+additionally each element has a "priority" associated with it.
+In a priority queue, an element with high priority is served before
+an element with low priority. If two elements have the same
 priority, they are served according to their order in the queue.
 
-While priority queues are often implemented with heaps, they are 
-conceptually distinct from heaps. A priority queue is an abstract 
+While priority queues are often implemented with heaps, they are
+conceptually distinct from heaps. A priority queue is an abstract
 concept like "a list" or "a map"; just as a list can be implemented
 with a linked list or an array, a priority queue can be implemented
-with a heap or a variety of other methods such as an unordered 
+with a heap or a variety of other methods such as an unordered
 array.
 
 ## References
diff --git a/src/data-structures/priority-queue/README.pt-BR.md b/src/data-structures/priority-queue/README.pt-BR.md
new file mode 100644
index 0000000000..e6d84e263a
--- /dev/null
+++ b/src/data-structures/priority-queue/README.pt-BR.md
@@ -0,0 +1,23 @@
+# Fila de Prioridade (Priority Queue)
+
+Na ciência da computação, uma **fila de prioridade** é um tipo de estrutura de
+dados abastrata que é como uma fila regular (regular queue) ou estrutura de
+dados de pilha (stack), mas adicionalmente cada elemento possui uma
+"prioridade" associada.
+
+Em uma fila de prioridade, um elemento com uma prioridade alta é servido
+antes de um elemento com baixa prioridade. Caso dois elementos posusam a
+mesma prioridade, eles serão servidos de acordo com sua ordem na fila.
+
+Enquanto as filas de prioridade são frequentemente implementadas com
+pilhas (stacks), elas são conceitualmente distintas das pilhas (stacks).
+A fila de prioridade é um conceito abstrato como uma "lista" (list) ou
+um "mapa" (map); assim como uma lista pode ser implementada com uma
+lista encadeada (liked list) ou um array, a fila de prioridade pode ser
+implementada com uma pilha (stack) ou com uma variedade de outros métodos,
+como um array não ordenado (unordered array).
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Priority_queue)
+- [YouTube](https://www.youtube.com/watch?v=wptevk0bshY&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=6)
diff --git a/src/data-structures/priority-queue/README.ru-RU.md b/src/data-structures/priority-queue/README.ru-RU.md
new file mode 100644
index 0000000000..7ce0c1f496
--- /dev/null
+++ b/src/data-structures/priority-queue/README.ru-RU.md
@@ -0,0 +1,22 @@
+# Очередь с приоритетом
+
+Очередь с приоритетом (англ. priority queue) — абстрактный тип данных в информатике,
+для каждого элемента которого можно вычислить его приоритет.
+
+В очереди с приоритетами элемент с высоким приоритетом обслуживается раньше
+элемента с низким приоритетом. Если два элемента имеют одинаковый приоритет, они
+обслуживаются в соответствии с их порядком в очереди.
+
+Очередь с приоритетом поддерживает две обязательные операции — добавить элемент и
+извлечь максимум(минимум).
+
+Хотя приоритетные очереди часто реализуются в виде куч(heaps), они
+концептуально отличаются от куч. Очередь приоритетов является абстрактной
+концепцией вроде «списка» или «карты»; так же, как список может быть реализован
+в виде связного списка или массива, так и очередь с приоритетом может быть реализована
+в виде кучи или множеством других методов, например в виде неупорядоченного массива.
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_%D1%81_%D0%BF%D1%80%D0%B8%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82%D0%BE%D0%BC_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5))
+- [YouTube](https://www.youtube.com/watch?v=y_2toG5-j_M)
diff --git a/src/data-structures/priority-queue/README.uk-UA.md b/src/data-structures/priority-queue/README.uk-UA.md
new file mode 100644
index 0000000000..2f0454479e
--- /dev/null
+++ b/src/data-structures/priority-queue/README.uk-UA.md
@@ -0,0 +1,21 @@
+# Черга з пріоритетом
+
+Черга з пріоритетом (англ. priority queue) - абстрактний тип даних в інформатиці,
+для кожного елемента якого можна визначити його пріоритет.
+
+У черзі з пріоритетами елемент із високим пріоритетом обслуговується раніше
+елемент з низьким пріоритетом. Якщо два елементи мають однаковий пріоритет, вони
+обслуговуються відповідно до їх порядку в черзі.
+
+Черга з пріоритетом підтримує дві обов'язкові операції – додати елемент та
+витягти максимум (мінімум).
+
+Хоча пріоритетні черги часто реалізуються у вигляді куп (heaps), вони
+концептуально відрізняються від куп. Черга пріоритетів є абстрактною
+концепцією на кшталт «списку» чи «карти»; так само, як список може бути реалізований
+у вигляді зв'язкового списку або масиву, так і черга з пріоритетом може бути реалізована
+у вигляді купи або безліччю інших методів, наприклад, у вигляді невпорядкованого масиву.
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%A7%D0%B5%D1%80%D0%B3%D0%B0_%D0%B7_%D0%BF%D1%80%D1%96%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D1%82%D0%BE%D0%BC)
diff --git a/src/data-structures/priority-queue/README.zh-CN.md b/src/data-structures/priority-queue/README.zh-CN.md
new file mode 100644
index 0000000000..caffcd7136
--- /dev/null
+++ b/src/data-structures/priority-queue/README.zh-CN.md
@@ -0,0 +1,15 @@
+# 优先队列
+
+在计算机科学中,  **优先级队列(priority queue)** 是一种抽象数据类型, 它类似于常规的队列或栈, 但每个元素都有与之关联的“优先级”。
+
+在优先队列中, 低优先级的元素之前前面应该是高优先级的元素。 如果两个元素具有相同的优先级, 则根据它们在队列中的顺序是它们的出现顺序即可。
+
+优先队列虽通常用堆来实现,但它在概念上与堆不同。优先队列是一个抽象概念，就像“列表”或“图”这样的抽象概念一样; 
+
+正如列表可以用链表或数组实现一样，优先队列可以用堆或各种其他方法实现,例如无序数组。
+
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Priority_queue)
+- [YouTube](https://www.youtube.com/watch?v=wptevk0bshY&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=6)
diff --git a/src/data-structures/priority-queue/__test__/PriorityQueue.test.js b/src/data-structures/priority-queue/__test__/PriorityQueue.test.js
index 264893d393..2ffe4acb74 100644
--- a/src/data-structures/priority-queue/__test__/PriorityQueue.test.js
+++ b/src/data-structures/priority-queue/__test__/PriorityQueue.test.js
@@ -20,6 +20,23 @@ describe('PriorityQueue', () => {
     expect(priorityQueue.peek()).toBe(100);
   });
 
+  it('should be possible to use objects in priority queue', () => {
+    const priorityQueue = new PriorityQueue();
+
+    const user1 = { name: 'Mike' };
+    const user2 = { name: 'Bill' };
+    const user3 = { name: 'Jane' };
+
+    priorityQueue.add(user1, 1);
+    expect(priorityQueue.peek()).toBe(user1);
+
+    priorityQueue.add(user2, 2);
+    expect(priorityQueue.peek()).toBe(user1);
+
+    priorityQueue.add(user3, 0);
+    expect(priorityQueue.peek()).toBe(user3);
+  });
+
   it('should poll from queue with respect to priorities', () => {
     const priorityQueue = new PriorityQueue();
 
@@ -34,7 +51,7 @@ describe('PriorityQueue', () => {
     expect(priorityQueue.poll()).toBe(5);
   });
 
-  it('should be possible to change priority of internal nodes', () => {
+  it('should be possible to change priority of head node', () => {
     const priorityQueue = new PriorityQueue();
 
     priorityQueue.add(10, 1);
@@ -42,6 +59,8 @@ describe('PriorityQueue', () => {
     priorityQueue.add(100, 0);
     priorityQueue.add(200, 0);
 
+    expect(priorityQueue.peek()).toBe(100);
+
     priorityQueue.changePriority(100, 10);
     priorityQueue.changePriority(10, 20);
 
@@ -51,7 +70,7 @@ describe('PriorityQueue', () => {
     expect(priorityQueue.poll()).toBe(10);
   });
 
-  it('should be possible to change priority of head node', () => {
+  it('should be possible to change priority of internal nodes', () => {
     const priorityQueue = new PriorityQueue();
 
     priorityQueue.add(10, 1);
@@ -59,6 +78,8 @@ describe('PriorityQueue', () => {
     priorityQueue.add(100, 0);
     priorityQueue.add(200, 0);
 
+    expect(priorityQueue.peek()).toBe(100);
+
     priorityQueue.changePriority(200, 10);
     priorityQueue.changePriority(10, 20);
 
diff --git a/src/data-structures/queue/Queue.js b/src/data-structures/queue/Queue.js
index 37d292af52..64e646fc00 100644
--- a/src/data-structures/queue/Queue.js
+++ b/src/data-structures/queue/Queue.js
@@ -2,10 +2,10 @@ import LinkedList from '../linked-list/LinkedList';
 
 export default class Queue {
   constructor() {
-    // We're going to implement Queue based on LinkedList since this
-    // structures a quite similar. Namely they both operates mostly with
-    // with theirs beginning and the end. Compare enqueue/de-queue
-    // operations of the Queue with append/prepend operations of LinkedList.
+    // We're going to implement Queue based on LinkedList since the two
+    // structures are quite similar. Namely, they both operate mostly on
+    // the elements at the beginning and the end. Compare enqueue/dequeue
+    // operations of Queue with append/deleteHead operations of LinkedList.
     this.linkedList = new LinkedList();
   }
 
@@ -13,39 +13,36 @@ export default class Queue {
    * @return {boolean}
    */
   isEmpty() {
-    // The queue is empty in case if its linked list don't have tail.
-    return !this.linkedList.tail;
+    return !this.linkedList.head;
   }
 
   /**
+   * Read the element at the front of the queue without removing it.
    * @return {*}
    */
   peek() {
-    if (!this.linkedList.head) {
-      // If linked list is empty then there is nothing to peek from.
+    if (this.isEmpty()) {
       return null;
     }
 
-    // Just read the value from the end of linked list without deleting it.
     return this.linkedList.head.value;
   }
 
   /**
+   * Add a new element to the end of the queue (the tail of the linked list).
+   * This element will be processed after all elements ahead of it.
    * @param {*} value
    */
   enqueue(value) {
-    // Enqueueing means to stand in the line. Therefore let's just add
-    // new value at the beginning of the linked list. It will need to wait
-    // until all previous nodes will be processed.
     this.linkedList.append(value);
   }
 
   /**
+   * Remove the element at the front of the queue (the head of the linked list).
+   * If the queue is empty, return null.
    * @return {*}
    */
   dequeue() {
-    // Let's try to delete the last node from linked list (the tail).
-    // If there is no tail in linked list (it is empty) just return null.
     const removedHead = this.linkedList.deleteHead();
     return removedHead ? removedHead.value : null;
   }
diff --git a/src/data-structures/queue/README.fr-FR.md b/src/data-structures/queue/README.fr-FR.md
new file mode 100644
index 0000000000..33d327744f
--- /dev/null
+++ b/src/data-structures/queue/README.fr-FR.md
@@ -0,0 +1,30 @@
+# File
+
+En informatique, une **file**, aussi appelée file d'attente, est
+sorte particulière de structure de données abstraite dans lequel
+les entités de la collection sont conservées dans l'ordre et les
+opérations principales sur la collection sont le résultat de l'ajout
+d'entités à la position terminale arrière, connue sous le nom de mise
+en file d'attente ("enqueue"), et de la suppression des entités de la
+position terminale avant, appelée retrait de la file d'attente ("dequeu").
+
+Cela fait de la file d'attente une structure de données PEPS (premier entré,
+premier sorti), en anglais FIFO (first in, first out). Dans une structure de données
+PEPS, le premier élément ajouté à la file d'attente sera le premier à être
+supprimé. Cela équivaut à l'exigence qu'une fois qu'un nouvel élément est
+ajouté, tous les éléments qui ont été ajoutés auparavant doivent être supprimés
+avant que le nouvel élément ne puisse être supprimé. Souvent, une opération d'aperçu
+ou de front est également intégrée, renvoyant la valeur de l'élément avant
+sans le retirer de la file d'attente. Une file d'attente est un exemple de
+structure de données linéaire, ou, plus abstraitement, une collection séquentielle.
+
+Représentation d'une file PEPS (premier entré, premier sorti)
+
+![Queue](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Références
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/File_(structure_de_donn%C3%A9es))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/queue/README.ja-JP.md b/src/data-structures/queue/README.ja-JP.md
new file mode 100644
index 0000000000..1ffa9c1a68
--- /dev/null
+++ b/src/data-structures/queue/README.ja-JP.md
@@ -0,0 +1,14 @@
+# キュー
+
+コンピュータサイエンスにおいて、**キュー**は特定の種類の抽象データ型またはコレクションです。コレクションの中のエンティティは順番に並べられており、コレクションに対する基本的な（または唯一の)操作は末尾にエンティティを追加するエンキューと、先頭からエンティティを削除するデキューがあります。これにより、キューは先入れ先出し(FIFO)のデータ構造となります。FIFOのデータ構造では、キューに追加された最初の要素が最初に削除されます。これは、新しい要素が追加されたら、その要素を削除するにはそれまでに追加された全ての要素が削除されなければならないという要件と同じです。多くの場合、ピークのような先頭の要素を検査する操作も備えていて、これはデキューせずに先頭の要素の値を返します。キューは線形のデータ構造や、より抽象的なシーケンシャルなコレクションの一例です。
+
+FIFO(先入れ先出し)のキュー
+
+![Queue](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/queue/README.ko-KR.md b/src/data-structures/queue/README.ko-KR.md
new file mode 100644
index 0000000000..606c8ff35e
--- /dev/null
+++ b/src/data-structures/queue/README.ko-KR.md
@@ -0,0 +1,23 @@
+# Queue
+
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md)
+
+컴퓨터 공학에서 **큐**는 일종의 추상 데이터 타입이자 컬렉션입니다. 큐 내부의 엔터티들은 순서를 유지하며 컬렉션의 가장 뒷 부분에 엔터티를 추가하는 인큐(enqueue), 컬렉션의 가장 앞에 위치한 엔터티를 제거하는 디큐(dequeue) 작업을 수행합니다. 이것은 큐를 선입선출 자료 구조로 만듭니다. 선입선출 자료 구조에서는, 추가된 첫 번째 요소가 가장 먼저 제거되는 요소가 됩니다. 이는 새로운 요소가 추가되면 이전에 추가되었던 모든 요소들을 제거해야 새로운 요소를 제거할 수 있다는것과 같은 의미입니다. 또한 큐의 가장 앞에 위치한 요소를 반환하기 위한 작업이 입력되면 디큐 작업 없이 해당 요소를 반환합니다.
+
+큐는 선형 자료 구조의 예시이며, 더 추상적으로는 순차적인 컬렉션입니다.
+
+선입선출 자료 구조인 큐를 나타내면 다음과 같습니다.
+
+![Queue](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 참고
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/queue/README.md b/src/data-structures/queue/README.md
index 3b6895f661..d8e0288b42 100644
--- a/src/data-structures/queue/README.md
+++ b/src/data-structures/queue/README.md
@@ -1,24 +1,35 @@
 # Queue
 
-In computer science, a **queue** is a particular kind of abstract data 
-type or collection in which the entities in the collection are 
-kept in order and the principle (or only) operations on the 
-collection are the addition of entities to the rear terminal 
-position, known as enqueue, and removal of entities from the 
-front terminal position, known as dequeue. This makes the queue 
-a First-In-First-Out (FIFO) data structure. In a FIFO data 
-structure, the first element added to the queue will be the 
-first one to be removed. This is equivalent to the requirement 
-that once a new element is added, all elements that were added 
-before have to be removed before the new element can be removed. 
-Often a peek or front operation is also entered, returning the 
-value of the front element without dequeuing it. A queue is an 
-example of a linear data structure, or more abstractly a 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Українська_](README.uk-UA.md)
+
+In computer science, a **queue** is a particular kind of abstract data
+type or collection in which the entities in the collection are
+kept in order and the principle (or only) operations on the
+collection are the addition of entities to the rear terminal
+position, known as enqueue, and removal of entities from the
+front terminal position, known as dequeue. This makes the queue
+a First-In-First-Out (FIFO) data structure. In a FIFO data
+structure, the first element added to the queue will be the
+first one to be removed. This is equivalent to the requirement
+that once a new element is added, all elements that were added
+before have to be removed before the new element can be removed.
+Often a peek or front operation is also entered, returning the
+value of the front element without dequeuing it. A queue is an
+example of a linear data structure, or more abstractly a
 sequential collection.
 
 Representation of a FIFO (first in, first out) queue
 
-![Queue](https://upload.wikimedia.org/wikipedia/commons/5/52/Data_Queue.svg)
+![Queue](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
 
 ## References
 
diff --git a/src/data-structures/queue/README.pt-BR.md b/src/data-structures/queue/README.pt-BR.md
new file mode 100644
index 0000000000..9464864120
--- /dev/null
+++ b/src/data-structures/queue/README.pt-BR.md
@@ -0,0 +1,30 @@
+# Fila (Queue)
+
+Na ciência da computação, uma **fila** é um tipo particular de abstração
+de tipo de dado ou coleção em que as entidades na coleção são mantidas em
+ordem e a causa primária (ou única) de operações na coleção são a
+adição de entidades à posição final da coleção, conhecido como enfileiramento
+(enqueue) e a remoção de entidades do posição inicial, conhecida como desenfileirar
+(dequeue).Isto torna a fila uma estrutura de dados tipo First-In-First-Out (FIFO).
+
+Em uma estrutura de dados FIFO, o primeiro elemento adicionado a fila
+será o primeiro a ser removido. Isso é equivalente ao requisito em que uma vez
+que um novo elemento é adicionado, todos os elementos que foram adicionados
+anteriormente devem ser removidos antes que o novo elemento possa ser removido.
+
+Muitas vezes uma espiada (peek) ou uma operação de frente é iniciada,
+retornando o valor do elemento da frente, sem desenfileira-lo. Uma lista é
+um exemplo de uma estrutura de dados linear, ou mais abstratamente uma
+coleção seqüencial.
+
+
+Representação de uma file FIFO (first in, first out)
+
+![Queue](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## References
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/queue/README.ru-RU.md b/src/data-structures/queue/README.ru-RU.md
new file mode 100644
index 0000000000..1f9e5ff6f1
--- /dev/null
+++ b/src/data-structures/queue/README.ru-RU.md
@@ -0,0 +1,21 @@
+# Очередь
+
+Очередь (англ. queue) - структура данных в информатике, в которой элементы
+хранятся в порядке их добавления. Добавление новых элементов(enqueue)
+осуществляется в конец списка. А удаление элементов (dequeue)
+осуществляется с начала. Таким образом очередь реализует принцип
+"первым вошёл - первым вышел" (FIFO). Часто реализуется операция чтения
+головного элемента (peek), которая возвращает первый в очереди элемент,
+при этом не удаляя его. Очередь является примером линейной структуры
+данных или последовательной коллекции.
+
+Иллюстрация работы с очередью.
+
+![Очередь](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## References
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5))
+- [YouTube](https://www.youtube.com/watch?v=GRsVMTlBIoE)
diff --git a/src/data-structures/queue/README.uk-UA.md b/src/data-structures/queue/README.uk-UA.md
new file mode 100644
index 0000000000..7934c7571a
--- /dev/null
+++ b/src/data-structures/queue/README.uk-UA.md
@@ -0,0 +1,21 @@
+# Черга
+
+Черга (англ. queue) – структура даних в інформатиці, в якій елементи
+зберігаються у порядку їх додавання. Додавання нових елементів(enqueue)
+здійснюється на кінець списку. А видалення елементів (dequeue)
+здійснюється із початку. Таким чином черга реалізує принцип
+"першим увійшов – першим вийшов" (FIFO). Часто реалізується операція читання
+головного елемента (peek), яка повертає перший у черзі елемент,
+при цьому не видаляючи його. Черга є прикладом лінійної структури
+даних чи послідовної колекції.
+
+Ілюстрація роботи з чергою.
+
+![Черга](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Список літератури
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%A7%D0%B5%D1%80%D0%B3%D0%B0_(%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B4%D0%B0%D0%BD%D0%B8%D1%85))
+- [YouTube](https://www.youtube.com/watch?v=ll4QLNSPn60)
diff --git a/src/data-structures/queue/README.vi-VN.md b/src/data-structures/queue/README.vi-VN.md
new file mode 100644
index 0000000000..84b3bdbfa6
--- /dev/null
+++ b/src/data-structures/queue/README.vi-VN.md
@@ -0,0 +1,22 @@
+# Hàng đợi (Queue)
+
+_Đọc bằng ngôn ngữ khác:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Українська_](README.uk-UA.md)
+
+Trong khoa học máy tính, một **hàng đợi** là một loại cụ thể của kiểu dữ liệu trừu tượng hoặc bộ sưu tập trong đó các phần tử trong bộ sưu tập được giữ theo thứ tự và nguyên tắc (hoặc chỉ) các hoạt động trên bộ sưu tập là thêm các phần tử vào vị trí cuối cùng, được gọi là đưa vào hàng đợi (enqueue), và loại bỏ các phần tử từ vị trí đầu tiên, được gọi là đưa ra khỏi hàng đợi (dequeue). Điều này khiến cho hàng đợi trở thành một cấu trúc dữ liệu First-In-First-Out (FIFO). Trong cấu trúc dữ liệu FIFO, phần tử đầu tiên được thêm vào hàng đợi sẽ là phần tử đầu tiên được loại bỏ. Điều này tương đương với yêu cầu rằng sau khi một phần tử mới được thêm vào, tất cả các phần tử đã được thêm vào trước đó phải được loại bỏ trước khi có thể loại bỏ phần tử mới. Thường thì cũng có thêm một hoạt động nhìn hay lấy phần đầu, trả về giá trị của phần tử đầu tiên mà không loại bỏ nó. Hàng đợi là một ví dụ về cấu trúc dữ liệu tuyến tính, hoặc trừu tượng hơn là một bộ sưu tập tuần tự.
+
+Hàng đợi FIFO (First-In-First-Out) có thể được biểu diễn như sau:
+
+![Queue](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Tham Khảo
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/queue/README.zh-CN.md b/src/data-structures/queue/README.zh-CN.md
new file mode 100644
index 0000000000..af608107d3
--- /dev/null
+++ b/src/data-structures/queue/README.zh-CN.md
@@ -0,0 +1,17 @@
+# 队列
+
+在计算机科学中, 一个 **队列(queue)** 是一种特殊类型的抽象数据类型或集合。集合中的实体按顺序保存。
+
+队列基本操作有两种：入队和出队。从队列的后端位置添加实体，称为入队；从队列的前端位置移除实体，称为出队。
+
+
+队列中元素先进先出 FIFO (first in, first out)的示意
+
+![Queue](./images/queue.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/queue/__test__/Queue.test.js b/src/data-structures/queue/__test__/Queue.test.js
index 8e673a611b..a0b2cd5b2b 100644
--- a/src/data-structures/queue/__test__/Queue.test.js
+++ b/src/data-structures/queue/__test__/Queue.test.js
@@ -22,7 +22,7 @@ describe('Queue', () => {
     queue.enqueue({ value: 'test1', key: 'key1' });
     queue.enqueue({ value: 'test2', key: 'key2' });
 
-    const stringifier = value => `${value.key}:${value.value}`;
+    const stringifier = (value) => `${value.key}:${value.value}`;
 
     expect(queue.toString(stringifier)).toBe('key1:test1,key2:test2');
     expect(queue.dequeue().value).toBe('test1');
diff --git a/src/data-structures/queue/images/queue.jpeg b/src/data-structures/queue/images/queue.jpeg
new file mode 100644
index 0000000000..579f4d8c83
Binary files /dev/null and b/src/data-structures/queue/images/queue.jpeg differ
diff --git a/src/data-structures/stack/README.fr-FR.md b/src/data-structures/stack/README.fr-FR.md
new file mode 100644
index 0000000000..0d6c388640
--- /dev/null
+++ b/src/data-structures/stack/README.fr-FR.md
@@ -0,0 +1,30 @@
+# Pile
+
+En informatique, une **pile** est un type de données abstrait
+qui sert de collection d'éléments, avec deux opérations principales:
+
+* **empiler** (en anglais *push*), qui ajoute un élément à la collection, et
+* **dépiler** (en anglais *pop*), qui supprime l'élément le plus récemment
+ajouté qui n'a pas encore été supprimé.
+
+L'ordre dans lequel les éléments sortent d'une pile donne
+lieu à son nom alternatif, LIFO ("last in, first out",
+littéralement "dernier arrivé, premier sorti"). En outre,
+une opération d'aperçu peut donner accès au sommet sans
+modifier la pile. Le nom "pile" pour ce type de structure
+vient de l'analogie avec un ensemble d'éléments physiques empilés
+les uns sur les autres, ce qui permet de retirer facilement un
+élément du haut de la pile, tout comme accéder à un élément plus
+profond dans le la pile peut nécessiter de retirer plusieurs
+autres articles en premier.
+
+Représentation simple de l'éxecution d'une pile avec des opérations empiler (push) et dépiler (pop).
+
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Références
+
+- [Wikipedia](https://fr.wikipedia.org/wiki/Pile_(informatique))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/stack/README.ja-JP.md b/src/data-structures/stack/README.ja-JP.md
new file mode 100644
index 0000000000..55ca25206c
--- /dev/null
+++ b/src/data-structures/stack/README.ja-JP.md
@@ -0,0 +1,19 @@
+# スタック
+
+コンピュータサイエンスにおいて、**スタック**は抽象データ型で、2つの主要な操作ができる要素のコレクションです。
+
+* **プッシュ**はコレクションに要素を追加します。
+* **ポップ**は最近追加された要素でまだ削除されていないものを削除します。
+
+要素がスタックから外れる順番から、LIFO(後入れ先出し)とも呼ばれます。スタックに変更を加えることなく、先頭の要素を検査するピーク操作を備えることもあります。「スタック」という名前は、物理的な物を上に積み重ねていく様子との類似性に由来しています。一番上の物を取ることは簡単ですが、スタックの下の方にあるものを取るときは先に上にある複数の物を取り除く必要があります。
+
+プッシュとポップの例
+
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/stack/README.ko-KR.md b/src/data-structures/stack/README.ko-KR.md
new file mode 100644
index 0000000000..c36a472e66
--- /dev/null
+++ b/src/data-structures/stack/README.ko-KR.md
@@ -0,0 +1,26 @@
+# 스택
+
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md)
+
+컴퓨터 과학에서, **스택**은 아래의 두가지 연산을 가진 요소들의 집합인 추상 자료형입니다.
+
+* **push**는 집합에 요소를 추가하는 것이며,
+* **pop**은 아직 제거되지 않은 가장 최근에 추가된 요소를 제거하는 연산입니다.
+
+요소가 스택에서 나오는 과정은 LIFO (last in, first out)라는 이름으로 확인할 수 있습니다. 추가적으로, peek 연산은 스택을 수정하지 않고 최상단의 요소에 접근할 수 있게 해줍니다. 이런 자료구조의 "스택"이라는 이름은 실제 물건들이 다른 물건들의 위에 쌓이게 되는 것에서 유추되었습니다. 스택의 최상단의 물건은 빼내기 쉽지만 깊이 있는 물건을 빼내려면 다른 물건들을 먼저 빼내야 하는게 필요합니다.
+
+다음은 push와 pop 연산을 실행하는 간단한 스택의 실행입니다.
+
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 참조
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/stack/README.md b/src/data-structures/stack/README.md
index efec55bcb5..612f926736 100644
--- a/src/data-structures/stack/README.md
+++ b/src/data-structures/stack/README.md
@@ -1,23 +1,34 @@
 # Stack
 
-In computer science, a **stack** is an abstract data type that serves 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Français_](README.fr-FR.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Українська_](README.uk-UA.md)
+
+In computer science, a **stack** is an abstract data type that serves
 as a collection of elements, with two principal operations:
 
 * **push**, which adds an element to the collection, and
 * **pop**, which removes the most recently added element that was not yet removed.
 
-The order in which elements come off a stack gives rise to its 
-alternative name, LIFO (last in, first out). Additionally, a 
-peek operation may give access to the top without modifying 
-the stack. The name "stack" for this type of structure comes 
-from the analogy to a set of physical items stacked on top of 
-each other, which makes it easy to take an item off the top 
-of the stack, while getting to an item deeper in the stack 
-may require taking off multiple other items first
+The order in which elements come off a stack gives rise to its
+alternative name, LIFO (last in, first out). Additionally, a
+peek operation may give access to the top without modifying
+the stack. The name "stack" for this type of structure comes
+from the analogy to a set of physical items stacked on top of
+each other, which makes it easy to take an item off the top
+of the stack, while getting to an item deeper in the stack
+may require taking off multiple other items first.
 
 Simple representation of a stack runtime with push and pop operations.
 
-![Stack](https://upload.wikimedia.org/wikipedia/commons/b/b4/Lifo_stack.png)
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
 
 ## References
 
diff --git a/src/data-structures/stack/README.pt-BR.md b/src/data-structures/stack/README.pt-BR.md
new file mode 100644
index 0000000000..ee862fe43a
--- /dev/null
+++ b/src/data-structures/stack/README.pt-BR.md
@@ -0,0 +1,28 @@
+# Pilha (Stack)
+
+Na ciência da computação, uma **pilha** é uma estrutura de dados abstrata
+que serve como uma coleção de elementos com duas operações principais:
+
+* **push**, pela qual adiciona um elemento à coleção, e
+* **pop**, pela qual remove o último elemento adicionado.
+
+A ordem em que os elementos saem de um _stack_ dá origem ao seu
+nome alternativo, LIFO (last in, first out). Adicionalmente, uma operação
+de espiada (peek) pode dar acesso ao topo sem modificar o _stack_.
+O nome "stack" para este tipo de estrutura vem da analogia de
+um conjunto de itens físicos empilhados uns sobre os outros,
+o que facilita retirar um item do topo da pilha, enquanto para chegar a
+um item mais profundo na pilha pode exigir a retirada de
+vários outros itens primeiro.
+
+Representação simples de um tempo de execução de pilha com operações
+_push_ e _pop_.
+
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/stack/README.ru-RU.md b/src/data-structures/stack/README.ru-RU.md
new file mode 100644
index 0000000000..3e33c87119
--- /dev/null
+++ b/src/data-structures/stack/README.ru-RU.md
@@ -0,0 +1,25 @@
+# Стек
+
+Стек (англ. stack — стопка) — абстрактный тип данных, представляющий собой
+список элементов, организованных по принципу LIFO (последним пришёл — первым вышел).
+
+Стек имеет две ключевые операции:
+* **добавление (push)** элемента в конец стека, и
+* **удаление (pop)**, последнего добавленного элемента.
+
+Дополнительная операция чтения головного элемента (peek) даёт доступ
+к последнему элементу стека без изменения самого стека.
+
+Чаще всего принцип работы стека сравнивают со стопкой тарелок: чтобы взять вторую
+сверху, нужно снять верхнюю.
+
+Иллюстрация работы со стеком.
+
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B5%D0%BA)
+- [YouTube](https://www.youtube.com/watch?v=tH8qi7lej5U)
diff --git a/src/data-structures/stack/README.uk-UA.md b/src/data-structures/stack/README.uk-UA.md
new file mode 100644
index 0000000000..f71d2274eb
--- /dev/null
+++ b/src/data-structures/stack/README.uk-UA.md
@@ -0,0 +1,25 @@
+# Стек
+
+Стек (англ. stack - стопка) - абстрактний тип даних, що представляє собою
+список елементів, організованих за принципом LIFO (останнім прийшов – першим вийшов).
+
+Стек має дві ключові операції:
+* **додавання (push)** елемента в кінець стеку, та
+* **видалення (pop)**, останнього доданого елемента.
+
+Додаткова операція для читання головного елемента (peek) дає доступ
+до останнього елементу стека без зміни самого стека.
+
+Найчастіше принцип роботи стека порівнюють із стопкою тарілок: щоб узяти другу
+зверху потрібно спочатку зняти верхню.
+
+Ілюстрація роботи зі стеком.
+
+![Стек](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%A1%D1%82%D0%B5%D0%BA)
+- [YouTube](https://www.youtube.com/watch?v=4jh1e1YCbYc)
diff --git a/src/data-structures/stack/README.vi-VN.md b/src/data-structures/stack/README.vi-VN.md
new file mode 100644
index 0000000000..f051f3201f
--- /dev/null
+++ b/src/data-structures/stack/README.vi-VN.md
@@ -0,0 +1,27 @@
+# Ngăn xếp (stack)
+
+_Đọc bằng ngôn ngữ khác:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_日本語_](README.ja-JP.md),
+[_Português_](README.pt-BR.md),
+[_한국어_](README.ko-KR.md),
+[_Español_](README.es-ES.md),
+[_Українська_](README.uk-UA.md)
+
+Trong khoa học máy tính, một ngăn xếp (stack) là một kiểu dữ liệu trừu tượng phục vụ như một bộ sưu tập các phần tử, với hai hoạt động chính:
+
+đẩy (push), thêm một phần tử vào bộ sưu tập, và
+lấy (pop), loại bỏ phần tử được thêm gần nhất mà chưa được loại bỏ.
+Thứ tự mà các phần tử được lấy ra khỏi ngăn xếp dẫn đến tên gọi thay thế của nó, là LIFO (last in, first out). Ngoài ra, một hoạt động nhìn có thể cung cấp quyền truy cập vào phần trên mà không làm thay đổi ngăn xếp. Tên "ngăn xếp" cho loại cấu trúc này đến từ sự tương tự với một bộ sưu tập các vật phẩm vật lý được xếp chồng lên nhau, điều này làm cho việc lấy một vật phẩm ra khỏi đỉnh của ngăn xếp dễ dàng, trong khi để đến được một vật phẩm sâu hơn trong ngăn xếp có thể đòi hỏi việc lấy ra nhiều vật phẩm khác trước đó.
+
+Biểu diễn đơn giản về thời gian chạy của một ngăn xếp với các hoạt động đẩy và lấy.
+
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Tham Khảo
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
+- [YouTube](https://www.youtube.com/watch?v=njTh_OwMljA&index=2&t=1s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/stack/README.zh-CN.md b/src/data-structures/stack/README.zh-CN.md
new file mode 100644
index 0000000000..f00e3eab00
--- /dev/null
+++ b/src/data-structures/stack/README.zh-CN.md
@@ -0,0 +1,23 @@
+# 栈
+
+在计算机科学中, 一个 **栈(stack)** 是一种抽象数据类型,用作表示元素的集合,具有两种主要操作:
+
+* **push**, 添加元素到栈的顶端(末尾);
+* **pop**, 移除栈最顶端(末尾)的元素.
+
+以上两种操作可以简单概括为“后进先出(LIFO = last in, first out)”。
+
+此外,应有一个 `peek` 操作用于访问栈当前顶端(末尾)的元素。
+
+"栈"这个名称,可类比于一组物体的堆叠(一摞书,一摞盘子之类的)。
+
+栈的 push 和 pop 操作的示意
+
+![Stack](./images/stack.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
+- [YouTube](https://www.youtube.com/watch?v=wjI1WNcIntg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=3&)
diff --git a/src/data-structures/stack/Stack.js b/src/data-structures/stack/Stack.js
index 762b428ec1..8d06141251 100644
--- a/src/data-structures/stack/Stack.js
+++ b/src/data-structures/stack/Stack.js
@@ -2,9 +2,9 @@ import LinkedList from '../linked-list/LinkedList';
 
 export default class Stack {
   constructor() {
-    // We're going to implement Queue based on LinkedList since this
-    // structures a quite similar. Compare push/pop operations of the Stack
-    // with append/deleteTail operations of LinkedList.
+    // We're going to implement Stack based on LinkedList since these
+    // structures are quite similar. Compare push/pop operations of the Stack
+    // with prepend/deleteHead operations of LinkedList.
     this.linkedList = new LinkedList();
   }
 
@@ -12,8 +12,8 @@ export default class Stack {
    * @return {boolean}
    */
   isEmpty() {
-    // The queue is empty in case if its linked list don't have tail.
-    return !this.linkedList.tail;
+    // The stack is empty if its linked list doesn't have a head.
+    return !this.linkedList.head;
   }
 
   /**
@@ -21,12 +21,12 @@ export default class Stack {
    */
   peek() {
     if (this.isEmpty()) {
-      // If linked list is empty then there is nothing to peek from.
+      // If the linked list is empty then there is nothing to peek from.
       return null;
     }
 
-    // Just read the value from the end of linked list without deleting it.
-    return this.linkedList.tail.value;
+    // Just read the value from the start of linked list without deleting it.
+    return this.linkedList.head.value;
   }
 
   /**
@@ -34,18 +34,18 @@ export default class Stack {
    */
   push(value) {
     // Pushing means to lay the value on top of the stack. Therefore let's just add
-    // new value at the end of the linked list.
-    this.linkedList.append(value);
+    // the new value at the start of the linked list.
+    this.linkedList.prepend(value);
   }
 
   /**
    * @return {*}
    */
   pop() {
-    // Let's try to delete the last node from linked list (the tail).
-    // If there is no tail in linked list (it is empty) just return null.
-    const removedTail = this.linkedList.deleteTail();
-    return removedTail ? removedTail.value : null;
+    // Let's try to delete the first node (the head) from the linked list.
+    // If there is no head (the linked list is empty) just return null.
+    const removedHead = this.linkedList.deleteHead();
+    return removedHead ? removedHead.value : null;
   }
 
   /**
@@ -54,8 +54,7 @@ export default class Stack {
   toArray() {
     return this.linkedList
       .toArray()
-      .map(linkedListNode => linkedListNode.value)
-      .reverse();
+      .map((linkedListNode) => linkedListNode.value);
   }
 
   /**
diff --git a/src/data-structures/stack/__test__/Stack.test.js b/src/data-structures/stack/__test__/Stack.test.js
index 5bfe133a6c..1112277e28 100644
--- a/src/data-structures/stack/__test__/Stack.test.js
+++ b/src/data-structures/stack/__test__/Stack.test.js
@@ -13,7 +13,7 @@ describe('Stack', () => {
     stack.push(1);
     stack.push(2);
 
-    expect(stack.toString()).toBe('1,2');
+    expect(stack.toString()).toBe('2,1');
   });
 
   it('should peek data from stack', () => {
@@ -56,9 +56,9 @@ describe('Stack', () => {
     stack.push({ value: 'test1', key: 'key1' });
     stack.push({ value: 'test2', key: 'key2' });
 
-    const stringifier = value => `${value.key}:${value.value}`;
+    const stringifier = (value) => `${value.key}:${value.value}`;
 
-    expect(stack.toString(stringifier)).toBe('key1:test1,key2:test2');
+    expect(stack.toString(stringifier)).toBe('key2:test2,key1:test1');
     expect(stack.pop().value).toBe('test2');
     expect(stack.pop().value).toBe('test1');
   });
diff --git a/src/data-structures/stack/images/stack.jpeg b/src/data-structures/stack/images/stack.jpeg
new file mode 100644
index 0000000000..6a3bf599c4
Binary files /dev/null and b/src/data-structures/stack/images/stack.jpeg differ
diff --git a/src/data-structures/stack/images/stack.jpg b/src/data-structures/stack/images/stack.jpg
new file mode 100644
index 0000000000..8c966f276f
Binary files /dev/null and b/src/data-structures/stack/images/stack.jpg differ
diff --git a/src/data-structures/tree/README.md b/src/data-structures/tree/README.md
index 48fe9fe44a..e492257d33 100644
--- a/src/data-structures/tree/README.md
+++ b/src/data-structures/tree/README.md
@@ -1,29 +1,35 @@
 # Tree
 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Português_](README.pt-BR.md)
+
 * [Binary Search Tree](binary-search-tree)
 * [AVL Tree](avl-tree)
 * [Red-Black Tree](red-black-tree)
 * [Segment Tree](segment-tree) - with min/max/sum range queries examples
 * [Fenwick Tree](fenwick-tree) (Binary Indexed Tree)
 
-In computer science, a **tree** is a widely used abstract data 
-type (ADT) — or data structure implementing this ADT—that 
-simulates a hierarchical tree structure, with a root value 
-and subtrees of children with a parent node, represented as 
+In computer science, a **tree** is a widely used abstract data
+type (ADT) — or data structure implementing this ADT—that
+simulates a hierarchical tree structure, with a root value
+and subtrees of children with a parent node, represented as
 a set of linked nodes.
 
-A tree data structure can be defined recursively (locally) 
-as a collection of nodes (starting at a root node), where 
-each node is a data structure consisting of a value, 
-together with a list of references to nodes (the "children"), 
-with the constraints that no reference is duplicated, and none 
+A tree data structure can be defined recursively (locally)
+as a collection of nodes (starting at a root node), where
+each node is a data structure consisting of a value,
+together with a list of references to nodes (the "children"),
+with the constraints that no reference is duplicated, and none
 points to the root.
 
-A simple unordered tree; in this diagram, the node labeled 7 has
+A simple unordered tree; in this diagram, the node labeled 3 has
 two children, labeled 2 and 6, and one parent, labeled 2. The
 root node, at the top, has no parent.
 
-![Tree](https://upload.wikimedia.org/wikipedia/commons/f/f7/Binary_tree.svg)
+![Tree](./images/tree.jpeg)
+
+*Made with [okso.app](https://okso.app)*
 
 ## References
 
diff --git a/src/data-structures/tree/README.pt-BR.md b/src/data-structures/tree/README.pt-BR.md
new file mode 100644
index 0000000000..7aade88caa
--- /dev/null
+++ b/src/data-structures/tree/README.pt-BR.md
@@ -0,0 +1,32 @@
+# Árvore (Tree)
+
+* [Árvore de Pesquisa Binária (Binary Search Tree)](binary-search-tree/README.pt-BR.md)
+* [Árvore AVL (AVL Tree)](avl-tree/README.pt-BR.md)
+* [Árvore Vermelha-Preta (Red-Black Tree)](red-black-tree/README.pt-BR.md)
+* [Árvore de Segmento (Segment Tree)](segment-tree/README.pt-BR.md) - com exemplos de consulta de intervalores min/max/sum
+* [Árvorem Fenwick (Fenwick Tree)](fenwick-tree/README.pt-BR.md) (Árvore Binária Indexada / Binary Indexed Tree)
+
+Na ciência da computação, uma **árvore** é uma estrutura de dados
+abstrada (ADT) amplamente utilizada - ou uma estrutura de dados
+implementando este ADT que simula uma estrutura hierárquica de árvore,
+com valor raíz e sub-árvores de filhos com um nó pai, representado
+como um conjunto de nós conectados.
+
+Uma estrutura de dados em árvore pode ser definida recursivamente como
+(localmente) uma coleção de nós (começando no nó raíz), aonde cada nó
+é uma estrutura de dados consistindo de um valor, junto com uma lista
+de referências aos nós (os "filhos"), com as restrições de que nenhuma
+referência é duplicada e nenhuma aponta para a raiz.
+
+Uma árvore não ordenada simples; neste diagrama, o nó rotulado como `7`
+possui dois filhos, rotulados como `2` e `6`, e um pai, rotulado como `2`.
+O nó raíz, no topo, não possui nenhum pai.
+
+![Tree](./images/tree.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Tree_(data_structure))
+- [YouTube](https://www.youtube.com/watch?v=oSWTXtMglKE&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=8)
diff --git a/src/data-structures/tree/README.zh-CN.md b/src/data-structures/tree/README.zh-CN.md
new file mode 100644
index 0000000000..2f4df983d4
--- /dev/null
+++ b/src/data-structures/tree/README.zh-CN.md
@@ -0,0 +1,26 @@
+# 树
+
+* [二叉搜索树](binary-search-tree)
+* [AVL树](avl-tree)
+* [红黑树](red-black-tree)
+* [线段树](segment-tree) - with min/max/sum range queries examples
+* [芬威克树/Fenwick Tree](fenwick-tree) (Binary Indexed Tree)
+
+在计算机科学中, **树(tree)** 是一种广泛使用的抽象数据类型(ADT)— 或实现此ADT的数据结构 — 模拟分层树结构, 具有根节点和有父节点的子树,表示为一组链接节点。
+
+树可以被(本地地)递归定义为一个(始于一个根节点的)节点集, 每个节点都是一个包含了值的数据结构, 除了值,还有该节点的节点引用列表(子节点)一起。
+树的节点之间没有引用重复的约束。
+
+一棵简单的无序树; 在下图中:
+
+标记为7的节点具有两个子节点, 标记为2和6;
+一个父节点,标记为2,作为根节点, 在顶部,没有父节点。
+
+![Tree](./images/tree.jpeg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Tree_(data_structure))
+- [YouTube](https://www.youtube.com/watch?v=oSWTXtMglKE&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=8)
diff --git a/src/data-structures/tree/avl-tree/AvlTree.js b/src/data-structures/tree/avl-tree/AvlTree.js
index 51e6a05f92..8a8b5d12cd 100644
--- a/src/data-structures/tree/avl-tree/AvlTree.js
+++ b/src/data-structures/tree/avl-tree/AvlTree.js
@@ -21,7 +21,11 @@ export default class AvlTree extends BinarySearchTree {
    * @return {boolean}
    */
   remove(value) {
-    throw new Error(`Can't remove ${value}. Remove method is not implemented yet`);
+    // Do standard BST removal.
+    super.remove(value);
+
+    // Balance the tree starting from the root node.
+    this.balance(this.root);
   }
 
   /**
diff --git a/src/data-structures/tree/avl-tree/README.md b/src/data-structures/tree/avl-tree/README.md
index ff313a180d..c70fca7bcd 100644
--- a/src/data-structures/tree/avl-tree/README.md
+++ b/src/data-structures/tree/avl-tree/README.md
@@ -1,5 +1,8 @@
 # AVL Tree
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
 In computer science, an **AVL tree** (named after inventors 
 Adelson-Velsky and Landis) is a self-balancing binary search 
 tree. It was the first such data structure to be invented. 
@@ -25,24 +28,24 @@ AVL tree with balance factors (green)
 
 **Left-Left Rotation**
 
-![Left-Left Rotation](http://btechsmartclass.com/DS/images/LL%20Rotation.png)
+![Left-Left Rotation](http://btechsmartclass.com/data_structures/ds_images/LL%20Rotation.png)
 
 **Right-Right Rotation**
 
-![Right-Right Rotation](http://btechsmartclass.com/DS/images/RR%20Rotation.png)
+![Right-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/RR%20Rotation.png)
 
 **Left-Right Rotation**
 
-![Left-Right Rotation](http://btechsmartclass.com/DS/images/LR%20Rotation.png)
+![Left-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/LR%20Rotation.png)
 
 **Right-Left Rotation**
 
-![Right-Right Rotation](http://btechsmartclass.com/DS/images/RL%20Rotation.png)
+![Right-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/RL%20Rotation.png)
 
 ## References
 
 * [Wikipedia](https://en.wikipedia.org/wiki/AVL_tree)
 * [Tutorials Point](https://www.tutorialspoint.com/data_structures_algorithms/avl_tree_algorithm.htm)
-* [BTech](http://btechsmartclass.com/DS/U5_T2.html)
+* [BTech](http://btechsmartclass.com/data_structures/avl-trees.html)
 * [AVL Tree Insertion on YouTube](https://www.youtube.com/watch?v=rbg7Qf8GkQ4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=12&)
 * [AVL Tree Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/AVLtree.html)
diff --git a/src/data-structures/tree/avl-tree/README.pt-BR.md b/src/data-structures/tree/avl-tree/README.pt-BR.md
new file mode 100644
index 0000000000..9b9e9ffb93
--- /dev/null
+++ b/src/data-structures/tree/avl-tree/README.pt-BR.md
@@ -0,0 +1,50 @@
+# Árvore AVL (AVL Tree)
+
+Na ciência da computação, uma **árvore AVL** (em homenagem aos
+inventores Adelson-Velsky e Landis) é uma árvore de pesquisa
+binária auto balanceada. Foi a primeira estrutura de dados a
+ser inventada.
+Em uma árvore AVL, as alturas de duas sub-árvores filhas
+de qualquer nó diferem no máximo em um; se a qualquer momento
+diferirem por em mais de um, um rebalanceamento é feito para
+restaurar esta propriedade.
+Pesquisa, inserção e exclusão possuem tempo `O(log n)` tanto na
+média quanto nos piores casos, onde `n` é o número de nós na
+árvore antes da operação. Inserções e exclusões podem exigir
+que a árvore seja reequilibrada por uma ou mais rotações.
+
+
+Animação mostrando a inserção de vários elementos em uma árvore AVL.
+Inclui as rotações de esquerda, direita, esquerda-direita e direita-esquerda.
+
+![AVL Tree](https://upload.wikimedia.org/wikipedia/commons/f/fd/AVL_Tree_Example.gif)
+
+Árvore AVL com fatores de equilíbrio (verde)
+
+![AVL Tree](https://upload.wikimedia.org/wikipedia/commons/a/ad/AVL-tree-wBalance_K.svg)
+
+### Rotações de Árvores AVL
+
+**Rotação Esquerda-Esquerda**
+
+![Left-Left Rotation](http://btechsmartclass.com/data_structures/ds_images/LL%20Rotation.png)
+
+**Rotação direita-direita**
+
+![Right-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/RR%20Rotation.png)
+
+**Rotação Esquerda-Direita**
+
+![Left-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/LR%20Rotation.png)
+
+**Rotação Direita-Esquerda**
+
+![Right-Right Rotation](http://btechsmartclass.com/data_structures/ds_images/RL%20Rotation.png)
+
+## Referências
+
+* [Wikipedia](https://en.wikipedia.org/wiki/AVL_tree)
+* [Tutorials Point](https://www.tutorialspoint.com/data_structures_algorithms/avl_tree_algorithm.htm)
+* [BTech](http://btechsmartclass.com/data_structures/avl-trees.html)
+* [AVL Tree Insertion on YouTube](https://www.youtube.com/watch?v=rbg7Qf8GkQ4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=12&)
+* [AVL Tree Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/AVLtree.html)
diff --git a/src/data-structures/tree/avl-tree/__test__/AvlTRee.test.js b/src/data-structures/tree/avl-tree/__test__/AvlTRee.test.js
index fb1f3ee413..cf64420091 100644
--- a/src/data-structures/tree/avl-tree/__test__/AvlTRee.test.js
+++ b/src/data-structures/tree/avl-tree/__test__/AvlTRee.test.js
@@ -230,13 +230,74 @@ describe('AvlTree', () => {
     expect(tree.root.height).toBe(3);
   });
 
-  it('should throw an error when trying to remove the node', () => {
-    const removeNodeAvlTree = () => {
-      const tree = new AvlTree();
+  it('should remove values from the tree with right-right rotation', () => {
+    const tree = new AvlTree();
+
+    tree.insert(10);
+    tree.insert(20);
+    tree.insert(30);
+    tree.insert(40);
+
+    expect(tree.toString()).toBe('10,20,30,40');
+
+    tree.remove(10);
+
+    expect(tree.toString()).toBe('20,30,40');
+    expect(tree.root.value).toBe(30);
+    expect(tree.root.left.value).toBe(20);
+    expect(tree.root.right.value).toBe(40);
+    expect(tree.root.balanceFactor).toBe(0);
+  });
+
+  it('should remove values from the tree with left-left rotation', () => {
+    const tree = new AvlTree();
+
+    tree.insert(10);
+    tree.insert(20);
+    tree.insert(30);
+    tree.insert(5);
+
+    expect(tree.toString()).toBe('5,10,20,30');
 
-      tree.remove(1);
-    };
+    tree.remove(30);
 
-    expect(removeNodeAvlTree).toThrowError();
+    expect(tree.toString()).toBe('5,10,20');
+    expect(tree.root.value).toBe(10);
+    expect(tree.root.left.value).toBe(5);
+    expect(tree.root.right.value).toBe(20);
+    expect(tree.root.balanceFactor).toBe(0);
+  });
+
+  it('should keep balance after removal', () => {
+    const tree = new AvlTree();
+
+    tree.insert(1);
+    tree.insert(2);
+    tree.insert(3);
+    tree.insert(4);
+    tree.insert(5);
+    tree.insert(6);
+    tree.insert(7);
+    tree.insert(8);
+    tree.insert(9);
+
+    expect(tree.toString()).toBe('1,2,3,4,5,6,7,8,9');
+    expect(tree.root.value).toBe(4);
+    expect(tree.root.height).toBe(3);
+    expect(tree.root.balanceFactor).toBe(-1);
+
+    tree.remove(8);
+
+    expect(tree.root.value).toBe(4);
+    expect(tree.root.balanceFactor).toBe(-1);
+
+    tree.remove(9);
+
+    expect(tree.contains(8)).toBeFalsy();
+    expect(tree.contains(9)).toBeFalsy();
+    expect(tree.toString()).toBe('1,2,3,4,5,6,7');
+    expect(tree.root.value).toBe(4);
+    expect(tree.root.height).toBe(2);
+    expect(tree.root.balanceFactor).toBe(0);
   });
 });
diff --git a/src/data-structures/tree/binary-search-tree/README.md b/src/data-structures/tree/binary-search-tree/README.md
index 3ceb6ce6e9..d05a891545 100644
--- a/src/data-structures/tree/binary-search-tree/README.md
+++ b/src/data-structures/tree/binary-search-tree/README.md
@@ -1,31 +1,36 @@
 # Binary Search Tree
 
-In computer science, **binary search trees** (BST), sometimes called 
-ordered or sorted binary trees, are a particular type of container: 
-data structures that store "items" (such as numbers, names etc.) 
-in memory. They allow fast lookup, addition and removal of 
-items, and can be used to implement either dynamic sets of 
-items, or lookup tables that allow finding an item by its key 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md)
+
+In computer science, **binary search trees** (BST), sometimes called
+ordered or sorted binary trees, are a particular type of container:
+data structures that store "items" (such as numbers, names etc.)
+in memory. They allow fast lookup, addition and removal of
+items, and can be used to implement either dynamic sets of
+items, or lookup tables that allow finding an item by its key
 (e.g., finding the phone number of a person by name).
 
-Binary search trees keep their keys in sorted order, so that lookup 
-and other operations can use the principle of binary search: 
-when looking for a key in a tree (or a place to insert a new key), 
-they traverse the tree from root to leaf, making comparisons to 
-keys stored in the nodes of the tree and deciding, on the basis 
-of the comparison, to continue searching in the left or right 
-subtrees. On average, this means that each comparison allows 
-the operations to skip about half of the tree, so that each 
-lookup, insertion or deletion takes time proportional to the 
-logarithm of the number of items stored in the tree. This is 
-much better than the linear time required to find items by key 
-in an (unsorted) array, but slower than the corresponding 
+Binary search trees keep their keys in sorted order, so that lookup
+and other operations can use the principle of binary search:
+when looking for a key in a tree (or a place to insert a new key),
+they traverse the tree from root to leaf, making comparisons to
+keys stored in the nodes of the tree and deciding, on the basis
+of the comparison, to continue searching in the left or right
+subtrees. On average, this means that each comparison allows
+the operations to skip about half of the tree, so that each
+lookup, insertion or deletion takes time proportional to the
+logarithm of the number of items stored in the tree. This is
+much better than the linear time required to find items by key
+in an (unsorted) array, but slower than the corresponding
 operations on hash tables.
 
 A binary search tree of size 9 and depth 3, with 8 at the root.
 The leaves are not drawn.
 
-![Binary Search Tree](https://upload.wikimedia.org/wikipedia/commons/d/da/Binary_search_tree.svg)
+![Trie](./images/binary-search-tree.jpg)
+
+*Made with [okso.app](https://okso.app)*
 
 ## Pseudocode for Basic Operations
 
@@ -42,7 +47,7 @@ insert(value)
   end if
 end insert
 ```
-    
+
 ```text
 insertNode(current, value)
   Pre: current is the node to start from
@@ -81,8 +86,8 @@ contains(root, value)
   end if
 end contains
 ```
-    
-     
+
+
 ### Deletion
 
 ```text
@@ -103,19 +108,31 @@ remove(value)
     else
       parent.right ← nodeToRemove.right
     end if
-  else if nodeToRemove.left = ø and nodeToRemove.right = ø
-   if nodeToRemove.value < parent.value
-     parent.left ←  nodeToRemove.left
-   else
-     parent.right ← nodeToRemove.left
-   end if
+  else if nodeToRemove.left != ø and nodeToRemove.right != ø
+    next ← nodeToRemove.right
+    while next.left != ø
+      next ← next.left
+    end while
+    if next != nodeToRemove.right
+      remove(next.value)
+      nodeToRemove.value ← next.value
+    else
+      nodeToRemove.value ← next.value
+      nodeToRemove.right ← nodeToRemove.right.right
+    end if
   else
-   largestValue ← nodeToRemove.left
-   while largestValue.right = ø
-     largestValue ← largestValue.right
-   end while
-   findParent(largestValue.value).right ← ø
-   nodeToRemove.value ← largestValue.value
+    if nodeToRemove.left = ø
+      next ← nodeToRemove.right
+    else
+      next ← nodeToRemove.left
+    end if
+    if root = nodeToRemove
+      root = next
+    else if parent.left = nodeToRemove
+      parent.left = next
+    else if parent.right = nodeToRemove
+      parent.right = next
+    end if
   end if
   count ← count - 1
   return true
@@ -171,7 +188,7 @@ findNode(root, value)
   end if
 end findNode
 ```
-    
+
 ### Find Minimum
 
 ```text
@@ -185,7 +202,7 @@ findMin(root)
   findMin(root.left)
 end findMin
 ```
-    
+
 ### Find Maximum
 
 ```text
@@ -199,7 +216,7 @@ findMax(root)
   findMax(root.right)
 end findMax
 ```
-    
+
 ### Traversal
 
 #### InOrder Traversal
@@ -208,7 +225,7 @@ end findMax
 inorder(root)
   Pre: root is the root node of the BST
   Post: the nodes in the BST have been visited in inorder
-  if root = ø
+  if root != ø
     inorder(root.left)
     yield root.value
     inorder(root.right)
@@ -222,28 +239,28 @@ end inorder
 preorder(root)
   Pre: root is the root node of the BST
   Post: the nodes in the BST have been visited in preorder
-  if root = ø
+  if root != ø
     yield root.value
     preorder(root.left)
     preorder(root.right)
   end if
 end preorder
 ```
-   
+
 #### PostOrder Traversal
 
 ```text
 postorder(root)
   Pre: root is the root node of the BST
   Post: the nodes in the BST have been visited in postorder
-  if root = ø
+  if root != ø
     postorder(root.left)
     postorder(root.right)
     yield root.value
   end if
 end postorder
 ```
-     
+
 ## Complexities
 
 ### Time Complexity
diff --git a/src/data-structures/tree/binary-search-tree/README.pt-BR.md b/src/data-structures/tree/binary-search-tree/README.pt-BR.md
new file mode 100644
index 0000000000..1e71714104
--- /dev/null
+++ b/src/data-structures/tree/binary-search-tree/README.pt-BR.md
@@ -0,0 +1,278 @@
+# Árvore de Pesquisa Binária (Binary Search Tree)
+
+Na ciência da computação **binary search trees** (BST), algumas vezes
+chamadas de árvores binárias ordenadas (_ordered or sorted binary trees_),
+é um tipo particular de container: estruturas de dados que armazenam
+"itens" (como números, nomes, etc.) na memória. Permite pesquisa rápida,
+adição e remoção de itens além de poder ser utilizado para implementar
+tanto conjuntos dinâmicos de itens ou, consultar tabelas que permitem
+encontrar um item por seu valor chave. E.g. encontrar o número de
+telefone de uma pessoa pelo seu nome.
+
+Árvore de Pesquisa Binária mantem seus valores chaves ordenados, para
+que uma pesquisa e outras operações possam usar o princípio da pesquisa
+binária: quando pesquisando por um valor chave na árvore (ou um lugar
+para inserir uma nova chave), eles atravessam a árvore da raiz para a folha,
+fazendo comparações com chaves armazenadas nos nós da árvore e decidindo então,
+com base nas comparações, continuar pesquisando nas sub-árvores a direita ou
+a esquerda. Em média isto significa que cara comparação permite as operações
+pular metade da árvore, para que então, cada pesquisa, inserção ou remoção
+consuma tempo proporcional ao logaritmo do número de itens armazenados na
+árvore. Isto é muito melhor do que um tempo linear necessário para encontrar
+itens por seu valor chave em um array (desorndenado - _unsorted_), mas muito
+mais lento do que operações similares em tableas de hash (_hash tables_).
+
+Uma pesquisa de árvore binária de tamanho 9 e profundidade 3, com valor 8
+na raíz.
+As folhas não foram desenhadas.
+
+![Trie](./images/binary-search-tree.jpg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Pseudocódigo para Operações Básicas
+
+### Inserção
+
+```text
+insert(value)
+  Pre: value has passed custom type checks for type T
+  Post: value has been placed in the correct location in the tree
+  if root = ø
+    root ← node(value)
+  else
+    insertNode(root, value)
+  end if
+end insert
+```
+
+```text
+insertNode(current, value)
+  Pre: current is the node to start from
+  Post: value has been placed in the correct location in the tree
+  if value < current.value
+    if current.left = ø
+      current.left ← node(value)
+    else
+      InsertNode(current.left, value)
+    end if
+  else
+    if current.right = ø
+      current.right ← node(value)
+    else
+      InsertNode(current.right, value)
+    end if
+  end if
+end insertNode
+```
+
+### Pesquisa
+
+```text
+contains(root, value)
+  Pre: root is the root node of the tree, value is what we would like to locate
+  Post: value is either located or not
+  if root = ø
+    return false
+  end if
+  if root.value = value
+    return true
+  else if value < root.value
+    return contains(root.left, value)
+  else
+    return contains(root.right, value)
+  end if
+end contains
+```
+
+
+### Remoção
+
+```text
+remove(value)
+  Pre: value is the value of the node to remove, root is the node of the BST
+      count is the number of items in the BST
+  Post: node with value is removed if found in which case yields true, otherwise false
+  nodeToRemove ← findNode(value)
+  if nodeToRemove = ø
+    return false
+  end if
+  parent ← findParent(value)
+  if count = 1
+    root ← ø
+  else if nodeToRemove.left = ø and nodeToRemove.right = ø
+    if nodeToRemove.value < parent.value
+      parent.left ←  nodeToRemove.right
+    else
+      parent.right ← nodeToRemove.right
+    end if
+  else if nodeToRemove.left != ø and nodeToRemove.right != ø
+    next ← nodeToRemove.right
+    while next.left != ø
+      next ← next.left
+    end while
+    if next != nodeToRemove.right
+      remove(next.value)
+      nodeToRemove.value ← next.value
+    else
+      nodeToRemove.value ← next.value
+      nodeToRemove.right ← nodeToRemove.right.right
+    end if
+  else
+    if nodeToRemove.left = ø
+      next ← nodeToRemove.right
+    else
+      next ← nodeToRemove.left
+    end if
+    if root = nodeToRemove
+      root = next
+    else if parent.left = nodeToRemove
+      parent.left = next
+    else if parent.right = nodeToRemove
+      parent.right = next
+    end if
+  end if
+  count ← count - 1
+  return true
+end remove
+```
+
+### Encontrar o Nó Pai
+
+```text
+findParent(value, root)
+  Pre: value is the value of the node we want to find the parent of
+       root is the root node of the BST and is != ø
+  Post: a reference to the prent node of value if found; otherwise ø
+  if value = root.value
+    return ø
+  end if
+  if value < root.value
+    if root.left = ø
+      return ø
+    else if root.left.value = value
+      return root
+    else
+      return findParent(value, root.left)
+    end if
+  else
+    if root.right = ø
+      return ø
+    else if root.right.value = value
+      return root
+    else
+      return findParent(value, root.right)
+    end if
+  end if
+end findParent
+```
+
+### Encontrar um Nó
+
+```text
+findNode(root, value)
+  Pre: value is the value of the node we want to find the parent of
+       root is the root node of the BST
+  Post: a reference to the node of value if found; otherwise ø
+  if root = ø
+    return ø
+  end if
+  if root.value = value
+    return root
+  else if value < root.value
+    return findNode(root.left, value)
+  else
+    return findNode(root.right, value)
+  end if
+end findNode
+```
+
+### Encontrar Mínimo
+
+```text
+findMin(root)
+  Pre: root is the root node of the BST
+    root = ø
+  Post: the smallest value in the BST is located
+  if root.left = ø
+    return root.value
+  end if
+  findMin(root.left)
+end findMin
+```
+
+### Encontrar Máximo
+
+```text
+findMax(root)
+  Pre: root is the root node of the BST
+    root = ø
+  Post: the largest value in the BST is located
+  if root.right = ø
+    return root.value
+  end if
+  findMax(root.right)
+end findMax
+```
+
+### Traversal
+
+#### Na Ordem Traversal (InOrder Traversal)
+
+```text
+inorder(root)
+  Pre: root is the root node of the BST
+  Post: the nodes in the BST have been visited in inorder
+  if root = ø
+    inorder(root.left)
+    yield root.value
+    inorder(root.right)
+  end if
+end inorder
+```
+
+#### Pré Ordem Traversal (PreOrder Traversal)
+
+```text
+preorder(root)
+  Pre: root is the root node of the BST
+  Post: the nodes in the BST have been visited in preorder
+  if root = ø
+    yield root.value
+    preorder(root.left)
+    preorder(root.right)
+  end if
+end preorder
+```
+
+#### Pós Ordem Traversal (PostOrder Traversal)
+
+```text
+postorder(root)
+  Pre: root is the root node of the BST
+  Post: the nodes in the BST have been visited in postorder
+  if root = ø
+    postorder(root.left)
+    postorder(root.right)
+    yield root.value
+  end if
+end postorder
+```
+
+## Complexidades
+
+### Complexidade de Tempo
+
+| Access    | Search    | Insertion | Deletion  |
+| :-------: | :-------: | :-------: | :-------: |
+| O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) |
+
+### Complexidade de Espaço
+
+O(n)
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_tree)
+- [Inserting to BST on YouTube](https://www.youtube.com/watch?v=wcIRPqTR3Kc&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=9&t=0s)
+- [BST Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/BST.html)
diff --git a/src/data-structures/tree/binary-search-tree/images/binary-search-tree.jpg b/src/data-structures/tree/binary-search-tree/images/binary-search-tree.jpg
new file mode 100644
index 0000000000..1309e4ee4d
Binary files /dev/null and b/src/data-structures/tree/binary-search-tree/images/binary-search-tree.jpg differ
diff --git a/src/data-structures/tree/fenwick-tree/FenwickTree.js b/src/data-structures/tree/fenwick-tree/FenwickTree.js
index 7bfb84264f..9146fa138c 100644
--- a/src/data-structures/tree/fenwick-tree/FenwickTree.js
+++ b/src/data-structures/tree/fenwick-tree/FenwickTree.js
@@ -60,7 +60,7 @@ export default class FenwickTree {
    */
   queryRange(leftIndex, rightIndex) {
     if (leftIndex > rightIndex) {
-      throw new Error('Left index can not be greater then right one');
+      throw new Error('Left index can not be greater than right one');
     }
 
     if (leftIndex === 1) {
diff --git a/src/data-structures/tree/fenwick-tree/README.md b/src/data-structures/tree/fenwick-tree/README.md
index adc6aa41bf..14129caa7c 100644
--- a/src/data-structures/tree/fenwick-tree/README.md
+++ b/src/data-structures/tree/fenwick-tree/README.md
@@ -1,5 +1,8 @@
 # Fenwick Tree / Binary Indexed Tree
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md) 
+
 A **Fenwick tree** or **binary indexed tree** is a data 
 structure that can efficiently update elements and 
 calculate prefix sums in a table of numbers.
diff --git a/src/data-structures/tree/fenwick-tree/README.pt-BR.md b/src/data-structures/tree/fenwick-tree/README.pt-BR.md
new file mode 100644
index 0000000000..b76bbc0f97
--- /dev/null
+++ b/src/data-structures/tree/fenwick-tree/README.pt-BR.md
@@ -0,0 +1,42 @@
+# Árvore Fenwick / Árvore Binária Indexada (Fenwick Tree / Binary Indexed Tree)
+
+Uma **árvore Fenwick** ou **árvore binária indexada** é um tipo de
+estrutura de dados que consegue eficiemente atualizar elementos e
+calcular soma dos prefixos em uma tabela de números.
+
+Quando comparado com um _flat array_ de números, a árvore Fenwick
+alcança um balanceamento muito melhor entre duas operações: atualização
+(_update_) do elemento e cálculo da soma do prefíxo. Em uma _flar array_
+de `n` números, você pode tanto armazenar elementos quando a soma dos
+prefixos. Em ambos os casos, computar a soma dos prefixos requer ou 
+atualizar um array de elementos também requerem um tempo linear, contudo,
+a demais operações podem ser realizadas com tempo constante.
+A árvore Fenwick permite ambas as operações serem realizadas com tempo
+`O(log n)`.
+
+Isto é possível devido a representação dos números como uma árvore, aonde
+os valores de cada nó é a soma dos números naquela sub-árvore. A estrutura
+de árvore permite operações a serem realizadas consumindo somente acessos
+a nós em `O(log n)`.
+
+## Implementação de Nós
+
+Árvore Binária Indexada é representada como um _array_. Em cada nó da Árvore
+Binária Indexada armazena a soma de alguns dos elementos de uma _array_
+fornecida. O tamanho da Árvore Binária Indexada é igual a `n` aonde `n` é o
+tamanho do _array_ de entrada. Na presente implementação nós utilizados o
+tamanho `n+1` para uma implementação fácil. Todos os índices são baseados em 1. 
+
+![Binary Indexed Tree](https://www.geeksforgeeks.org/wp-content/uploads/BITSum.png)
+
+Na imagem abaixo você pode ver o exemplo animado da criação de uma árvore
+binária indexada para o _array_ `[1, 2, 3, 4, 5]`, sendo inseridos um após
+o outro.
+
+![Fenwick Tree](https://upload.wikimedia.org/wikipedia/commons/d/dc/BITDemo.gif)
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Fenwick_tree)
+- [GeeksForGeeks](https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/)
+- [YouTube](https://www.youtube.com/watch?v=CWDQJGaN1gY&index=18&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
diff --git a/src/data-structures/tree/images/tree.jpeg b/src/data-structures/tree/images/tree.jpeg
new file mode 100644
index 0000000000..c888843d6c
Binary files /dev/null and b/src/data-structures/tree/images/tree.jpeg differ
diff --git a/src/data-structures/tree/red-black-tree/README.md b/src/data-structures/tree/red-black-tree/README.md
index ed488046b9..566089f976 100644
--- a/src/data-structures/tree/red-black-tree/README.md
+++ b/src/data-structures/tree/red-black-tree/README.md
@@ -1,5 +1,8 @@
 # Red–Black Tree
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md) 
+
 A **red–black tree** is a kind of self-balancing binary search 
 tree in computer science. Each node of the binary tree has 
 an extra bit, and that bit is often interpreted as the 
diff --git a/src/data-structures/tree/red-black-tree/README.pt-BR.md b/src/data-structures/tree/red-black-tree/README.pt-BR.md
new file mode 100644
index 0000000000..6c80def9cd
--- /dev/null
+++ b/src/data-structures/tree/red-black-tree/README.pt-BR.md
@@ -0,0 +1,92 @@
+# Árvore Vermelha-Preta (Red-Black Tree)
+
+Uma **árvore vermelha-preta** é um tipo de árvore de pesquisa
+binária auto balanceada na ciência da computação. Cada nó da 
+árvore binária possui um _bit_ extra, e este _bit_ é frequentemente
+interpretado com a cor (vermelho ou preto) do nó. Estas cores de _bits_
+são utilizadas para garantir que a árvore permanece aproximadamente
+equilibrada durante as operações de inserções e remoções.
+
+O equilíbrio é preservado através da pintura de cada nó da árvore com
+uma das duas cores, de maneira que satisfaça certas propriedades, das
+quais restringe nos piores dos casos, o quão desequilibrada a árvore
+pode se tornar. Quando a árvore é modificada, a nova árvore é
+subsequentemente reorganizada e repintada para restaurar as
+propriedades de coloração. As propriedades são designadas de tal modo que
+esta reorganização e nova pintura podem ser realizadas eficientemente.
+
+O balanceamento de uma árvore não é perfeito, mas é suficientemente bom
+para permitir e garantir uma pesquisa no tempo `O(log n)`, aonde `n` é o
+número total de elementos na árvore.
+Operações de inserções e remoções, juntamente com a reorganização e 
+repintura da árvore, também são executados no tempo `O (log n)`.
+
+Um exemplo de uma árvore vermalha-preta:
+
+![red-black tree](https://upload.wikimedia.org/wikipedia/commons/6/66/Red-black_tree_example.svg)
+
+## Propriedades
+
+Em adição aos requerimentos impostos pela árvore de pesquisa binária,
+as seguintes condições devem ser satisfeitas pela árvore vermelha-preta:
+
+- Cada nó é tanto vermelho ou preto.
+- O nó raíz é preto. Esta regra algumas vezes é omitida.
+Tendo em vista que a raíz pode sempre ser alterada de vermelho para preto,
+mas não de preto para vermelho, esta regra tem pouco efeito na análise.
+- Todas as folhas (Nulo/NIL) são pretas.
+- Caso um nó é vermelho, então seus filhos serão pretos.
+- Cada caminho de um determinado nó para qualquer um dos seus nós nulos (NIL)
+descendentes contém o mesmo número de nós pretos.
+
+Algumas definições: o número de nós pretos da raiz até um nó é a
+**profundidade preta**(_black depth_) do nó; o número uniforme de nós pretos
+em todos os caminhos da raíz até as folhas são chamados de **altura negra**
+(_black-height_) da árvore vermelha-preta.
+
+Essas restrições impõem uma propriedade crítica de árvores vermelhas e pretas:
+_o caminho da raiz até a folha mais distante não possui mais que o dobro do
+comprimento do caminho da raiz até a folha mais próxima_.
+O resultado é que a árvore é grosseiramente balanceada na altura.
+
+Tendo em vista que operações como inserções, remoção e pesquisa de valores
+requerem nos piores dos casos um tempo proporcional a altura da ávore,
+este limite superior teórico na altura permite que as árvores vermelha-preta
+sejam eficientes no pior dos casos, ao contrário das árvores de busca binária
+comuns.
+
+## Balanceamento durante a inserção
+
+### Se o tio é VERMELHO
+![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase2.png)
+
+### Se o tio é PRETO
+
+- Caso Esquerda Esquerda (`p` é o filho a esquerda de `g` e `x`, é o filho a esquerda de `p`)
+- Caso Esquerda Direita (`p` é o filho a esquerda de `g` e `x`, é o filho a direita de `p`)
+- Caso Direita Direita (`p` é o filho a direita de `g` e `x`, é o filho da direita de `p`)
+- Caso Direita Esqueda (`p` é o filho a direita de `g` e `x`, é o filho a esquerda de `p`)
+
+#### Caso Esquerda Esquerda (Veja g, p e x)
+
+![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3a1.png)
+
+#### Caso Esquerda Direita (Veja g, p e x)
+
+![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3b.png)
+
+#### Caso Direita Direita (Veja g, p e x)
+
+![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3c.png)
+
+#### Caso Direita Esquerda (Veja g, p e x)
+
+![Red Black Tree Balancing](https://www.geeksforgeeks.org/wp-content/uploads/redBlackCase3d.png)
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree)
+- [Red Black Tree Insertion by Tushar Roy (YouTube)](https://www.youtube.com/watch?v=UaLIHuR1t8Q&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=63)
+- [Red Black Tree Deletion by Tushar Roy (YouTube)](https://www.youtube.com/watch?v=CTvfzU_uNKE&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=64)
+- [Red Black Tree Insertion on GeeksForGeeks](https://www.geeksforgeeks.org/red-black-tree-set-2-insert/)
+- [Red Black Tree Interactive Visualisations](https://www.cs.usfca.edu/~galles/visualization/RedBlack.html)
diff --git a/src/data-structures/tree/red-black-tree/RedBlackTree.js b/src/data-structures/tree/red-black-tree/RedBlackTree.js
index 6bb47130c8..6e747afc75 100644
--- a/src/data-structures/tree/red-black-tree/RedBlackTree.js
+++ b/src/data-structures/tree/red-black-tree/RedBlackTree.js
@@ -155,7 +155,7 @@ export default class RedBlackTree extends BinarySearchTree {
       parentNode.parent = null;
     }
 
-    // Swap colors of granParent and parent nodes.
+    // Swap colors of grandParentNode and parentNode.
     this.swapNodeColors(parentNode, grandParentNode);
 
     // Return new root node.
diff --git a/src/data-structures/tree/segment-tree/README.md b/src/data-structures/tree/segment-tree/README.md
index 0049daf19c..7c332b0262 100644
--- a/src/data-structures/tree/segment-tree/README.md
+++ b/src/data-structures/tree/segment-tree/README.md
@@ -1,5 +1,8 @@
 # Segment Tree
 
+_Read this in other languages:_
+[_Português_](README.pt-BR.md) 
+
 In computer science, a **segment tree** also known as a statistic tree 
 is a tree data structure used for storing information about intervals, 
 or segments. It allows querying which of the stored segments contain 
@@ -40,7 +43,7 @@ and geographic information systems.
 Current implementation of Segment Tree implies that you may
 pass any binary (with two input params) function to it and 
 thus you're able to do range query for variety of functions.
-In tests you may fins examples of doing `min`, `max` and `sam` range
+In tests you may find examples of doing `min`, `max` and `sum` range
 queries on SegmentTree.
  
 ## References
diff --git a/src/data-structures/tree/segment-tree/README.pt-BR.md b/src/data-structures/tree/segment-tree/README.pt-BR.md
new file mode 100644
index 0000000000..729a35c4ab
--- /dev/null
+++ b/src/data-structures/tree/segment-tree/README.pt-BR.md
@@ -0,0 +1,48 @@
+# Árvore de Segmento (Segment Tree)
+
+Na ciência da computação, uma **árvore de segmento** também conhecida como
+árvore estatística é uma árvore de estrutura de dados utilizadas para
+armazenar informações sobre intervalores ou segmentos. Ela permite pesquisas
+no qual os segmentos armazenados contém um ponto fornecido. Isto é,
+em princípio, uma estrutura estática; ou seja, é uma estrutura que não pode
+ser modificada depois de inicializada. Uma estrutura de dados similar é a
+árvore de intervalos.
+
+Uma árvore de segmento é uma árvore binária. A raíz da árvore representa a
+_array_ inteira. Os dois filhos da raíz representam a primeira e a segunda
+metade da _array_. Similarmente, os filhos de cada nó correspondem ao número
+das duas metadas da _array_ correspondente do nó.
+
+Nós construímos a árvore debaixo para cima, com o valor de cada nó sendo o 
+"mínimo" (ou qualquer outra função) dos valores de seus filhos. Isto consumirá
+tempo `O(n log n)`. O número de oprações realizadas é equivalente a altura da
+árvore, pela qual consome tempo `O(log n)`. Para fazer consultas de intervalos,
+cada nó divide a consulta em duas partes, sendo uma sub consulta para cada filho.
+Se uma pesquisa contém todo o _subarray_ de um nó, nós podemos utilizar do valor
+pré-calculado do nó. Utilizando esta otimização, nós podemos provar que somente
+operações mínimas `O(log n)` são realizadas.
+
+![Min Segment Tree](https://www.geeksforgeeks.org/wp-content/uploads/RangeMinimumQuery.png)
+
+![Sum Segment Tree](https://www.geeksforgeeks.org/wp-content/uploads/segment-tree1.png)
+
+## Aplicação
+
+Uma árvore de segmento é uma estrutura de dados designada a realizar
+certas operações de _array_ eficientemente, especialmente aquelas envolvendo
+consultas de intervalos.
+
+Aplicações da árvore de segmentos são nas áreas de computação geométrica e
+sistemas de informação geográficos.
+
+A implementação atual da Árvore de Segmentos implica que você pode passar
+qualquer função binária (com dois parâmetros de entradas) e então, você
+será capaz de realizar consultas de intervalos para uma variedade de funções.
+Nos testes você poderá encontrar exemplos realizando `min`, `max` e consultas de
+intervalo `sum` na árvore segmentada (SegmentTree).
+ 
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Segment_tree)
+- [YouTube](https://www.youtube.com/watch?v=ZBHKZF5w4YU&index=65&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
+- [GeeksForGeeks](https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/)
diff --git a/src/data-structures/trie/README.ko-KO.md b/src/data-structures/trie/README.ko-KO.md
new file mode 100644
index 0000000000..e57142ece4
--- /dev/null
+++ b/src/data-structures/trie/README.ko-KO.md
@@ -0,0 +1,19 @@
+# Trie
+
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_Português_](README.pt-BR.md),
+[_Українська_](README.uk-UA.md),
+[_한국어_](README.ko-KO.md)
+
+컴퓨터 과학에서 **트라이**는 디지털 트리라고도 불리며 때로는 기수 트리 또는 접두사 트리(접두사로 검색할 수 있기 때문에)라고도 불리며 일종의 검색 트리입니다. 키가 보통 문자열인 동적 집합 또는 연관 배열을 저장하는 데 사용되는 순서가 지정된 트리 데이터 구조입니다. 이진 검색 트리와 달리 트리의 어떤 노드도 해당 노드와 연결된 키를 저장하지 않으며 대신 트리의 위치가 해당 노드와 연결된 키를 정의합니다. 노드의 모든 하위 항목은 해당 노드와 연결된 문자열의 공통 접두사를 가지며 루트는 빈 문자열과 연결됩니다. 값은 모든 노드와 반드시 연결되지는 않습니다. 오히려 값은 나뭇잎과 관심 있는 키에 해당하는 일부 내부 노드에만 연결되는 경향이 있습니다. 접두사 트리의 공간에 최적화된 표현은 콤팩트 접두사 트리를 참조하십시오.
+
+![Trie](./images/trie.jpg)
+
+_Made with [okso.app](https://okso.app)_
+
+## 참조
+
+- [Wikipedia](<https://ko.wikipedia.org/wiki/%ED%8A%B8%EB%9D%BC%EC%9D%B4_(%EC%BB%B4%ED%93%A8%ED%8C%85)>)
+- [YouTube](https://www.youtube.com/watch?v=zIjfhVPRZCg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=7&t=0s)
diff --git a/src/data-structures/trie/README.md b/src/data-structures/trie/README.md
index 6a8ee662d8..b3bba5de1e 100644
--- a/src/data-structures/trie/README.md
+++ b/src/data-structures/trie/README.md
@@ -1,21 +1,30 @@
 # Trie
 
-In computer science, a **trie**, also called digital tree and sometimes 
-radix tree or prefix tree (as they can be searched by prefixes), 
-is a kind of search tree—an ordered tree data structure that is 
-used to store a dynamic set or associative array where the keys 
-are usually strings. Unlike a binary search tree, no node in the 
-tree stores the key associated with that node; instead, its 
+_Read this in other languages:_
+[_简体中文_](README.zh-CN.md),
+[_Русский_](README.ru-RU.md),
+[_Português_](README.pt-BR.md),
+[_Українська_](README.uk-UA.md),
+[_한국어_](README.ko-KO.md)
+
+In computer science, a **trie**, also called digital tree and sometimes
+radix tree or prefix tree (as they can be searched by prefixes),
+is a kind of search tree—an ordered tree data structure that is
+used to store a dynamic set or associative array where the keys
+are usually strings. Unlike a binary search tree, no node in the
+tree stores the key associated with that node; instead, its
 position in the tree defines the key with which it is associated.
 All the descendants of a node have a common prefix of the string
-associated with that node, and the root is associated with the 
-empty string. Values are not necessarily associated with every 
-node. Rather, values tend only to be associated with leaves, 
-and with some inner nodes that correspond to keys of interest. 
-For the space-optimized presentation of prefix tree, see compact 
+associated with that node, and the root is associated with the
+empty string. Values are not necessarily associated with every
+node. Rather, values tend only to be associated with leaves,
+and with some inner nodes that correspond to keys of interest.
+For the space-optimized presentation of prefix tree, see compact
 prefix tree.
 
-![Trie](https://upload.wikimedia.org/wikipedia/commons/b/be/Trie_example.svg)
+![Trie](./images/trie.jpg)
+
+*Made with [okso.app](https://okso.app)*
 
 ## References
 
diff --git a/src/data-structures/trie/README.pt-BR.md b/src/data-structures/trie/README.pt-BR.md
new file mode 100644
index 0000000000..50a294b77c
--- /dev/null
+++ b/src/data-structures/trie/README.pt-BR.md
@@ -0,0 +1,27 @@
+# Árvore de Prefixos (Trie)
+
+Na ciência da computação, uma **trie**, também chamada de árvore digital (digital tree)
+e algumas vezes de _radix tree_ ou _prefix tree_ (tendo em vista que eles
+podem ser pesquisados por prefixos), é um tipo de árvore de pesquisa, uma 
+estrutura de dados de árvore ordenada que é usado para armazenar um
+conjunto dinâmico ou matriz associativa onde as chaves são geralmente _strings_.
+Ao contrário de uma árvore de pesquisa binária (binary search tree),
+nenhum nó na árvore armazena a chave associada a esse nó; em vez disso,
+sua posição na árvore define a chave com a qual ela está associada.
+Todos os descendentes de um nó possuem em comum o prefixo de uma _string_
+associada com aquele nó, e a raiz é associada com uma _string_ vazia.
+Valores não são necessariamente associados a todos nós. Em vez disso,
+os valores tendem a ser associados apenas a folhas e com alguns nós
+internos que correspondem a chaves de interesse.
+
+Para a apresentação otimizada do espaço da árvore de prefixo (_prefix tree_),
+veja árvore de prefixo compacto.
+
+![Trie](./images/trie.jpg)
+
+*Made with [okso.app](https://okso.app)*
+
+## Referências
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Trie)
+- [YouTube](https://www.youtube.com/watch?v=zIjfhVPRZCg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=7&t=0s)
diff --git a/src/data-structures/trie/README.ru-RU.md b/src/data-structures/trie/README.ru-RU.md
new file mode 100644
index 0000000000..30a4536383
--- /dev/null
+++ b/src/data-structures/trie/README.ru-RU.md
@@ -0,0 +1,28 @@
+# Префиксное дерево
+
+**Префиксное дерево** (также бор, луч, нагруженное или суффиксное дерево) в информатике - упорядоченная древовидная
+структура данных, которая используется для хранения динамических множеств или ассоциативных массивов, где
+ключом обычно выступают строки. Дерево называется префиксным, потому что поиск осуществляется по префиксам.
+
+В отличие от бинарного дерева, узлы не содержат ключи, соответствующие узлу. Представляет собой корневое дерево, каждое
+ребро которого помечено каким-то символом так, что для любого узла все рёбра, соединяющие этот узел с его сыновьями,
+помечены разными символами. Некоторые узлы префиксного дерева выделены (на рисунке они подписаны цифрами) и считается,
+что префиксное дерево содержит данную строку-ключ тогда и только тогда, когда эту строку можно прочитать на пути из
+корня до некоторого выделенного узла.
+
+Таким образом, в отличие от бинарных деревьев поиска, ключ, идентифицирующий конкретный узел дерева, не явно хранится в
+данном узле, а неявно задаётся положением данного узла в дереве. Получить ключ можно выписыванием подряд символов,
+помечающих рёбра на пути от корня до узла. Ключ корня дерева — пустая строка. Часто в выделенных узлах хранят
+дополнительную информацию, связанную с ключом, и обычно выделенными являются только листья и, возможно, некоторые
+внутренние узлы.
+
+![Префиксное дерево](./images/trie.jpg)
+
+*Made with [okso.app](https://okso.app)*
+
+На рисунке представлено префиксное дерево, содержащее ключи «A», «to», «tea», «ted», «ten», «i», «in», «inn».
+
+## Ссылки
+
+- [Wikipedia](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE)
+- [YouTube](https://www.youtube.com/watch?v=zIjfhVPRZCg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=7&t=0s)
diff --git a/src/data-structures/trie/README.uk-UA.md b/src/data-structures/trie/README.uk-UA.md
new file mode 100644
index 0000000000..3b1f01f0aa
--- /dev/null
+++ b/src/data-structures/trie/README.uk-UA.md
@@ -0,0 +1,27 @@
+# Префіксне дерево
+
+**Префіксне дерево** (Також промінь, навантажене або суфіксне дерево) в інформатиці - впорядкована деревоподібна
+структура даних, яка використовується для зберігання динамічних множин або асоціативних масивів, де
+ключем зазвичай виступають рядки. Дерево називається префіксним, тому що пошук здійснюється за префіксами.
+
+На відміну від бінарного дерева, вузли не містять ключів, що відповідають вузлу. Являє собою кореневе дерево, кожне
+ребро якого позначено якимось символом так, що для будь-якого вузла всі ребра, що з'єднують цей вузол з його синами,
+позначені різними символами. Деякі вузли префіксного дерева виділені (на малюнку вони підписані цифрами) і вважається,
+що префіксне дерево містить цей рядок-ключ тоді і тільки тоді, коли цей рядок можна прочитати на шляху з
+кореня до певного виділеного вузла.
+
+Таким чином, на відміну від бінарних дерев пошуку, ключ, що ідентифікує конкретний вузол дерева, не явно зберігається в
+цьому вузлі, а неявно задається положенням цього вузла в дереві. Отримати ключ можна виписуванням поспіль символів,
+помічають ребра по дорозі від кореня до вузла. Ключ кореня дерева - порожній рядок. Часто у виділених вузлах зберігають
+додаткову інформацію, пов'язану з ключем, і зазвичай виділеними є тільки листя і, можливо, деякі
+внутрішні вузли.
+
+![Префіксне дерево](./images/trie.jpg)
+
+*Made with [okso.app](https://okso.app)*
+
+На малюнку представлено префіксне дерево, що містить ключі. «A», «to», «tea», «ted», «ten», «i», «in», «inn».
+
+## Посилання
+
+- [Wikipedia](https://uk.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D1%84%D1%96%D0%BA%D1%81%D0%BD%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE)
diff --git a/src/data-structures/trie/README.zh-CN.md b/src/data-structures/trie/README.zh-CN.md
new file mode 100644
index 0000000000..e812f57523
--- /dev/null
+++ b/src/data-structures/trie/README.zh-CN.md
@@ -0,0 +1,18 @@
+# 字典树
+
+在计算机科学中, **字典树(trie,中文又被称为”单词查找树“或 ”键树“)**, 也称为数字树,有时候也被称为基数树或前缀树（因为它们可以通过前缀搜索）,它是一种搜索树--一种已排序的数据结构,通常用于存储动态集或键为字符串的关联数组。
+
+与二叉搜索树不同, 树上没有节点存储与该节点关联的键; 相反,节点在树上的位置定义了与之关联的键。一个节点的全部后代节点都有一个与该节点关联的通用的字符串前缀, 与根节点关联的是空字符串。
+
+值对于字典树中关联的节点来说,不是必需的,相反,值往往和相关的叶子相关,以及与一些键相关的内部节点相关。
+
+有关字典树的空间优化示意,请参阅紧凑前缀树
+
+![Trie](./images/trie.jpg)
+
+*Made with [okso.app](https://okso.app)*
+
+## 参考
+
+- [Wikipedia](https://en.wikipedia.org/wiki/Trie)
+- [YouTube](https://www.youtube.com/watch?v=zIjfhVPRZCg&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=7&t=0s)
diff --git a/src/data-structures/trie/Trie.js b/src/data-structures/trie/Trie.js
index 631ea1131f..bd618d20f4 100644
--- a/src/data-structures/trie/Trie.js
+++ b/src/data-structures/trie/Trie.js
@@ -24,6 +24,45 @@ export default class Trie {
     return this;
   }
 
+  /**
+   * @param {string} word
+   * @return {Trie}
+   */
+  deleteWord(word) {
+    const depthFirstDelete = (currentNode, charIndex = 0) => {
+      if (charIndex >= word.length) {
+        // Return if we're trying to delete the character that is out of word's scope.
+        return;
+      }
+
+      const character = word[charIndex];
+      const nextNode = currentNode.getChild(character);
+
+      if (nextNode == null) {
+        // Return if we're trying to delete a word that has not been added to the Trie.
+        return;
+      }
+
+      // Go deeper.
+      depthFirstDelete(nextNode, charIndex + 1);
+
+      // Since we're going to delete a word let's un-mark its last character isCompleteWord flag.
+      if (charIndex === (word.length - 1)) {
+        nextNode.isCompleteWord = false;
+      }
+
+      // childNode is deleted only if:
+      // - childNode has NO children
+      // - childNode.isCompleteWord === false
+      currentNode.removeChild(character);
+    };
+
+    // Start depth-first deletion from the head node.
+    depthFirstDelete(this.head);
+
+    return this;
+  }
+
   /**
    * @param {string} word
    * @return {string[]}
diff --git a/src/data-structures/trie/TrieNode.js b/src/data-structures/trie/TrieNode.js
index cf89fdd605..f650743787 100644
--- a/src/data-structures/trie/TrieNode.js
+++ b/src/data-structures/trie/TrieNode.js
@@ -29,7 +29,33 @@ export default class TrieNode {
       this.children.set(character, new TrieNode(character, isCompleteWord));
     }
 
-    return this.children.get(character);
+    const childNode = this.children.get(character);
+
+    // In cases similar to adding "car" after "carpet" we need to mark "r" character as complete.
+    childNode.isCompleteWord = childNode.isCompleteWord || isCompleteWord;
+
+    return childNode;
+  }
+
+  /**
+   * @param {string} character
+   * @return {TrieNode}
+   */
+  removeChild(character) {
+    const childNode = this.getChild(character);
+
+    // Delete childNode only if:
+    // - childNode has NO children,
+    // - childNode.isCompleteWord === false.
+    if (
+      childNode
+      && !childNode.isCompleteWord
+      && !childNode.hasChildren()
+    ) {
+      this.children.delete(character);
+    }
+
+    return this;
   }
 
   /**
@@ -40,6 +66,14 @@ export default class TrieNode {
     return this.children.has(character);
   }
 
+  /**
+   * Check whether current TrieNode has children or not.
+   * @return {boolean}
+   */
+  hasChildren() {
+    return this.children.getKeys().length !== 0;
+  }
+
   /**
    * @return {string[]}
    */
diff --git a/src/data-structures/trie/__test__/Trie.test.js b/src/data-structures/trie/__test__/Trie.test.js
index b66db94a07..4d60ad9c0c 100644
--- a/src/data-structures/trie/__test__/Trie.test.js
+++ b/src/data-structures/trie/__test__/Trie.test.js
@@ -23,6 +23,45 @@ describe('Trie', () => {
     expect(trie.head.getChild('c').getChild('a').getChild('t').toString()).toBe('t*');
   });
 
+  it('should delete words from trie', () => {
+    const trie = new Trie();
+
+    trie.addWord('carpet');
+    trie.addWord('car');
+    trie.addWord('cat');
+    trie.addWord('cart');
+    expect(trie.doesWordExist('carpet')).toBe(true);
+    expect(trie.doesWordExist('car')).toBe(true);
+    expect(trie.doesWordExist('cart')).toBe(true);
+    expect(trie.doesWordExist('cat')).toBe(true);
+
+    // Try to delete not-existing word first.
+    trie.deleteWord('carpool');
+    expect(trie.doesWordExist('carpet')).toBe(true);
+    expect(trie.doesWordExist('car')).toBe(true);
+    expect(trie.doesWordExist('cart')).toBe(true);
+    expect(trie.doesWordExist('cat')).toBe(true);
+
+    trie.deleteWord('carpet');
+    expect(trie.doesWordExist('carpet')).toEqual(false);
+    expect(trie.doesWordExist('car')).toEqual(true);
+    expect(trie.doesWordExist('cart')).toBe(true);
+    expect(trie.doesWordExist('cat')).toBe(true);
+
+    trie.deleteWord('cat');
+    expect(trie.doesWordExist('car')).toEqual(true);
+    expect(trie.doesWordExist('cart')).toBe(true);
+    expect(trie.doesWordExist('cat')).toBe(false);
+
+    trie.deleteWord('car');
+    expect(trie.doesWordExist('car')).toEqual(false);
+    expect(trie.doesWordExist('cart')).toBe(true);
+
+    trie.deleteWord('cart');
+    expect(trie.doesWordExist('car')).toEqual(false);
+    expect(trie.doesWordExist('cart')).toBe(false);
+  });
+
   it('should suggests next characters', () => {
     const trie = new Trie();
 
@@ -41,11 +80,14 @@ describe('Trie', () => {
 
     trie.addWord('cat');
     trie.addWord('cats');
+    trie.addWord('carpet');
     trie.addWord('car');
     trie.addWord('caption');
 
     expect(trie.doesWordExist('cat')).toBe(true);
     expect(trie.doesWordExist('cats')).toBe(true);
+    expect(trie.doesWordExist('carpet')).toBe(true);
+    expect(trie.doesWordExist('car')).toBe(true);
     expect(trie.doesWordExist('cap')).toBe(false);
     expect(trie.doesWordExist('call')).toBe(false);
   });
diff --git a/src/data-structures/trie/__test__/TrieNode.test.js b/src/data-structures/trie/__test__/TrieNode.test.js
index 3edacd24b3..c12c0113af 100644
--- a/src/data-structures/trie/__test__/TrieNode.test.js
+++ b/src/data-structures/trie/__test__/TrieNode.test.js
@@ -30,6 +30,16 @@ describe('TrieNode', () => {
     expect(trieNode.getChild('b')).toBeUndefined();
   });
 
+  it('should check if node has children', () => {
+    const trieNode = new TrieNode('c');
+
+    expect(trieNode.hasChildren()).toBe(false);
+
+    trieNode.addChild('a');
+
+    expect(trieNode.hasChildren()).toBe(true);
+  });
+
   it('should check if node has specific child', () => {
     const trieNode = new TrieNode('c');
 
@@ -49,4 +59,32 @@ describe('TrieNode', () => {
 
     expect(trieNode.suggestChildren()).toEqual(['a', 'o']);
   });
+
+  it('should delete child node if the child node has NO children', () => {
+    const trieNode = new TrieNode('c');
+    trieNode.addChild('a');
+    expect(trieNode.hasChild('a')).toBe(true);
+
+    trieNode.removeChild('a');
+    expect(trieNode.hasChild('a')).toBe(false);
+  });
+
+  it('should NOT delete child node if the child node has children', () => {
+    const trieNode = new TrieNode('c');
+    trieNode.addChild('a');
+    const childNode = trieNode.getChild('a');
+    childNode.addChild('r');
+
+    trieNode.removeChild('a');
+    expect(trieNode.hasChild('a')).toEqual(true);
+  });
+
+  it('should NOT delete child node if the child node completes a word', () => {
+    const trieNode = new TrieNode('c');
+    const IS_COMPLETE_WORD = true;
+    trieNode.addChild('a', IS_COMPLETE_WORD);
+
+    trieNode.removeChild('a');
+    expect(trieNode.hasChild('a')).toEqual(true);
+  });
 });
diff --git a/src/data-structures/trie/images/trie.jpg b/src/data-structures/trie/images/trie.jpg
new file mode 100644
index 0000000000..001b04f2e7
Binary files /dev/null and b/src/data-structures/trie/images/trie.jpg differ
diff --git a/src/playground/__test__/playground.test.js b/src/playground/__test__/playground.test.js
index 501a87a728..84a3e8301f 100644
--- a/src/playground/__test__/playground.test.js
+++ b/src/playground/__test__/playground.test.js
@@ -1,5 +1,8 @@
+import playground from '../playground';
+
 describe('playground', () => {
-  it('should perform playground tasks', () => {
-    // Place your playground tests here.
+  it('should return correct results', () => {
+    // Replace the next dummy test with your playground function tests.
+    expect(playground()).toBe(120);
   });
 });
diff --git a/src/playground/playground.js b/src/playground/playground.js
index 7872c9e3b8..b8c5b210bc 100644
--- a/src/playground/playground.js
+++ b/src/playground/playground.js
@@ -1 +1,12 @@
-// Place your playground code here.
+// Import any algorithmic dependencies you need for your playground code here.
+import factorial from '../algorithms/math/factorial/factorial';
+
+// Write your playground code inside the playground() function.
+// Test your code from __tests__/playground.test.js
+// Launch playground tests by running: npm test -- 'playground'
+function playground() {
+  // Replace the next line with your playground code.
+  return factorial(5);
+}
+
+export default playground;
diff --git a/src/utils/comparator/Comparator.js b/src/utils/comparator/Comparator.js
index 152c1ec1a6..f5c41b5c18 100644
--- a/src/utils/comparator/Comparator.js
+++ b/src/utils/comparator/Comparator.js
@@ -1,12 +1,15 @@
 export default class Comparator {
   /**
-   * @param {function(a: *, b: *)} [compareFunction]
+   * Constructor.
+   * @param {function(a: *, b: *)} [compareFunction] - It may be custom compare function that, let's
+   * say may compare custom objects together.
    */
   constructor(compareFunction) {
     this.compare = compareFunction || Comparator.defaultCompareFunction;
   }
 
   /**
+   * Default comparison function. It just assumes that "a" and "b" are strings or numbers.
    * @param {(string|number)} a
    * @param {(string|number)} b
    * @returns {number}
@@ -19,26 +22,59 @@ export default class Comparator {
     return a < b ? -1 : 1;
   }
 
+  /**
+   * Checks if two variables are equal.
+   * @param {*} a
+   * @param {*} b
+   * @return {boolean}
+   */
   equal(a, b) {
     return this.compare(a, b) === 0;
   }
 
+  /**
+   * Checks if variable "a" is less than "b".
+   * @param {*} a
+   * @param {*} b
+   * @return {boolean}
+   */
   lessThan(a, b) {
     return this.compare(a, b) < 0;
   }
 
+  /**
+   * Checks if variable "a" is greater than "b".
+   * @param {*} a
+   * @param {*} b
+   * @return {boolean}
+   */
   greaterThan(a, b) {
     return this.compare(a, b) > 0;
   }
 
+  /**
+   * Checks if variable "a" is less than or equal to "b".
+   * @param {*} a
+   * @param {*} b
+   * @return {boolean}
+   */
   lessThanOrEqual(a, b) {
     return this.lessThan(a, b) || this.equal(a, b);
   }
 
+  /**
+   * Checks if variable "a" is greater than or equal to "b".
+   * @param {*} a
+   * @param {*} b
+   * @return {boolean}
+   */
   greaterThanOrEqual(a, b) {
     return this.greaterThan(a, b) || this.equal(a, b);
   }
 
+  /**
+   * Reverses the comparison order.
+   */
   reverse() {
     const compareOriginal = this.compare;
     this.compare = (a, b) => compareOriginal(b, a);
