diff --git a/.gitignore b/.gitignore
index 485dee6..c75370f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,7 @@
.idea
+.git
+user_interface/.DS_Store
+user_interface/dist
+user_interface/node_modules
+backend/tests/.*
+__pycache__
diff --git a/Bandit.py b/Bandit.py
deleted file mode 100644
index 089e6db..0000000
--- a/Bandit.py
+++ /dev/null
@@ -1,299 +0,0 @@
-from __future__ import annotations
-
-from typing import Dict, Tuple, List, Optional, Union
-import numpy as np
-from CustomExceptions import CombinationLengthException, CombinationValuesException, WinningMoneyException, \
- ProbabilitiesSumException, ProbabilitiesArrayLengthException, SettingStateException
-
-
-class OneHandBandit:
- """
- There are m*n slots(columns,row)
- and k pictures for each slot(column)
- Pictures represented as integers 0..k-1
- (You can create visuals by setting them to real image files)
- """
- players: List[OneHandBandit] = []
-
- def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None):
- """
- m: number of columns
- n: number of rows
- k: number of pictures
- k(number of pictures) must be greater or equal to n(number of rows)
- """
-
- self.drumsColumns = [[i for i in range(k)] for _ in range(m)]
- self.columnsNumb = m
- self.rowsNumb = n
- self.picturesNumb = k
-
- self.probabilities: List[float, ...] = list()
- self.setProbabilities(probs)
-
- self.combinations: Dict[Tuple[int, ...], int] = dict()
- self.price: int = 0
-
- self.money: int = 0
- self.moneySpent = 0
- self.moneyWon: int = 0
-
- # maybe not in constructor???
- # old code string: self.state = self.displayCurrentTurn()
- self.state: List[List[int,],] = list()
-
- OneHandBandit.players.append(self)
-
- # price of the 1 turn of game???
-
- # winning combinations input
- # combinations like (7,7,7):100$, (3,5,7):200$, ('king','king','king'):500$ etc
- def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
- """
- combs: dict of winning combinations
- example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
- """
- combs = combs.copy()
- for comb in combs:
- if len(comb) != self.columnsNumb:
- raise CombinationLengthException
- elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
- raise CombinationValuesException
- elif combs[comb] < 0:
- raise WinningMoneyException
-
- self.combinations = combs
-
- def addWinningComb(self, comb: Tuple[int, ...], win: int):
- self.combinations[comb] = win
-
- # the price of 1 turn
- def setPriceOfGame(self, price):
- self.price = price
-
- # money player gives to bandit
- def startGame(self, money: int, modeling: bool = False) -> None:
- if money > 0:
- self.money = money
- if not modeling:
- print(f"you deposited {money} money")
-
- # methods to good display of games...
-
- # Here is the trickiest part
- # we want to differ probabilities of images
- # on each column and row
- def setProbabilities(self, probs: Optional[List[float, ...]] = None):
- f"""
- set probabilities({self.probabilities}) for corresponding pictures(with indices from 0 till {self.picturesNumb})
- , where sum of probabilities have to equal 1
- """
- if probs is None:
- # uniform distribution
- probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
-
- if 0.001 < abs(sum(probs) - 1.0):
- # sum of probs dont equal 1
- raise ProbabilitiesSumException
-
- if len(probs) != self.picturesNumb:
- raise ProbabilitiesArrayLengthException
-
- self.probabilities = probs.copy()
-
- def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt"):
- f"""
- read probabilities from .txt file, where probabilities values delimit with one space,
- then use {self.setProbabilities} function
- raise:
- ValueError if probabilities values are not float
- """
- with open(filename, "r") as f:
- probsLine = f.readline()
- probs = list(map(lambda x: float(x), probsLine.split(" ")))
-
- self.setProbabilities(probs)
-
- def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
- newProbs = np.array(self.probabilities, dtype=float)
- divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
- newProbs[rolledPicturesIdxs] = 0.0
- newProbs = newProbs / divideCoeff
- return list(newProbs)
-
- def genBaseProbabilities(self, coef: float):
- # todo: now
- # не дуже розумію що мається на увазі під "рандомно генерувати ймовірності",
- # не розумію як їх генерувати, в залежності від коефіцієнту віддачі ігрового автомата,
- # не розумію, що означає (Монте-Карло) цилк гравця
- pass
-
- def getOneHandBanditReturn(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None) \
- -> Tuple[int, float]:
- oldProbs = self.probabilities
- oldMoneySpent = self.moneySpent
- oldMoneyWon = self.moneyWon
- if probs is not None:
- self.setProbabilities(probs)
- self.moneyWon = 0
- self.moneySpent = 0
-
- money = self.price * gamesCount
-
- modeling = True
- self.startGame(money, modeling=modeling)
- self.play(gamesCount, modeling=modeling)
- gameReturn = self.moneyWon
- gameReturnPercentage = gameReturn / self.moneySpent
- if probs is not None:
- self.setProbabilities(oldProbs)
- self.moneyWon = oldMoneyWon
- self.moneySpent = oldMoneySpent
-
- return gameReturn, gameReturnPercentage
-
- # randomly display n*m result of turn
- def currentTurn(self, modeling: bool = False) -> int:
- """
- return: np.array([np.array(dtype=int), ])
- example:
- np.array([np.array([0, 1, ... , self.picturesNumb-1]), ])
- """
- self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
- self.money -= self.price
- self.moneySpent += self.price
- won = self.currentWon()
-
- if not modeling:
- self.printState()
-
- return won
-
- # this methods could be with further changes....
- # our probalities are not necessary uniform!!!!
- # get random i-th column
- # TODO: generalize and modify this!!!
- # !!! U should implement different options for this method...
- def turnColumn(self, i) -> List[int,]:
- # get n(rowsNumb) images from k(picturesNumb) without duplicates
- # easy uniform probability form
- # could be kept for testing
- # but more complex form also implemented
- downIdx = self.rowsNumb - 1
- upIdx = 0
- rolledPicturesIdx = []
- resColumn = [-1, ] * self.rowsNumb
- for idx in range(self.rowsNumb):
- newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
-
- assert abs(sum(newProbs) - 1) < 0.001
-
- rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
- rolledPicturesIdx.append(rolledPicIdx)
-
- if (idx % 2) == 0:
- resColumn[downIdx] = rolledPicIdx
- downIdx -= 1
- else:
- resColumn[upIdx] = rolledPicIdx
- upIdx += 1
-
- return resColumn
-
- def getRow(self, i) -> np.array:
- return np.array([self.state[j][i] for j in range(self.columnsNumb)])
-
- def getColumn(self, j):
- return self.state[j]
-
- def printState(self):
- print(f"current state: \n{np.array(self.state).T}")
- # print("money spent:", self.moneySpent)
- print("money won:", self.moneyWon)
- print("money remained:", self.money)
-
- # getters ..setters
- def getState(self) -> np.array:
- return self.state
-
- def setState(self, state: List[List[int, ], ]) -> None:
- if all([len(col) == len(set(col)) for col in state]):
- self.state = state
- else:
- raise SettingStateException
-
- def currentWon(self) -> int:
- won = 0
- for i in range(self.rowsNumb):
- comb = tuple(self.getRow(i))
- if comb in self.combinations:
- # it is possible to have several winning combinations....
- won += self.combinations[comb]
-
- self.moneyWon += won # ???? not sure if it is needed
- return won
-
- # number of turns
- def play(self, n, modeling: bool = False):
- if self.money < self.price * n:
- print("Not enough money")
- return None
- # play game n times
- for _ in range(n):
- won = self.currentTurn(modeling=modeling)
- if not modeling:
- print(f"you won {won} money for one turn")
- print(f"you spent {self.price} money for one turn")
- print()
-
- # winning sum in fact could be (and often is) negative ;-))
- @property
- def currentPlayerWon(self):
- return self.moneyWon - self.moneySpent # not sure
-
- # todo: мається на увазі загальний виграш всіх гравців?
- @property
- def totalPlayersWon(self):
- totalWin = 0
- for p in OneHandBandit.players:
- totalWin += p.currentPlayerWon
- return totalWin
-
- # Our main goal
- # !! modification needed
- def getWinCoef(self):
- return self.moneyWon / self.moneySpent
-
-
-if __name__ == "__main__":
- p1 = OneHandBandit(3, 3, 4, probs=[0.5, 0.2, 0.2, 0.1])
- p1.setPriceOfGame(10)
- p1.setWinningCombs({(0, 0, 0): 15, (1, 1, 1): 15, (2, 2, 2): 15, (1, 2, 3): 20, })
- p1.addWinningComb((3, 3, 3), 15)
- modelingGamesCount = 10 ** 4
- returnMoney, returnCoef = p1.getOneHandBanditReturn(gamesCount=modelingGamesCount)
- print(f"this one hand bandit return: {returnMoney} with return coefficient: {returnCoef} "
- f"for {modelingGamesCount} games")
- p1.startGame(100)
- p1.play(10)
-
- print(f"{p1.currentPlayerWon=}\n")
-
- # p2 = OneHandBandit(4, 3, 5, probs=[0.1, 0.1, 0.1, 0.1, 0.6])
- p2 = OneHandBandit(4, 3, 5)
- p2.setProbabilitiesFromTxtFile()
- p2.setPriceOfGame(10)
- p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
- p2.addWinningComb((4, 4, 4, 4), 15)
-
- modelingGamesCount = 10 ** 4
- returnMoney, returnCoef = p1.getOneHandBanditReturn(gamesCount=modelingGamesCount)
- print(f"this one hand bandit return: {returnMoney} with return coefficient: {returnCoef} "
- f"for {modelingGamesCount} games")
-
- p2.startGame(100)
- p2.play(10)
-
- print(f"{p2.currentPlayerWon=}")
-
- print(f"{p2.totalPlayersWon=}")
diff --git a/Pipfile b/Pipfile
index c52bbfe..d8c303b 100644
--- a/Pipfile
+++ b/Pipfile
@@ -4,11 +4,18 @@ verify_ssl = true
name = "pypi"
[packages]
-numpy = "*"
jupyter = "*"
-coverage = "*"
+numpy = "*"
+pipfile = "*"
+flask = "*"
+tqdm = "*"
+scipy = "*"
+matplotlib = "*"
[dev-packages]
+coverage = "*"
+pyment = "*"
+pdoc3 = "*"
[requires]
python_version = "3.8"
diff --git a/Pipfile.lock b/Pipfile.lock
index 8de89fb..da1b91c 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "1a4782ac8359026ac65fa6f2bb8cadbb28adcd196024854cad66b7b8e6b661d8"
+ "sha256": "6aedd154b55287cd81c4ddc32f38e3137cca7cd7b2269d982f4837be8ee92202"
},
"pipfile-spec": 6,
"requires": {
@@ -144,60 +144,29 @@
],
"version": "==1.15.0"
},
+ "click": {
+ "hashes": [
+ "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
+ "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==8.1.3"
+ },
"colorama": {
"hashes": [
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
],
- "markers": "sys_platform == 'win32'",
+ "markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'",
"version": "==0.4.4"
},
- "coverage": {
+ "cycler": {
"hashes": [
- "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9",
- "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d",
- "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf",
- "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7",
- "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6",
- "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4",
- "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059",
- "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39",
- "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536",
- "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac",
- "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c",
- "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903",
- "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d",
- "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05",
- "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684",
- "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1",
- "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f",
- "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7",
- "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca",
- "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad",
- "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca",
- "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d",
- "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92",
- "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4",
- "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf",
- "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6",
- "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1",
- "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4",
- "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359",
- "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3",
- "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620",
- "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512",
- "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69",
- "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2",
- "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518",
- "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0",
- "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa",
- "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4",
- "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e",
- "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1",
- "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2"
+ "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3",
+ "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"
],
- "index": "pypi",
- "version": "==6.3.2"
+ "markers": "python_version >= '3.6'",
+ "version": "==0.11.0"
},
"debugpy": {
"hashes": [
@@ -261,6 +230,30 @@
],
"version": "==2.15.3"
},
+ "flask": {
+ "hashes": [
+ "sha256:315ded2ddf8a6281567edb27393010fe3406188bafbfe65a3339d5787d89e477",
+ "sha256:fad5b446feb0d6db6aec0c3184d16a8c1f6c3e464b511649c8918a9be100b4fe"
+ ],
+ "index": "pypi",
+ "version": "==2.1.2"
+ },
+ "fonttools": {
+ "hashes": [
+ "sha256:c0fdcfa8ceebd7c1b2021240bd46ef77aa8e7408cf10434be55df52384865f8e",
+ "sha256:f829c579a8678fa939a1d9e9894d01941db869de44390adb49ce67055a06cc2a"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==4.33.3"
+ },
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:5d26852efe48c0a32b0509ffbc583fda1a2266545a78d104a6f4aff3db17d700",
+ "sha256:c58c8eb8a762858f49e18436ff552e83914778e50e9d2f1660535ffb364552ec"
+ ],
+ "markers": "python_version < '3.10'",
+ "version": "==4.11.4"
+ },
"importlib-resources": {
"hashes": [
"sha256:b6062987dfc51f0fcb809187cffbd60f35df7acb4589091f154214af6d0d49d3",
@@ -271,19 +264,19 @@
},
"ipykernel": {
"hashes": [
- "sha256:0e28273e290858393e86e152b104e5506a79c13d25b951ac6eca220051b4be60",
- "sha256:2b0987af43c0d4b62cecb13c592755f599f96f29aafe36c01731aaa96df30d39"
+ "sha256:720f2a07aadf70ca05b034f2cdd0590a8bd56581ddf3b7ec4a37075366270e89",
+ "sha256:cbf50ed3dc9998d4077d8cd263275f232cbe67dbd2e79cd6d2644f3a4418148b"
],
"markers": "python_version >= '3.7'",
- "version": "==6.13.0"
+ "version": "==6.14.0"
},
"ipython": {
"hashes": [
- "sha256:341456643a764c28f670409bbd5d2518f9b82c013441084ff2c2fc999698f83b",
- "sha256:807ae3cf43b84693c9272f70368440a9a7eaa2e7e6882dad943c32fbf7e51402"
+ "sha256:7ca74052a38fa25fe9bedf52da0be7d3fdd2fb027c3b778ea78dfe8c212937d1",
+ "sha256:f2db3a10254241d9b447232cec8b424847f338d9d36f9a577a6192c332a46abd"
],
"markers": "python_version >= '3.3'",
- "version": "==8.3.0"
+ "version": "==8.4.0"
},
"ipython-genutils": {
"hashes": [
@@ -299,6 +292,14 @@
],
"version": "==7.7.0"
},
+ "itsdangerous": {
+ "hashes": [
+ "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
+ "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.1.2"
+ },
"jedi": {
"hashes": [
"sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d",
@@ -317,11 +318,11 @@
},
"jsonschema": {
"hashes": [
- "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83",
- "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823"
+ "sha256:1c92d2db1900b668201f1797887d66453ab1fbfea51df8e4b46236689c427baf",
+ "sha256:9d6397ba4a6c0bf0300736057f649e3e12ecbc07d3e81a0dacb72de4e9801957"
],
"markers": "python_version >= '3.7'",
- "version": "==4.4.0"
+ "version": "==4.6.0"
},
"jupyter": {
"hashes": [
@@ -334,11 +335,11 @@
},
"jupyter-client": {
"hashes": [
- "sha256:3bcc8e08a294d0fa9406e48cfe17e11ef0efdb7c504fe8cc335128e3ef8f3dac",
- "sha256:671dd2d90d03f41716b09627a4eb06bb37875f92bf6563cc2ce4fe71c61c5cda"
+ "sha256:17d74b0d0a7b24f1c8c527b24fcf4607c56bee542ffe8e3418e50b21e514b621",
+ "sha256:aa9a6c32054b290374f95f73bb0cae91455c58dfb84f65c8591912b8f65e6d56"
],
"markers": "python_version >= '3.7'",
- "version": "==7.3.0"
+ "version": "==7.3.4"
},
"jupyter-console": {
"hashes": [
@@ -372,6 +373,55 @@
"markers": "python_version >= '3.6'",
"version": "==1.1.0"
},
+ "kiwisolver": {
+ "hashes": [
+ "sha256:007799c7fa934646318fc128b033bb6e6baabe7fbad521bfb2279aac26225cd7",
+ "sha256:130c6c35eded399d3967cf8a542c20b671f5ba85bd6f210f8b939f868360e9eb",
+ "sha256:1858ad3cb686eccc7c6b7c5eac846a1cfd45aacb5811b2cf575e80b208f5622a",
+ "sha256:1ae7aa0784aeadfbd693c27993727792fbe1455b84d49970bad5886b42976b18",
+ "sha256:1d2c744aeedce22c122bb42d176b4aa6d063202a05a4abdacb3e413c214b3694",
+ "sha256:21a3a98f0a21fc602663ca9bce2b12a4114891bdeba2dea1e9ad84db59892fca",
+ "sha256:22ccba48abae827a0f952a78a7b1a7ff01866131e5bbe1f826ce9bda406bf051",
+ "sha256:26b5a70bdab09e6a2f40babc4f8f992e3771751e144bda1938084c70d3001c09",
+ "sha256:2d76780d9c65c7529cedd49fa4802d713e60798d8dc3b0d5b12a0a8f38cca51c",
+ "sha256:325fa1b15098e44fe4590a6c5c09a212ca10c6ebb5d96f7447d675f6c8340e4e",
+ "sha256:3a297d77b3d6979693f5948df02b89431ae3645ec95865e351fb45578031bdae",
+ "sha256:3b1dcbc49923ac3c973184a82832e1f018dec643b1e054867d04a3a22255ec6a",
+ "sha256:40240da438c0ebfe2aa76dd04b844effac6679423df61adbe3437d32f23468d9",
+ "sha256:46c6e5018ba31d5ee7582f323d8661498a154dea1117486a571db4c244531f24",
+ "sha256:46fb56fde006b7ef5f8eaa3698299b0ea47444238b869ff3ced1426aa9fedcb5",
+ "sha256:4dc350cb65fe4e3f737d50f0465fa6ea0dcae0e5722b7edf5d5b0a0e3cd2c3c7",
+ "sha256:51078855a16b7a4984ed2067b54e35803d18bca9861cb60c60f6234b50869a56",
+ "sha256:547111ef7cf13d73546c2de97ce434935626c897bdec96a578ca100b5fcd694b",
+ "sha256:5fb73cc8a34baba1dfa546ae83b9c248ef6150c238b06fc53d2773685b67ec67",
+ "sha256:654280c5f41831ddcc5a331c0e3ce2e480bbc3d7c93c18ecf6236313aae2d61a",
+ "sha256:6b3136eecf7e1b4a4d23e4b19d6c4e7a8e0b42d55f30444e3c529700cdacaa0d",
+ "sha256:7118ca592d25b2957ff7b662bc0fe4f4c2b5d5b27814b9b1bc9f2fb249a970e7",
+ "sha256:71af5b43e4fa286a35110fc5bb740fdeae2b36ca79fbcf0a54237485baeee8be",
+ "sha256:747190fcdadc377263223f8f72b038381b3b549a8a3df5baf4d067da4749b046",
+ "sha256:8395064d63b26947fa2c9faeea9c3eee35e52148c5339c37987e1d96fbf009b3",
+ "sha256:84f85adfebd7d3c3db649efdf73659e1677a2cf3fa6e2556a3f373578af14bf7",
+ "sha256:86bcf0009f2012847a688f2f4f9b16203ca4c835979a02549aa0595d9f457cc8",
+ "sha256:ab8a15c2750ae8d53e31f77a94f846d0a00772240f1c12817411fa2344351f86",
+ "sha256:af24b21c2283ca69c416a8a42cde9764dc36c63d3389645d28c69b0e93db3cd7",
+ "sha256:afe173ac2646c2636305ab820cc0380b22a00a7bca4290452e7166b4f4fa49d0",
+ "sha256:b9eb88593159a53a5ee0b0159daee531ff7dd9c87fa78f5d807ca059c7eb1b2b",
+ "sha256:c16635f8dddbeb1b827977d0b00d07b644b040aeb9ff8607a9fc0997afa3e567",
+ "sha256:ca3eefb02ef17257fae8b8555c85e7c1efdfd777f671384b0e4ef27409b02720",
+ "sha256:caa59e2cae0e23b1e225447d7a9ddb0f982f42a6a22d497a484dfe62a06f7c0e",
+ "sha256:cb55258931448d61e2d50187de4ee66fc9d9f34908b524949b8b2b93d0c57136",
+ "sha256:d248c46c0aa406695bda2abf99632db991f8b3a6d46018721a2892312a99f069",
+ "sha256:d2578e5149ff49878934debfacf5c743fab49eca5ecdb983d0b218e1e554c498",
+ "sha256:dd22085446f3eca990d12a0878eeb5199dc9553b2e71716bfe7bed9915a472ab",
+ "sha256:e7cf940af5fee00a92e281eb157abe8770227a5255207818ea9a34e54a29f5b2",
+ "sha256:f70f3d028794e31cf9d1a822914efc935aadb2438ec4e8d4871d95eb1ce032d6",
+ "sha256:fd2842a0faed9ab9aba0922c951906132d9384be89690570f0ed18cd4f20e658",
+ "sha256:fd628e63ffdba0112e3ddf1b1e9f3db29dd8262345138e08f4938acbc6d0805a",
+ "sha256:ffd7cf165ff71afb202b3f36daafbf298932bee325aac9f58e1c9cd55838bef0"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==1.4.3"
+ },
"markupsafe": {
"hashes": [
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
@@ -418,6 +468,47 @@
"markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
+ "matplotlib": {
+ "hashes": [
+ "sha256:03bbb3f5f78836855e127b5dab228d99551ad0642918ccbf3067fcd52ac7ac5e",
+ "sha256:24173c23d1bcbaed5bf47b8785d27933a1ac26a5d772200a0f3e0e38f471b001",
+ "sha256:2a0967d4156adbd0d46db06bc1a877f0370bce28d10206a5071f9ecd6dc60b79",
+ "sha256:2e8bda1088b941ead50caabd682601bece983cadb2283cafff56e8fcddbf7d7f",
+ "sha256:31fbc2af27ebb820763f077ec7adc79b5a031c2f3f7af446bd7909674cd59460",
+ "sha256:364e6bca34edc10a96aa3b1d7cd76eb2eea19a4097198c1b19e89bee47ed5781",
+ "sha256:3d8e129af95b156b41cb3be0d9a7512cc6d73e2b2109f82108f566dbabdbf377",
+ "sha256:44c6436868186564450df8fd2fc20ed9daaef5caad699aa04069e87099f9b5a8",
+ "sha256:48cf850ce14fa18067f2d9e0d646763681948487a8080ec0af2686468b4607a2",
+ "sha256:49a5938ed6ef9dda560f26ea930a2baae11ea99e1c2080c8714341ecfda72a89",
+ "sha256:4a05f2b37222319753a5d43c0a4fd97ed4ff15ab502113e3f2625c26728040cf",
+ "sha256:4a44cdfdb9d1b2f18b1e7d315eb3843abb097869cd1ef89cfce6a488cd1b5182",
+ "sha256:4fa28ca76ac5c2b2d54bc058b3dad8e22ee85d26d1ee1b116a6fd4d2277b6a04",
+ "sha256:5844cea45d804174bf0fac219b4ab50774e504bef477fc10f8f730ce2d623441",
+ "sha256:5a32ea6e12e80dedaca2d4795d9ed40f97bfa56e6011e14f31502fdd528b9c89",
+ "sha256:6c623b355d605a81c661546af7f24414165a8a2022cddbe7380a31a4170fa2e9",
+ "sha256:751d3815b555dcd6187ad35b21736dc12ce6925fc3fa363bbc6dc0f86f16484f",
+ "sha256:75c406c527a3aa07638689586343f4b344fcc7ab1f79c396699eb550cd2b91f7",
+ "sha256:77157be0fc4469cbfb901270c205e7d8adb3607af23cef8bd11419600647ceed",
+ "sha256:7d7705022df2c42bb02937a2a824f4ec3cca915700dd80dc23916af47ff05f1a",
+ "sha256:7f409716119fa39b03da3d9602bd9b41142fab7a0568758cd136cd80b1bf36c8",
+ "sha256:9480842d5aadb6e754f0b8f4ebeb73065ac8be1855baa93cd082e46e770591e9",
+ "sha256:9776e1a10636ee5f06ca8efe0122c6de57ffe7e8c843e0fb6e001e9d9256ec95",
+ "sha256:a91426ae910819383d337ba0dc7971c7cefdaa38599868476d94389a329e599b",
+ "sha256:b4fedaa5a9aa9ce14001541812849ed1713112651295fdddd640ea6620e6cf98",
+ "sha256:b6c63cd01cad0ea8704f1fd586e9dc5777ccedcd42f63cbbaa3eae8dd41172a1",
+ "sha256:b8d3f4e71e26307e8c120b72c16671d70c5cd08ae412355c11254aa8254fb87f",
+ "sha256:c4b82c2ae6d305fcbeb0eb9c93df2602ebd2f174f6e8c8a5d92f9445baa0c1d3",
+ "sha256:c772264631e5ae61f0bd41313bbe48e1b9bcc95b974033e1118c9caa1a84d5c6",
+ "sha256:c87973ddec10812bddc6c286b88fdd654a666080fbe846a1f7a3b4ba7b11ab78",
+ "sha256:e2b696699386766ef171a259d72b203a3c75d99d03ec383b97fc2054f52e15cf",
+ "sha256:ea75df8e567743207e2b479ba3d8843537be1c146d4b1e3e395319a4e1a77fe9",
+ "sha256:ebc27ad11df3c1661f4677a7762e57a8a91dd41b466c3605e90717c9a5f90c82",
+ "sha256:ee0b8e586ac07f83bb2950717e66cb305e2859baf6f00a9c39cc576e0ce9629c",
+ "sha256:ee175a571e692fc8ae8e41ac353c0e07259113f4cb063b0ec769eff9717e84bb"
+ ],
+ "index": "pypi",
+ "version": "==3.5.2"
+ },
"matplotlib-inline": {
"hashes": [
"sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee",
@@ -435,11 +526,11 @@
},
"nbclient": {
"hashes": [
- "sha256:2eed35fc954716cdf0a01ea8cbdd9f9316761479008570059e2f5de29e139423",
- "sha256:3f89a403c6badf24d2855a455b69a80985b3b27e04111243fdb6a88a28d27031"
+ "sha256:cdef7757cead1735d2c70cc66095b072dced8a1e6d1c7639ef90cd3e04a11f2e",
+ "sha256:f251bba200a2b401a061dfd700a7a70b5772f664fb49d4a2d3e5536ec0e98c76"
],
"markers": "python_version >= '3.7'",
- "version": "==0.6.0"
+ "version": "==0.6.4"
},
"nbconvert": {
"hashes": [
@@ -451,11 +542,11 @@
},
"nbformat": {
"hashes": [
- "sha256:38856d97de49e8292e2d5d8f595e9d26f02abfd87e075d450af4511870b40538",
- "sha256:fcc5ab8cb74e20b19570b5be809e2dba9b82836fd2761a89066ad43394ba29f5"
+ "sha256:0d6072aaec95dddc39735c144ee8bbc6589c383fb462e4058abc855348152dad",
+ "sha256:44ba5ca6acb80c5d5a500f1e5b83ede8cbe364d5a495c4c8cf60aaf1ba656501"
],
"markers": "python_version >= '3.7'",
- "version": "==5.3.0"
+ "version": "==5.4.0"
},
"nest-asyncio": {
"hashes": [
@@ -467,37 +558,39 @@
},
"notebook": {
"hashes": [
- "sha256:709b1856a564fe53054796c80e17a67262071c86bfbdfa6b96aaa346113c555a",
- "sha256:b4a6baf2eba21ce67a0ca11a793d1781b06b8078f34d06c710742e55f3eee505"
+ "sha256:6268c9ec9048cff7a45405c990c29ac9ca40b0bc3ec29263d218c5e01f2b4e86",
+ "sha256:8c07a3bb7640e371f8a609bdbb2366a1976c6a2589da8ef917f761a61e3ad8b1"
],
"markers": "python_version >= '3.7'",
- "version": "==6.4.11"
+ "version": "==6.4.12"
},
"numpy": {
"hashes": [
- "sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676",
- "sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4",
- "sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce",
- "sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123",
- "sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1",
- "sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e",
- "sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5",
- "sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d",
- "sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a",
- "sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab",
- "sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75",
- "sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168",
- "sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4",
- "sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f",
- "sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18",
- "sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62",
- "sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe",
- "sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430",
- "sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802",
- "sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa"
+ "sha256:0791fbd1e43bf74b3502133207e378901272f3c156c4df4954cad833b1380207",
+ "sha256:1ce7ab2053e36c0a71e7a13a7475bd3b1f54750b4b433adc96313e127b870887",
+ "sha256:2d487e06ecbf1dc2f18e7efce82ded4f705f4bd0cd02677ffccfb39e5c284c7e",
+ "sha256:37431a77ceb9307c28382c9773da9f306435135fae6b80b62a11c53cfedd8802",
+ "sha256:3e1ffa4748168e1cc8d3cde93f006fe92b5421396221a02f2274aab6ac83b077",
+ "sha256:425b390e4619f58d8526b3dcf656dde069133ae5c240229821f01b5f44ea07af",
+ "sha256:43a8ca7391b626b4c4fe20aefe79fec683279e31e7c79716863b4b25021e0e74",
+ "sha256:4c6036521f11a731ce0648f10c18ae66d7143865f19f7299943c985cdc95afb5",
+ "sha256:59d55e634968b8f77d3fd674a3cf0b96e85147cd6556ec64ade018f27e9479e1",
+ "sha256:64f56fc53a2d18b1924abd15745e30d82a5782b2cab3429aceecc6875bd5add0",
+ "sha256:7228ad13744f63575b3a972d7ee4fd61815b2879998e70930d4ccf9ec721dce0",
+ "sha256:9ce7df0abeabe7fbd8ccbf343dc0db72f68549856b863ae3dd580255d009648e",
+ "sha256:a911e317e8c826ea632205e63ed8507e0dc877dcdc49744584dfc363df9ca08c",
+ "sha256:b89bf9b94b3d624e7bb480344e91f68c1c6c75f026ed6755955117de00917a7c",
+ "sha256:ba9ead61dfb5d971d77b6c131a9dbee62294a932bf6a356e48c75ae684e635b3",
+ "sha256:c1d937820db6e43bec43e8d016b9b3165dcb42892ea9f106c70fb13d430ffe72",
+ "sha256:cc7f00008eb7d3f2489fca6f334ec19ca63e31371be28fd5dad955b16ec285bd",
+ "sha256:d4c5d5eb2ec8da0b4f50c9a843393971f31f1d60be87e0fb0917a49133d257d6",
+ "sha256:e96d7f3096a36c8754207ab89d4b3282ba7b49ea140e4973591852c77d09eb76",
+ "sha256:f0725df166cf4785c0bc4cbfb320203182b1ecd30fee6e541c8752a92df6aa32",
+ "sha256:f3eb268dbd5cfaffd9448113539e44e2dd1c5ca9ce25576f7c04a5453edc26fa",
+ "sha256:fb7a980c81dd932381f8228a426df8aeb70d59bbcda2af075b627bbc50207cba"
],
"index": "pypi",
- "version": "==1.22.3"
+ "version": "==1.22.4"
},
"packaging": {
"hashes": [
@@ -530,6 +623,57 @@
],
"version": "==0.7.5"
},
+ "pillow": {
+ "hashes": [
+ "sha256:088df396b047477dd1bbc7de6e22f58400dae2f21310d9e2ec2933b2ef7dfa4f",
+ "sha256:09e67ef6e430f90caa093528bd758b0616f8165e57ed8d8ce014ae32df6a831d",
+ "sha256:0b4d5ad2cd3a1f0d1df882d926b37dbb2ab6c823ae21d041b46910c8f8cd844b",
+ "sha256:0b525a356680022b0af53385944026d3486fc8c013638cf9900eb87c866afb4c",
+ "sha256:1d4331aeb12f6b3791911a6da82de72257a99ad99726ed6b63f481c0184b6fb9",
+ "sha256:20d514c989fa28e73a5adbddd7a171afa5824710d0ab06d4e1234195d2a2e546",
+ "sha256:2b291cab8a888658d72b575a03e340509b6b050b62db1f5539dd5cd18fd50578",
+ "sha256:3f6c1716c473ebd1649663bf3b42702d0d53e27af8b64642be0dd3598c761fb1",
+ "sha256:42dfefbef90eb67c10c45a73a9bc1599d4dac920f7dfcbf4ec6b80cb620757fe",
+ "sha256:488f3383cf5159907d48d32957ac6f9ea85ccdcc296c14eca1a4e396ecc32098",
+ "sha256:4d45dbe4b21a9679c3e8b3f7f4f42a45a7d3ddff8a4a16109dff0e1da30a35b2",
+ "sha256:53c27bd452e0f1bc4bfed07ceb235663a1df7c74df08e37fd6b03eb89454946a",
+ "sha256:55e74faf8359ddda43fee01bffbc5bd99d96ea508d8a08c527099e84eb708f45",
+ "sha256:59789a7d06c742e9d13b883d5e3569188c16acb02eeed2510fd3bfdbc1bd1530",
+ "sha256:5b650dbbc0969a4e226d98a0b440c2f07a850896aed9266b6fedc0f7e7834108",
+ "sha256:66daa16952d5bf0c9d5389c5e9df562922a59bd16d77e2a276e575d32e38afd1",
+ "sha256:6e760cf01259a1c0a50f3c845f9cad1af30577fd8b670339b1659c6d0e7a41dd",
+ "sha256:7502539939b53d7565f3d11d87c78e7ec900d3c72945d4ee0e2f250d598309a0",
+ "sha256:769a7f131a2f43752455cc72f9f7a093c3ff3856bf976c5fb53a59d0ccc704f6",
+ "sha256:7c150dbbb4a94ea4825d1e5f2c5501af7141ea95825fadd7829f9b11c97aaf6c",
+ "sha256:8844217cdf66eabe39567118f229e275f0727e9195635a15e0e4b9227458daaf",
+ "sha256:8a66fe50386162df2da701b3722781cbe90ce043e7d53c1fd6bd801bca6b48d4",
+ "sha256:9370d6744d379f2de5d7fa95cdbd3a4d92f0b0ef29609b4b1687f16bc197063d",
+ "sha256:937a54e5694684f74dcbf6e24cc453bfc5b33940216ddd8f4cd8f0f79167f765",
+ "sha256:9c857532c719fb30fafabd2371ce9b7031812ff3889d75273827633bca0c4602",
+ "sha256:a4165205a13b16a29e1ac57efeee6be2dfd5b5408122d59ef2145bc3239fa340",
+ "sha256:b3fe2ff1e1715d4475d7e2c3e8dabd7c025f4410f79513b4ff2de3d51ce0fa9c",
+ "sha256:b6617221ff08fbd3b7a811950b5c3f9367f6e941b86259843eab77c8e3d2b56b",
+ "sha256:b761727ed7d593e49671d1827044b942dd2f4caae6e51bab144d4accf8244a84",
+ "sha256:baf3be0b9446a4083cc0c5bb9f9c964034be5374b5bc09757be89f5d2fa247b8",
+ "sha256:c17770a62a71718a74b7548098a74cd6880be16bcfff5f937f900ead90ca8e92",
+ "sha256:c67db410508b9de9c4694c57ed754b65a460e4812126e87f5052ecf23a011a54",
+ "sha256:d78ca526a559fb84faaaf84da2dd4addef5edb109db8b81677c0bb1aad342601",
+ "sha256:e9ed59d1b6ee837f4515b9584f3d26cf0388b742a11ecdae0d9237a94505d03a",
+ "sha256:f054b020c4d7e9786ae0404278ea318768eb123403b18453e28e47cdb7a0a4bf",
+ "sha256:f372d0f08eff1475ef426344efe42493f71f377ec52237bf153c5713de987251",
+ "sha256:f3f6a6034140e9e17e9abc175fc7a266a6e63652028e157750bd98e804a8ed9a",
+ "sha256:ffde4c6fabb52891d81606411cbfaf77756e3b561b566efd270b3ed3791fde4e"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==9.1.1"
+ },
+ "pipfile": {
+ "hashes": [
+ "sha256:f7d9f15de8b660986557eb3cc5391aa1a16207ac41bc378d03f414762d36c984"
+ ],
+ "index": "pypi",
+ "version": "==0.0.2"
+ },
"prometheus-client": {
"hashes": [
"sha256:522fded625282822a89e2773452f42df14b5a8e84a86433e3f8a189c1d54dc01",
@@ -548,41 +692,41 @@
},
"psutil": {
"hashes": [
- "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5",
- "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a",
- "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4",
- "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841",
- "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d",
- "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d",
- "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0",
- "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845",
- "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf",
- "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b",
- "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07",
- "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618",
- "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2",
- "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd",
- "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666",
- "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce",
- "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3",
- "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d",
- "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25",
- "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492",
- "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b",
- "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d",
- "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2",
- "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203",
- "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2",
- "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94",
- "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9",
- "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64",
- "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56",
- "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3",
- "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c",
- "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"
+ "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685",
+ "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc",
+ "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36",
+ "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1",
+ "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329",
+ "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81",
+ "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de",
+ "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4",
+ "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574",
+ "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237",
+ "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22",
+ "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b",
+ "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0",
+ "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954",
+ "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021",
+ "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537",
+ "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87",
+ "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0",
+ "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc",
+ "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af",
+ "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4",
+ "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453",
+ "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689",
+ "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8",
+ "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680",
+ "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e",
+ "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9",
+ "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b",
+ "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d",
+ "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2",
+ "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5",
+ "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"
],
- "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==5.9.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==5.9.1"
},
"pure-eval": {
"hashes": [
@@ -608,11 +752,11 @@
},
"pyparsing": {
"hashes": [
- "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954",
- "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"
+ "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
+ "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
],
"markers": "python_full_version >= '3.6.8'",
- "version": "==3.0.8"
+ "version": "==3.0.9"
},
"pyrsistent": {
"hashes": [
@@ -682,72 +826,111 @@
},
"pyzmq": {
"hashes": [
- "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b",
- "sha256:0ca6cd58f62a2751728016d40082008d3b3412a7f28ddfb4a2f0d3c130f69e74",
- "sha256:1621e7a2af72cced1f6ec8ca8ca91d0f76ac236ab2e8828ac8fe909512d566cb",
- "sha256:18cd854b423fce44951c3a4d3e686bac8f1243d954f579e120a1714096637cc0",
- "sha256:2841997a0d85b998cbafecb4183caf51fd19c4357075dfd33eb7efea57e4c149",
- "sha256:2b97502c16a5ec611cd52410bdfaab264997c627a46b0f98d3f666227fd1ea2d",
- "sha256:3a4c9886d61d386b2b493377d980f502186cd71d501fffdba52bd2a0880cef4f",
- "sha256:3c1895c95be92600233e476fe283f042e71cf8f0b938aabf21b7aafa62a8dac9",
- "sha256:42abddebe2c6a35180ca549fadc7228d23c1e1f76167c5ebc8a936b5804ea2df",
- "sha256:468bd59a588e276961a918a3060948ae68f6ff5a7fa10bb2f9160c18fe341067",
- "sha256:480b9931bfb08bf8b094edd4836271d4d6b44150da051547d8c7113bf947a8b0",
- "sha256:53f4fd13976789ffafedd4d46f954c7bb01146121812b72b4ddca286034df966",
- "sha256:62bcade20813796c426409a3e7423862d50ff0639f5a2a95be4b85b09a618666",
- "sha256:67db33bea0a29d03e6eeec55a8190e033318cee3cbc732ba8fd939617cbf762d",
- "sha256:6b217b8f9dfb6628f74b94bdaf9f7408708cb02167d644edca33f38746ca12dd",
- "sha256:7661fc1d5cb73481cf710a1418a4e1e301ed7d5d924f91c67ba84b2a1b89defd",
- "sha256:76c532fd68b93998aab92356be280deec5de8f8fe59cd28763d2cc8a58747b7f",
- "sha256:79244b9e97948eaf38695f4b8e6fc63b14b78cc37f403c6642ba555517ac1268",
- "sha256:7c58f598d9fcc52772b89a92d72bf8829c12d09746a6d2c724c5b30076c1f11d",
- "sha256:7dc09198e4073e6015d9a8ea093fc348d4e59de49382476940c3dd9ae156fba8",
- "sha256:80e043a89c6cadefd3a0712f8a1322038e819ebe9dbac7eca3bce1721bcb63bf",
- "sha256:851977788b9caa8ed011f5f643d3ee8653af02c5fc723fa350db5125abf2be7b",
- "sha256:8eddc033e716f8c91c6a2112f0a8ebc5e00532b4a6ae1eb0ccc48e027f9c671c",
- "sha256:902319cfe23366595d3fa769b5b751e6ee6750a0a64c5d9f757d624b2ac3519e",
- "sha256:954e73c9cd4d6ae319f1c936ad159072b6d356a92dcbbabfd6e6204b9a79d356",
- "sha256:ab888624ed68930442a3f3b0b921ad7439c51ba122dbc8c386e6487a658e4a4e",
- "sha256:acebba1a23fb9d72b42471c3771b6f2f18dcd46df77482612054bd45c07dfa36",
- "sha256:b4ebed0977f92320f6686c96e9e8dd29eed199eb8d066936bac991afc37cbb70",
- "sha256:badb868fff14cfd0e200eaa845887b1011146a7d26d579aaa7f966c203736b92",
- "sha256:be4e0f229cf3a71f9ecd633566bd6f80d9fa6afaaff5489492be63fe459ef98c",
- "sha256:c0f84360dcca3481e8674393bdf931f9f10470988f87311b19d23cda869bb6b7",
- "sha256:c1e41b32d6f7f9c26bc731a8b529ff592f31fc8b6ef2be9fa74abd05c8a342d7",
- "sha256:c88fa7410e9fc471e0858638f403739ee869924dd8e4ae26748496466e27ac59",
- "sha256:cf98fd7a6c8aaa08dbc699ffae33fd71175696d78028281bc7b832b26f00ca57",
- "sha256:d072f7dfbdb184f0786d63bda26e8a0882041b1e393fbe98940395f7fab4c5e2",
- "sha256:d1b5d457acbadcf8b27561deeaa386b0217f47626b29672fa7bd31deb6e91e1b",
- "sha256:d3dcb5548ead4f1123851a5ced467791f6986d68c656bc63bfff1bf9e36671e2",
- "sha256:d6157793719de168b199194f6b6173f0ccd3bf3499e6870fac17086072e39115",
- "sha256:d728b08448e5ac3e4d886b165385a262883c34b84a7fe1166277fe675e1c197a",
- "sha256:de8df0684398bd74ad160afdc2a118ca28384ac6f5e234eb0508858d8d2d9364",
- "sha256:e6a02cf7271ee94674a44f4e62aa061d2d049001c844657740e156596298b70b",
- "sha256:ea12133df25e3a6918718fbb9a510c6ee5d3fdd5a346320421aac3882f4feeea",
- "sha256:ea5a79e808baef98c48c884effce05c31a0698c1057de8fc1c688891043c1ce1",
- "sha256:f43b4a2e6218371dd4f41e547bd919ceeb6ebf4abf31a7a0669cd11cd91ea973",
- "sha256:f762442bab706fd874064ca218b33a1d8e40d4938e96c24dafd9b12e28017f45",
- "sha256:f89468059ebc519a7acde1ee50b779019535db8dcf9b8c162ef669257fef7a93",
- "sha256:f907c7359ce8bf7f7e63c82f75ad0223384105f5126f313400b7e8004d9b33c3"
+ "sha256:057176dd3f5ccf5aad4abd662d76b6a39bbf799baaf2f39cd4fdaf2eab326e43",
+ "sha256:05ec90a8da618f2398f9d1aa20b18a9ef332992c6ac23e8c866099faad6ef0d6",
+ "sha256:154de02b15422af28b53d29a02de72121ba503634955017255573fc1f995143d",
+ "sha256:16b832adb5d8716f46051da5533c480250bf126984ce86804db6137a3a7f931b",
+ "sha256:1df26aa854bdd3a8341bf199064dd6aa6e240f2eaa3c9fa8d217e5d8b868c73e",
+ "sha256:28f9164fb2658b7b414fa0894c75b1a9c61375774cdc1bdb7298beb042a2cd87",
+ "sha256:2951c29b8649f3672af9dca8ff61d86310d3664d9629788b1c66422fb13b1239",
+ "sha256:2b08774057ae7ce8a2eb4e7d54db05358234440706ce43a85814500c5d7bd22e",
+ "sha256:2e2ac40f7a91c740ec68d6db07ae19ea9259c959333c68bee56ab2c799a67d66",
+ "sha256:312e56799410c34797417a4060a8bd37d4db1f06d1ec0c54f7c8fd81e0d90376",
+ "sha256:38f778a74e3889392e949326cfd0e9b2eb37dcbb2980d98fad2c51703d523db2",
+ "sha256:3955dd5bbbe02f454655296ee36a66c334c7102a29b8458223d168c0380edfd5",
+ "sha256:425ba851a6f9892bde1da2024d82e2fe6796bd77e3391fb96665c50fe9d4c6a5",
+ "sha256:48bbc2db041ab28eeee4a3e8ada0ed336640946dd5a8e53dbd3805f9dbdcf0dc",
+ "sha256:4fbcd657cda75574fd1315a4c44bd322bc2e219039fb09f146bbe6f8aef039e9",
+ "sha256:523ba7fd4d8fe75ad09c1e574a648892b75a97d0cfc8005727681053ac19555b",
+ "sha256:53b2c1326c2e484d450932d2be739f064b7cb572faabec38386098a28516a529",
+ "sha256:540d7146c3cdc9bbffab039ea067f494eba24d1abe5bd33eb9f963c01e3305d4",
+ "sha256:563d4281c4dbdf647d93114420151d33f895afc4c46b7115a67a0aa5347e6624",
+ "sha256:67a049bcf967a39993858beed873ed3405536019820922d4efacfe35ab3da51a",
+ "sha256:67ec63ae3c9c1fa2e077fcb42e77035e2121a04f987464bdf9945a28535d30ad",
+ "sha256:68e22c5d3be451e87d47f956b397a7823bfbde2176341bc902fba30f96831d7e",
+ "sha256:6ab4b6108e69f63c917cd7ef7217c5727955b1ac90600e44a13ed5312019a014",
+ "sha256:6bd7f18bd4cf51ea8d7e54825902cf36f9d2f35cc51ef618373988d5398b8dd0",
+ "sha256:6cd53e861bccc0bdc4620f68fb4a91d5bcfe9f4213cf8e200fa498044d33a6dc",
+ "sha256:6d346e551fa64b89d57a4ac74b9bc66703413f02f50093e089e861999ec5cccc",
+ "sha256:6ff8708fabc9f9bc2949f457d39b4088c9656c4c9ac15fbbbbaafce8f6d07833",
+ "sha256:7626e8384275a7dea6f3d1f749fb5e00299042e9c895fc3dbe24cb154909c242",
+ "sha256:7e7346b2b33dcd4a2171dd8a9870ae283eec8f6231dcbcf237a0f41e74751a50",
+ "sha256:81623c67cb71b93b5f7e06c9107f3781738ae86866db830c950223d87af2a235",
+ "sha256:83f1c76068faf62c32a36dd62dc4db642c2027bbbd960f8f6345b59e9d4dc472",
+ "sha256:8679bb1dd723ecbea03b1f96c98972815775fd8ec756c440a14f289c436c472e",
+ "sha256:86fb683cb9a9c0bb7476988b7957393ecdd22777d87d804442c66e62c99197f9",
+ "sha256:8757c62f7960cd26122f7aaaf86eda1e016fa85734c3777b8054dd334d7dea4d",
+ "sha256:894be7d17228e7328cc188096c0162697211ec91761f6812fff12790cbe11c66",
+ "sha256:8a0f240bf43c29be1bd82d77e602a61c798e9de02e5f8bb7bb414cb814f43236",
+ "sha256:8c3abf7eab5b76ae162c4fbb16d514a947fc57fd995b64e5ea8ef8ba3b888a69",
+ "sha256:93332c6972e4c91522c4810e907f3aea067424338071161b39cacded022559df",
+ "sha256:97d6c676dc97d593625d9fc48154f2ffeabb619a1e6fe8d2a5b53f97e3e9bdee",
+ "sha256:99dd85f0ca1db8d17a01a25c2bbb7784d25a2d39497c6beddbe96bff74194e04",
+ "sha256:9c7fb691fb07ec7ab99fd173bb0e7e0248d31bf83d484a87b917a342f63812c9",
+ "sha256:b3bc3cf200aab74f3d758586ac50295214eda496ac6a6636e0c881c5958d9123",
+ "sha256:bba54f97578943f48f621b4a7afb8eb022370da26a88b88ccc9fee9f3ef7ce45",
+ "sha256:bd2a13a0f8367e50347cbac87ae230ae1953935443240238f956bf10668bead6",
+ "sha256:cbc1184349ca6e5112898aa7fc3efa1b1bbae24ab1edc774cfd09cbfd3b091d7",
+ "sha256:cd82cca9c489e441574804dbda2dd8e114cf3be7935b03de11dade2c9478aea6",
+ "sha256:ce8ba5ed8b0a7a203922d61cff45ee6001a41a9359f04f00d055a4e988755569",
+ "sha256:cfee22e072a382b92ee0709dbb8203dabd52d54258051e770d9d2a81b162530b",
+ "sha256:d977df6f7c4109ed1d96ffb6795f6af77114be606ae4556efbfc9cac725db65d",
+ "sha256:da72a384a1d7e87490ca71182f3ab469ed21d847adc16b70c34faac5a3b12801",
+ "sha256:ddf4ad1d651e6c9234945061e1a31fe27a4be0dea21c498b87b186fadf8f5919",
+ "sha256:eb0ae5dfda83bbce660179d7b41c1c38fd833a54d2e6d9b258c644f3b75ef94d",
+ "sha256:f4c7d370badc60ac94a554bc571a46d03e39d8aacfba8006b334512e184aed59",
+ "sha256:f6c378b435a26fda8996579c0e324b108d2ca0d01b4661503a75634e5155559f",
+ "sha256:f6c9d30888503f2f5f87d6d41f016301352dd98da4a861bd10663c3a2d99d3b5",
+ "sha256:fab8a7877275060f7b303e1f91c218069a2814a616b6a5ee2d8a3737deb15915",
+ "sha256:fc32e7d7f98cac3d8d5153ed2cb583158ae3d446a6efb8e28ccb1c54a09f4169"
],
"markers": "python_version >= '3.6'",
- "version": "==22.3.0"
+ "version": "==23.1.0"
},
"qtconsole": {
"hashes": [
- "sha256:75f2ded876444454edcb5a53262149e33b53db3a4a53116b7c3df52830905b0f",
- "sha256:8e3520fdc75e46abc4cc6cffeca16fa2652754109b8ae839fa28e27d1eba5625"
+ "sha256:b73723fac43938b684dcb237a88510dc7721c43a726cea8ade179a2927c0a2f3",
+ "sha256:d364592d7ede3257f1e17fcdbfd339c26e2cc638ca4fa4ee56a724e26ea13c81"
],
"markers": "python_version >= '3.7'",
- "version": "==5.3.0"
+ "version": "==5.3.1"
},
"qtpy": {
"hashes": [
- "sha256:adfd073ffbd2de81dc7aaa0b983499ef5c59c96adcfdcc9dea60d42ca885eb8f",
- "sha256:d93f2c98e97387fcc9d623d509772af5b6c15ab9d8f9f4c5dfbad9a73ad34812"
+ "sha256:aee0586081f943029312becece9f63977b0a9e3788f77a6ac8cc74802bb173d6",
+ "sha256:ca8cd4217175186344299ee4c0f7e7adcf362c70852ba35b255a534077025c06"
],
- "markers": "python_version >= '3.6'",
- "version": "==2.0.1"
+ "markers": "python_version >= '3.7'",
+ "version": "==2.1.0"
+ },
+ "scipy": {
+ "hashes": [
+ "sha256:02b567e722d62bddd4ac253dafb01ce7ed8742cf8031aea030a41414b86c1125",
+ "sha256:1166514aa3bbf04cb5941027c6e294a000bba0cf00f5cdac6c77f2dad479b434",
+ "sha256:1da52b45ce1a24a4a22db6c157c38b39885a990a566748fc904ec9f03ed8c6ba",
+ "sha256:23b22fbeef3807966ea42d8163322366dd89da9bebdc075da7034cee3a1441ca",
+ "sha256:28d2cab0c6ac5aa131cc5071a3a1d8e1366dad82288d9ec2ca44df78fb50e649",
+ "sha256:2ef0fbc8bcf102c1998c1f16f15befe7cffba90895d6e84861cd6c6a33fb54f6",
+ "sha256:3b69b90c9419884efeffaac2c38376d6ef566e6e730a231e15722b0ab58f0328",
+ "sha256:4b93ec6f4c3c4d041b26b5f179a6aab8f5045423117ae7a45ba9710301d7e462",
+ "sha256:4e53a55f6a4f22de01ffe1d2f016e30adedb67a699a310cdcac312806807ca81",
+ "sha256:6311e3ae9cc75f77c33076cb2794fb0606f14c8f1b1c9ff8ce6005ba2c283621",
+ "sha256:65b77f20202599c51eb2771d11a6b899b97989159b7975e9b5259594f1d35ef4",
+ "sha256:6cc6b33139eb63f30725d5f7fa175763dc2df6a8f38ddf8df971f7c345b652dc",
+ "sha256:70de2f11bf64ca9921fda018864c78af7147025e467ce9f4a11bc877266900a6",
+ "sha256:70ebc84134cf0c504ce6a5f12d6db92cb2a8a53a49437a6bb4edca0bc101f11c",
+ "sha256:83606129247e7610b58d0e1e93d2c5133959e9cf93555d3c27e536892f1ba1f2",
+ "sha256:93d07494a8900d55492401917a119948ed330b8c3f1d700e0b904a578f10ead4",
+ "sha256:9c4e3ae8a716c8b3151e16c05edb1daf4cb4d866caa385e861556aff41300c14",
+ "sha256:9dd4012ac599a1e7eb63c114d1eee1bcfc6dc75a29b589ff0ad0bb3d9412034f",
+ "sha256:9e3fb1b0e896f14a85aa9a28d5f755daaeeb54c897b746df7a55ccb02b340f33",
+ "sha256:a0aa8220b89b2e3748a2836fbfa116194378910f1a6e78e4675a095bcd2c762d",
+ "sha256:d3b3c8924252caaffc54d4a99f1360aeec001e61267595561089f8b5900821bb",
+ "sha256:e013aed00ed776d790be4cb32826adb72799c61e318676172495383ba4570aa4",
+ "sha256:f3e7a8867f307e3359cc0ed2c63b61a1e33a19080f92fe377bc7d49f646f2ec1"
+ ],
+ "index": "pypi",
+ "version": "==1.8.1"
},
"send2trash": {
"hashes": [
@@ -781,11 +964,11 @@
},
"terminado": {
"hashes": [
- "sha256:874d4ea3183536c1782d13c7c91342ef0cf4e5ee1d53633029cbc972c8760bd8",
- "sha256:94d1cfab63525993f7d5c9b469a50a18d0cdf39435b59785715539dd41e36c0d"
+ "sha256:0d5f126fbfdb5887b25ae7d9d07b0d716b1cc0ccaacc71c1f3c14d228e065197",
+ "sha256:ab4eeedccfcc1e6134bfee86106af90852c69d602884ea3a1e8ca6d4486e9bfe"
],
"markers": "python_version >= '3.7'",
- "version": "==0.13.3"
+ "version": "==0.15.0"
},
"tinycss2": {
"hashes": [
@@ -795,6 +978,14 @@
"markers": "python_version >= '3.6'",
"version": "==1.1.1"
},
+ "toml": {
+ "hashes": [
+ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+ "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
+ ],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==0.10.2"
+ },
"tornado": {
"hashes": [
"sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb",
@@ -842,13 +1033,21 @@
"markers": "python_version >= '3.5'",
"version": "==6.1"
},
+ "tqdm": {
+ "hashes": [
+ "sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d",
+ "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"
+ ],
+ "index": "pypi",
+ "version": "==4.64.0"
+ },
"traitlets": {
"hashes": [
- "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7",
- "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"
+ "sha256:1530d04badddc6a73d50b7ee34667d4b96914da352109117b4280cb56523a51b",
+ "sha256:74803a1baa59af70f023671d86d5c7a834c931186df26d50d362ee6a1ff021fd"
],
"markers": "python_version >= '3.7'",
- "version": "==5.1.1"
+ "version": "==5.2.2.post1"
},
"wcwidth": {
"hashes": [
@@ -864,6 +1063,14 @@
],
"version": "==0.5.1"
},
+ "werkzeug": {
+ "hashes": [
+ "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6",
+ "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.1.2"
+ },
"widgetsnbextension": {
"hashes": [
"sha256:4fd321cad39fdcf8a8e248a657202d42917ada8e8ed5dd3f60f073e0d54ceabd",
@@ -880,5 +1087,147 @@
"version": "==3.8.0"
}
},
- "develop": {}
+ "develop": {
+ "coverage": {
+ "hashes": [
+ "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749",
+ "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982",
+ "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3",
+ "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9",
+ "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428",
+ "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e",
+ "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c",
+ "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9",
+ "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264",
+ "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605",
+ "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397",
+ "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d",
+ "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c",
+ "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815",
+ "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068",
+ "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b",
+ "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4",
+ "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4",
+ "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3",
+ "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84",
+ "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83",
+ "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4",
+ "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8",
+ "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb",
+ "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d",
+ "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df",
+ "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6",
+ "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b",
+ "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72",
+ "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13",
+ "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df",
+ "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc",
+ "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6",
+ "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28",
+ "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b",
+ "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4",
+ "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad",
+ "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46",
+ "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3",
+ "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9",
+ "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"
+ ],
+ "index": "pypi",
+ "version": "==6.4.1"
+ },
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:5d26852efe48c0a32b0509ffbc583fda1a2266545a78d104a6f4aff3db17d700",
+ "sha256:c58c8eb8a762858f49e18436ff552e83914778e50e9d2f1660535ffb364552ec"
+ ],
+ "markers": "python_version < '3.10'",
+ "version": "==4.11.4"
+ },
+ "mako": {
+ "hashes": [
+ "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba",
+ "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==1.2.0"
+ },
+ "markdown": {
+ "hashes": [
+ "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874",
+ "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==3.3.7"
+ },
+ "markupsafe": {
+ "hashes": [
+ "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
+ "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
+ "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
+ "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
+ "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
+ "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
+ "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
+ "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
+ "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
+ "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
+ "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
+ "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
+ "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
+ "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
+ "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
+ "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
+ "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
+ "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
+ "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
+ "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
+ "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
+ "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
+ "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
+ "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
+ "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
+ "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
+ "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
+ "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
+ "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
+ "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
+ "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
+ "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
+ "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
+ "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
+ "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
+ "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
+ "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
+ "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
+ "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
+ "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.1.1"
+ },
+ "pdoc3": {
+ "hashes": [
+ "sha256:5f22e7bcb969006738e1aa4219c75a32f34c2d62d46dc9d2fb2d3e0b0287e4b7",
+ "sha256:ba45d1ada1bd987427d2bf5cdec30b2631a3ff5fb01f6d0e77648a572ce6028b"
+ ],
+ "index": "pypi",
+ "version": "==0.10.0"
+ },
+ "pyment": {
+ "hashes": [
+ "sha256:951a4c52d6791ccec55bc739811169eed69917d3874f5fe722866623a697f39d",
+ "sha256:a0c6ec59d06d24aeec3eaecb22115d0dc95d09e14209b2df838381fdf47a78cc"
+ ],
+ "index": "pypi",
+ "version": "==0.3.3"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad",
+ "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"
+ ],
+ "markers": "python_version < '3.10'",
+ "version": "==3.8.0"
+ }
+ }
}
diff --git a/README.md b/README.md
index 18e51dc..14efd99 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,47 @@
-# Slots
-
-Modelling of slot- game.
-
-There is an output matrix $N \times M$.
-
-There are $P$ items(symbols) corresponding for each column.
-The probability of the $i$-th item to be on the any row on the $j$-th
-column is equal $p_{i,j}$.
-The column is formed by the next rule:
-at first the bottom row is form ed, than another symbol is generated
-on the upper row - this symbol cannot be the same that is on bottom row:
-so the probality of $i_1$-th item to be there is $p_{i_1}/(1-p_{i})$, where
-$i \neq i_1$. The same way, the next symbol cannot equal to any of the previous
-ones, therefore, the probabilyty of the $i2$-th item to be on the column is
-$p_{i_2}/(1-p_{i}-p_{i_1}))$. By continuing so on, the last probability is
-$p_{i_N}/(1-p_{i}-p_{i_1} -p_{i_{N-1}}))$.
-
-If there are same $l$ symbols in the first $K$ columns then the output result
-is $S_{l,K}$.
+## Клонуємо даний репозиторій.
+#### git clone {repo-url}
+
+## Для роботи частини backend,
+Необхідно встановити Python, бажано версії 3.8, та pip.
+
+За бажанням можна встановити pipenv.
+
+У корні репозиторія відкриваємо консоль
+#### та вводимо
+#### pip install -r requirements.txt, якщо встановили Python та pip
+#### pipenv install Pipfile, якщо встановили Python, pip та pipenv
+
+Після встановлення Python, pip, можливо pipenv та необхідних пакетів, переходимо до директорії backend.
+
+Запишіть в файл probabilities.txt бажані ймовірності, сума яких має дорівнювати 1
+
+#### або запустіть файл Bandit.py командою:
+#### python Bandit.py, якщо ви встановили Python та pip
+#### pipenv run python Bandit.py, якщо ви встановили Python, pip та pipenv
+
+На запит програми введіть бажаний коефіцієнт віддачі автомату,
+через деякий час будуть згенеровані ймовірності для даного коефіцієнту віддачі,
+які запишуться в файл probabilities.txt.
+
+(якщо вводити коефіцієнт віддачі, округляючи до десятих, обчислення займе менше часу,
+ніж обчислення для коефіцієнту віддачі округленого до сотих)
+
+### Далі запускаємо flask сервер, який буде обмінюватися даними з сайтом на JavaScript.
+#### запустіть файл wsgi.py командою:
+#### python wsgi.py, якщо ви встановили Python та pip
+#### pipenv run python wsgi.py, якщо ви встановили Python, pip та pipenv
+Якщо не було ніяких проблем йдемо далі.
+### Документацію до коду частини backend можна знайти у директорії Slots\backend\doc\backend
+
+## Для роботи частини user_interface,
+яка являє собою сайт написаний на JavaScript,
+який спілкується з сервером на Python фреймворці Flask,
+необхідно встановити npm - менеджер пакетів.
+
+Після встановлення npm, відкриваємо консоль та переходимо у директорію user_interface,
+#### вводимо в консоль npm install
+для встановлення всіх необхідних пакетів для роботи сайту.
+У директорії user_interface повинна з'явитися нова директорія node_modules.
+#### Для старту сервера сайту необхідно ввести в консоль npm start.
+Далі переходимо за адресою http://localhost:8080
+та використовуємо сайт
\ No newline at end of file
diff --git a/backend/Bandit.py b/backend/Bandit.py
new file mode 100644
index 0000000..2b880c5
--- /dev/null
+++ b/backend/Bandit.py
@@ -0,0 +1,524 @@
+from __future__ import annotations
+
+import os
+from multiprocessing import Pool
+from typing import Dict, Tuple, List, Optional, Union
+import numpy as np
+from tqdm import tqdm
+
+from backend.CustomExceptions import *
+from backend.GenAlgorithm import GeneticAlgorithm
+
+
+class OneHandBandit:
+ """ """
+ players: List[OneHandBandit] = []
+
+ def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None):
+ """функція конструктор для класу OneHandBandit
+ Args:
+ m: number of columns
+ n: number of rows
+ k: number of pictures
+
+ k(number of pictures) must be greater or equal to n(number of rows)
+
+ probs: probability of each picture appearance
+ """
+
+ self.drumsColumns = [[i for i in range(k)] for _ in range(m)]
+ self.columnsNumb = m
+ self.rowsNumb = n
+ self.picturesNumb = k
+
+ self.probabilities: List[float, ...] = list()
+ self.setProbabilities(probs)
+
+ self.combinations: Dict[Tuple[int, ...], int] = dict()
+ self.price: int = 0
+
+ self.money: int = 0
+ self.moneySpent = 0
+ self.moneyWon: int = 0
+
+ self.state: List[List[int,],] = list()
+
+ self.genAlgo = GeneticAlgorithm(self)
+
+ OneHandBandit.players.append(self)
+
+ def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
+ """функція, яка встановлює виграшні комбінації
+
+ Args:
+ combs: dict of winning combinations
+
+ example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+ Returns:
+
+ """
+ combs = combs.copy()
+ for comb in combs:
+ if len(comb) != self.columnsNumb:
+ raise CombinationLengthException
+ elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
+ raise CombinationValuesException
+ elif combs[comb] < 0:
+ raise WinningMoneyException
+
+ self.combinations = combs
+
+ def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None:
+ """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
+ Args:
+ comb: комбінація індексів зображень
+ win: виграшна сума
+
+ Returns:
+
+ """
+ self.combinations[comb] = win
+
+ # the price of 1 turn
+ def setPriceOfGame(self, price) -> None:
+ """функція, яка встановлює вартість однієї гри
+
+ Args:
+ price: вартість однієї гри
+
+ Returns:
+
+ """
+ self.price = price
+
+ # money player gives to bandit
+ def startGame(self, money: int, modeling: bool = False) -> None:
+ """функція, яка вносить до автомату певну суму грошей, на які можна грати
+
+ Args:
+ money: сума грошей, яка вноситься до автомату
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+ if money > 0:
+ self.money = money
+ if not modeling:
+ print(f"you deposited {money} money")
+
+ def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None:
+ """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+ слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
+ Args:
+ probs: ймовірності зображень
+
+ Returns:
+
+ """
+ if probs is None:
+ # uniform distribution
+ probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
+
+ if 0.001 < abs(sum(probs) - 1.0):
+ # sum of probs dont equal 1
+ raise ProbabilitiesSumException
+
+ if len(probs) != self.picturesNumb:
+ raise ProbabilitiesArrayLengthException
+
+ self.probabilities = probs.copy()
+
+ def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None:
+ """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
+ Args:
+ filename: ім'я файлу з ймовірностями
+
+ Returns:
+
+ """
+ with open(filename, "r") as f:
+ probsLine = f.readline()
+ probs = list(map(lambda x: float(x), probsLine.split(" ")))
+
+ self.setProbabilities(probs)
+
+ def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None,
+ debug: bool = False) -> List[float]:
+ """функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
+ Можлива генерація випадкових ймовірностей
+ та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
+ Args:
+ probsCount: довжина списку ймовірностей
+ returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+
+ якщо returnCoef is None, то генеруються випадкові ймовірності
+
+ якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+ згенерований список ймовірностей
+
+ """
+ probs = []
+ if returnCoef is None:
+ # generate random probabilities
+ high = 1
+ e = 0.25 * high
+
+ for _ in range(probsCount - 1):
+ probs.append(np.random.uniform(0, high - e))
+ high -= probs[-1]
+ e = 0.01 * high
+ probs.append(high)
+ return probs
+ else:
+ probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug)
+ return probs
+
+ def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None,
+ isGraph: bool = False) -> Union[float, List[float]]:
+ """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
+ функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ коефіцієнт віддачі автомату, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+ """
+ oldProbs = self.probabilities
+ oldMoneySpent = self.moneySpent
+ oldMoneyWon = self.moneyWon
+ if probs is not None:
+ self.setProbabilities(probs)
+ self.moneyWon = 0
+ self.moneySpent = 0
+
+ money = self.price * gamesCount
+
+ modeling = True
+ self.startGame(money, modeling=modeling)
+ meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph)
+ gameReturn = self.moneyWon
+ gameReturnPercentage = gameReturn / self.moneySpent
+ if probs is not None:
+ self.setProbabilities(oldProbs)
+ self.moneyWon = oldMoneyWon
+ self.moneySpent = oldMoneySpent
+ if isGraph:
+ return meanRCs
+
+ return gameReturnPercentage
+
+ def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None,
+ coresCount: int = -1):
+ """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+ шляхом розпаралелювання роботи.
+
+ функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+ але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ coresCount: кількість процесів, які запустить дана функція
+
+ Returns:
+ коефіцієнт віддачі автомату
+
+ """
+ if coresCount == -1:
+ coresCount = int(os.cpu_count() * 0.75)
+ if coresCount == 0:
+ coresCount = 1
+ elif coresCount == 0:
+ return self.getReturnCoef(gamesCount, probs)
+
+ gamesCountList = [gamesCount // coresCount, ] * coresCount
+ gamesCountList[-1] += (gamesCount - sum(gamesCountList))
+
+ args = zip(gamesCountList, [probs, ] * len(gamesCountList), )
+ # args = list(args)
+
+ with Pool(processes=coresCount) as executor:
+ results = executor.starmap(self.getReturnCoef, args)
+
+ res = sum(results) / coresCount
+ return res
+
+ # @benchmark
+ def currentTurn(self, modeling: bool = False) -> int:
+ """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+ та викликає функцію self.setState
+
+ Args:
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+ виграш гравця
+
+ """
+ self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
+ self.money -= self.price
+ self.moneySpent += self.price
+ won = self.currentWon()
+
+ if not modeling:
+ self.printState()
+
+ return won
+
+ def turnColumn(self, i) -> List[int,]:
+ """функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
+ Args:
+ i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+ де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ downIdx = self.rowsNumb - 1
+ upIdx = 0
+ rolledPicturesIdx = []
+ resColumn = [-1, ] * self.rowsNumb
+ for idx in range(self.rowsNumb):
+ newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
+
+ # assert abs(sum(newProbs) - 1) < 0.001
+
+ rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
+ rolledPicturesIdx.append(rolledPicIdx)
+
+ if (idx % 2) == 0:
+ resColumn[downIdx] = rolledPicIdx
+ downIdx -= 1
+ else:
+ resColumn[upIdx] = rolledPicIdx
+ upIdx += 1
+
+ return resColumn
+
+ def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
+ """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+ точніше описано в файлі README.ipynb
+
+ Args:
+ rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика
+
+ Returns:
+ значення ймовірностей для наступної генерації
+
+ """
+ newProbs = np.array(self.probabilities, dtype=float)
+ divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
+ newProbs[rolledPicturesIdxs] = 0.0
+ newProbs = newProbs / divideCoeff
+ return list(newProbs)
+
+ def currentWon(self) -> int:
+ """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
+ Args:
+
+ Returns:
+ весь виграш гравця
+
+ """
+ won = 0
+ for i in range(self.rowsNumb):
+ comb = tuple(self.getRow(i))
+ if comb in self.combinations:
+ # it is possible to have several winning combinations....
+ won += self.combinations[comb]
+
+ self.moneyWon += won # ???? not sure if it is needed
+ return won
+
+ def getRow(self, i) -> np.array:
+ """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
+ Args:
+ i: індекс рядка матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return np.array([self.state[j][i] for j in range(self.columnsNumb)])
+
+ def getColumn(self, j):
+ """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
+ Args:
+ j: індекс стовпця матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return self.state[j]
+
+ def getState(self) -> np.array:
+ """функція, яка повертає матрицю self.state(матрицю однієї гри)
+
+ Args:
+
+ Returns:
+ транспонована(задля спрощення її відображення) матриця self.state
+
+ """
+ return np.array(self.state).T
+
+ def setState(self, state: List[List[int,],]) -> None:
+ """функція, яка встановлює матрицю однієї гри
+
+ Args:
+ state: матриця індексів зображень однієї гри
+
+ Returns:
+
+ """
+ if all([len(col) == len(set(col)) for col in state]):
+ self.state = state
+ else:
+ raise SettingStateException
+
+ def printState(self) -> None:
+ """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися"""
+ print(f"current state: \n{self.getState()}")
+ # print("money spent:", self.moneySpent)
+ print("money won:", self.moneyWon)
+ print("money remained:", self.money)
+
+ def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]],
+ List[float]]:
+ """функція, яка проводить n ігор
+
+ Args:
+ n: кількість ігор
+ modeling: параметр, який впливає на інформативність функції
+ isGraph: параметра, який керує поверненням спеціальних значень
+
+ Returns:
+ список виграшів гравця за n ігор, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+ """
+ if self.money < self.price * n:
+ print("Not enough money")
+ return None
+
+ wons = []
+ meanRCs = []
+ # play game n times
+ for i in range(n):
+ won = self.currentTurn(modeling=modeling)
+ wons.append(won)
+ if not modeling:
+ print(f"you won {won} money for one turn")
+ print(f"you spent {self.price} money for one turn")
+ print()
+
+ if isGraph:
+ meanRCs.append(sum(wons) / ((i + 1) * self.price))
+
+ if isGraph:
+ return meanRCs
+
+ if not modeling:
+ return wons
+
+ # winning sum in fact could be (and often is) negative ;-))
+ @property
+ def currentPlayerWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+ віднявши від нього витрати гравця
+
+ Args:
+
+ Returns:
+ виграш гравця - витрати гравця
+
+ """
+ return self.moneyWon - self.moneySpent # not sure
+
+ @property
+ def totalPlayersWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+ який вона обраховує, використовуючи функцію self.currentPlayerWon
+
+ Args:
+
+ Returns:
+ виграш всіх гравців - витрати всіх гравців
+
+ """
+ totalWin = 0
+ for p in OneHandBandit.players:
+ totalWin += p.currentPlayerWon
+ return totalWin
+
+
+if __name__ == "__main__":
+ p2 = OneHandBandit(4, 3, 5)
+ # p2.setProbabilitiesFromTxtFile("probabilities.txt")
+
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+
+ # p2.startGame(100)
+ # p2.play(10)
+ try:
+ returnCoef = float(
+ input("write down return coefficient for probabilities you would like get for(Example 0.9): "))
+ except Exception as e:
+ print("during input error occurred:", e)
+ print("return coefficient set to 0.9")
+ returnCoef = 0.9
+ # np.random.seed(42)
+ probs = p2.genBaseProbabilities(p2.picturesNumb, returnCoef, debug=True)
+ print(f"{probs=}")
+ print(f"fitness val for probs and goalValue={returnCoef}:",
+ p2.genAlgo.fitness_func(probs=probs, goalValue=returnCoef))
+ print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+
+ probsStrs = " ".join(map(str, probs))
+ with open("probabilities.txt", "w") as f:
+ f.write(probsStrs)
+ # modelingGamesCount = 2000
+ #
+ # print(p2.genBaseProbabilities(5, returnCoef=0.8))
+ # p2.getReturnCoefWithNCores(modelingGamesCount)
+ # l = []
+ # t0 = time.time()
+ # for i in range(10):
+ #
+ # returnCoef = p2.getReturnCoefWithNCores(gamesCount=modelingGamesCount, coresCount=5)
+ # l.append(returnCoef)
+ # print(f"{i}) this one hand bandit return coefficient: {returnCoef} "
+ # f"for {modelingGamesCount} games")
+ # print("min max range:", max(l) - min(l))
+ # print(time.time() - t0, "secs")
+
+# метод ньютона
+
+# генетичний алгоритм(рандомна мутація)
diff --git a/CustomExceptions.py b/backend/CustomExceptions.py
similarity index 100%
rename from CustomExceptions.py
rename to backend/CustomExceptions.py
diff --git a/backend/GenAlgorithm.py b/backend/GenAlgorithm.py
new file mode 100644
index 0000000..5211545
--- /dev/null
+++ b/backend/GenAlgorithm.py
@@ -0,0 +1,385 @@
+from typing import List, Iterable, Tuple, Union
+
+import numpy as np
+from scipy.stats import truncnorm
+from tqdm import tqdm
+
+
+class GeneticAlgorithm:
+ """ """
+
+ def __init__(self, bandit):
+ from backend.Bandit import OneHandBandit
+ self.bandit: OneHandBandit = bandit
+
+ def geneticAlgorithm(self, probsCount: int, goalValue: float,
+ populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000,
+ generationsCount: int = 100, eps: float = 0.001, debug: bool = False,
+ isGraph: bool = False) -> np.array:
+ """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+ в виді списка ймовірностей
+
+ Args:
+ probsCount: довжина списку ймовірностей, який ви хотіли б отримати
+ goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+ populationSize: розмір популяції генетичного алгоритму
+ population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі,
+ яка надалі буде використовуватися функцією фітнесу(fitness_func)
+ generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+ eps: похибка, яку дозволяє поточний генетичний алгоритм:
+ генетичний алгоритм зупиниться,
+ якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+ debug: параметр, який регулює інформативність алгоритму:
+
+ якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+
+ якщо debug рівне True алгоритм інформує про зміни в поколіннях
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: список ймовірностей,
+ які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+ якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях
+
+ """
+ if population is None:
+ population = []
+ for _ in range(populationSize):
+ population.append(np.array(self.bandit.genBaseProbabilities(probsCount)))
+
+ # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+ # [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+ # [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+ # [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+ # [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.65333333, 0.677 , 0.80883333, 0.5645 , 0.81633333
+
+ # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436],
+ # [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697],
+ # [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489],
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802
+
+ # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда
+ fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount,
+ debug=debug, isGraph=isGraph)
+
+
+ fitnessVals = []
+ returnCoefsInGenerations = []
+ # if debug is True:
+ # iterator = range(generationsCount)
+ # else:
+ # iterator = tqdm(range(generationsCount))
+
+ # for generationIdx in tqdm(range(generationsCount)):
+ for generationIdx in range(generationsCount):
+ if debug is True:
+ print("\n######################generation:", generationIdx)
+
+ fitnessVals = np.array([fitness(ps) for ps in population])
+ if isGraph is True:
+ mass = fitnessVals.copy()
+ fitnessVals = np.array([i[1] for i in mass])
+ returnCoefsInGenerations.append([i[0] for i in mass])
+
+ if debug is True:
+ print(f"{fitnessVals=}\n")
+
+ # Обчислення коефіцієнту виживання кожного індивіду
+ surviveProbs = self.getSurviveProbs(fitnessVals)
+ # print(f"{surviveProbs=}\n")
+
+ # Перевірка умов зупинки Генетичного Алгоритму
+ if any(fitnessVals <= eps):
+ if debug is True:
+ print("\n-----------------fitness values to break cycle:", fitnessVals)
+ break
+
+ # Відбір
+ population = self.selection(population, surviveProbs)
+
+ # Скрещування
+ population = self.crossOver(population)
+ for idx in range(len(population)):
+ self.changeProbs(population[idx])
+
+ # Мутація
+ # ймовірність мутації кожної особини
+ mutateProb = 0.2
+ for idx in range(len(population)):
+ rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0]
+ if rNumb:
+ self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug)
+
+ if isGraph is True:
+ return returnCoefsInGenerations
+
+ mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0])
+ population = [population[i[1]] for i in mass]
+ return population[0]
+
+ # @benchmark
+ def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False,
+ gamesCount: int = 5000) -> Union[float, Tuple]:
+ """функція обраховує значення функції фітнесу:
+ чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+ і навпаки.
+
+ Args:
+ probs: ймовірності для яких необхідно порахувати значення фітнес функції
+ goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція
+ debug: параметр, який регулює інформативність функції
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: значення фітнес функції
+ якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції)
+
+ """
+ currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1)
+ # print(f"\n{probs=}")
+ if debug is True:
+ print(f"currFitness={abs(currReturnCoef - goalValue)}")
+ print(f"{currReturnCoef=}\n")
+ if isGraph is True:
+ return currReturnCoef, abs(currReturnCoef - goalValue)
+
+ return abs(currReturnCoef - goalValue)
+
+ def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]):
+ """функція обраховує ймовірність виживання кожної окремої особини з популяції
+
+ Args:
+ fitnessVals: список значень функції фітнесу особин для популяції
+
+ Returns:
+ список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+ """
+ fitnessVals[fitnessVals == 0] = 0.000001
+
+ invS = sum(1 / fitnessVals)
+ surviveProbs = (1 / fitnessVals) / invS
+ return surviveProbs
+
+ def selection(self, population: List[np.array,], surviveProbs):
+ """функція реалізовує відбір методом рулетки для генетичного алгоритму
+
+ Args:
+ population: поточна популяція
+ surviveProbs: ймовірності виживання кожної особини з поточної популяції
+
+ Returns:
+ нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+ """
+ #
+ indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs))
+ newPopulation = list(np.array(population)[indices])
+ return newPopulation
+
+ def crossOver(self, population):
+ """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
+ Args:
+ population: поточна популяція
+
+ Returns:
+ нова популяція, такого ж розміру, як і стара, але з особин утворених в результаті скрещювання
+
+ """
+ # len(population) повинно бути парним
+
+ # N = (len(population) // 2) * 2
+ N = len(population)
+ families = [population[i: (i + 2)] for i in range(0, N, 2)]
+ # if len(population) % 2 == 1:
+ # families.append([population[-1], population[0]])
+
+ # ймовірність скрещування
+ crossOverProb = 0.9
+ successors = [self.getSuccessor(f, crossOverProb) for f in families]
+ newPopulation = []
+ for i in successors:
+ newPopulation.extend(i)
+
+ return newPopulation
+
+ def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float):
+ """функціє реалізує одноточкове скрещування для генетичного алгоритму
+
+ Args:
+ family: список з двох особин для скрещування
+ crossOverProb: ймовірність скрещювання(досить велика ймовірність)
+
+ Returns:
+ повертає двох особин:
+
+ якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання
+
+ якщо скрещування не відбулось: повертаються батьки(список family)
+
+ """
+ parent1, parent2 = family
+ gensCount = len(parent1)
+
+ # low border is inclusive, high border is exclusive
+ splitIdx = np.random.randint(1, gensCount)
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2
+ successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:]))
+
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1
+ successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:]))
+
+ # low border is inclusive, high border is exclusive
+ rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0]
+ if rIdx == 1:
+ self.changeProbs(successor1)
+ self.changeProbs(successor2)
+ return [successor1, successor2]
+ else:
+ return family
+
+ def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None:
+ """функція реалізує мутацію певної особини для генетичного алгоритму,
+ де новий мутований ген вибирається,
+ як значення нормально розподіленої випадкової величини
+ з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
+ Args:
+ person: особина, яка буде піддана мутації
+ indpb: ймовірність мутації одного гена особини(однієї ймовірності)
+ delta: максимальна різниця між поточним геном та мутованим
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+
+ def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
+ """
+
+ Args:
+ mean: (Default value = 0)
+ sd: (Default value = 1)
+ low: (Default value = 0)
+ upp: (Default value = 10)
+
+ Returns:
+
+ """
+ return truncnorm(
+ (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)
+
+ person0 = person.copy()
+ for idx in range(len(person)):
+ if np.random.uniform() <= indpb:
+ if debug is True:
+ print("mutation of one gen in one person_________________")
+ leftBorder = max(0, person[idx] - delta)
+ rightBorder = min(1, person[idx] + delta)
+ # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1))
+ newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs()
+ if debug is True:
+ print(f"old {person=}")
+ print(f"old gen:{person[idx]}, new gen:{newGenVal}\n")
+ # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0]
+ person[idx] = newGenVal
+ self.changeProbs(person, indsNot2ch=[idx, ])
+
+ if debug is True:
+ print(f"new {person=}\n")
+
+ if any(person < 0):
+ print(f"{person0=}")
+ print(f"{person=}")
+
+ def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None:
+ """якщо indsNot2ch порожній,
+ функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
+ якщо indsNot2ch не порожній,
+ функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+ чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
+ Args:
+ probs: масив ймовірностей
+ eps: похибка, яку дозволяє функція
+ indsNot2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ sVal = probs.sum()
+ value = abs(1 - sVal)
+
+ if value < eps:
+ maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0]
+ indices = list(range(len(probs)))
+ indices.remove(maxElIdx)
+
+ probs[maxElIdx] = (1 - probs[indices].sum())
+ return
+
+ if indsNot2ch is None:
+ inds2ch = list(range(len(probs)))
+ else:
+ inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch]
+
+ if sVal > 1:
+ self.decreaseProbs(probs, value, inds2ch)
+ else:
+ self.increaseProbs(probs, value, inds2ch)
+
+ # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps
+ self.changeProbs(probs)
+
+ def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None:
+ """value не повинно бути більше за sum(probs)
+ зменшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] - percentVals
+
+ def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]):
+ """value не повинно бути більше за sum(probs)
+ збільшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] + percentVals
diff --git a/backend/benchmark.py b/backend/benchmark.py
new file mode 100644
index 0000000..12df887
--- /dev/null
+++ b/backend/benchmark.py
@@ -0,0 +1,19 @@
+import time
+
+
+def benchmark(func):
+ """функція декоратор, яка використовується для обрахунку часу роботи функції func
+
+ Args:
+ func: функція, яка декорується
+
+ Returns:
+ функція _benchmark
+
+ """
+ def _benchmark(*args, **kwargs):
+ t0 = time.time()
+ res = func(*args, **kwargs)
+ print(f"{func.__name__} elapsed {time.time() - t0} secs")
+ return res
+ return _benchmark
diff --git a/backend/doc/backend/Bandit.html b/backend/doc/backend/Bandit.html
new file mode 100644
index 0000000..056a316
--- /dev/null
+++ b/backend/doc/backend/Bandit.html
@@ -0,0 +1,1909 @@
+
+
+
+
+
+
+backend.Bandit API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expand source code
+
+from __future__ import annotations
+
+import os
+from multiprocessing import Pool
+from typing import Dict, Tuple, List, Optional, Union
+import numpy as np
+from tqdm import tqdm
+
+from backend.CustomExceptions import *
+from backend.GenAlgorithm import GeneticAlgorithm
+
+
+class OneHandBandit:
+ """ """
+ players: List[OneHandBandit] = []
+
+ def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None):
+ """функція конструктор для класу OneHandBandit
+ Args:
+ m: number of columns
+ n: number of rows
+ k: number of pictures
+
+ k(number of pictures) must be greater or equal to n(number of rows)
+
+ probs: probability of each picture appearance
+ """
+
+ self.drumsColumns = [[i for i in range(k)] for _ in range(m)]
+ self.columnsNumb = m
+ self.rowsNumb = n
+ self.picturesNumb = k
+
+ self.probabilities: List[float, ...] = list()
+ self.setProbabilities(probs)
+
+ self.combinations: Dict[Tuple[int, ...], int] = dict()
+ self.price: int = 0
+
+ self.money: int = 0
+ self.moneySpent = 0
+ self.moneyWon: int = 0
+
+ self.state: List[List[int,],] = list()
+
+ self.genAlgo = GeneticAlgorithm(self)
+
+ OneHandBandit.players.append(self)
+
+ def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
+ """функція, яка встановлює виграшні комбінації
+
+ Args:
+ combs: dict of winning combinations
+
+ example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+ Returns:
+
+ """
+ combs = combs.copy()
+ for comb in combs:
+ if len(comb) != self.columnsNumb:
+ raise CombinationLengthException
+ elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
+ raise CombinationValuesException
+ elif combs[comb] < 0:
+ raise WinningMoneyException
+
+ self.combinations = combs
+
+ def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None:
+ """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
+ Args:
+ comb: комбінація індексів зображень
+ win: виграшна сума
+
+ Returns:
+
+ """
+ self.combinations[comb] = win
+
+ # the price of 1 turn
+ def setPriceOfGame(self, price) -> None:
+ """функція, яка встановлює вартість однієї гри
+
+ Args:
+ price: вартість однієї гри
+
+ Returns:
+
+ """
+ self.price = price
+
+ # money player gives to bandit
+ def startGame(self, money: int, modeling: bool = False) -> None:
+ """функція, яка вносить до автомату певну суму грошей, на які можна грати
+
+ Args:
+ money: сума грошей, яка вноситься до автомату
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+ if money > 0:
+ self.money = money
+ if not modeling:
+ print(f"you deposited {money} money")
+
+ def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None:
+ """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+ слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
+ Args:
+ probs: ймовірності зображень
+
+ Returns:
+
+ """
+ if probs is None:
+ # uniform distribution
+ probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
+
+ if 0.001 < abs(sum(probs) - 1.0):
+ # sum of probs dont equal 1
+ raise ProbabilitiesSumException
+
+ if len(probs) != self.picturesNumb:
+ raise ProbabilitiesArrayLengthException
+
+ self.probabilities = probs.copy()
+
+ def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None:
+ """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
+ Args:
+ filename: ім'я файлу з ймовірностями
+
+ Returns:
+
+ """
+ with open(filename, "r") as f:
+ probsLine = f.readline()
+ probs = list(map(lambda x: float(x), probsLine.split(" ")))
+
+ self.setProbabilities(probs)
+
+ def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None,
+ debug: bool = False) -> List[float]:
+ """функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
+ Можлива генерація випадкових ймовірностей
+ та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
+ Args:
+ probsCount: довжина списку ймовірностей
+ returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+
+ якщо returnCoef is None, то генеруються випадкові ймовірності
+
+ якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+ згенерований список ймовірностей
+
+ """
+ probs = []
+ if returnCoef is None:
+ # generate random probabilities
+ high = 1
+ e = 0.25 * high
+
+ for _ in range(probsCount - 1):
+ probs.append(np.random.uniform(0, high - e))
+ high -= probs[-1]
+ e = 0.01 * high
+ probs.append(high)
+ return probs
+ else:
+ probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug)
+ return probs
+
+ def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None,
+ isGraph: bool = False) -> Union[float, List[float]]:
+ """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
+ функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ коефіцієнт віддачі автомату, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+ """
+ oldProbs = self.probabilities
+ oldMoneySpent = self.moneySpent
+ oldMoneyWon = self.moneyWon
+ if probs is not None:
+ self.setProbabilities(probs)
+ self.moneyWon = 0
+ self.moneySpent = 0
+
+ money = self.price * gamesCount
+
+ modeling = True
+ self.startGame(money, modeling=modeling)
+ meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph)
+ gameReturn = self.moneyWon
+ gameReturnPercentage = gameReturn / self.moneySpent
+ if probs is not None:
+ self.setProbabilities(oldProbs)
+ self.moneyWon = oldMoneyWon
+ self.moneySpent = oldMoneySpent
+ if isGraph:
+ return meanRCs
+
+ return gameReturnPercentage
+
+ def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None,
+ coresCount: int = -1):
+ """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+ шляхом розпаралелювання роботи.
+
+ функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+ але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ coresCount: кількість процесів, які запустить дана функція
+
+ Returns:
+ коефіцієнт віддачі автомату
+
+ """
+ if coresCount == -1:
+ coresCount = int(os.cpu_count() * 0.75)
+ if coresCount == 0:
+ coresCount = 1
+ elif coresCount == 0:
+ return self.getReturnCoef(gamesCount, probs)
+
+ gamesCountList = [gamesCount // coresCount, ] * coresCount
+ gamesCountList[-1] += (gamesCount - sum(gamesCountList))
+
+ args = zip(gamesCountList, [probs, ] * len(gamesCountList), )
+ # args = list(args)
+
+ with Pool(processes=coresCount) as executor:
+ results = executor.starmap(self.getReturnCoef, args)
+
+ res = sum(results) / coresCount
+ return res
+
+ # @benchmark
+ def currentTurn(self, modeling: bool = False) -> int:
+ """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+ та викликає функцію self.setState
+
+ Args:
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+ виграш гравця
+
+ """
+ self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
+ self.money -= self.price
+ self.moneySpent += self.price
+ won = self.currentWon()
+
+ if not modeling:
+ self.printState()
+
+ return won
+
+ def turnColumn(self, i) -> List[int,]:
+ """функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
+ Args:
+ i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+ де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ downIdx = self.rowsNumb - 1
+ upIdx = 0
+ rolledPicturesIdx = []
+ resColumn = [-1, ] * self.rowsNumb
+ for idx in range(self.rowsNumb):
+ newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
+
+ # assert abs(sum(newProbs) - 1) < 0.001
+
+ rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
+ rolledPicturesIdx.append(rolledPicIdx)
+
+ if (idx % 2) == 0:
+ resColumn[downIdx] = rolledPicIdx
+ downIdx -= 1
+ else:
+ resColumn[upIdx] = rolledPicIdx
+ upIdx += 1
+
+ return resColumn
+
+ def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
+ """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+ точніше описано в файлі README.ipynb
+
+ Args:
+ rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика
+
+ Returns:
+ значення ймовірностей для наступної генерації
+
+ """
+ newProbs = np.array(self.probabilities, dtype=float)
+ divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
+ newProbs[rolledPicturesIdxs] = 0.0
+ newProbs = newProbs / divideCoeff
+ return list(newProbs)
+
+ def currentWon(self) -> int:
+ """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
+ Args:
+
+ Returns:
+ весь виграш гравця
+
+ """
+ won = 0
+ for i in range(self.rowsNumb):
+ comb = tuple(self.getRow(i))
+ if comb in self.combinations:
+ # it is possible to have several winning combinations....
+ won += self.combinations[comb]
+
+ self.moneyWon += won # ???? not sure if it is needed
+ return won
+
+ def getRow(self, i) -> np.array:
+ """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
+ Args:
+ i: індекс рядка матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return np.array([self.state[j][i] for j in range(self.columnsNumb)])
+
+ def getColumn(self, j):
+ """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
+ Args:
+ j: індекс стовпця матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return self.state[j]
+
+ def getState(self) -> np.array:
+ """функція, яка повертає матрицю self.state(матрицю однієї гри)
+
+ Args:
+
+ Returns:
+ транспонована(задля спрощення її відображення) матриця self.state
+
+ """
+ return np.array(self.state).T
+
+ def setState(self, state: List[List[int,],]) -> None:
+ """функція, яка встановлює матрицю однієї гри
+
+ Args:
+ state: матриця індексів зображень однієї гри
+
+ Returns:
+
+ """
+ if all([len(col) == len(set(col)) for col in state]):
+ self.state = state
+ else:
+ raise SettingStateException
+
+ def printState(self) -> None:
+ """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися"""
+ print(f"current state: \n{self.getState()}")
+ # print("money spent:", self.moneySpent)
+ print("money won:", self.moneyWon)
+ print("money remained:", self.money)
+
+ def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]],
+ List[float]]:
+ """функція, яка проводить n ігор
+
+ Args:
+ n: кількість ігор
+ modeling: параметр, який впливає на інформативність функції
+ isGraph: параметра, який керує поверненням спеціальних значень
+
+ Returns:
+ список виграшів гравця за n ігор, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+ """
+ if self.money < self.price * n:
+ print("Not enough money")
+ return None
+
+ wons = []
+ meanRCs = []
+ # play game n times
+ for i in range(n):
+ won = self.currentTurn(modeling=modeling)
+ wons.append(won)
+ if not modeling:
+ print(f"you won {won} money for one turn")
+ print(f"you spent {self.price} money for one turn")
+ print()
+
+ if isGraph:
+ meanRCs.append(sum(wons) / ((i + 1) * self.price))
+
+ if isGraph:
+ return meanRCs
+
+ if not modeling:
+ return wons
+
+ # winning sum in fact could be (and often is) negative ;-))
+ @property
+ def currentPlayerWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+ віднявши від нього витрати гравця
+
+ Args:
+
+ Returns:
+ виграш гравця - витрати гравця
+
+ """
+ return self.moneyWon - self.moneySpent # not sure
+
+ @property
+ def totalPlayersWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+ який вона обраховує, використовуючи функцію self.currentPlayerWon
+
+ Args:
+
+ Returns:
+ виграш всіх гравців - витрати всіх гравців
+
+ """
+ totalWin = 0
+ for p in OneHandBandit.players:
+ totalWin += p.currentPlayerWon
+ return totalWin
+
+
+if __name__ == "__main__":
+ p2 = OneHandBandit(4, 3, 5)
+ # p2.setProbabilitiesFromTxtFile("probabilities.txt")
+
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+
+ # p2.startGame(100)
+ # p2.play(10)
+ try:
+ returnCoef = float(
+ input("write down return coefficient for probabilities you would like get for(Example 0.9): "))
+ except Exception as e:
+ print("during input error occurred:", e)
+ print("return coefficient set to 0.9")
+ returnCoef = 0.9
+ # np.random.seed(42)
+ probs = p2.genBaseProbabilities(p2.picturesNumb, returnCoef, debug=True)
+ print(f"{probs=}")
+ print(f"fitness val for probs and goalValue={returnCoef}:",
+ p2.genAlgo.fitness_func(probs=probs, goalValue=returnCoef))
+ print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+
+ probsStrs = " ".join(map(str, probs))
+ with open("probabilities.txt", "w") as f:
+ f.write(probsStrs)
+ # modelingGamesCount = 2000
+ #
+ # print(p2.genBaseProbabilities(5, returnCoef=0.8))
+ # p2.getReturnCoefWithNCores(modelingGamesCount)
+ # l = []
+ # t0 = time.time()
+ # for i in range(10):
+ #
+ # returnCoef = p2.getReturnCoefWithNCores(gamesCount=modelingGamesCount, coresCount=5)
+ # l.append(returnCoef)
+ # print(f"{i}) this one hand bandit return coefficient: {returnCoef} "
+ # f"for {modelingGamesCount} games")
+ # print("min max range:", max(l) - min(l))
+ # print(time.time() - t0, "secs")
+
+# метод ньютона
+
+# генетичний алгоритм(рандомна мутація)
+
+
+
+
+
+
+
+
+
+class OneHandBandit
+( m: int, n: int, k: int, probs: Optional[List[float, ...]] = None)
+
+
+функція конструктор для класу OneHandBandit
+
Args
+
+m
+number of columns
+n
+number of rows
+k
+
+number of pictures
+k(number of pictures) must be greater or equal to n(number of rows)
+
+probs
+probability of each picture appearance
+
+
+
+Expand source code
+
+class OneHandBandit:
+ """ """
+ players: List[OneHandBandit] = []
+
+ def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None):
+ """функція конструктор для класу OneHandBandit
+ Args:
+ m: number of columns
+ n: number of rows
+ k: number of pictures
+
+ k(number of pictures) must be greater or equal to n(number of rows)
+
+ probs: probability of each picture appearance
+ """
+
+ self.drumsColumns = [[i for i in range(k)] for _ in range(m)]
+ self.columnsNumb = m
+ self.rowsNumb = n
+ self.picturesNumb = k
+
+ self.probabilities: List[float, ...] = list()
+ self.setProbabilities(probs)
+
+ self.combinations: Dict[Tuple[int, ...], int] = dict()
+ self.price: int = 0
+
+ self.money: int = 0
+ self.moneySpent = 0
+ self.moneyWon: int = 0
+
+ self.state: List[List[int,],] = list()
+
+ self.genAlgo = GeneticAlgorithm(self)
+
+ OneHandBandit.players.append(self)
+
+ def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
+ """функція, яка встановлює виграшні комбінації
+
+ Args:
+ combs: dict of winning combinations
+
+ example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+ Returns:
+
+ """
+ combs = combs.copy()
+ for comb in combs:
+ if len(comb) != self.columnsNumb:
+ raise CombinationLengthException
+ elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
+ raise CombinationValuesException
+ elif combs[comb] < 0:
+ raise WinningMoneyException
+
+ self.combinations = combs
+
+ def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None:
+ """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
+ Args:
+ comb: комбінація індексів зображень
+ win: виграшна сума
+
+ Returns:
+
+ """
+ self.combinations[comb] = win
+
+ # the price of 1 turn
+ def setPriceOfGame(self, price) -> None:
+ """функція, яка встановлює вартість однієї гри
+
+ Args:
+ price: вартість однієї гри
+
+ Returns:
+
+ """
+ self.price = price
+
+ # money player gives to bandit
+ def startGame(self, money: int, modeling: bool = False) -> None:
+ """функція, яка вносить до автомату певну суму грошей, на які можна грати
+
+ Args:
+ money: сума грошей, яка вноситься до автомату
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+ if money > 0:
+ self.money = money
+ if not modeling:
+ print(f"you deposited {money} money")
+
+ def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None:
+ """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+ слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
+ Args:
+ probs: ймовірності зображень
+
+ Returns:
+
+ """
+ if probs is None:
+ # uniform distribution
+ probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
+
+ if 0.001 < abs(sum(probs) - 1.0):
+ # sum of probs dont equal 1
+ raise ProbabilitiesSumException
+
+ if len(probs) != self.picturesNumb:
+ raise ProbabilitiesArrayLengthException
+
+ self.probabilities = probs.copy()
+
+ def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None:
+ """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
+ Args:
+ filename: ім'я файлу з ймовірностями
+
+ Returns:
+
+ """
+ with open(filename, "r") as f:
+ probsLine = f.readline()
+ probs = list(map(lambda x: float(x), probsLine.split(" ")))
+
+ self.setProbabilities(probs)
+
+ def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None,
+ debug: bool = False) -> List[float]:
+ """функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
+ Можлива генерація випадкових ймовірностей
+ та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
+ Args:
+ probsCount: довжина списку ймовірностей
+ returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+
+ якщо returnCoef is None, то генеруються випадкові ймовірності
+
+ якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+ згенерований список ймовірностей
+
+ """
+ probs = []
+ if returnCoef is None:
+ # generate random probabilities
+ high = 1
+ e = 0.25 * high
+
+ for _ in range(probsCount - 1):
+ probs.append(np.random.uniform(0, high - e))
+ high -= probs[-1]
+ e = 0.01 * high
+ probs.append(high)
+ return probs
+ else:
+ probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug)
+ return probs
+
+ def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None,
+ isGraph: bool = False) -> Union[float, List[float]]:
+ """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
+ функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ коефіцієнт віддачі автомату, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+ """
+ oldProbs = self.probabilities
+ oldMoneySpent = self.moneySpent
+ oldMoneyWon = self.moneyWon
+ if probs is not None:
+ self.setProbabilities(probs)
+ self.moneyWon = 0
+ self.moneySpent = 0
+
+ money = self.price * gamesCount
+
+ modeling = True
+ self.startGame(money, modeling=modeling)
+ meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph)
+ gameReturn = self.moneyWon
+ gameReturnPercentage = gameReturn / self.moneySpent
+ if probs is not None:
+ self.setProbabilities(oldProbs)
+ self.moneyWon = oldMoneyWon
+ self.moneySpent = oldMoneySpent
+ if isGraph:
+ return meanRCs
+
+ return gameReturnPercentage
+
+ def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None,
+ coresCount: int = -1):
+ """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+ шляхом розпаралелювання роботи.
+
+ функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+ але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ coresCount: кількість процесів, які запустить дана функція
+
+ Returns:
+ коефіцієнт віддачі автомату
+
+ """
+ if coresCount == -1:
+ coresCount = int(os.cpu_count() * 0.75)
+ if coresCount == 0:
+ coresCount = 1
+ elif coresCount == 0:
+ return self.getReturnCoef(gamesCount, probs)
+
+ gamesCountList = [gamesCount // coresCount, ] * coresCount
+ gamesCountList[-1] += (gamesCount - sum(gamesCountList))
+
+ args = zip(gamesCountList, [probs, ] * len(gamesCountList), )
+ # args = list(args)
+
+ with Pool(processes=coresCount) as executor:
+ results = executor.starmap(self.getReturnCoef, args)
+
+ res = sum(results) / coresCount
+ return res
+
+ # @benchmark
+ def currentTurn(self, modeling: bool = False) -> int:
+ """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+ та викликає функцію self.setState
+
+ Args:
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+ виграш гравця
+
+ """
+ self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
+ self.money -= self.price
+ self.moneySpent += self.price
+ won = self.currentWon()
+
+ if not modeling:
+ self.printState()
+
+ return won
+
+ def turnColumn(self, i) -> List[int,]:
+ """функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
+ Args:
+ i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+ де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ downIdx = self.rowsNumb - 1
+ upIdx = 0
+ rolledPicturesIdx = []
+ resColumn = [-1, ] * self.rowsNumb
+ for idx in range(self.rowsNumb):
+ newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
+
+ # assert abs(sum(newProbs) - 1) < 0.001
+
+ rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
+ rolledPicturesIdx.append(rolledPicIdx)
+
+ if (idx % 2) == 0:
+ resColumn[downIdx] = rolledPicIdx
+ downIdx -= 1
+ else:
+ resColumn[upIdx] = rolledPicIdx
+ upIdx += 1
+
+ return resColumn
+
+ def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
+ """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+ точніше описано в файлі README.ipynb
+
+ Args:
+ rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика
+
+ Returns:
+ значення ймовірностей для наступної генерації
+
+ """
+ newProbs = np.array(self.probabilities, dtype=float)
+ divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
+ newProbs[rolledPicturesIdxs] = 0.0
+ newProbs = newProbs / divideCoeff
+ return list(newProbs)
+
+ def currentWon(self) -> int:
+ """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
+ Args:
+
+ Returns:
+ весь виграш гравця
+
+ """
+ won = 0
+ for i in range(self.rowsNumb):
+ comb = tuple(self.getRow(i))
+ if comb in self.combinations:
+ # it is possible to have several winning combinations....
+ won += self.combinations[comb]
+
+ self.moneyWon += won # ???? not sure if it is needed
+ return won
+
+ def getRow(self, i) -> np.array:
+ """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
+ Args:
+ i: індекс рядка матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return np.array([self.state[j][i] for j in range(self.columnsNumb)])
+
+ def getColumn(self, j):
+ """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
+ Args:
+ j: індекс стовпця матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return self.state[j]
+
+ def getState(self) -> np.array:
+ """функція, яка повертає матрицю self.state(матрицю однієї гри)
+
+ Args:
+
+ Returns:
+ транспонована(задля спрощення її відображення) матриця self.state
+
+ """
+ return np.array(self.state).T
+
+ def setState(self, state: List[List[int,],]) -> None:
+ """функція, яка встановлює матрицю однієї гри
+
+ Args:
+ state: матриця індексів зображень однієї гри
+
+ Returns:
+
+ """
+ if all([len(col) == len(set(col)) for col in state]):
+ self.state = state
+ else:
+ raise SettingStateException
+
+ def printState(self) -> None:
+ """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися"""
+ print(f"current state: \n{self.getState()}")
+ # print("money spent:", self.moneySpent)
+ print("money won:", self.moneyWon)
+ print("money remained:", self.money)
+
+ def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]],
+ List[float]]:
+ """функція, яка проводить n ігор
+
+ Args:
+ n: кількість ігор
+ modeling: параметр, який впливає на інформативність функції
+ isGraph: параметра, який керує поверненням спеціальних значень
+
+ Returns:
+ список виграшів гравця за n ігор, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+ """
+ if self.money < self.price * n:
+ print("Not enough money")
+ return None
+
+ wons = []
+ meanRCs = []
+ # play game n times
+ for i in range(n):
+ won = self.currentTurn(modeling=modeling)
+ wons.append(won)
+ if not modeling:
+ print(f"you won {won} money for one turn")
+ print(f"you spent {self.price} money for one turn")
+ print()
+
+ if isGraph:
+ meanRCs.append(sum(wons) / ((i + 1) * self.price))
+
+ if isGraph:
+ return meanRCs
+
+ if not modeling:
+ return wons
+
+ # winning sum in fact could be (and often is) negative ;-))
+ @property
+ def currentPlayerWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+ віднявши від нього витрати гравця
+
+ Args:
+
+ Returns:
+ виграш гравця - витрати гравця
+
+ """
+ return self.moneyWon - self.moneySpent # not sure
+
+ @property
+ def totalPlayersWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+ який вона обраховує, використовуючи функцію self.currentPlayerWon
+
+ Args:
+
+ Returns:
+ виграш всіх гравців - витрати всіх гравців
+
+ """
+ totalWin = 0
+ for p in OneHandBandit.players:
+ totalWin += p.currentPlayerWon
+ return totalWin
+
+Class variables
+
+var players : List[OneHandBandit ]
+
+
+
+
+Instance variables
+
+var currentPlayerWon
+
+функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+віднявши від нього витрати гравця
+
Args:
+
Returns
+
виграш гравця - витрати гравця
+
+
+Expand source code
+
+@property
+def currentPlayerWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+ віднявши від нього витрати гравця
+
+ Args:
+
+ Returns:
+ виграш гравця - витрати гравця
+
+ """
+ return self.moneyWon - self.moneySpent # not sure
+
+
+var totalPlayersWon
+
+функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+який вона обраховує, використовуючи функцію self.currentPlayerWon
+
Args:
+
Returns
+
виграш всіх гравців - витрати всіх гравців
+
+
+Expand source code
+
+@property
+def totalPlayersWon(self):
+ """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+ який вона обраховує, використовуючи функцію self.currentPlayerWon
+
+ Args:
+
+ Returns:
+ виграш всіх гравців - витрати всіх гравців
+
+ """
+ totalWin = 0
+ for p in OneHandBandit.players:
+ totalWin += p.currentPlayerWon
+ return totalWin
+
+
+
+Methods
+
+
+def addWinningComb (self, comb: Tuple[int, ...], win: int) ‑> None
+
+
+функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
Args
+
+comb
+комбінація індексів зображень
+win
+виграшна сума
+
+
Returns:
+
+
+Expand source code
+
+def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None:
+ """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
+ Args:
+ comb: комбінація індексів зображень
+ win: виграшна сума
+
+ Returns:
+
+ """
+ self.combinations[comb] = win
+
+
+
+def currentTurn (self, modeling: bool = False) ‑> int
+
+
+функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+та викликає функцію self.setState
+
Args
+
+modeling
+параметр, який регулює інформативність функції
+
+
Returns
+
виграш гравця
+
+
+Expand source code
+
+def currentTurn(self, modeling: bool = False) -> int:
+ """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+ та викликає функцію self.setState
+
+ Args:
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+ виграш гравця
+
+ """
+ self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
+ self.money -= self.price
+ self.moneySpent += self.price
+ won = self.currentWon()
+
+ if not modeling:
+ self.printState()
+
+ return won
+
+
+
+def currentWon (self) ‑> int
+
+
+функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
Args:
+
Returns
+
весь виграш гравця
+
+
+Expand source code
+
+def currentWon(self) -> int:
+ """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
+ Args:
+
+ Returns:
+ весь виграш гравця
+
+ """
+ won = 0
+ for i in range(self.rowsNumb):
+ comb = tuple(self.getRow(i))
+ if comb in self.combinations:
+ # it is possible to have several winning combinations....
+ won += self.combinations[comb]
+
+ self.moneyWon += won # ???? not sure if it is needed
+ return won
+
+
+
+def genBaseProbabilities (self, probsCount: int, returnCoef: Optional[float] = None, debug: bool = False) ‑> List[float]
+
+
+функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
Можлива генерація випадкових ймовірностей
+та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
Args
+
+probsCount
+довжина списку ймовірностей
+returnCoef
+
+коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+якщо returnCoef is None, то генеруються випадкові ймовірності
+якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+debug
+параметр, який регулює інформативність функції
+
+
Returns
+
згенерований список ймовірностей
+
+
+Expand source code
+
+def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None,
+ debug: bool = False) -> List[float]:
+ """функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
+ Можлива генерація випадкових ймовірностей
+ та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
+ Args:
+ probsCount: довжина списку ймовірностей
+ returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+
+ якщо returnCoef is None, то генеруються випадкові ймовірності
+
+ якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+ згенерований список ймовірностей
+
+ """
+ probs = []
+ if returnCoef is None:
+ # generate random probabilities
+ high = 1
+ e = 0.25 * high
+
+ for _ in range(probsCount - 1):
+ probs.append(np.random.uniform(0, high - e))
+ high -= probs[-1]
+ e = 0.01 * high
+ probs.append(high)
+ return probs
+ else:
+ probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug)
+ return probs
+
+
+
+def genTurnProbabilities (self, rolledPicturesIdxs: List[int, ...]) ‑> List
+
+
+функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+точніше описано в файлі README.ipynb
+
Args
+
+rolledPicturesIdxs
+зображення, які уже були згенеровані у рядках одного стовпчика
+
+
Returns
+
значення ймовірностей для наступної генерації
+
+
+Expand source code
+
+def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
+ """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+ точніше описано в файлі README.ipynb
+
+ Args:
+ rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика
+
+ Returns:
+ значення ймовірностей для наступної генерації
+
+ """
+ newProbs = np.array(self.probabilities, dtype=float)
+ divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
+ newProbs[rolledPicturesIdxs] = 0.0
+ newProbs = newProbs / divideCoeff
+ return list(newProbs)
+
+
+
+def getColumn (self, j)
+
+
+функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
Args
+
+j
+індекс стовпця матриці гри
+
+
Returns
+
список індексів зображень
+
+
+Expand source code
+
+def getColumn(self, j):
+ """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
+ Args:
+ j: індекс стовпця матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return self.state[j]
+
+
+
+def getReturnCoef (self, gamesCount: int = 10000, probs: Optional[List[float, ...]] = None, isGraph: bool = False) ‑> Union[float, List[float]]
+
+
+функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
Args
+
+gamesCount
+кількість ігор, які будуть промодельовані
+probs
+ймовірності зображень, з якими буде проводитись кожна гра
+isGraph
+параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+
Returns
+
коефіцієнт віддачі автомату, якщо isGraph рівне False
+
список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+
+Expand source code
+
+def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None,
+ isGraph: bool = False) -> Union[float, List[float]]:
+ """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
+ функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ коефіцієнт віддачі автомату, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+ """
+ oldProbs = self.probabilities
+ oldMoneySpent = self.moneySpent
+ oldMoneyWon = self.moneyWon
+ if probs is not None:
+ self.setProbabilities(probs)
+ self.moneyWon = 0
+ self.moneySpent = 0
+
+ money = self.price * gamesCount
+
+ modeling = True
+ self.startGame(money, modeling=modeling)
+ meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph)
+ gameReturn = self.moneyWon
+ gameReturnPercentage = gameReturn / self.moneySpent
+ if probs is not None:
+ self.setProbabilities(oldProbs)
+ self.moneyWon = oldMoneyWon
+ self.moneySpent = oldMoneySpent
+ if isGraph:
+ return meanRCs
+
+ return gameReturnPercentage
+
+
+
+def getReturnCoefWithNCores (self, gamesCount: int = 1000, probs: Optional[List[float, ...]] = None, coresCount: int = -1)
+
+
+функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+шляхом розпаралелювання роботи.
+
функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
Args
+
+gamesCount
+кількість ігор, які будуть промодельовані
+probs
+ймовірності зображень, з якими буде проводитись кожна гра
+coresCount
+кількість процесів, які запустить дана функція
+
+
Returns
+
коефіцієнт віддачі автомату
+
+
+Expand source code
+
+def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None,
+ coresCount: int = -1):
+ """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+ шляхом розпаралелювання роботи.
+
+ функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+ але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
+ Args:
+ gamesCount: кількість ігор, які будуть промодельовані
+ probs: ймовірності зображень, з якими буде проводитись кожна гра
+ coresCount: кількість процесів, які запустить дана функція
+
+ Returns:
+ коефіцієнт віддачі автомату
+
+ """
+ if coresCount == -1:
+ coresCount = int(os.cpu_count() * 0.75)
+ if coresCount == 0:
+ coresCount = 1
+ elif coresCount == 0:
+ return self.getReturnCoef(gamesCount, probs)
+
+ gamesCountList = [gamesCount // coresCount, ] * coresCount
+ gamesCountList[-1] += (gamesCount - sum(gamesCountList))
+
+ args = zip(gamesCountList, [probs, ] * len(gamesCountList), )
+ # args = list(args)
+
+ with Pool(processes=coresCount) as executor:
+ results = executor.starmap(self.getReturnCoef, args)
+
+ res = sum(results) / coresCount
+ return res
+
+
+
+def getRow (self, i) ‑>
+
+
+функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
Args
+
+i
+індекс рядка матриці гри
+
+
Returns
+
список індексів зображень
+
+
+Expand source code
+
+def getRow(self, i) -> np.array:
+ """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
+ Args:
+ i: індекс рядка матриці гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ return np.array([self.state[j][i] for j in range(self.columnsNumb)])
+
+
+
+def getState (self) ‑>
+
+
+функція, яка повертає матрицю self.state(матрицю однієї гри)
+
Args:
+
Returns
+
транспонована(задля спрощення її відображення) матриця self.state
+
+
+Expand source code
+
+def getState(self) -> np.array:
+ """функція, яка повертає матрицю self.state(матрицю однієї гри)
+
+ Args:
+
+ Returns:
+ транспонована(задля спрощення її відображення) матриця self.state
+
+ """
+ return np.array(self.state).T
+
+
+
+def play (self, n, modeling: bool = False, isGraph: bool = False) ‑> Union[List[int], None, List[float]]
+
+
+функція, яка проводить n ігор
+
Args
+
+n
+кількість ігор
+modeling
+параметр, який впливає на інформативність функції
+isGraph
+параметра, який керує поверненням спеціальних значень
+
+
Returns
+
список виграшів гравця за n ігор, якщо isGraph рівне False
+
список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+
+Expand source code
+
+def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]],
+ List[float]]:
+ """функція, яка проводить n ігор
+
+ Args:
+ n: кількість ігор
+ modeling: параметр, який впливає на інформативність функції
+ isGraph: параметра, який керує поверненням спеціальних значень
+
+ Returns:
+ список виграшів гравця за n ігор, якщо isGraph рівне False
+
+ список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+ """
+ if self.money < self.price * n:
+ print("Not enough money")
+ return None
+
+ wons = []
+ meanRCs = []
+ # play game n times
+ for i in range(n):
+ won = self.currentTurn(modeling=modeling)
+ wons.append(won)
+ if not modeling:
+ print(f"you won {won} money for one turn")
+ print(f"you spent {self.price} money for one turn")
+ print()
+
+ if isGraph:
+ meanRCs.append(sum(wons) / ((i + 1) * self.price))
+
+ if isGraph:
+ return meanRCs
+
+ if not modeling:
+ return wons
+
+
+
+def printState (self) ‑> None
+
+
+функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися
+
+
+Expand source code
+
+def printState(self) -> None:
+ """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися"""
+ print(f"current state: \n{self.getState()}")
+ # print("money spent:", self.moneySpent)
+ print("money won:", self.moneyWon)
+ print("money remained:", self.money)
+
+
+
+def setPriceOfGame (self, price) ‑> None
+
+
+функція, яка встановлює вартість однієї гри
+
Args
+
+price
+вартість однієї гри
+
+
Returns:
+
+
+Expand source code
+
+def setPriceOfGame(self, price) -> None:
+ """функція, яка встановлює вартість однієї гри
+
+ Args:
+ price: вартість однієї гри
+
+ Returns:
+
+ """
+ self.price = price
+
+
+
+def setProbabilities (self, probs: Optional[List[float, ...]] = None) ‑> None
+
+
+функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
Args
+
+probs
+ймовірності зображень
+
+
Returns:
+
+
+Expand source code
+
+def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None:
+ """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+ слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
+ Args:
+ probs: ймовірності зображень
+
+ Returns:
+
+ """
+ if probs is None:
+ # uniform distribution
+ probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
+
+ if 0.001 < abs(sum(probs) - 1.0):
+ # sum of probs dont equal 1
+ raise ProbabilitiesSumException
+
+ if len(probs) != self.picturesNumb:
+ raise ProbabilitiesArrayLengthException
+
+ self.probabilities = probs.copy()
+
+
+
+def setProbabilitiesFromTxtFile (self, filename: str = 'probabilities.txt') ‑> None
+
+
+функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
Args
+
+filename
+ім'я файлу з ймовірностями
+
+
Returns:
+
+
+Expand source code
+
+def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None:
+ """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
+ Args:
+ filename: ім'я файлу з ймовірностями
+
+ Returns:
+
+ """
+ with open(filename, "r") as f:
+ probsLine = f.readline()
+ probs = list(map(lambda x: float(x), probsLine.split(" ")))
+
+ self.setProbabilities(probs)
+
+
+
+def setState (self, state: List[List[int,],]) ‑> None
+
+
+функція, яка встановлює матрицю однієї гри
+
Args
+
+state
+матриця індексів зображень однієї гри
+
+
Returns:
+
+
+Expand source code
+
+def setState(self, state: List[List[int,],]) -> None:
+ """функція, яка встановлює матрицю однієї гри
+
+ Args:
+ state: матриця індексів зображень однієї гри
+
+ Returns:
+
+ """
+ if all([len(col) == len(set(col)) for col in state]):
+ self.state = state
+ else:
+ raise SettingStateException
+
+
+
+def setWinningCombs (self, combs: Dict[Tuple[int, ...], int])
+
+
+функція, яка встановлює виграшні комбінації
+
Args
+
+combs
+
+dict of winning combinations
+example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+
+
Returns:
+
+
+Expand source code
+
+def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
+ """функція, яка встановлює виграшні комбінації
+
+ Args:
+ combs: dict of winning combinations
+
+ example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+ Returns:
+
+ """
+ combs = combs.copy()
+ for comb in combs:
+ if len(comb) != self.columnsNumb:
+ raise CombinationLengthException
+ elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
+ raise CombinationValuesException
+ elif combs[comb] < 0:
+ raise WinningMoneyException
+
+ self.combinations = combs
+
+
+
+def startGame (self, money: int, modeling: bool = False) ‑> None
+
+
+функція, яка вносить до автомату певну суму грошей, на які можна грати
+
Args
+
+money
+сума грошей, яка вноситься до автомату
+modeling
+параметр, який регулює інформативність функції
+
+
Returns:
+
+
+Expand source code
+
+def startGame(self, money: int, modeling: bool = False) -> None:
+ """функція, яка вносить до автомату певну суму грошей, на які можна грати
+
+ Args:
+ money: сума грошей, яка вноситься до автомату
+ modeling: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+ if money > 0:
+ self.money = money
+ if not modeling:
+ print(f"you deposited {money} money")
+
+
+
+def turnColumn (self, i) ‑> List[int]
+
+
+функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
Args
+
+i
+індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+
Returns
+
список індексів зображень
+
+
+Expand source code
+
+def turnColumn(self, i) -> List[int,]:
+ """функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
+ Args:
+ i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+ де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+ Returns:
+ список індексів зображень
+
+ """
+ downIdx = self.rowsNumb - 1
+ upIdx = 0
+ rolledPicturesIdx = []
+ resColumn = [-1, ] * self.rowsNumb
+ for idx in range(self.rowsNumb):
+ newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
+
+ # assert abs(sum(newProbs) - 1) < 0.001
+
+ rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
+ rolledPicturesIdx.append(rolledPicIdx)
+
+ if (idx % 2) == 0:
+ resColumn[downIdx] = rolledPicIdx
+ downIdx -= 1
+ else:
+ resColumn[upIdx] = rolledPicIdx
+ upIdx += 1
+
+ return resColumn
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/CustomExceptions.html b/backend/doc/backend/CustomExceptions.html
new file mode 100644
index 0000000..a3aa729
--- /dev/null
+++ b/backend/doc/backend/CustomExceptions.html
@@ -0,0 +1,317 @@
+
+
+
+
+
+
+backend.CustomExceptions API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module backend.CustomExceptions
+
+
+
+
+Expand source code
+
+class SettingStateException(Exception):
+ def __str__(self):
+ return "invalid values for state massive: values in each column have to be distinct"
+
+
+class SettingWinningCombinationsException(Exception):
+ def __str__(self):
+ return "invalid values for dict combinations:"
+
+
+class CombinationLengthException(SettingWinningCombinationsException):
+ """
+ key length of dict combinations have to equal number columns of massive state in class OneHandBandit
+ """
+
+ def __str__(self):
+ return super().__str__() + " key length of dict combinations have to equal number columns " \
+ "of massive state in class OneHandBandit"
+
+
+class CombinationValuesException(SettingWinningCombinationsException):
+ def __str__(self):
+ return super().__str__() + " key values of dict combinations have to be " \
+ "lower than (picturesNumb - 1) in class OneHandBandit" \
+ "and greater than 0"
+
+
+class WinningMoneyException(SettingWinningCombinationsException):
+ def __str__(self):
+ return super().__str__() + " winning money have to be greater than 0"
+
+
+class SettingProbabilities2PicturesException(Exception):
+ def __str__(self):
+ return "error when set the probabilities for pictures;"
+
+
+class ProbabilitiesArrayLengthException(SettingProbabilities2PicturesException):
+ def __str__(self):
+ return super().__str__() + " probabilities array must be the same length as pictures array length"
+
+
+class ProbabilitiesSumException(SettingProbabilities2PicturesException):
+ def __str__(self):
+ return super().__str__() + " probabilities array sum must equal 1"
+
+
+
+
+
+
+
+
+
+class CombinationLengthException
+( *args, **kwargs)
+
+
+key length of dict combinations have to equal number columns of massive state in class OneHandBandit
+
+
+Expand source code
+
+class CombinationLengthException(SettingWinningCombinationsException):
+ """
+ key length of dict combinations have to equal number columns of massive state in class OneHandBandit
+ """
+
+ def __str__(self):
+ return super().__str__() + " key length of dict combinations have to equal number columns " \
+ "of massive state in class OneHandBandit"
+
+Ancestors
+
+
+
+class CombinationValuesException
+( *args, **kwargs)
+
+
+Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class CombinationValuesException(SettingWinningCombinationsException):
+ def __str__(self):
+ return super().__str__() + " key values of dict combinations have to be " \
+ "lower than (picturesNumb - 1) in class OneHandBandit" \
+ "and greater than 0"
+
+Ancestors
+
+
+
+class ProbabilitiesArrayLengthException
+( *args, **kwargs)
+
+
+Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class ProbabilitiesArrayLengthException(SettingProbabilities2PicturesException):
+ def __str__(self):
+ return super().__str__() + " probabilities array must be the same length as pictures array length"
+
+Ancestors
+
+
+
+class ProbabilitiesSumException
+( *args, **kwargs)
+
+
+Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class ProbabilitiesSumException(SettingProbabilities2PicturesException):
+ def __str__(self):
+ return super().__str__() + " probabilities array sum must equal 1"
+
+Ancestors
+
+
+
+class SettingProbabilities2PicturesException
+( *args, **kwargs)
+
+
+Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class SettingProbabilities2PicturesException(Exception):
+ def __str__(self):
+ return "error when set the probabilities for pictures;"
+
+Ancestors
+
+builtins.Exception
+builtins.BaseException
+
+Subclasses
+
+
+
+class SettingStateException
+( *args, **kwargs)
+
+
+Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class SettingStateException(Exception):
+ def __str__(self):
+ return "invalid values for state massive: values in each column have to be distinct"
+
+Ancestors
+
+builtins.Exception
+builtins.BaseException
+
+
+
+class SettingWinningCombinationsException
+( *args, **kwargs)
+
+
+Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class SettingWinningCombinationsException(Exception):
+ def __str__(self):
+ return "invalid values for dict combinations:"
+
+Ancestors
+
+builtins.Exception
+builtins.BaseException
+
+Subclasses
+
+
+
+class WinningMoneyException
+( *args, **kwargs)
+
+
+Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class WinningMoneyException(SettingWinningCombinationsException):
+ def __str__(self):
+ return super().__str__() + " winning money have to be greater than 0"
+
+Ancestors
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/GenAlgorithm.html b/backend/doc/backend/GenAlgorithm.html
new file mode 100644
index 0000000..35f0f4d
--- /dev/null
+++ b/backend/doc/backend/GenAlgorithm.html
@@ -0,0 +1,1484 @@
+
+
+
+
+
+
+backend.GenAlgorithm API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module backend.GenAlgorithm
+
+
+
+
+Expand source code
+
+from typing import List, Iterable, Tuple, Union
+
+import numpy as np
+from scipy.stats import truncnorm
+from tqdm import tqdm
+
+
+class GeneticAlgorithm:
+ """ """
+
+ def __init__(self, bandit):
+ from backend.Bandit import OneHandBandit
+ self.bandit: OneHandBandit = bandit
+
+ def geneticAlgorithm(self, probsCount: int, goalValue: float,
+ populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000,
+ generationsCount: int = 100, eps: float = 0.001, debug: bool = False,
+ isGraph: bool = False) -> np.array:
+ """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+ в виді списка ймовірностей
+
+ Args:
+ probsCount: довжина списку ймовірностей, який ви хотіли б отримати
+ goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+ populationSize: розмір популяції генетичного алгоритму
+ population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі,
+ яка надалі буде використовуватися функцією фітнесу(fitness_func)
+ generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+ eps: похибка, яку дозволяє поточний генетичний алгоритм:
+ генетичний алгоритм зупиниться,
+ якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+ debug: параметр, який регулює інформативність алгоритму:
+
+ якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+
+ якщо debug рівне True алгоритм інформує про зміни в поколіннях
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: список ймовірностей,
+ які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+ якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях
+
+ """
+ if population is None:
+ population = []
+ for _ in range(populationSize):
+ population.append(np.array(self.bandit.genBaseProbabilities(probsCount)))
+
+ # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+ # [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+ # [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+ # [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+ # [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.65333333, 0.677 , 0.80883333, 0.5645 , 0.81633333
+
+ # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436],
+ # [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697],
+ # [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489],
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802
+
+ # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда
+ fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount,
+ debug=debug, isGraph=isGraph)
+
+
+ fitnessVals = []
+ returnCoefsInGenerations = []
+ # if debug is True:
+ # iterator = range(generationsCount)
+ # else:
+ # iterator = tqdm(range(generationsCount))
+
+ # for generationIdx in tqdm(range(generationsCount)):
+ for generationIdx in range(generationsCount):
+ if debug is True:
+ print("\n######################generation:", generationIdx)
+
+ fitnessVals = np.array([fitness(ps) for ps in population])
+ if isGraph is True:
+ mass = fitnessVals.copy()
+ fitnessVals = np.array([i[1] for i in mass])
+ returnCoefsInGenerations.append([i[0] for i in mass])
+
+ if debug is True:
+ print(f"{fitnessVals=}\n")
+
+ # Обчислення коефіцієнту виживання кожного індивіду
+ surviveProbs = self.getSurviveProbs(fitnessVals)
+ # print(f"{surviveProbs=}\n")
+
+ # Перевірка умов зупинки Генетичного Алгоритму
+ if any(fitnessVals <= eps):
+ if debug is True:
+ print("\n-----------------fitness values to break cycle:", fitnessVals)
+ break
+
+ # Відбір
+ population = self.selection(population, surviveProbs)
+
+ # Скрещування
+ population = self.crossOver(population)
+ for idx in range(len(population)):
+ self.changeProbs(population[idx])
+
+ # Мутація
+ # ймовірність мутації кожної особини
+ mutateProb = 0.2
+ for idx in range(len(population)):
+ rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0]
+ if rNumb:
+ self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug)
+
+ if isGraph is True:
+ return returnCoefsInGenerations
+
+ mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0])
+ population = [population[i[1]] for i in mass]
+ return population[0]
+
+ # @benchmark
+ def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False,
+ gamesCount: int = 5000) -> Union[float, Tuple]:
+ """функція обраховує значення функції фітнесу:
+ чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+ і навпаки.
+
+ Args:
+ probs: ймовірності для яких необхідно порахувати значення фітнес функції
+ goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція
+ debug: параметр, який регулює інформативність функції
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: значення фітнес функції
+ якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції)
+
+ """
+ currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1)
+ # print(f"\n{probs=}")
+ if debug is True:
+ print(f"currFitness={abs(currReturnCoef - goalValue)}")
+ print(f"{currReturnCoef=}\n")
+ if isGraph is True:
+ return currReturnCoef, abs(currReturnCoef - goalValue)
+
+ return abs(currReturnCoef - goalValue)
+
+ def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]):
+ """функція обраховує ймовірність виживання кожної окремої особини з популяції
+
+ Args:
+ fitnessVals: список значень функції фітнесу особин для популяції
+
+ Returns:
+ список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+ """
+ fitnessVals[fitnessVals == 0] = 0.000001
+
+ invS = sum(1 / fitnessVals)
+ surviveProbs = (1 / fitnessVals) / invS
+ return surviveProbs
+
+ def selection(self, population: List[np.array,], surviveProbs):
+ """функція реалізовує відбір методом рулетки для генетичного алгоритму
+
+ Args:
+ population: поточна популяція
+ surviveProbs: ймовірності виживання кожної особини з поточної популяції
+
+ Returns:
+ нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+ """
+ #
+ indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs))
+ newPopulation = list(np.array(population)[indices])
+ return newPopulation
+
+ def crossOver(self, population):
+ """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
+ Args:
+ population: поточна популяція
+
+ Returns:
+ нова популяція, такого ж розміру, як і стара, але з особин утворених в результаті скрещювання
+
+ """
+ # len(population) повинно бути парним
+
+ # N = (len(population) // 2) * 2
+ N = len(population)
+ families = [population[i: (i + 2)] for i in range(0, N, 2)]
+ # if len(population) % 2 == 1:
+ # families.append([population[-1], population[0]])
+
+ # ймовірність скрещування
+ crossOverProb = 0.9
+ successors = [self.getSuccessor(f, crossOverProb) for f in families]
+ newPopulation = []
+ for i in successors:
+ newPopulation.extend(i)
+
+ return newPopulation
+
+ def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float):
+ """функціє реалізує одноточкове скрещування для генетичного алгоритму
+
+ Args:
+ family: список з двох особин для скрещування
+ crossOverProb: ймовірність скрещювання(досить велика ймовірність)
+
+ Returns:
+ повертає двох особин:
+
+ якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання
+
+ якщо скрещування не відбулось: повертаються батьки(список family)
+
+ """
+ parent1, parent2 = family
+ gensCount = len(parent1)
+
+ # low border is inclusive, high border is exclusive
+ splitIdx = np.random.randint(1, gensCount)
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2
+ successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:]))
+
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1
+ successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:]))
+
+ # low border is inclusive, high border is exclusive
+ rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0]
+ if rIdx == 1:
+ self.changeProbs(successor1)
+ self.changeProbs(successor2)
+ return [successor1, successor2]
+ else:
+ return family
+
+ def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None:
+ """функція реалізує мутацію певної особини для генетичного алгоритму,
+ де новий мутований ген вибирається,
+ як значення нормально розподіленої випадкової величини
+ з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
+ Args:
+ person: особина, яка буде піддана мутації
+ indpb: ймовірність мутації одного гена особини(однієї ймовірності)
+ delta: максимальна різниця між поточним геном та мутованим
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+
+ def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
+ """
+
+ Args:
+ mean: (Default value = 0)
+ sd: (Default value = 1)
+ low: (Default value = 0)
+ upp: (Default value = 10)
+
+ Returns:
+
+ """
+ return truncnorm(
+ (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)
+
+ person0 = person.copy()
+ for idx in range(len(person)):
+ if np.random.uniform() <= indpb:
+ if debug is True:
+ print("mutation of one gen in one person_________________")
+ leftBorder = max(0, person[idx] - delta)
+ rightBorder = min(1, person[idx] + delta)
+ # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1))
+ newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs()
+ if debug is True:
+ print(f"old {person=}")
+ print(f"old gen:{person[idx]}, new gen:{newGenVal}\n")
+ # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0]
+ person[idx] = newGenVal
+ self.changeProbs(person, indsNot2ch=[idx, ])
+
+ if debug is True:
+ print(f"new {person=}\n")
+
+ if any(person < 0):
+ print(f"{person0=}")
+ print(f"{person=}")
+
+ def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None:
+ """якщо indsNot2ch порожній,
+ функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
+ якщо indsNot2ch не порожній,
+ функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+ чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
+ Args:
+ probs: масив ймовірностей
+ eps: похибка, яку дозволяє функція
+ indsNot2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ sVal = probs.sum()
+ value = abs(1 - sVal)
+
+ if value < eps:
+ maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0]
+ indices = list(range(len(probs)))
+ indices.remove(maxElIdx)
+
+ probs[maxElIdx] = (1 - probs[indices].sum())
+ return
+
+ if indsNot2ch is None:
+ inds2ch = list(range(len(probs)))
+ else:
+ inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch]
+
+ if sVal > 1:
+ self.decreaseProbs(probs, value, inds2ch)
+ else:
+ self.increaseProbs(probs, value, inds2ch)
+
+ # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps
+ self.changeProbs(probs)
+
+ def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None:
+ """value не повинно бути більше за sum(probs)
+ зменшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] - percentVals
+
+ def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]):
+ """value не повинно бути більше за sum(probs)
+ збільшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] + percentVals
+
+
+
+
+
+
+
+
+
+class GeneticAlgorithm
+( bandit)
+
+
+
+
+
+Expand source code
+
+class GeneticAlgorithm:
+ """ """
+
+ def __init__(self, bandit):
+ from backend.Bandit import OneHandBandit
+ self.bandit: OneHandBandit = bandit
+
+ def geneticAlgorithm(self, probsCount: int, goalValue: float,
+ populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000,
+ generationsCount: int = 100, eps: float = 0.001, debug: bool = False,
+ isGraph: bool = False) -> np.array:
+ """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+ в виді списка ймовірностей
+
+ Args:
+ probsCount: довжина списку ймовірностей, який ви хотіли б отримати
+ goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+ populationSize: розмір популяції генетичного алгоритму
+ population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі,
+ яка надалі буде використовуватися функцією фітнесу(fitness_func)
+ generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+ eps: похибка, яку дозволяє поточний генетичний алгоритм:
+ генетичний алгоритм зупиниться,
+ якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+ debug: параметр, який регулює інформативність алгоритму:
+
+ якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+
+ якщо debug рівне True алгоритм інформує про зміни в поколіннях
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: список ймовірностей,
+ які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+ якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях
+
+ """
+ if population is None:
+ population = []
+ for _ in range(populationSize):
+ population.append(np.array(self.bandit.genBaseProbabilities(probsCount)))
+
+ # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+ # [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+ # [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+ # [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+ # [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.65333333, 0.677 , 0.80883333, 0.5645 , 0.81633333
+
+ # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436],
+ # [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697],
+ # [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489],
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802
+
+ # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда
+ fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount,
+ debug=debug, isGraph=isGraph)
+
+
+ fitnessVals = []
+ returnCoefsInGenerations = []
+ # if debug is True:
+ # iterator = range(generationsCount)
+ # else:
+ # iterator = tqdm(range(generationsCount))
+
+ # for generationIdx in tqdm(range(generationsCount)):
+ for generationIdx in range(generationsCount):
+ if debug is True:
+ print("\n######################generation:", generationIdx)
+
+ fitnessVals = np.array([fitness(ps) for ps in population])
+ if isGraph is True:
+ mass = fitnessVals.copy()
+ fitnessVals = np.array([i[1] for i in mass])
+ returnCoefsInGenerations.append([i[0] for i in mass])
+
+ if debug is True:
+ print(f"{fitnessVals=}\n")
+
+ # Обчислення коефіцієнту виживання кожного індивіду
+ surviveProbs = self.getSurviveProbs(fitnessVals)
+ # print(f"{surviveProbs=}\n")
+
+ # Перевірка умов зупинки Генетичного Алгоритму
+ if any(fitnessVals <= eps):
+ if debug is True:
+ print("\n-----------------fitness values to break cycle:", fitnessVals)
+ break
+
+ # Відбір
+ population = self.selection(population, surviveProbs)
+
+ # Скрещування
+ population = self.crossOver(population)
+ for idx in range(len(population)):
+ self.changeProbs(population[idx])
+
+ # Мутація
+ # ймовірність мутації кожної особини
+ mutateProb = 0.2
+ for idx in range(len(population)):
+ rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0]
+ if rNumb:
+ self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug)
+
+ if isGraph is True:
+ return returnCoefsInGenerations
+
+ mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0])
+ population = [population[i[1]] for i in mass]
+ return population[0]
+
+ # @benchmark
+ def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False,
+ gamesCount: int = 5000) -> Union[float, Tuple]:
+ """функція обраховує значення функції фітнесу:
+ чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+ і навпаки.
+
+ Args:
+ probs: ймовірності для яких необхідно порахувати значення фітнес функції
+ goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція
+ debug: параметр, який регулює інформативність функції
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: значення фітнес функції
+ якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції)
+
+ """
+ currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1)
+ # print(f"\n{probs=}")
+ if debug is True:
+ print(f"currFitness={abs(currReturnCoef - goalValue)}")
+ print(f"{currReturnCoef=}\n")
+ if isGraph is True:
+ return currReturnCoef, abs(currReturnCoef - goalValue)
+
+ return abs(currReturnCoef - goalValue)
+
+ def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]):
+ """функція обраховує ймовірність виживання кожної окремої особини з популяції
+
+ Args:
+ fitnessVals: список значень функції фітнесу особин для популяції
+
+ Returns:
+ список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+ """
+ fitnessVals[fitnessVals == 0] = 0.000001
+
+ invS = sum(1 / fitnessVals)
+ surviveProbs = (1 / fitnessVals) / invS
+ return surviveProbs
+
+ def selection(self, population: List[np.array,], surviveProbs):
+ """функція реалізовує відбір методом рулетки для генетичного алгоритму
+
+ Args:
+ population: поточна популяція
+ surviveProbs: ймовірності виживання кожної особини з поточної популяції
+
+ Returns:
+ нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+ """
+ #
+ indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs))
+ newPopulation = list(np.array(population)[indices])
+ return newPopulation
+
+ def crossOver(self, population):
+ """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
+ Args:
+ population: поточна популяція
+
+ Returns:
+ нова популяція, такого ж розміру, як і стара, але з особин утворених в результаті скрещювання
+
+ """
+ # len(population) повинно бути парним
+
+ # N = (len(population) // 2) * 2
+ N = len(population)
+ families = [population[i: (i + 2)] for i in range(0, N, 2)]
+ # if len(population) % 2 == 1:
+ # families.append([population[-1], population[0]])
+
+ # ймовірність скрещування
+ crossOverProb = 0.9
+ successors = [self.getSuccessor(f, crossOverProb) for f in families]
+ newPopulation = []
+ for i in successors:
+ newPopulation.extend(i)
+
+ return newPopulation
+
+ def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float):
+ """функціє реалізує одноточкове скрещування для генетичного алгоритму
+
+ Args:
+ family: список з двох особин для скрещування
+ crossOverProb: ймовірність скрещювання(досить велика ймовірність)
+
+ Returns:
+ повертає двох особин:
+
+ якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання
+
+ якщо скрещування не відбулось: повертаються батьки(список family)
+
+ """
+ parent1, parent2 = family
+ gensCount = len(parent1)
+
+ # low border is inclusive, high border is exclusive
+ splitIdx = np.random.randint(1, gensCount)
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2
+ successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:]))
+
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1
+ successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:]))
+
+ # low border is inclusive, high border is exclusive
+ rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0]
+ if rIdx == 1:
+ self.changeProbs(successor1)
+ self.changeProbs(successor2)
+ return [successor1, successor2]
+ else:
+ return family
+
+ def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None:
+ """функція реалізує мутацію певної особини для генетичного алгоритму,
+ де новий мутований ген вибирається,
+ як значення нормально розподіленої випадкової величини
+ з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
+ Args:
+ person: особина, яка буде піддана мутації
+ indpb: ймовірність мутації одного гена особини(однієї ймовірності)
+ delta: максимальна різниця між поточним геном та мутованим
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+
+ def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
+ """
+
+ Args:
+ mean: (Default value = 0)
+ sd: (Default value = 1)
+ low: (Default value = 0)
+ upp: (Default value = 10)
+
+ Returns:
+
+ """
+ return truncnorm(
+ (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)
+
+ person0 = person.copy()
+ for idx in range(len(person)):
+ if np.random.uniform() <= indpb:
+ if debug is True:
+ print("mutation of one gen in one person_________________")
+ leftBorder = max(0, person[idx] - delta)
+ rightBorder = min(1, person[idx] + delta)
+ # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1))
+ newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs()
+ if debug is True:
+ print(f"old {person=}")
+ print(f"old gen:{person[idx]}, new gen:{newGenVal}\n")
+ # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0]
+ person[idx] = newGenVal
+ self.changeProbs(person, indsNot2ch=[idx, ])
+
+ if debug is True:
+ print(f"new {person=}\n")
+
+ if any(person < 0):
+ print(f"{person0=}")
+ print(f"{person=}")
+
+ def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None:
+ """якщо indsNot2ch порожній,
+ функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
+ якщо indsNot2ch не порожній,
+ функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+ чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
+ Args:
+ probs: масив ймовірностей
+ eps: похибка, яку дозволяє функція
+ indsNot2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ sVal = probs.sum()
+ value = abs(1 - sVal)
+
+ if value < eps:
+ maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0]
+ indices = list(range(len(probs)))
+ indices.remove(maxElIdx)
+
+ probs[maxElIdx] = (1 - probs[indices].sum())
+ return
+
+ if indsNot2ch is None:
+ inds2ch = list(range(len(probs)))
+ else:
+ inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch]
+
+ if sVal > 1:
+ self.decreaseProbs(probs, value, inds2ch)
+ else:
+ self.increaseProbs(probs, value, inds2ch)
+
+ # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps
+ self.changeProbs(probs)
+
+ def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None:
+ """value не повинно бути більше за sum(probs)
+ зменшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] - percentVals
+
+ def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]):
+ """value не повинно бути більше за sum(probs)
+ збільшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] + percentVals
+
+Methods
+
+
+def changeProbs (self, probs: , eps: float = 0.0001, indsNot2ch: List[int] = None) ‑> None
+
+
+якщо indsNot2ch порожній,
+функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
якщо indsNot2ch не порожній,
+функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
Args
+
+probs
+масив ймовірностей
+eps
+похибка, яку дозволяє функція
+indsNot2ch
+список індексів ймовірностей, які не можна змінювати
+
+
Returns:
+
+
+Expand source code
+
+def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None:
+ """якщо indsNot2ch порожній,
+ функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
+ якщо indsNot2ch не порожній,
+ функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+ чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
+ Args:
+ probs: масив ймовірностей
+ eps: похибка, яку дозволяє функція
+ indsNot2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ sVal = probs.sum()
+ value = abs(1 - sVal)
+
+ if value < eps:
+ maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0]
+ indices = list(range(len(probs)))
+ indices.remove(maxElIdx)
+
+ probs[maxElIdx] = (1 - probs[indices].sum())
+ return
+
+ if indsNot2ch is None:
+ inds2ch = list(range(len(probs)))
+ else:
+ inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch]
+
+ if sVal > 1:
+ self.decreaseProbs(probs, value, inds2ch)
+ else:
+ self.increaseProbs(probs, value, inds2ch)
+
+ # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps
+ self.changeProbs(probs)
+
+
+
+def crossOver (self, population)
+
+
+функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
Args
+
+population
+поточна популяція
+
+
Returns
+
нова популяція, такого ж розміру, як і
+стара, але з особин утворених в результаті скрещювання
+
+
+Expand source code
+
+def crossOver(self, population):
+ """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
+ Args:
+ population: поточна популяція
+
+ Returns:
+ нова популяція, такого ж розміру, як і стара, але з особин утворених в результаті скрещювання
+
+ """
+ # len(population) повинно бути парним
+
+ # N = (len(population) // 2) * 2
+ N = len(population)
+ families = [population[i: (i + 2)] for i in range(0, N, 2)]
+ # if len(population) % 2 == 1:
+ # families.append([population[-1], population[0]])
+
+ # ймовірність скрещування
+ crossOverProb = 0.9
+ successors = [self.getSuccessor(f, crossOverProb) for f in families]
+ newPopulation = []
+ for i in successors:
+ newPopulation.extend(i)
+
+ return newPopulation
+
+
+
+def decreaseProbs (self, probs: numpy.ndarray, value, inds2ch: List[int]) ‑> None
+
+
+value не повинно бути більше за sum(probs)
+зменшуємо кожне число пропорційно:
+
якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
якщо індекс ймовірності p0 не належить списку inds2ch
+та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
Args
+
+probs
+список ймовірностей
+value
+значення на яке сумарно треба зменшити ймовірності
+inds2ch
+список індексів ймовірностей, які не можна змінювати
+
+
Returns:
+
+
+Expand source code
+
+def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None:
+ """value не повинно бути більше за sum(probs)
+ зменшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] - percentVals
+
+
+
+def fitness_func (self, probs: List[float], goalValue: float, debug: bool = False, isGraph: bool = False, gamesCount: int = 5000) ‑> Union[float, Tuple[]]
+
+
+функція обраховує значення функції фітнесу:
+чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+і навпаки.
+
Args
+
+probs
+ймовірності для яких необхідно порахувати значення фітнес функції
+goalValue
+значення коефіцієнту віддачі при якому рахується значення фітнес функція
+debug
+параметр, який регулює інформативність функції
+gamesCount
+кількість ігор, для функції обрахунку коефіцієнту віддачі
+isGraph
+параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+
Returns
+
+якщо isGraph рівне False
+значення фітнес функції
+якщо isGraph рівне True
+(коефіцієнт віддачі, значення фітнес функції)
+
+
+
+Expand source code
+
+def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False,
+ gamesCount: int = 5000) -> Union[float, Tuple]:
+ """функція обраховує значення функції фітнесу:
+ чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+ і навпаки.
+
+ Args:
+ probs: ймовірності для яких необхідно порахувати значення фітнес функції
+ goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція
+ debug: параметр, який регулює інформативність функції
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: значення фітнес функції
+ якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції)
+
+ """
+ currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1)
+ # print(f"\n{probs=}")
+ if debug is True:
+ print(f"currFitness={abs(currReturnCoef - goalValue)}")
+ print(f"{currReturnCoef=}\n")
+ if isGraph is True:
+ return currReturnCoef, abs(currReturnCoef - goalValue)
+
+ return abs(currReturnCoef - goalValue)
+
+
+
+def geneticAlgorithm (self, probsCount: int, goalValue: float, populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000, generationsCount: int = 100, eps: float = 0.001, debug: bool = False, isGraph: bool = False) ‑>
+
+
+основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+в виді списка ймовірностей
+
Args
+
+probsCount
+довжина списку ймовірностей, який ви хотіли б отримати
+goalValue
+значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+populationSize
+розмір популяції генетичного алгоритму
+population
+список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+gamesCount
+кількість ігор, для функції обрахунку коефіцієнту віддачі,
+яка надалі буде використовуватися функцією фітнесу(fitness_func)
+generationsCount
+максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+eps
+похибка, яку дозволяє поточний генетичний алгоритм:
+генетичний алгоритм зупиниться,
+якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+debug
+
+параметр, який регулює інформативність алгоритму:
+якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+якщо debug рівне True алгоритм інформує про зміни в поколіннях
+
+isGraph
+параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+
Returns
+
+якщо isGraph рівне False
+список ймовірностей,
+які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+якщо isGraph рівне True
+списки коефіцієнтів віддачі популяції у різних поколіннях
+
+
+
+Expand source code
+
+def geneticAlgorithm(self, probsCount: int, goalValue: float,
+ populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000,
+ generationsCount: int = 100, eps: float = 0.001, debug: bool = False,
+ isGraph: bool = False) -> np.array:
+ """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+ в виді списка ймовірностей
+
+ Args:
+ probsCount: довжина списку ймовірностей, який ви хотіли б отримати
+ goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+ populationSize: розмір популяції генетичного алгоритму
+ population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+ gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі,
+ яка надалі буде використовуватися функцією фітнесу(fitness_func)
+ generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+ eps: похибка, яку дозволяє поточний генетичний алгоритм:
+ генетичний алгоритм зупиниться,
+ якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+ debug: параметр, який регулює інформативність алгоритму:
+
+ якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+
+ якщо debug рівне True алгоритм інформує про зміни в поколіннях
+ isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+ Returns:
+ якщо isGraph рівне False: список ймовірностей,
+ які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+ якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях
+
+ """
+ if population is None:
+ population = []
+ for _ in range(populationSize):
+ population.append(np.array(self.bandit.genBaseProbabilities(probsCount)))
+
+ # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+ # [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+ # [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+ # [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+ # [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.65333333, 0.677 , 0.80883333, 0.5645 , 0.81633333
+
+ # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436],
+ # [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923],
+ # [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697],
+ # [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489],
+ # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+ # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802
+
+ # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда
+ fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount,
+ debug=debug, isGraph=isGraph)
+
+
+ fitnessVals = []
+ returnCoefsInGenerations = []
+ # if debug is True:
+ # iterator = range(generationsCount)
+ # else:
+ # iterator = tqdm(range(generationsCount))
+
+ # for generationIdx in tqdm(range(generationsCount)):
+ for generationIdx in range(generationsCount):
+ if debug is True:
+ print("\n######################generation:", generationIdx)
+
+ fitnessVals = np.array([fitness(ps) for ps in population])
+ if isGraph is True:
+ mass = fitnessVals.copy()
+ fitnessVals = np.array([i[1] for i in mass])
+ returnCoefsInGenerations.append([i[0] for i in mass])
+
+ if debug is True:
+ print(f"{fitnessVals=}\n")
+
+ # Обчислення коефіцієнту виживання кожного індивіду
+ surviveProbs = self.getSurviveProbs(fitnessVals)
+ # print(f"{surviveProbs=}\n")
+
+ # Перевірка умов зупинки Генетичного Алгоритму
+ if any(fitnessVals <= eps):
+ if debug is True:
+ print("\n-----------------fitness values to break cycle:", fitnessVals)
+ break
+
+ # Відбір
+ population = self.selection(population, surviveProbs)
+
+ # Скрещування
+ population = self.crossOver(population)
+ for idx in range(len(population)):
+ self.changeProbs(population[idx])
+
+ # Мутація
+ # ймовірність мутації кожної особини
+ mutateProb = 0.2
+ for idx in range(len(population)):
+ rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0]
+ if rNumb:
+ self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug)
+
+ if isGraph is True:
+ return returnCoefsInGenerations
+
+ mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0])
+ population = [population[i[1]] for i in mass]
+ return population[0]
+
+
+
+def getSuccessor (self, family: array(typing.List[typing.Iterable[float]], dtype=object), crossOverProb: float)
+
+
+функціє реалізує одноточкове скрещування для генетичного алгоритму
+
Args
+
+family
+список з двох особин для скрещування
+crossOverProb
+ймовірність скрещювання(досить велика ймовірність)
+
+
Returns
+
повертає двох особин:
+
+якщо скрещування відбулось
+повертаються нові особини, які є результатом скрещювання
+якщо скрещування не відбулось
+повертаються батьки(список family)
+
+
+
+Expand source code
+
+def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float):
+ """функціє реалізує одноточкове скрещування для генетичного алгоритму
+
+ Args:
+ family: список з двох особин для скрещування
+ crossOverProb: ймовірність скрещювання(досить велика ймовірність)
+
+ Returns:
+ повертає двох особин:
+
+ якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання
+
+ якщо скрещування не відбулось: повертаються батьки(список family)
+
+ """
+ parent1, parent2 = family
+ gensCount = len(parent1)
+
+ # low border is inclusive, high border is exclusive
+ splitIdx = np.random.randint(1, gensCount)
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2
+ successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:]))
+
+ # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1
+ successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:]))
+
+ # low border is inclusive, high border is exclusive
+ rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0]
+ if rIdx == 1:
+ self.changeProbs(successor1)
+ self.changeProbs(successor2)
+ return [successor1, successor2]
+ else:
+ return family
+
+
+
+def getSurviveProbs (self, fitnessVals: array(typing.List[typing.Iterable[float]], dtype=object)) ‑> array(typing.List[typing.Iterable[float]], dtype=object)
+
+
+функція обраховує ймовірність виживання кожної окремої особини з популяції
+
Args
+
+fitnessVals
+список значень функції фітнесу особин для популяції
+
+
Returns
+
список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+
+Expand source code
+
+def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]):
+ """функція обраховує ймовірність виживання кожної окремої особини з популяції
+
+ Args:
+ fitnessVals: список значень функції фітнесу особин для популяції
+
+ Returns:
+ список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+ """
+ fitnessVals[fitnessVals == 0] = 0.000001
+
+ invS = sum(1 / fitnessVals)
+ surviveProbs = (1 / fitnessVals) / invS
+ return surviveProbs
+
+
+
+def increaseProbs (self, probs: numpy.ndarray, value, inds2ch: List[int])
+
+
+value не повинно бути більше за sum(probs)
+збільшуємо кожне число пропорційно:
+
якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
якщо індекс ймовірності p0 не належить списку inds2ch
+та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
Args
+
+probs
+список ймовірностей
+value
+значення на яке сумарно треба зменшити ймовірності
+inds2ch
+список індексів ймовірностей, які не можна змінювати
+
+
Returns:
+
+
+Expand source code
+
+def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]):
+ """value не повинно бути більше за sum(probs)
+ збільшуємо кожне число пропорційно:
+
+ якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+ якщо індекс ймовірності p0 не належить списку inds2ch
+ та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
+ Args:
+ probs: список ймовірностей
+ value: значення на яке сумарно треба зменшити ймовірності
+ inds2ch: список індексів ймовірностей, які не можна змінювати
+
+ Returns:
+
+ """
+ s = sum(probs[inds2ch])
+ percents = probs[inds2ch] / s
+ percentVals = percents * value
+ probs[inds2ch] = probs[inds2ch] + percentVals
+
+
+
+def mutatePerson (self, person: , indpb: float = 0.01, delta: float = 1, debug: bool = False) ‑> None
+
+
+функція реалізує мутацію певної особини для генетичного алгоритму,
+де новий мутований ген вибирається,
+як значення нормально розподіленої випадкової величини
+з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
Args
+
+person
+особина, яка буде піддана мутації
+indpb
+ймовірність мутації одного гена особини(однієї ймовірності)
+delta
+максимальна різниця між поточним геном та мутованим
+debug
+параметр, який регулює інформативність функції
+
+
Returns:
+
+
+Expand source code
+
+def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None:
+ """функція реалізує мутацію певної особини для генетичного алгоритму,
+ де новий мутований ген вибирається,
+ як значення нормально розподіленої випадкової величини
+ з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
+ Args:
+ person: особина, яка буде піддана мутації
+ indpb: ймовірність мутації одного гена особини(однієї ймовірності)
+ delta: максимальна різниця між поточним геном та мутованим
+ debug: параметр, який регулює інформативність функції
+
+ Returns:
+
+ """
+
+ def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
+ """
+
+ Args:
+ mean: (Default value = 0)
+ sd: (Default value = 1)
+ low: (Default value = 0)
+ upp: (Default value = 10)
+
+ Returns:
+
+ """
+ return truncnorm(
+ (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)
+
+ person0 = person.copy()
+ for idx in range(len(person)):
+ if np.random.uniform() <= indpb:
+ if debug is True:
+ print("mutation of one gen in one person_________________")
+ leftBorder = max(0, person[idx] - delta)
+ rightBorder = min(1, person[idx] + delta)
+ # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1))
+ newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs()
+ if debug is True:
+ print(f"old {person=}")
+ print(f"old gen:{person[idx]}, new gen:{newGenVal}\n")
+ # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0]
+ person[idx] = newGenVal
+ self.changeProbs(person, indsNot2ch=[idx, ])
+
+ if debug is True:
+ print(f"new {person=}\n")
+
+ if any(person < 0):
+ print(f"{person0=}")
+ print(f"{person=}")
+
+
+
+def selection (self, population: List[], surviveProbs)
+
+
+функція реалізовує відбір методом рулетки для генетичного алгоритму
+
Args
+
+population
+поточна популяція
+surviveProbs
+ймовірності виживання кожної особини з поточної популяції
+
+
Returns
+
нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+
+Expand source code
+
+def selection(self, population: List[np.array,], surviveProbs):
+ """функція реалізовує відбір методом рулетки для генетичного алгоритму
+
+ Args:
+ population: поточна популяція
+ surviveProbs: ймовірності виживання кожної особини з поточної популяції
+
+ Returns:
+ нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+ """
+ #
+ indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs))
+ newPopulation = list(np.array(population)[indices])
+ return newPopulation
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/benchmark.html b/backend/doc/backend/benchmark.html
new file mode 100644
index 0000000..b5e2771
--- /dev/null
+++ b/backend/doc/backend/benchmark.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+backend.benchmark API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module backend.benchmark
+
+
+
+
+Expand source code
+
+import time
+
+
+def benchmark(func):
+ """функція декоратор, яка використовується для обрахунку часу роботи функції func
+
+ Args:
+ func: функція, яка декорується
+
+ Returns:
+ функція _benchmark
+
+ """
+ def _benchmark(*args, **kwargs):
+ t0 = time.time()
+ res = func(*args, **kwargs)
+ print(f"{func.__name__} elapsed {time.time() - t0} secs")
+ return res
+ return _benchmark
+
+
+
+
+
+
+
+
+def benchmark (func)
+
+
+функція декоратор, яка використовується для обрахунку часу роботи функції func
+
Args
+
+func
+функція, яка декорується
+
+
Returns
+
функція _benchmark
+
+
+Expand source code
+
+def benchmark(func):
+ """функція декоратор, яка використовується для обрахунку часу роботи функції func
+
+ Args:
+ func: функція, яка декорується
+
+ Returns:
+ функція _benchmark
+
+ """
+ def _benchmark(*args, **kwargs):
+ t0 = time.time()
+ res = func(*args, **kwargs)
+ print(f"{func.__name__} elapsed {time.time() - t0} secs")
+ return res
+ return _benchmark
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/index.html b/backend/doc/backend/index.html
new file mode 100644
index 0000000..c9d708b
--- /dev/null
+++ b/backend/doc/backend/index.html
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+backend API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/test.html b/backend/doc/backend/test.html
new file mode 100644
index 0000000..22d91cd
--- /dev/null
+++ b/backend/doc/backend/test.html
@@ -0,0 +1,429 @@
+
+
+
+
+
+
+backend.test API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expand source code
+
+import time
+
+from backend.Bandit import OneHandBandit
+import numpy as np
+import matplotlib.pyplot as plt
+
+
+def test1():
+ """
+ 5 разів рахує ймовірності для
+ двох заданих коефіцієнтів віддачі {0.9, 0.95};
+
+ при різній кількості ігор: {1000, 3000, 5000, 10000};
+
+ при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}.
+
+ Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях,
+ обрахованний на 20000 ігор.
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+ # p2.addWinningComb((1, 1, 2, 2), 15)
+ # p2.addWinningComb((2, 2, 3, 3), 15)
+ # p2.addWinningComb((3, 3, 4, 4), 15)
+ np.random.seed(42)
+
+ # print("\ngamesCount=", 1000, "----------------\n")
+ # test11(p2, gamesCount=1000)
+ #
+ # print("\ngamesCount=", 3000, "----------------\n")
+ # test11(p2, gamesCount=3000)
+ #
+ print("\ngamesCount=", 5000, "----------------\n")
+ test11(p2, gamesCount=5000)
+
+ print("\ngamesCount=", 10000, "---------------\n")
+ test11(p2, gamesCount=10000)
+
+
+def test11(bandit, gamesCount):
+ print("\nstarts with bad values --------------\n")
+ population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+ [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+ [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+ [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+ [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+ [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+
+ print("\ngoal value: 0.9\n")
+ badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.9, gamesCount=gamesCount) for i in population]
+ print(f"{badFitnessVals=}")
+ test12(bandit, 0.9, gamesCount, population=population)
+
+ print("\ngoal value: 0.95\n")
+ badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.95, gamesCount=gamesCount) for i in population]
+ print(f"{badFitnessVals=}")
+ test12(bandit, 0.95, gamesCount, population=population)
+
+ print("\nstart with random values ------------\n")
+ print("\ngoal value: 0.9\n")
+ test12(bandit, 0.9, gamesCount)
+
+ print("\ngoal value: 0.95\n")
+ test12(bandit, 0.95, gamesCount)
+
+
+def test12(bandit, goalValue, gamesCount, population=None):
+ timeVals = []
+ returnCoefVals = []
+ for i in range(5):
+ print(i, end=" ")
+ t0 = time.time()
+ probs = bandit.genAlgo.geneticAlgorithm(5, goalValue, population=population, gamesCount=gamesCount, )
+ dt = time.time() - t0
+ timeVals.append(dt)
+ returnCoefVals.append(bandit.getReturnCoefWithNCores(gamesCount=20000, probs=probs))
+
+ timeVals = np.array(timeVals)
+ returnCoefVals = np.array(returnCoefVals)
+ print("For 200 iterations:")
+ print(f"min time: {min(timeVals)}, max time: {max(timeVals)}, mean time: {timeVals.mean()}")
+ print(f"{timeVals=}")
+ print(f"min returnCoefVals: {min(returnCoefVals)}, max returnCoefVals: {max(returnCoefVals)}, "
+ f"mean returnCoefVals: {returnCoefVals.mean()}")
+ print(f"{returnCoefVals=}")
+
+
+def test2():
+ """
+ малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+
+ # np.random.seed(42)
+ # probs = p2.genBaseProbabilities(5, 0.9, debug=True)
+ # print(f"{probs=}")
+ # print("fitness val for probs and goalValue=0.9:", p2.genAlgo.fitness_func(probs=probs, goalValue=0.9))
+ # print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+ probs = [0.81674399, 0.10322578, 0.06974247, 0.0050837, 0.00520406]
+ gamesCount = 5000
+ meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+ plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+ plt.xlabel("Номер гри")
+ plt.ylabel("Середнє значення коефіцієнту віддачі")
+ plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+ plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+ plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+ plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+ plt.show()
+
+ gamesCount = 10000
+ meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+ plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+ plt.xlabel("Номер гри")
+ plt.ylabel("Середнє значення коефіцієнту віддачі")
+ plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+ plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+ plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+ plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+ plt.show()
+
+
+def test3():
+ """
+ функція, яка малює графік роботі генетичного алгоритму
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+ returnCoef = 0.95
+ returnCoefsInGenerations = p2.genAlgo.geneticAlgorithm(p2.picturesNumb, returnCoef, isGraph=True, debug=True)
+ plt.ylabel("коефіцієт віддачі")
+ plt.xlabel("покоління")
+ xs = [i * (len(returnCoefsInGenerations[0]) + 3) - 6 for i in range(1, len(returnCoefsInGenerations) + 1)]
+ labels = list(range(len(returnCoefsInGenerations)))
+ plt.xticks(xs, labels)
+ plt.plot([0, len(returnCoefsInGenerations) * (len(returnCoefsInGenerations[0]) + 3)], [returnCoef, returnCoef],
+ color="red")
+
+
+ for idx, returnCoefs in enumerate(returnCoefsInGenerations):
+ plt.plot([(len(returnCoefs) + 3) * idx + i for i in range(len(returnCoefs))], returnCoefs, "*")
+ x0 = (len(returnCoefs) + 3) * idx + len(returnCoefs) + 1
+ plt.plot([x0, x0], [0, 1], color="green")
+
+ plt.show()
+
+
+if __name__ == '__main__':
+ # test1()
+ # test2()
+ test3()
+
+
+
+
+
+
+
+
+def test1 ()
+
+
+5 разів рахує ймовірності для
+двох заданих коефіцієнтів віддачі {0.9, 0.95};
+
при різній кількості ігор: {1000, 3000, 5000, 10000};
+
при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}.
+
Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях,
+обрахованний на 20000 ігор.
+
+
+Expand source code
+
+def test1():
+ """
+ 5 разів рахує ймовірності для
+ двох заданих коефіцієнтів віддачі {0.9, 0.95};
+
+ при різній кількості ігор: {1000, 3000, 5000, 10000};
+
+ при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}.
+
+ Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях,
+ обрахованний на 20000 ігор.
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+ # p2.addWinningComb((1, 1, 2, 2), 15)
+ # p2.addWinningComb((2, 2, 3, 3), 15)
+ # p2.addWinningComb((3, 3, 4, 4), 15)
+ np.random.seed(42)
+
+ # print("\ngamesCount=", 1000, "----------------\n")
+ # test11(p2, gamesCount=1000)
+ #
+ # print("\ngamesCount=", 3000, "----------------\n")
+ # test11(p2, gamesCount=3000)
+ #
+ print("\ngamesCount=", 5000, "----------------\n")
+ test11(p2, gamesCount=5000)
+
+ print("\ngamesCount=", 10000, "---------------\n")
+ test11(p2, gamesCount=10000)
+
+
+
+def test11 (bandit, gamesCount)
+
+
+
+
+
+Expand source code
+
+def test11(bandit, gamesCount):
+ print("\nstarts with bad values --------------\n")
+ population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+ [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+ [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+ [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+ [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+ [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+
+ print("\ngoal value: 0.9\n")
+ badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.9, gamesCount=gamesCount) for i in population]
+ print(f"{badFitnessVals=}")
+ test12(bandit, 0.9, gamesCount, population=population)
+
+ print("\ngoal value: 0.95\n")
+ badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.95, gamesCount=gamesCount) for i in population]
+ print(f"{badFitnessVals=}")
+ test12(bandit, 0.95, gamesCount, population=population)
+
+ print("\nstart with random values ------------\n")
+ print("\ngoal value: 0.9\n")
+ test12(bandit, 0.9, gamesCount)
+
+ print("\ngoal value: 0.95\n")
+ test12(bandit, 0.95, gamesCount)
+
+
+
+def test12 (bandit, goalValue, gamesCount, population=None)
+
+
+
+
+
+Expand source code
+
+def test12(bandit, goalValue, gamesCount, population=None):
+ timeVals = []
+ returnCoefVals = []
+ for i in range(5):
+ print(i, end=" ")
+ t0 = time.time()
+ probs = bandit.genAlgo.geneticAlgorithm(5, goalValue, population=population, gamesCount=gamesCount, )
+ dt = time.time() - t0
+ timeVals.append(dt)
+ returnCoefVals.append(bandit.getReturnCoefWithNCores(gamesCount=20000, probs=probs))
+
+ timeVals = np.array(timeVals)
+ returnCoefVals = np.array(returnCoefVals)
+ print("For 200 iterations:")
+ print(f"min time: {min(timeVals)}, max time: {max(timeVals)}, mean time: {timeVals.mean()}")
+ print(f"{timeVals=}")
+ print(f"min returnCoefVals: {min(returnCoefVals)}, max returnCoefVals: {max(returnCoefVals)}, "
+ f"mean returnCoefVals: {returnCoefVals.mean()}")
+ print(f"{returnCoefVals=}")
+
+
+
+def test2 ()
+
+
+малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри
+
+
+Expand source code
+
+def test2():
+ """
+ малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+
+ # np.random.seed(42)
+ # probs = p2.genBaseProbabilities(5, 0.9, debug=True)
+ # print(f"{probs=}")
+ # print("fitness val for probs and goalValue=0.9:", p2.genAlgo.fitness_func(probs=probs, goalValue=0.9))
+ # print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+ probs = [0.81674399, 0.10322578, 0.06974247, 0.0050837, 0.00520406]
+ gamesCount = 5000
+ meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+ plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+ plt.xlabel("Номер гри")
+ plt.ylabel("Середнє значення коефіцієнту віддачі")
+ plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+ plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+ plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+ plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+ plt.show()
+
+ gamesCount = 10000
+ meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+ plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+ plt.xlabel("Номер гри")
+ plt.ylabel("Середнє значення коефіцієнту віддачі")
+ plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+ plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+ plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+ plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+ plt.show()
+
+
+
+def test3 ()
+
+
+функція, яка малює графік роботі генетичного алгоритму
+
+
+Expand source code
+
+def test3():
+ """
+ функція, яка малює графік роботі генетичного алгоритму
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+ returnCoef = 0.95
+ returnCoefsInGenerations = p2.genAlgo.geneticAlgorithm(p2.picturesNumb, returnCoef, isGraph=True, debug=True)
+ plt.ylabel("коефіцієт віддачі")
+ plt.xlabel("покоління")
+ xs = [i * (len(returnCoefsInGenerations[0]) + 3) - 6 for i in range(1, len(returnCoefsInGenerations) + 1)]
+ labels = list(range(len(returnCoefsInGenerations)))
+ plt.xticks(xs, labels)
+ plt.plot([0, len(returnCoefsInGenerations) * (len(returnCoefsInGenerations[0]) + 3)], [returnCoef, returnCoef],
+ color="red")
+
+
+ for idx, returnCoefs in enumerate(returnCoefsInGenerations):
+ plt.plot([(len(returnCoefs) + 3) * idx + i for i in range(len(returnCoefs))], returnCoefs, "*")
+ x0 = (len(returnCoefs) + 3) * idx + len(returnCoefs) + 1
+ plt.plot([x0, x0], [0, 1], color="green")
+
+ plt.show()
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/tests/OneHandBandit_test.html b/backend/doc/backend/tests/OneHandBandit_test.html
new file mode 100644
index 0000000..64226b4
--- /dev/null
+++ b/backend/doc/backend/tests/OneHandBandit_test.html
@@ -0,0 +1,522 @@
+
+
+
+
+
+
+backend.tests.OneHandBandit_test API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module backend.tests.OneHandBandit_test
+
+
+
+
+Expand source code
+
+from unittest import TestCase, main
+
+from backend.Bandit import OneHandBandit
+from backend.CustomExceptions import *
+
+
+class OneHandBanditTest(TestCase):
+ def setUp(self):
+ self.banditObj = OneHandBandit(3, 3, 8)
+
+ def test_setWinningCombs(self):
+ combs = {(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500}
+ self.banditObj.setWinningCombs(combs)
+ # combs[(4,3,0)] = 5
+ self.assertDictEqual(combs, self.banditObj.combinations)
+
+ def test_setWinningCombs_exception_0(self):
+ with self.assertRaises(CombinationLengthException):
+ self.banditObj.setWinningCombs({(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0, 0): 500})
+
+ def test_setWinningCombs_exception_1(self):
+ with self.assertRaises(CombinationValuesException):
+ self.banditObj.setWinningCombs({(7, 7, 8): 100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+ def test_setWinningCombs_exception_2(self):
+ with self.assertRaises(WinningMoneyException):
+ self.banditObj.setWinningCombs({(7, 7, 7): -100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+ def test_addWinningComb(self):
+ comb = (7, 7, 7)
+ win = 1000
+ self.banditObj.addWinningComb(comb, win)
+ # win = 500
+ self.assertEqual(self.banditObj.combinations[comb], win)
+
+ def test_setPriceOfGame(self):
+ price = 10
+ self.banditObj.setPriceOfGame(price)
+ self.assertEqual(self.banditObj.price, price)
+
+ def test_startGame(self):
+ money = 100
+ self.banditObj.startGame(money)
+ self.assertEqual(self.banditObj.money, money)
+
+ def test_setProbabilities_NoneProbs(self):
+ probs = None
+ self.banditObj.setProbabilities(probs)
+ self.assertListEqual(self.banditObj.probabilities,
+ [1 / self.banditObj.picturesNumb for _ in range(self.banditObj.picturesNumb)])
+
+ def test_setProbabilities(self):
+ probs = [0.1, ] * 7 + [0.3]
+ self.banditObj.setProbabilities(probs)
+ # probs[0] = 0.5
+ self.assertListEqual(list(self.banditObj.probabilities), probs)
+
+ def test_setProbabilities_exception_0(self):
+ probs = [0.1, ] * 7 + [0.4]
+ with self.assertRaises(ProbabilitiesSumException):
+ self.banditObj.setProbabilities(probs=probs)
+
+ def test_setProbabilities_exception_1(self):
+ probs = [0.1, ] * 8 + [0.2]
+ with self.assertRaises(ProbabilitiesArrayLengthException):
+ self.banditObj.setProbabilities(probs=probs)
+
+ def test_setProbabilitiesFromTxtFile(self):
+ filename = "backend/tests/test_probabilities.txt"
+ with open(filename, "r") as f:
+ probs = list(map(float, f.readline().split(" ")))
+ self.banditObj.setProbabilitiesFromTxtFile(filename)
+ self.assertListEqual(probs, self.banditObj.probabilities)
+
+ def test_setState_exception_0(self):
+ state = [[1, 2, 3], [2, 3, 4], [3, 4, 3]]
+ with self.assertRaises(SettingStateException):
+ self.banditObj.setState(state)
+
+ def test_turnColumn(self):
+ turnColumn = self.banditObj.turnColumn(0)
+ self.assertNotIn(-1, turnColumn)
+ self.assertEqual(self.banditObj.rowsNumb, len(turnColumn))
+
+
+if __name__ == '__main__':
+ main()
+
+
+
+
+
+
+
+
+
+class OneHandBanditTest
+( methodName='runTest')
+
+
+A class whose instances are single test cases.
+
By default, the test code itself should be placed in a method named
+'runTest'.
+
If the fixture may be used for many test cases, create as
+many test methods as are needed. When instantiating such a TestCase
+subclass, specify in the constructor arguments the name of the test method
+that the instance is to execute.
+
Test authors should subclass TestCase for their own tests. Construction
+and deconstruction of the test's environment ('fixture') can be
+implemented by overriding the 'setUp' and 'tearDown' methods respectively.
+
If it is necessary to override the init method, the base class
+init method must always be called. It is important that subclasses
+should not change the signature of their init method, since instances
+of the classes are instantiated automatically by parts of the framework
+in order to be run.
+
When subclassing TestCase, you can set these attributes:
+* failureException: determines which exception will be raised when
+the instance's assertion methods fail; test methods raising this
+exception will be deemed to have 'failed' rather than 'errored'.
+* longMessage: determines whether long messages (including repr of
+objects used in assert methods) will be printed on failure in addition
+to any explicit message passed.
+* maxDiff: sets the maximum length of a diff in failure messages
+by assert methods using difflib. It is looked up as an instance
+attribute so can be configured by individual tests if required.
+
Create an instance of the class that will use the named test
+method when executed. Raises a ValueError if the instance does
+not have a method with the specified name.
+
+
+Expand source code
+
+class OneHandBanditTest(TestCase):
+ def setUp(self):
+ self.banditObj = OneHandBandit(3, 3, 8)
+
+ def test_setWinningCombs(self):
+ combs = {(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500}
+ self.banditObj.setWinningCombs(combs)
+ # combs[(4,3,0)] = 5
+ self.assertDictEqual(combs, self.banditObj.combinations)
+
+ def test_setWinningCombs_exception_0(self):
+ with self.assertRaises(CombinationLengthException):
+ self.banditObj.setWinningCombs({(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0, 0): 500})
+
+ def test_setWinningCombs_exception_1(self):
+ with self.assertRaises(CombinationValuesException):
+ self.banditObj.setWinningCombs({(7, 7, 8): 100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+ def test_setWinningCombs_exception_2(self):
+ with self.assertRaises(WinningMoneyException):
+ self.banditObj.setWinningCombs({(7, 7, 7): -100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+ def test_addWinningComb(self):
+ comb = (7, 7, 7)
+ win = 1000
+ self.banditObj.addWinningComb(comb, win)
+ # win = 500
+ self.assertEqual(self.banditObj.combinations[comb], win)
+
+ def test_setPriceOfGame(self):
+ price = 10
+ self.banditObj.setPriceOfGame(price)
+ self.assertEqual(self.banditObj.price, price)
+
+ def test_startGame(self):
+ money = 100
+ self.banditObj.startGame(money)
+ self.assertEqual(self.banditObj.money, money)
+
+ def test_setProbabilities_NoneProbs(self):
+ probs = None
+ self.banditObj.setProbabilities(probs)
+ self.assertListEqual(self.banditObj.probabilities,
+ [1 / self.banditObj.picturesNumb for _ in range(self.banditObj.picturesNumb)])
+
+ def test_setProbabilities(self):
+ probs = [0.1, ] * 7 + [0.3]
+ self.banditObj.setProbabilities(probs)
+ # probs[0] = 0.5
+ self.assertListEqual(list(self.banditObj.probabilities), probs)
+
+ def test_setProbabilities_exception_0(self):
+ probs = [0.1, ] * 7 + [0.4]
+ with self.assertRaises(ProbabilitiesSumException):
+ self.banditObj.setProbabilities(probs=probs)
+
+ def test_setProbabilities_exception_1(self):
+ probs = [0.1, ] * 8 + [0.2]
+ with self.assertRaises(ProbabilitiesArrayLengthException):
+ self.banditObj.setProbabilities(probs=probs)
+
+ def test_setProbabilitiesFromTxtFile(self):
+ filename = "backend/tests/test_probabilities.txt"
+ with open(filename, "r") as f:
+ probs = list(map(float, f.readline().split(" ")))
+ self.banditObj.setProbabilitiesFromTxtFile(filename)
+ self.assertListEqual(probs, self.banditObj.probabilities)
+
+ def test_setState_exception_0(self):
+ state = [[1, 2, 3], [2, 3, 4], [3, 4, 3]]
+ with self.assertRaises(SettingStateException):
+ self.banditObj.setState(state)
+
+ def test_turnColumn(self):
+ turnColumn = self.banditObj.turnColumn(0)
+ self.assertNotIn(-1, turnColumn)
+ self.assertEqual(self.banditObj.rowsNumb, len(turnColumn))
+
+Ancestors
+
+unittest.case.TestCase
+
+Methods
+
+
+def setUp (self)
+
+
+Hook method for setting up the test fixture before exercising it.
+
+
+Expand source code
+
+def setUp(self):
+ self.banditObj = OneHandBandit(3, 3, 8)
+
+
+
+def test_addWinningComb (self)
+
+
+
+
+
+Expand source code
+
+def test_addWinningComb(self):
+ comb = (7, 7, 7)
+ win = 1000
+ self.banditObj.addWinningComb(comb, win)
+ # win = 500
+ self.assertEqual(self.banditObj.combinations[comb], win)
+
+
+
+def test_setPriceOfGame (self)
+
+
+
+
+
+Expand source code
+
+def test_setPriceOfGame(self):
+ price = 10
+ self.banditObj.setPriceOfGame(price)
+ self.assertEqual(self.banditObj.price, price)
+
+
+
+def test_setProbabilities (self)
+
+
+
+
+
+Expand source code
+
+def test_setProbabilities(self):
+ probs = [0.1, ] * 7 + [0.3]
+ self.banditObj.setProbabilities(probs)
+ # probs[0] = 0.5
+ self.assertListEqual(list(self.banditObj.probabilities), probs)
+
+
+
+def test_setProbabilitiesFromTxtFile (self)
+
+
+
+
+
+Expand source code
+
+def test_setProbabilitiesFromTxtFile(self):
+ filename = "backend/tests/test_probabilities.txt"
+ with open(filename, "r") as f:
+ probs = list(map(float, f.readline().split(" ")))
+ self.banditObj.setProbabilitiesFromTxtFile(filename)
+ self.assertListEqual(probs, self.banditObj.probabilities)
+
+
+
+def test_setProbabilities_NoneProbs (self)
+
+
+
+
+
+Expand source code
+
+def test_setProbabilities_NoneProbs(self):
+ probs = None
+ self.banditObj.setProbabilities(probs)
+ self.assertListEqual(self.banditObj.probabilities,
+ [1 / self.banditObj.picturesNumb for _ in range(self.banditObj.picturesNumb)])
+
+
+
+def test_setProbabilities_exception_0 (self)
+
+
+
+
+
+Expand source code
+
+def test_setProbabilities_exception_0(self):
+ probs = [0.1, ] * 7 + [0.4]
+ with self.assertRaises(ProbabilitiesSumException):
+ self.banditObj.setProbabilities(probs=probs)
+
+
+
+def test_setProbabilities_exception_1 (self)
+
+
+
+
+
+Expand source code
+
+def test_setProbabilities_exception_1(self):
+ probs = [0.1, ] * 8 + [0.2]
+ with self.assertRaises(ProbabilitiesArrayLengthException):
+ self.banditObj.setProbabilities(probs=probs)
+
+
+
+def test_setState_exception_0 (self)
+
+
+
+
+
+Expand source code
+
+def test_setState_exception_0(self):
+ state = [[1, 2, 3], [2, 3, 4], [3, 4, 3]]
+ with self.assertRaises(SettingStateException):
+ self.banditObj.setState(state)
+
+
+
+def test_setWinningCombs (self)
+
+
+
+
+
+Expand source code
+
+def test_setWinningCombs(self):
+ combs = {(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500}
+ self.banditObj.setWinningCombs(combs)
+ # combs[(4,3,0)] = 5
+ self.assertDictEqual(combs, self.banditObj.combinations)
+
+
+
+def test_setWinningCombs_exception_0 (self)
+
+
+
+
+
+Expand source code
+
+def test_setWinningCombs_exception_0(self):
+ with self.assertRaises(CombinationLengthException):
+ self.banditObj.setWinningCombs({(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0, 0): 500})
+
+
+
+def test_setWinningCombs_exception_1 (self)
+
+
+
+
+
+Expand source code
+
+def test_setWinningCombs_exception_1(self):
+ with self.assertRaises(CombinationValuesException):
+ self.banditObj.setWinningCombs({(7, 7, 8): 100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+
+
+def test_setWinningCombs_exception_2 (self)
+
+
+
+
+
+Expand source code
+
+def test_setWinningCombs_exception_2(self):
+ with self.assertRaises(WinningMoneyException):
+ self.banditObj.setWinningCombs({(7, 7, 7): -100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+
+
+def test_startGame (self)
+
+
+
+
+
+Expand source code
+
+def test_startGame(self):
+ money = 100
+ self.banditObj.startGame(money)
+ self.assertEqual(self.banditObj.money, money)
+
+
+
+def test_turnColumn (self)
+
+
+
+
+
+Expand source code
+
+def test_turnColumn(self):
+ turnColumn = self.banditObj.turnColumn(0)
+ self.assertNotIn(-1, turnColumn)
+ self.assertEqual(self.banditObj.rowsNumb, len(turnColumn))
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/tests/index.html b/backend/doc/backend/tests/index.html
new file mode 100644
index 0000000..f2028a2
--- /dev/null
+++ b/backend/doc/backend/tests/index.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+backend.tests API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Namespace backend.tests
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/doc/backend/wsgi.html b/backend/doc/backend/wsgi.html
new file mode 100644
index 0000000..01994b6
--- /dev/null
+++ b/backend/doc/backend/wsgi.html
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+backend.wsgi API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Expand source code
+
+from flask import Flask, Response
+
+from backend.Bandit import OneHandBandit
+
+app = Flask(__name__)
+
+# відображення індексу картинки до назви картинки
+dict_ = {0: "at_at",
+ 1: "c3po",
+ 2: "darth_vader",
+ 3: "death_star",
+ 4: "falcon",
+ 5: "r2d2"}
+
+wastedMoney = 0
+wonMoney = 0
+
+
+@app.route("/getState", methods=["GET", ])
+def getState():
+ """
+ обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef
+ Returns:
+ об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef
+ """
+ global wastedMoney, wonMoney
+ bandit.startGame(bandit.price)
+ won = bandit.play(n=1)[0]
+
+ wastedMoney = wastedMoney + bandit.price
+ wonMoney = wonMoney + won
+ returnCoef = (wonMoney / wastedMoney)
+ print(wonMoney)
+ print(wastedMoney)
+
+ currentState = bandit.getState().T
+
+ currentState = [dict_[int(i)] for i in currentState.flatten()]
+ print(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+ resp = Response(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+ resp.headers['Access-Control-Allow-Origin'] = '*'
+ return resp
+
+
+if __name__ == '__main__':
+ bandit = OneHandBandit(4, 3, 5)
+
+ # l = bandit.genBaseProbabilities(bandit.picturesNumb, returnCoef)
+ # bandit.setProbabilities(l)
+ bandit.setProbabilitiesFromTxtFile("probabilities.txt")
+
+ bandit.setPriceOfGame(10)
+ bandit.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ bandit.addWinningComb((4, 4, 4, 4), 15)
+
+ app.run(debug=True, host="0.0.0.0")
+
+
+
+
+
+
+
+
+def getState ()
+
+
+обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef
+
Returns
+
об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef
+
+
+Expand source code
+
+@app.route("/getState", methods=["GET", ])
+def getState():
+ """
+ обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef
+ Returns:
+ об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef
+ """
+ global wastedMoney, wonMoney
+ bandit.startGame(bandit.price)
+ won = bandit.play(n=1)[0]
+
+ wastedMoney = wastedMoney + bandit.price
+ wonMoney = wonMoney + won
+ returnCoef = (wonMoney / wastedMoney)
+ print(wonMoney)
+ print(wastedMoney)
+
+ currentState = bandit.getState().T
+
+ currentState = [dict_[int(i)] for i in currentState.flatten()]
+ print(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+ resp = Response(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+ resp.headers['Access-Control-Allow-Origin'] = '*'
+ return resp
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/probabilities.txt b/backend/probabilities.txt
new file mode 100644
index 0000000..1800424
--- /dev/null
+++ b/backend/probabilities.txt
@@ -0,0 +1 @@
+0.008082330274997888 0.09521255517386346 0.0007740974727614077 0.05778087326126356 0.8381501438171137
\ No newline at end of file
diff --git a/backend/test.py b/backend/test.py
new file mode 100644
index 0000000..b98671a
--- /dev/null
+++ b/backend/test.py
@@ -0,0 +1,160 @@
+import time
+
+from backend.Bandit import OneHandBandit
+import numpy as np
+import matplotlib.pyplot as plt
+
+
+def test1():
+ """
+ 5 разів рахує ймовірності для
+ двох заданих коефіцієнтів віддачі {0.9, 0.95};
+
+ при різній кількості ігор: {1000, 3000, 5000, 10000};
+
+ при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}.
+
+ Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях,
+ обрахованний на 20000 ігор.
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+ # p2.addWinningComb((1, 1, 2, 2), 15)
+ # p2.addWinningComb((2, 2, 3, 3), 15)
+ # p2.addWinningComb((3, 3, 4, 4), 15)
+ np.random.seed(42)
+
+ # print("\ngamesCount=", 1000, "----------------\n")
+ # test11(p2, gamesCount=1000)
+ #
+ # print("\ngamesCount=", 3000, "----------------\n")
+ # test11(p2, gamesCount=3000)
+ #
+ print("\ngamesCount=", 5000, "----------------\n")
+ test11(p2, gamesCount=5000)
+
+ print("\ngamesCount=", 10000, "---------------\n")
+ test11(p2, gamesCount=10000)
+
+
+def test11(bandit, gamesCount):
+ print("\nstarts with bad values --------------\n")
+ population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+ [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+ [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+ [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+ [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+ [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+
+ print("\ngoal value: 0.9\n")
+ badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.9, gamesCount=gamesCount) for i in population]
+ print(f"{badFitnessVals=}")
+ test12(bandit, 0.9, gamesCount, population=population)
+
+ print("\ngoal value: 0.95\n")
+ badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.95, gamesCount=gamesCount) for i in population]
+ print(f"{badFitnessVals=}")
+ test12(bandit, 0.95, gamesCount, population=population)
+
+ print("\nstart with random values ------------\n")
+ print("\ngoal value: 0.9\n")
+ test12(bandit, 0.9, gamesCount)
+
+ print("\ngoal value: 0.95\n")
+ test12(bandit, 0.95, gamesCount)
+
+
+def test12(bandit, goalValue, gamesCount, population=None):
+ timeVals = []
+ returnCoefVals = []
+ for i in range(5):
+ print(i, end=" ")
+ t0 = time.time()
+ probs = bandit.genAlgo.geneticAlgorithm(5, goalValue, population=population, gamesCount=gamesCount, )
+ dt = time.time() - t0
+ timeVals.append(dt)
+ returnCoefVals.append(bandit.getReturnCoefWithNCores(gamesCount=20000, probs=probs))
+
+ timeVals = np.array(timeVals)
+ returnCoefVals = np.array(returnCoefVals)
+ print("For 200 iterations:")
+ print(f"min time: {min(timeVals)}, max time: {max(timeVals)}, mean time: {timeVals.mean()}")
+ print(f"{timeVals=}")
+ print(f"min returnCoefVals: {min(returnCoefVals)}, max returnCoefVals: {max(returnCoefVals)}, "
+ f"mean returnCoefVals: {returnCoefVals.mean()}")
+ print(f"{returnCoefVals=}")
+
+
+def test2():
+ """
+ малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+
+ # np.random.seed(42)
+ # probs = p2.genBaseProbabilities(5, 0.9, debug=True)
+ # print(f"{probs=}")
+ # print("fitness val for probs and goalValue=0.9:", p2.genAlgo.fitness_func(probs=probs, goalValue=0.9))
+ # print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+ probs = [0.81674399, 0.10322578, 0.06974247, 0.0050837, 0.00520406]
+ gamesCount = 5000
+ meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+ plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+ plt.xlabel("Номер гри")
+ plt.ylabel("Середнє значення коефіцієнту віддачі")
+ plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+ plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+ plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+ plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+ plt.show()
+
+ gamesCount = 10000
+ meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+ plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+ plt.xlabel("Номер гри")
+ plt.ylabel("Середнє значення коефіцієнту віддачі")
+ plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+ plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+ plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+ plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+ plt.show()
+
+
+def test3():
+ """
+ функція, яка малює графік роботі генетичного алгоритму
+ """
+ p2 = OneHandBandit(4, 3, 5)
+ p2.setPriceOfGame(10)
+ p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ p2.addWinningComb((4, 4, 4, 4), 15)
+ returnCoef = 0.95
+ returnCoefsInGenerations = p2.genAlgo.geneticAlgorithm(p2.picturesNumb, returnCoef, isGraph=True, debug=True)
+ plt.ylabel("коефіцієт віддачі")
+ plt.xlabel("покоління")
+ xs = [i * (len(returnCoefsInGenerations[0]) + 3) - 6 for i in range(1, len(returnCoefsInGenerations) + 1)]
+ labels = list(range(len(returnCoefsInGenerations)))
+ plt.xticks(xs, labels)
+ plt.plot([0, len(returnCoefsInGenerations) * (len(returnCoefsInGenerations[0]) + 3)], [returnCoef, returnCoef],
+ color="red")
+
+
+ for idx, returnCoefs in enumerate(returnCoefsInGenerations):
+ plt.plot([(len(returnCoefs) + 3) * idx + i for i in range(len(returnCoefs))], returnCoefs, "*")
+ x0 = (len(returnCoefs) + 3) * idx + len(returnCoefs) + 1
+ plt.plot([x0, x0], [0, 1], color="green")
+
+ plt.show()
+
+
+if __name__ == '__main__':
+ # test1()
+ # test2()
+ test3()
diff --git a/backend/test_files/test1.txt b/backend/test_files/test1.txt
new file mode 100644
index 0000000..8ca7a2e
--- /dev/null
+++ b/backend/test_files/test1.txt
@@ -0,0 +1,183 @@
+файл містить вивід функції test1 з backend/test.py
+
+gamesCount= 1000 ----------------
+
+
+starts with bad values --------------
+
+
+goal value: 0.9
+
+badFitnessVals=[0.6717221828490433, 0.7435447673045121, 0.8069808646350106, 0.5758327427356486, 0.8300023623907394, 0.8033723127805339]
+0 1 2 3 4 For 5 iterations:
+min time: 149.7915756702423, max time: 694.9565689563751, mean time: 329.48206744194033
+timeVals=array([149.79157567, 236.70668268, 282.7156949 , 694.95656896,
+ 283.239815 ])
+min returnCoefVals: 0.21132464820694963, max returnCoefVals: 0.9001994202418744, mean returnCoefVals: 0.7395649235088427
+returnCoefVals=array([0.84720295, 0.87837432, 0.90019942, 0.21132465, 0.86072328])
+
+goal value: 0.95
+
+badFitnessVals=[0.7405031892274981, 0.7116406803685329, 0.8484467280888258, 0.6157158043940467, 0.8754488542404913, 0.8051263879045594]
+0 1 2 3 4 For 5 iterations:
+min time: 82.46748614311218, max time: 406.40651512145996, mean time: 207.6385106086731
+timeVals=array([137.2547853 , 288.5302372 , 123.53352928, 82.46748614,
+ 406.40651512])
+min returnCoefVals: 0.9144017550180804, max returnCoefVals: 0.9886492847185767, mean returnCoefVals: 0.9567253771853947
+returnCoefVals=array([0.91440176, 0.95005252, 0.97335005, 0.95717328, 0.98864928])
+
+start with random values ------------
+
+
+goal value: 0.9
+
+0 1 2 3 4 For 5 iterations:
+min time: 34.59854745864868, max time: 109.6488606929779, mean time: 63.13373408317566
+timeVals=array([ 61.54462576, 68.5349226 , 34.59854746, 109.64886069,
+ 41.34171391])
+min returnCoefVals: 0.8632494283911148, max returnCoefVals: 0.9590031566874829, mean returnCoefVals: 0.919180040942625
+returnCoefVals=array([0.93047641, 0.94697408, 0.95900316, 0.89619713, 0.86324943])
+
+goal value: 0.95
+
+0 1 2 3 4 For 5 iterations:
+min time: 13.632518291473389, max time: 247.90915441513062, mean time: 116.95825953483582
+timeVals=array([226.62800479, 62.0311029 , 13.63251829, 247.90915442,
+ 34.59051728])
+min returnCoefVals: 0.8923253419794754, max returnCoefVals: 1.0209758382135063, mean returnCoefVals: 0.9596896151384253
+returnCoefVals=array([1.02097584, 0.96634935, 0.92910023, 0.89232534, 0.98969732])
+
+gamesCount= 3000 ----------------
+
+
+starts with bad values --------------
+
+
+goal value: 0.9
+
+badFitnessVals=[0.6725, 0.6955, 0.8046666666666666, 0.5608333333333333, 0.8101666666666667, 0.766]
+0 1 2 3 4 For 5 iterations:
+min time: 112.19143176078796, max time: 474.1458783149719, mean time: 270.50732202529906
+timeVals=array([474.14587831, 112.19143176, 259.00318789, 357.30249095,
+ 149.89362121])
+min returnCoefVals: 0.8640750956654886, max returnCoefVals: 0.9093508301354873, mean returnCoefVals: 0.8949254100822376
+returnCoefVals=array([0.90680014, 0.90935083, 0.90570142, 0.8640751 , 0.88869956])
+
+goal value: 0.95
+
+badFitnessVals=[0.7595, 0.7188333333333333, 0.8651666666666666, 0.5965, 0.8733333333333333, 0.8278333333333333]
+0 1 2 3 4 For 5 iterations:
+min time: 210.90868711471558, max time: 533.5097453594208, mean time: 326.5112485408783
+timeVals=array([358.59228086, 286.59976077, 242.94576859, 533.50974536,
+ 210.90868711])
+min returnCoefVals: 0.908023283587729, max returnCoefVals: 0.9917260766556416, mean returnCoefVals: 0.9559916201515204
+returnCoefVals=array([0.97305303, 0.90802328, 0.9738796 , 0.93327611, 0.99172608])
+
+start with random values ------------
+
+
+goal value: 0.9
+
+0 1 2 3 4 For 5 iterations:
+min time: 62.77412390708923, max time: 405.95093607902527, mean time: 209.34744334220886
+timeVals=array([ 62.77412391, 99.4375124 , 109.37965012, 405.95093608,
+ 369.19499421])
+min returnCoefVals: 0.8451757544570048, max returnCoefVals: 0.9333002265743816, mean returnCoefVals: 0.8891703173315832
+returnCoefVals=array([0.87562351, 0.84517575, 0.93330023, 0.90952487, 0.88222723])
+
+goal value: 0.95
+
+0 1 2 3 4 For 5 iterations:
+min time: 127.80276036262512, max time: 500.08165740966797, mean time: 296.5248766899109
+timeVals=array([500.08165741, 307.40381241, 228.15567279, 319.18048048,
+ 127.80276036])
+min returnCoefVals: 0.9428745198357916, max returnCoefVals: 0.968400453238727, mean returnCoefVals: 0.9552203946031785
+returnCoefVals=array([0.96840045, 0.96179907, 0.94287452, 0.95475228, 0.94827564])
+
+gamesCount= 5000 ----------------
+
+
+starts with bad values --------------
+
+
+goal value: 0.9
+
+badFitnessVals=[0.6693011336271035, 0.6738115605523647, 0.806394893286656, 0.574381129697388, 0.8220963834635651, 0.7539071916191626]
+0 1 2 3 4 For 200 iterations:
+min time: 220.82359957695007, max time: 2249.837205886841, mean time: 1102.7905313968658
+timeVals=array([2249.83720589, 220.82359958, 1453.57248044, 1260.42282152,
+ 329.29654956])
+min returnCoefVals: 0.8743758258884359, max returnCoefVals: 0.9390986325019308, mean returnCoefVals: 0.9008993013244353
+returnCoefVals=array([0.92549864, 0.88500009, 0.93909863, 0.87437583, 0.88052332])
+
+goal value: 0.95
+
+badFitnessVals=[0.727081970512756, 0.7366138071995264, 0.865297017010397, 0.6100981710049289, 0.8684978182889922, 0.815291386015484]
+0 1 2 3 4 For 200 iterations:
+min time: 131.77604126930237, max time: 436.46209478378296, mean time: 266.3572997570038
+timeVals=array([131.77604127, 284.70347071, 163.83466268, 436.46209478,
+ 315.01022935])
+min returnCoefVals: 0.9266001412734978, max returnCoefVals: 0.9694254957729657, mean returnCoefVals: 0.9486550169259804
+returnCoefVals=array([0.92660014, 0.9694255 , 0.94094781, 0.95400108, 0.95230055])
+
+start with random values ------------
+
+
+goal value: 0.9
+
+0 1 2 3 4 For 200 iterations:
+min time: 66.20206689834595, max time: 1455.0551316738129, mean time: 624.9127726554871
+timeVals=array([ 148.31742382, 1455.05513167, 66.2020669 , 893.35809159,
+ 561.63114929])
+min returnCoefVals: 0.8802232772002837, max returnCoefVals: 0.9276979122200076, mean returnCoefVals: 0.893969891741798
+returnCoefVals=array([0.88022328, 0.92769791, 0.88427587, 0.89025177, 0.88740063])
+
+goal value: 0.95
+
+0 1 2 3 4 For 200 iterations:
+min time: 65.85777378082275, max time: 1811.382836818695, mean time: 890.9894399642944
+timeVals=array([ 65.85777378, 703.63702512, 1811.38283682, 1488.73205781,
+ 385.33750629])
+min returnCoefVals: 0.92187436884618, max returnCoefVals: 0.9880531681354042, mean returnCoefVals: 0.952495670856441
+returnCoefVals=array([0.94959848, 0.92187437, 0.98805317, 0.9480258 , 0.95492653])
+
+gamesCount= 10000 ---------------
+
+
+starts with bad values --------------
+
+
+goal value: 0.9
+
+badFitnessVals=[0.6734093637454982, 0.684498949280311, 0.8029534568318346, 0.57559682555657, 0.814001049521605, 0.7723481608212147]
+0 1 2 3 4 For 200 iterations:
+min time: 273.2618703842163, max time: 852.4494953155518, mean time: 537.1537239074707
+timeVals=array([852.44949532, 273.26187038, 320.71004343, 592.26527667,
+ 647.08193374])
+min returnCoefVals: 0.8873242721573508, max returnCoefVals: 0.9011484056951423, mean returnCoefVals: 0.8919290219876561
+returnCoefVals=array([0.89144937, 0.88815012, 0.90114841, 0.88732427, 0.89157294])
+
+goal value: 0.95
+
+badFitnessVals=[0.7272468867786634, 0.7320543187334814, 0.8504465858199567, 0.6214343821360879, 0.8657467178488161, 0.8229499584264843]
+0 1 2 3 4 For 200 iterations:
+min time: 144.8367953300476, max time: 1645.4430599212646, mean time: 875.4130566120148
+timeVals=array([ 498.60299063, 144.83679533, 1645.44305992, 834.15014529,
+ 1254.03229189])
+min returnCoefVals: 0.9138756229446034, max returnCoefVals: 0.9596222815684867, mean returnCoefVals: 0.9410536271018406
+returnCoefVals=array([0.95962228, 0.95557516, 0.92887361, 0.91387562, 0.94732146])
+
+start with random values ------------
+
+
+goal value: 0.9
+
+0 1 2 3 4 For 200 iterations:
+min time: 44.28335762023926, max time: 1528.0146882534027, mean time: 673.5708031177521
+timeVals=array([ 493.48089433, 378.16429234, 1528.01468825, 923.91078305,
+ 44.28335762])
+min returnCoefVals: 0.8981776513483433, max returnCoefVals: 0.9134251281200084, mean returnCoefVals: 0.9065753456904911
+returnCoefVals=array([0.90422577, 0.90482422, 0.91222396, 0.91342513, 0.89817765])
+
+goal value: 0.95
+
diff --git a/backend/test_files/test2_10000GamesCount.png b/backend/test_files/test2_10000GamesCount.png
new file mode 100644
index 0000000..dcbc8a9
Binary files /dev/null and b/backend/test_files/test2_10000GamesCount.png differ
diff --git a/backend/test_files/test2_5000GamesCount.png b/backend/test_files/test2_5000GamesCount.png
new file mode 100644
index 0000000..5bf5805
Binary files /dev/null and b/backend/test_files/test2_5000GamesCount.png differ
diff --git a/backend/test_files/test3_returnCoef_0_9.png b/backend/test_files/test3_returnCoef_0_9.png
new file mode 100644
index 0000000..848e3ef
Binary files /dev/null and b/backend/test_files/test3_returnCoef_0_9.png differ
diff --git a/backend/test_files/test3_returnCoef_0_9_5.png b/backend/test_files/test3_returnCoef_0_9_5.png
new file mode 100644
index 0000000..3cc42d8
Binary files /dev/null and b/backend/test_files/test3_returnCoef_0_9_5.png differ
diff --git a/tests/OneHandBandit_test.py b/backend/tests/OneHandBandit_test.py
similarity index 95%
rename from tests/OneHandBandit_test.py
rename to backend/tests/OneHandBandit_test.py
index e1fc2c5..c248e5d 100644
--- a/tests/OneHandBandit_test.py
+++ b/backend/tests/OneHandBandit_test.py
@@ -1,7 +1,7 @@
from unittest import TestCase, main
-from Bandit import OneHandBandit
-from CustomExceptions import *
+from backend.Bandit import OneHandBandit
+from backend.CustomExceptions import *
class OneHandBanditTest(TestCase):
@@ -66,7 +66,7 @@ def test_setProbabilities_exception_1(self):
self.banditObj.setProbabilities(probs=probs)
def test_setProbabilitiesFromTxtFile(self):
- filename = "tests/test_probabilities.txt"
+ filename = "backend/tests/test_probabilities.txt"
with open(filename, "r") as f:
probs = list(map(float, f.readline().split(" ")))
self.banditObj.setProbabilitiesFromTxtFile(filename)
diff --git a/tests/test_probabilities.txt b/backend/tests/test_probabilities.txt
similarity index 100%
rename from tests/test_probabilities.txt
rename to backend/tests/test_probabilities.txt
diff --git a/backend/wsgi.py b/backend/wsgi.py
new file mode 100644
index 0000000..ade27a4
--- /dev/null
+++ b/backend/wsgi.py
@@ -0,0 +1,56 @@
+from flask import Flask, Response
+
+from backend.Bandit import OneHandBandit
+
+app = Flask(__name__)
+
+# відображення індексу картинки до назви картинки
+dict_ = {0: "at_at",
+ 1: "c3po",
+ 2: "darth_vader",
+ 3: "death_star",
+ 4: "falcon",
+ 5: "r2d2"}
+
+wastedMoney = 0
+wonMoney = 0
+
+
+@app.route("/getState", methods=["GET", ])
+def getState():
+ """
+ обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef
+ Returns:
+ об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef
+ """
+ global wastedMoney, wonMoney
+ bandit.startGame(bandit.price)
+ won = bandit.play(n=1)[0]
+
+ wastedMoney = wastedMoney + bandit.price
+ wonMoney = wonMoney + won
+ returnCoef = (wonMoney / wastedMoney)
+ print(wonMoney)
+ print(wastedMoney)
+
+ currentState = bandit.getState().T
+
+ currentState = [dict_[int(i)] for i in currentState.flatten()]
+ print(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+ resp = Response(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+ resp.headers['Access-Control-Allow-Origin'] = '*'
+ return resp
+
+
+if __name__ == '__main__':
+ bandit = OneHandBandit(4, 3, 5)
+
+ # l = bandit.genBaseProbabilities(bandit.picturesNumb, returnCoef)
+ # bandit.setProbabilities(l)
+ bandit.setProbabilitiesFromTxtFile("probabilities.txt")
+
+ bandit.setPriceOfGame(10)
+ bandit.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+ bandit.addWinningComb((4, 4, 4, 4), 15)
+
+ app.run(debug=True, host="0.0.0.0")
diff --git a/probabilities.txt b/probabilities.txt
deleted file mode 100644
index 038682f..0000000
--- a/probabilities.txt
+++ /dev/null
@@ -1 +0,0 @@
-0.1 0.1 0.1 0.1 0.6
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..0a2be8e
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,89 @@
+argon2-cffi==21.3.0
+argon2-cffi-bindings==21.2.0
+asttokens==2.0.5
+astunparse==1.6.3
+attrs==21.4.0
+backcall==0.2.0
+beautifulsoup4==4.11.1
+bleach==5.0.0
+cffi==1.15.0
+click==8.1.3
+colorama==0.4.4
+coverage==6.4.1
+cycler==0.11.0
+debugpy==1.6.0
+decorator==5.1.1
+defusedxml==0.7.1
+entrypoints==0.4
+executing==0.8.3
+fastjsonschema==2.15.3
+Flask==2.1.2
+fonttools==4.33.3
+importlib-metadata==4.11.4
+importlib-resources==5.7.1
+ipykernel==6.14.0
+ipython==8.4.0
+ipython-genutils==0.2.0
+ipywidgets==7.7.0
+itsdangerous==2.1.2
+jedi==0.18.1
+Jinja2==3.1.2
+jsonschema==4.6.0
+jupyter==1.0.0
+jupyter-client==7.3.4
+jupyter-console==6.4.3
+jupyter-core==4.10.0
+jupyterlab-pygments==0.2.2
+jupyterlab-widgets==1.1.0
+kiwisolver==1.4.3
+Mako==1.2.0
+Markdown==3.3.7
+MarkupSafe==2.1.1
+matplotlib==3.5.2
+matplotlib-inline==0.1.3
+mistune==0.8.4
+nbclient==0.6.4
+nbconvert==6.5.0
+nbformat==5.4.0
+nest-asyncio==1.5.5
+notebook==6.4.12
+numpy==1.22.4
+packaging==21.3
+pandocfilters==1.5.0
+parso==0.8.3
+pdoc==12.0.2
+pdoc3==0.10.0
+pickleshare==0.7.5
+Pillow==9.1.1
+pipfile==0.0.2
+prometheus-client==0.14.1
+prompt-toolkit==3.0.29
+psutil==5.9.1
+pure-eval==0.2.2
+pycparser==2.21
+Pygments==2.12.0
+Pyment==0.3.3
+pyparsing==3.0.9
+pyrsistent==0.18.1
+python-dateutil==2.8.2
+pywin32==304
+pywinpty==2.0.5
+pyzmq==23.1.0
+qtconsole==5.3.1
+QtPy==2.1.0
+scipy==1.8.1
+Send2Trash==1.8.0
+six==1.16.0
+soupsieve==2.3.2.post1
+stack-data==0.2.0
+terminado==0.15.0
+tinycss2==1.1.1
+toml==0.10.2
+tornado==6.1
+tqdm==4.64.0
+traitlets==5.2.2.post1
+wcwidth==0.2.5
+webencodings==0.5.1
+Werkzeug==2.1.2
+widgetsnbextension==3.6.0
+zipp==3.8.0
diff --git a/user_interface/.babelrc b/user_interface/.babelrc
new file mode 100644
index 0000000..1320b9a
--- /dev/null
+++ b/user_interface/.babelrc
@@ -0,0 +1,3 @@
+{
+ "presets": ["@babel/preset-env"]
+}
diff --git a/user_interface/.prettierrc b/user_interface/.prettierrc
new file mode 100644
index 0000000..222861c
--- /dev/null
+++ b/user_interface/.prettierrc
@@ -0,0 +1,4 @@
+{
+ "tabWidth": 2,
+ "useTabs": false
+}
diff --git a/user_interface/.travis.yml b/user_interface/.travis.yml
new file mode 100644
index 0000000..6ea8fb5
--- /dev/null
+++ b/user_interface/.travis.yml
@@ -0,0 +1,13 @@
+language: node_js
+node_js:
+ - "lts/*"
+script:
+ - npm run build
+deploy:
+ provider: pages
+ skip_cleanup: true
+ github_token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable
+ keep_history: true
+ on:
+ branch: master
+ local_dir: ./dist
diff --git a/user_interface/package-lock.json b/user_interface/package-lock.json
new file mode 100644
index 0000000..3b3ebaf
--- /dev/null
+++ b/user_interface/package-lock.json
@@ -0,0 +1,5216 @@
+{
+ "name": "html5-slot-machine",
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@ampproject/remapping": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz",
+ "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/trace-mapping": "^0.3.0"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
+ "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.16.7"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz",
+ "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
+ "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.17.7",
+ "@babel/helper-compilation-targets": "^7.17.7",
+ "@babel/helper-module-transforms": "^7.17.7",
+ "@babel/helpers": "^7.17.8",
+ "@babel/parser": "^7.17.8",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.1.2",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
+ "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.17.0",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+ "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz",
+ "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-explode-assignable-expression": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz",
+ "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.17.7",
+ "@babel/helper-validator-option": "^7.16.7",
+ "browserslist": "^4.17.5",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz",
+ "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-member-expression-to-functions": "^7.16.7",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/helper-replace-supers": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7"
+ }
+ },
+ "@babel/helper-create-regexp-features-plugin": {
+ "version": "7.17.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz",
+ "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "regexpu-core": "^5.0.1"
+ }
+ },
+ "@babel/helper-define-polyfill-provider": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz",
+ "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-compilation-targets": "^7.13.0",
+ "@babel/helper-module-imports": "^7.12.13",
+ "@babel/helper-plugin-utils": "^7.13.0",
+ "@babel/traverse": "^7.13.0",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2",
+ "semver": "^6.1.2"
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
+ "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-explode-assignable-expression": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz",
+ "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
+ "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
+ "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
+ "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz",
+ "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.17.0"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
+ "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz",
+ "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/helper-simple-access": "^7.17.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz",
+ "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
+ "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
+ "dev": true
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz",
+ "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-wrap-function": "^7.16.8",
+ "@babel/types": "^7.16.8"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz",
+ "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-member-expression-to-functions": "^7.16.7",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/traverse": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz",
+ "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.17.0"
+ }
+ },
+ "@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz",
+ "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.0"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
+ "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+ "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz",
+ "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==",
+ "dev": true
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz",
+ "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.16.8",
+ "@babel/types": "^7.16.8"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz",
+ "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.16.10",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
+ "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
+ "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==",
+ "dev": true
+ },
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.16.7",
+ "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.16.7.tgz",
+ "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz",
+ "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.16.7"
+ }
+ },
+ "@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz",
+ "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-remap-async-to-generator": "^7.16.8",
+ "@babel/plugin-syntax-async-generators": "^7.8.4"
+ }
+ },
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz",
+ "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-proposal-class-static-block": {
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz",
+ "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.17.6",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5"
+ }
+ },
+ "@babel/plugin-proposal-dynamic-import": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz",
+ "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-export-namespace-from": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz",
+ "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-json-strings": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz",
+ "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-json-strings": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-logical-assignment-operators": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz",
+ "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz",
+ "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-numeric-separator": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz",
+ "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz",
+ "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.17.0",
+ "@babel/helper-compilation-targets": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.16.7"
+ }
+ },
+ "@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz",
+ "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-optional-chaining": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz",
+ "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-private-methods": {
+ "version": "7.16.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz",
+ "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.16.10",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz",
+ "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-create-class-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ }
+ },
+ "@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz",
+ "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@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-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-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-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-transform-arrow-functions": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz",
+ "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz",
+ "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-remap-async-to-generator": "^7.16.8"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz",
+ "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz",
+ "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz",
+ "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-replace-supers": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz",
+ "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz",
+ "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-dotall-regex": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz",
+ "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz",
+ "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz",
+ "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz",
+ "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz",
+ "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-compilation-targets": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz",
+ "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-member-expression-literals": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz",
+ "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz",
+ "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ }
+ },
+ "@babel/plugin-transform-modules-commonjs": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz",
+ "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.17.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-simple-access": "^7.17.7",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ }
+ },
+ "@babel/plugin-transform-modules-systemjs": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz",
+ "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.16.7",
+ "@babel/helper-module-transforms": "^7.17.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ }
+ },
+ "@babel/plugin-transform-modules-umd": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz",
+ "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz",
+ "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-new-target": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz",
+ "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz",
+ "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-replace-supers": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz",
+ "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-property-literals": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz",
+ "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz",
+ "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "^0.14.2"
+ }
+ },
+ "@babel/plugin-transform-reserved-words": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz",
+ "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz",
+ "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz",
+ "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz",
+ "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz",
+ "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz",
+ "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-unicode-escapes": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz",
+ "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz",
+ "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.16.11",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz",
+ "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.16.8",
+ "@babel/helper-compilation-targets": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-validator-option": "^7.16.7",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7",
+ "@babel/plugin-proposal-async-generator-functions": "^7.16.8",
+ "@babel/plugin-proposal-class-properties": "^7.16.7",
+ "@babel/plugin-proposal-class-static-block": "^7.16.7",
+ "@babel/plugin-proposal-dynamic-import": "^7.16.7",
+ "@babel/plugin-proposal-export-namespace-from": "^7.16.7",
+ "@babel/plugin-proposal-json-strings": "^7.16.7",
+ "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
+ "@babel/plugin-proposal-numeric-separator": "^7.16.7",
+ "@babel/plugin-proposal-object-rest-spread": "^7.16.7",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.16.7",
+ "@babel/plugin-proposal-optional-chaining": "^7.16.7",
+ "@babel/plugin-proposal-private-methods": "^7.16.11",
+ "@babel/plugin-proposal-private-property-in-object": "^7.16.7",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.16.7",
+ "@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-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.16.7",
+ "@babel/plugin-transform-async-to-generator": "^7.16.8",
+ "@babel/plugin-transform-block-scoped-functions": "^7.16.7",
+ "@babel/plugin-transform-block-scoping": "^7.16.7",
+ "@babel/plugin-transform-classes": "^7.16.7",
+ "@babel/plugin-transform-computed-properties": "^7.16.7",
+ "@babel/plugin-transform-destructuring": "^7.16.7",
+ "@babel/plugin-transform-dotall-regex": "^7.16.7",
+ "@babel/plugin-transform-duplicate-keys": "^7.16.7",
+ "@babel/plugin-transform-exponentiation-operator": "^7.16.7",
+ "@babel/plugin-transform-for-of": "^7.16.7",
+ "@babel/plugin-transform-function-name": "^7.16.7",
+ "@babel/plugin-transform-literals": "^7.16.7",
+ "@babel/plugin-transform-member-expression-literals": "^7.16.7",
+ "@babel/plugin-transform-modules-amd": "^7.16.7",
+ "@babel/plugin-transform-modules-commonjs": "^7.16.8",
+ "@babel/plugin-transform-modules-systemjs": "^7.16.7",
+ "@babel/plugin-transform-modules-umd": "^7.16.7",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8",
+ "@babel/plugin-transform-new-target": "^7.16.7",
+ "@babel/plugin-transform-object-super": "^7.16.7",
+ "@babel/plugin-transform-parameters": "^7.16.7",
+ "@babel/plugin-transform-property-literals": "^7.16.7",
+ "@babel/plugin-transform-regenerator": "^7.16.7",
+ "@babel/plugin-transform-reserved-words": "^7.16.7",
+ "@babel/plugin-transform-shorthand-properties": "^7.16.7",
+ "@babel/plugin-transform-spread": "^7.16.7",
+ "@babel/plugin-transform-sticky-regex": "^7.16.7",
+ "@babel/plugin-transform-template-literals": "^7.16.7",
+ "@babel/plugin-transform-typeof-symbol": "^7.16.7",
+ "@babel/plugin-transform-unicode-escapes": "^7.16.7",
+ "@babel/plugin-transform-unicode-regex": "^7.16.7",
+ "@babel/preset-modules": "^0.1.5",
+ "@babel/types": "^7.16.8",
+ "babel-plugin-polyfill-corejs2": "^0.3.0",
+ "babel-plugin-polyfill-corejs3": "^0.5.0",
+ "babel-plugin-polyfill-regenerator": "^0.3.0",
+ "core-js-compat": "^3.20.2",
+ "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/runtime": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
+ "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "@babel/template": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+ "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/parser": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
+ "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.17.3",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-hoist-variables": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "@babel/parser": "^7.17.3",
+ "@babel/types": "^7.17.0",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.17.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
+ "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "dev": true
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz",
+ "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==",
+ "dev": true
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.11",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
+ "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz",
+ "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "@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,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@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
+ },
+ "@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": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@types/body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+ "dev": true,
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/bonjour": {
+ "version": "3.5.10",
+ "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz",
+ "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/connect-history-api-fallback": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz",
+ "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==",
+ "dev": true,
+ "requires": {
+ "@types/express-serve-static-core": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/eslint": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
+ "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "@types/eslint-scope": {
+ "version": "3.7.3",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz",
+ "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==",
+ "dev": true,
+ "requires": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "@types/estree": {
+ "version": "0.0.51",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
+ "dev": true
+ },
+ "@types/express": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+ "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+ "dev": true,
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.28",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz",
+ "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "dev": true,
+ "requires": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==",
+ "dev": true
+ },
+ "@types/http-proxy": {
+ "version": "1.17.8",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
+ "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@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
+ },
+ "@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+ "dev": true
+ },
+ "@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "17.0.23",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
+ "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==",
+ "dev": true
+ },
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "@types/retry": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz",
+ "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==",
+ "dev": true
+ },
+ "@types/serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==",
+ "dev": true,
+ "requires": {
+ "@types/express": "*"
+ }
+ },
+ "@types/serve-static": {
+ "version": "1.13.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+ "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+ "dev": true,
+ "requires": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "@types/sockjs": {
+ "version": "0.3.33",
+ "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz",
+ "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/ws": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+ "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+ "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/helper-numbers": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+ "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+ "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+ "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-numbers": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+ "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+ "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+ "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+ "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+ "dev": true,
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+ "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+ "dev": true,
+ "requires": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+ "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
+ "dev": true
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+ "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/helper-wasm-section": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-opt": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "@webassemblyjs/wast-printer": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+ "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+ "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+ "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+ "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webpack-cli/configtest": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz",
+ "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==",
+ "dev": true
+ },
+ "@webpack-cli/info": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz",
+ "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==",
+ "dev": true,
+ "requires": {
+ "envinfo": "^7.7.3"
+ }
+ },
+ "@webpack-cli/serve": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz",
+ "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==",
+ "dev": true
+ },
+ "@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
+ "acorn": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+ "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+ "dev": true
+ },
+ "acorn-import-assertions": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+ "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+ "dev": true
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "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"
+ }
+ },
+ "ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ }
+ }
+ },
+ "ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true
+ },
+ "ansi-html-community": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+ "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
+ "dev": true
+ },
+ "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
+ },
+ "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"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "array-differ": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz",
+ "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==",
+ "dev": true
+ },
+ "array-flatten": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+ "dev": true
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "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
+ },
+ "arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "babel-loader": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz",
+ "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==",
+ "dev": true,
+ "requires": {
+ "find-cache-dir": "^3.3.1",
+ "loader-utils": "^2.0.0",
+ "make-dir": "^3.1.0",
+ "schema-utils": "^2.6.5"
+ }
+ },
+ "babel-plugin-dynamic-import-node": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+ "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
+ "dev": true,
+ "requires": {
+ "object.assign": "^4.1.0"
+ }
+ },
+ "babel-plugin-polyfill-corejs2": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz",
+ "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.13.11",
+ "@babel/helper-define-polyfill-provider": "^0.3.1",
+ "semver": "^6.1.1"
+ }
+ },
+ "babel-plugin-polyfill-corejs3": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz",
+ "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-define-polyfill-provider": "^0.3.1",
+ "core-js-compat": "^3.21.0"
+ }
+ },
+ "babel-plugin-polyfill-regenerator": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz",
+ "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-define-polyfill-provider": "^0.3.1"
+ }
+ },
+ "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
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+ "dev": true
+ },
+ "big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "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
+ },
+ "body-parser": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
+ "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.8.1",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.9.7",
+ "raw-body": "2.4.3",
+ "type-is": "~1.6.18"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "bonjour": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+ "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+ "dev": true,
+ "requires": {
+ "array-flatten": "^2.1.0",
+ "deep-equal": "^1.0.1",
+ "dns-equal": "^1.0.0",
+ "dns-txt": "^2.0.2",
+ "multicast-dns": "^6.0.1",
+ "multicast-dns-service-types": "^1.1.0"
+ }
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+ "dev": 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.20.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
+ "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001317",
+ "electron-to-chromium": "^1.4.84",
+ "escalade": "^3.1.1",
+ "node-releases": "^2.0.2",
+ "picocolors": "^1.0.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
+ },
+ "buffer-indexof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
+ "dev": true
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "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"
+ }
+ },
+ "camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "dev": true,
+ "requires": {
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001320",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz",
+ "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==",
+ "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": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ },
+ "chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "dev": true
+ },
+ "clean-css": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz",
+ "integrity": "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.6.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
+ }
+ }
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true
+ },
+ "clean-webpack-plugin": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz",
+ "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==",
+ "dev": true,
+ "requires": {
+ "del": "^4.1.1"
+ }
+ },
+ "clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ }
+ },
+ "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": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colorette": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
+ "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
+ "dev": true
+ },
+ "commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "dev": true
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+ "dev": true
+ },
+ "compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "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
+ },
+ "connect-history-api-fallback": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+ "dev": true
+ },
+ "content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.2.1"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
+ "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+ "dev": true
+ },
+ "core-js-compat": {
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz",
+ "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.19.1",
+ "semver": "7.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+ "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+ "dev": true
+ }
+ }
+ },
+ "core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "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": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "css-loader": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz",
+ "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.4.7",
+ "postcss-modules-extract-imports": "^3.0.0",
+ "postcss-modules-local-by-default": "^4.0.0",
+ "postcss-modules-scope": "^3.0.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^7.3.5"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ }
+ }
+ },
+ "css-select": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ }
+ },
+ "css-what": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz",
+ "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==",
+ "dev": true
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "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": {
+ "ms": "2.1.2"
+ }
+ },
+ "deep-equal": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
+ "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+ "dev": true,
+ "requires": {
+ "is-arguments": "^1.0.4",
+ "is-date-object": "^1.0.1",
+ "is-regex": "^1.0.4",
+ "object-is": "^1.0.1",
+ "object-keys": "^1.1.1",
+ "regexp.prototype.flags": "^1.2.0"
+ }
+ },
+ "default-gateway": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
+ "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==",
+ "dev": true,
+ "requires": {
+ "execa": "^5.0.0"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "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
+ },
+ "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
+ }
+ }
+ },
+ "define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "del": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
+ "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
+ "dev": true,
+ "requires": {
+ "@types/glob": "^7.1.1",
+ "globby": "^6.1.0",
+ "is-path-cwd": "^2.0.0",
+ "is-path-in-cwd": "^2.0.0",
+ "p-map": "^2.0.0",
+ "pify": "^4.0.1",
+ "rimraf": "^2.6.3"
+ }
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+ "dev": true
+ },
+ "detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+ "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": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "dns-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+ "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+ "dev": true
+ },
+ "dns-packet": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
+ "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
+ "dev": true,
+ "requires": {
+ "ip": "^1.1.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "dns-txt": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+ "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+ "dev": true,
+ "requires": {
+ "buffer-indexof": "^1.0.0"
+ }
+ },
+ "dom-converter": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+ "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "dev": true,
+ "requires": {
+ "utila": "~0.4"
+ }
+ },
+ "dom-serializer": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+ "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+ "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.2.0"
+ }
+ },
+ "domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ }
+ },
+ "dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "requires": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.4.96",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz",
+ "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "enhanced-resolve": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
+ "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ }
+ },
+ "entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true
+ },
+ "envinfo": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
+ "dev": true
+ },
+ "es-module-lexer": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
+ "dev": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "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": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "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
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ },
+ "events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true
+ },
+ "execa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "express": {
+ "version": "4.17.3",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
+ "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.19.2",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.4.2",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.1.2",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.9.7",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.17.2",
+ "serve-static": "1.14.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~1.5.0",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
+ }
+ },
+ "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.11",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
+ "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
+ "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
+ },
+ "fastest-levenshtein": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
+ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==",
+ "dev": true
+ },
+ "fastq": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+ "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "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": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "find-cache-dir": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
+ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ }
+ },
+ "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": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.14.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
+ "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
+ "dev": true
+ },
+ "forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "dev": true
+ },
+ "fs-monkey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
+ "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "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
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "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
+ },
+ "get-intrinsic": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+ "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "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-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"
+ }
+ },
+ "glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+ "dev": true
+ },
+ "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
+ },
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.9",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
+ "dev": true
+ },
+ "handle-thing": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+ "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,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "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.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "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"
+ }
+ }
+ }
+ },
+ "html-entities": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
+ "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==",
+ "dev": true
+ },
+ "html-minifier-terser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+ "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+ "dev": true,
+ "requires": {
+ "camel-case": "^4.1.2",
+ "clean-css": "^5.2.2",
+ "commander": "^8.3.0",
+ "he": "^1.2.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.10.0"
+ }
+ },
+ "html-webpack-plugin": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+ "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+ "dev": true,
+ "requires": {
+ "@types/html-minifier-terser": "^6.0.0",
+ "html-minifier-terser": "^6.0.2",
+ "lodash": "^4.17.21",
+ "pretty-error": "^4.0.0",
+ "tapable": "^2.0.0"
+ }
+ },
+ "htmlparser2": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+ "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.0.0",
+ "domutils": "^2.5.2",
+ "entities": "^2.0.0"
+ }
+ },
+ "http-deceiver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+ "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.1"
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz",
+ "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==",
+ "dev": true
+ },
+ "http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "http-proxy-middleware": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz",
+ "integrity": "sha512-m/4FxX17SUvz4lJ5WPXOHDUuCwIqXLfLHs1s0uZ3oYjhoXlx9csYxaOa0ElDEJ+h8Q4iJ1s+lTMbiCa4EXIJqg==",
+ "dev": true,
+ "requires": {
+ "@types/http-proxy": "^1.17.8",
+ "http-proxy": "^1.18.1",
+ "is-glob": "^4.0.1",
+ "is-plain-obj": "^3.0.0",
+ "micromatch": "^4.0.2"
+ }
+ },
+ "human-signals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "dev": true
+ },
+ "husky": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz",
+ "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+ "dev": true
+ },
+ "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": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ }
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "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.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "interpret": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
+ "dev": true
+ },
+ "ip": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz",
+ "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==",
+ "dev": true
+ },
+ "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-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,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-core-module": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
+ "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "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": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "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": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "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-path-cwd": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
+ "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
+ "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^2.1.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
+ "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.2"
+ }
+ },
+ "is-plain-obj": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
+ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+ "dev": true
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "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": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "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
+ },
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "dependencies": {
+ "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": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "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
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "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
+ },
+ "json5": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+ "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "loader-runner": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
+ "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
+ "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ }
+ },
+ "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": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
+ "dev": true
+ },
+ "lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "dev": true
+ },
+ "memfs": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
+ "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
+ "dev": true,
+ "requires": {
+ "fs-monkey": "1.0.3"
+ }
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "dev": true
+ },
+ "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
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+ "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": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "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
+ },
+ "mini-css-extract-plugin": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz",
+ "integrity": "sha512-ndG8nxCEnAemsg4FSgS+yNyHKgkTB4nPKqCOgh65j3/30qqC5RaSQQXMm++Y6sb6E1zRSxPkztj9fqxhS1Eo6w==",
+ "dev": true,
+ "requires": {
+ "schema-utils": "^4.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.3"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ }
+ }
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "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": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.6"
+ }
+ },
+ "mri": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
+ "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
+ "dev": true
+ },
+ "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
+ },
+ "multicast-dns": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+ "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+ "dev": true,
+ "requires": {
+ "dns-packet": "^1.3.1",
+ "thunky": "^1.0.2"
+ }
+ },
+ "multicast-dns-service-types": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+ "dev": true
+ },
+ "multimatch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz",
+ "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==",
+ "dev": true,
+ "requires": {
+ "@types/minimatch": "^3.0.3",
+ "array-differ": "^3.0.0",
+ "array-union": "^2.1.0",
+ "arrify": "^2.0.1",
+ "minimatch": "^3.0.4"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "nanoid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz",
+ "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==",
+ "dev": true
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "requires": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node-forge": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz",
+ "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
+ "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==",
+ "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": {
+ "path-key": "^3.0.0"
+ }
+ },
+ "nth-check": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+ "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "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-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
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "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": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "open": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
+ "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+ "dev": true,
+ "requires": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ }
+ },
+ "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": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "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": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-map": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+ "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+ "dev": true
+ },
+ "p-retry": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz",
+ "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==",
+ "dev": true,
+ "requires": {
+ "@types/retry": "^0.12.0",
+ "retry": "^0.13.1"
+ }
+ },
+ "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
+ },
+ "param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "dev": true,
+ "requires": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
+ "pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "dev": true,
+ "requires": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "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
+ },
+ "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": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "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
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+ "dev": true
+ },
+ "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
+ },
+ "picocolors": {
+ "version": "1.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
+ },
+ "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"
+ }
+ },
+ "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": {
+ "find-up": "^4.0.0"
+ }
+ },
+ "portfinder": {
+ "version": "1.0.28",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+ "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.2",
+ "debug": "^3.1.1",
+ "mkdirp": "^0.5.5"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "postcss": {
+ "version": "8.4.12",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz",
+ "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.1",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "postcss-modules-extract-imports": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
+ "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+ "dev": true
+ },
+ "postcss-modules-local-by-default": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
+ "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.1.0"
+ }
+ },
+ "postcss-modules-scope": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz",
+ "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.4"
+ }
+ },
+ "postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^5.0.0"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.9",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz",
+ "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "prettier": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz",
+ "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==",
+ "dev": true
+ },
+ "pretty-error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+ "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.20",
+ "renderkid": "^3.0.0"
+ }
+ },
+ "pretty-quick": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.3.tgz",
+ "integrity": "sha512-kOCi2FJabvuh1as9enxYmrnBC6tVMoVOenMaBqRfsvBHB0cbpYHjdQEpSglpASDFEXVwplpcGR4CLEaisYAFcA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^3.0.0",
+ "execa": "^4.0.0",
+ "find-up": "^4.1.0",
+ "ignore": "^5.1.4",
+ "mri": "^1.1.5",
+ "multimatch": "^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": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "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"
+ }
+ }
+ }
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "requires": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "dependencies": {
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true
+ }
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "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
+ },
+ "qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
+ "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
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
+ "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "1.8.1",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "rechoir": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+ "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.9.0"
+ }
+ },
+ "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.0.1",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz",
+ "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.2"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.9",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz",
+ "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.8.4"
+ }
+ },
+ "regexp.prototype.flags": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
+ "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ }
+ },
+ "regexpu-core": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz",
+ "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.0.1",
+ "regjsgen": "^0.6.0",
+ "regjsparser": "^0.8.2",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.0.0"
+ }
+ },
+ "regjsgen": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz",
+ "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.8.4",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz",
+ "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==",
+ "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
+ }
+ }
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
+ "dev": true
+ },
+ "renderkid": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+ "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+ "dev": true,
+ "requires": {
+ "css-select": "^4.1.3",
+ "dom-converter": "^0.2.0",
+ "htmlparser2": "^6.1.0",
+ "lodash": "^4.17.21",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.22.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+ "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.8.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "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": {
+ "resolve-from": "^5.0.0"
+ }
+ },
+ "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
+ },
+ "retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "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": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "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": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "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
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+ "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.5",
+ "ajv": "^6.12.4",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+ "dev": true
+ },
+ "selfsigned": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz",
+ "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==",
+ "dev": true,
+ "requires": {
+ "node-forge": "^1"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "send": {
+ "version": "0.17.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
+ "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "1.8.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ }
+ }
+ },
+ "serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
+ "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.17.2"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.2"
+ }
+ },
+ "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,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "sockjs": {
+ "version": "0.3.24",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+ "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+ "dev": true,
+ "requires": {
+ "faye-websocket": "^0.11.3",
+ "uuid": "^8.3.2",
+ "websocket-driver": "^0.7.4"
+ }
+ },
+ "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-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.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
+ }
+ }
+ },
+ "spdy": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "^1.2.7",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ }
+ },
+ "spdy-transport": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+ "dev": true
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "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": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "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
+ },
+ "style-loader": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
+ "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
+ "dev": true
+ },
+ "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,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "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
+ },
+ "tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true
+ },
+ "terser": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
+ "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map": "~0.7.2",
+ "source-map-support": "~0.5.20"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ }
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz",
+ "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==",
+ "dev": true,
+ "requires": {
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^3.1.1",
+ "serialize-javascript": "^6.0.0",
+ "source-map": "^0.6.1",
+ "terser": "^5.7.2"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "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
+ }
+ }
+ },
+ "thunky": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+ "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": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "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": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "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": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==",
+ "dev": true
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "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": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "utila": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+ "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "dev": true
+ },
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "dev": true
+ },
+ "watchpack": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
+ "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
+ "dev": true,
+ "requires": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ }
+ },
+ "wbuf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "dev": true,
+ "requires": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "webpack": {
+ "version": "5.70.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz",
+ "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==",
+ "dev": true,
+ "requires": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^0.0.51",
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/wasm-edit": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "acorn": "^8.4.1",
+ "acorn-import-assertions": "^1.7.6",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.9.2",
+ "es-module-lexer": "^0.9.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-better-errors": "^1.0.2",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.1.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.1.3",
+ "watchpack": "^2.3.1",
+ "webpack-sources": "^3.2.3"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ }
+ }
+ },
+ "webpack-cli": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz",
+ "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==",
+ "dev": true,
+ "requires": {
+ "@discoveryjs/json-ext": "^0.5.0",
+ "@webpack-cli/configtest": "^1.1.1",
+ "@webpack-cli/info": "^1.4.1",
+ "@webpack-cli/serve": "^1.6.1",
+ "colorette": "^2.0.14",
+ "commander": "^7.0.0",
+ "execa": "^5.0.0",
+ "fastest-levenshtein": "^1.0.12",
+ "import-local": "^3.0.2",
+ "interpret": "^2.2.0",
+ "rechoir": "^0.7.0",
+ "webpack-merge": "^5.7.3"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "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"
+ }
+ },
+ "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
+ },
+ "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
+ }
+ }
+ },
+ "webpack-dev-middleware": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
+ "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
+ "dev": true,
+ "requires": {
+ "colorette": "^2.0.10",
+ "memfs": "^3.4.1",
+ "mime-types": "^2.1.31",
+ "range-parser": "^1.2.1",
+ "schema-utils": "^4.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.3"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ }
+ }
+ }
+ },
+ "webpack-dev-server": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz",
+ "integrity": "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==",
+ "dev": true,
+ "requires": {
+ "@types/bonjour": "^3.5.9",
+ "@types/connect-history-api-fallback": "^1.3.5",
+ "@types/express": "^4.17.13",
+ "@types/serve-index": "^1.9.1",
+ "@types/sockjs": "^0.3.33",
+ "@types/ws": "^8.2.2",
+ "ansi-html-community": "^0.0.8",
+ "bonjour": "^3.5.0",
+ "chokidar": "^3.5.3",
+ "colorette": "^2.0.10",
+ "compression": "^1.7.4",
+ "connect-history-api-fallback": "^1.6.0",
+ "default-gateway": "^6.0.3",
+ "del": "^6.0.0",
+ "express": "^4.17.1",
+ "graceful-fs": "^4.2.6",
+ "html-entities": "^2.3.2",
+ "http-proxy-middleware": "^2.0.0",
+ "ipaddr.js": "^2.0.1",
+ "open": "^8.0.9",
+ "p-retry": "^4.5.0",
+ "portfinder": "^1.0.28",
+ "schema-utils": "^4.0.0",
+ "selfsigned": "^2.0.0",
+ "serve-index": "^1.9.1",
+ "sockjs": "^0.3.21",
+ "spdy": "^4.0.2",
+ "strip-ansi": "^7.0.0",
+ "webpack-dev-middleware": "^5.3.1",
+ "ws": "^8.4.2"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.3"
+ }
+ },
+ "ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true
+ },
+ "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
+ },
+ "del": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz",
+ "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==",
+ "dev": true,
+ "requires": {
+ "globby": "^11.0.1",
+ "graceful-fs": "^4.2.4",
+ "is-glob": "^4.0.1",
+ "is-path-cwd": "^2.2.0",
+ "is-path-inside": "^3.0.2",
+ "p-map": "^4.0.0",
+ "rimraf": "^3.0.2",
+ "slash": "^3.0.0"
+ }
+ },
+ "globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "requires": {
+ "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"
+ }
+ },
+ "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
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "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": {
+ "glob": "^7.1.3"
+ }
+ },
+ "schema-utils": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+ "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
+ "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
+ "webpack-merge": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
+ "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+ "dev": true,
+ "requires": {
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
+ }
+ },
+ "webpack-sources": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "dev": true
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "wildcard": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "ws": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
+ "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+}
diff --git a/user_interface/package.json b/user_interface/package.json
new file mode 100644
index 0000000..0d4721b
--- /dev/null
+++ b/user_interface/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "html5-slot-machine",
+ "description": "Modern casino slot machine game using only plain JavaScript (Web Animations API)",
+ "scripts": {
+ "start": "webpack serve --mode development"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.15.0",
+ "@babel/preset-env": "^7.15.0",
+ "babel-loader": "^8.1.0",
+ "clean-webpack-plugin": "^4.0.0",
+ "css-loader": "^6.4.0",
+ "html-webpack-plugin": "^5.3.2",
+ "husky": "^7.0.1",
+ "mini-css-extract-plugin": "^2.4.3",
+ "prettier": "^2.3.2",
+ "pretty-quick": "^3.1.1",
+ "style-loader": "^3.2.1",
+ "webpack": "^5.49.0",
+ "webpack-cli": "^4.7.2",
+ "webpack-dev-server": "^4.3.1"
+ }
+}
diff --git a/user_interface/src/assets/bg.jpg b/user_interface/src/assets/bg.jpg
new file mode 100644
index 0000000..df3ba0d
Binary files /dev/null and b/user_interface/src/assets/bg.jpg differ
diff --git a/user_interface/src/assets/bg_small.jpg b/user_interface/src/assets/bg_small.jpg
new file mode 100644
index 0000000..df3ba0d
Binary files /dev/null and b/user_interface/src/assets/bg_small.jpg differ
diff --git a/user_interface/src/assets/symbols/License and Author Info.pdf b/user_interface/src/assets/symbols/License and Author Info.pdf
new file mode 100644
index 0000000..a281f47
Binary files /dev/null and b/user_interface/src/assets/symbols/License and Author Info.pdf differ
diff --git a/user_interface/src/assets/symbols/at_at.png b/user_interface/src/assets/symbols/at_at.png
new file mode 100644
index 0000000..48b0d53
Binary files /dev/null and b/user_interface/src/assets/symbols/at_at.png differ
diff --git a/user_interface/src/assets/symbols/c3po.png b/user_interface/src/assets/symbols/c3po.png
new file mode 100644
index 0000000..56faf51
Binary files /dev/null and b/user_interface/src/assets/symbols/c3po.png differ
diff --git a/user_interface/src/assets/symbols/darth_vader.png b/user_interface/src/assets/symbols/darth_vader.png
new file mode 100644
index 0000000..4d77f82
Binary files /dev/null and b/user_interface/src/assets/symbols/darth_vader.png differ
diff --git a/user_interface/src/assets/symbols/death_star.png b/user_interface/src/assets/symbols/death_star.png
new file mode 100644
index 0000000..454b04e
Binary files /dev/null and b/user_interface/src/assets/symbols/death_star.png differ
diff --git a/user_interface/src/assets/symbols/falcon.png b/user_interface/src/assets/symbols/falcon.png
new file mode 100644
index 0000000..32b05f7
Binary files /dev/null and b/user_interface/src/assets/symbols/falcon.png differ
diff --git a/user_interface/src/assets/symbols/r2d2.png b/user_interface/src/assets/symbols/r2d2.png
new file mode 100644
index 0000000..8f8122a
Binary files /dev/null and b/user_interface/src/assets/symbols/r2d2.png differ
diff --git a/user_interface/src/assets/symbols/stormtrooper.png b/user_interface/src/assets/symbols/stormtrooper.png
new file mode 100644
index 0000000..6815f6b
Binary files /dev/null and b/user_interface/src/assets/symbols/stormtrooper.png differ
diff --git a/user_interface/src/assets/symbols/tie_ln.png b/user_interface/src/assets/symbols/tie_ln.png
new file mode 100644
index 0000000..4380dfa
Binary files /dev/null and b/user_interface/src/assets/symbols/tie_ln.png differ
diff --git a/user_interface/src/assets/symbols/yoda.png b/user_interface/src/assets/symbols/yoda.png
new file mode 100644
index 0000000..e67a02b
Binary files /dev/null and b/user_interface/src/assets/symbols/yoda.png differ
diff --git a/user_interface/src/css/style.css b/user_interface/src/css/style.css
new file mode 100644
index 0000000..a5ee414
--- /dev/null
+++ b/user_interface/src/css/style.css
@@ -0,0 +1,104 @@
+body {
+ width: 100vw;
+ height: 100vh;
+ padding: 1rem;
+ background-image: url("../assets/bg.jpg");
+ background-size: cover;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 24px;
+ font-family: "Roboto Condensed", sans-serif;
+ /*opacity: 0.4;*/
+}
+
+@media only screen and (max-width: 1200px) {
+body {
+ background-image: url("../assets/bg_small.jpg");
+}
+}
+
+#reels {
+ display: flex;
+ width: 100vw;
+ height: calc((3 / 4) * 100vw);
+ max-height: calc(90vh - 50px - 40px);
+ max-width: calc((4 / 3) * (90vh - 50px - 40px));
+ margin-left: 700px;
+ /*opacity: 0.9;*/
+}
+
+.reel {
+ overflow: hidden;
+ width: 30%;
+ height: 100%;
+ /*opacity: 0.5;*/
+}
+
+.reel > .icons > img {
+ width: calc(100% + 6px);
+ margin: -3px 0 0 -3px;
+ height: auto;
+ /*opacity: 0.5;*/
+
+ /* enable gpu accelaration, fixes iOS flicker */
+ transform: translate3d(0, 0, 0);
+}
+
+#controls {
+ background-color: rgba(255, 255, 255, 0.7);
+ height: 50px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 5px 30px;
+}
+
+#controls label {
+ display: flex;
+ align-items: center;
+ margin: 0;
+ /*opacity: 0.5;*/
+
+}
+
+#controls label input {
+ margin-right: 5px;
+ /*opacity: 0.5;*/
+}
+
+input[type="checkbox"] {
+ width: 40px;
+ height: 40px;
+}
+
+#jackpot {
+ color: #c7f7ff;
+ font-size: 40px;
+ text-align: center;
+ margin-left: 700px;
+}
+
+#slot.inverted .reel {
+ transform: scaleY(-1);
+}
+
+#slot.inverted .reel > .icons > img {
+ transform: scaleY(-1);
+}
+
+#win {
+ position: absolute;
+ margin: 0 0 0 170px;
+}
+
+#spin_cost {
+ margin: 0 0 0 70px;
+ color: rgb(116, 116, 116);
+ position: absolute;
+}
+
+#returnCoef {
+ margin: 0 0 0 250px;
+ position: absolute;
+}
\ No newline at end of file
diff --git a/user_interface/src/index.html b/user_interface/src/index.html
new file mode 100644
index 0000000..3512aaf
--- /dev/null
+++ b/user_interface/src/index.html
@@ -0,0 +1,42 @@
+
+
+
+ HTML 5 Slot Machine
+
+
+
+
+
+
+
Jackpot: соточка за курсач
+
+
+
SPIN
+
+
COST: 10
+
WIN: 0
+
Return coefficient: 0.0
+
+
+
+ Autoplay
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/user_interface/src/js/Reel.js b/user_interface/src/js/Reel.js
new file mode 100644
index 0000000..3f5d554
--- /dev/null
+++ b/user_interface/src/js/Reel.js
@@ -0,0 +1,76 @@
+import Symbol from "./Symbol.js";
+
+export default class Reel {
+ constructor(reelContainer, idx, initialSymbols) {
+ this.reelContainer = reelContainer;
+ this.idx = idx;
+
+ this.symbolContainer = document.createElement("div");
+ this.symbolContainer.classList.add("icons");
+ this.reelContainer.appendChild(this.symbolContainer);
+
+ this.animation = this.symbolContainer.animate(
+ [
+ { transform: "none", filter: "blur(0)" },
+ { filter: "blur(2px)", offset: 0.5 },
+ {
+ transform: `translateY(-${
+ ((Math.floor(this.factor) * 10) /
+ (3 + Math.floor(this.factor) * 10)) *
+ 100
+ }%)`,
+ filter: "blur(0)",
+ },
+ ],
+ {
+ duration: this.factor * 1000,
+ easing: "ease-in-out",
+ }
+ );
+ this.animation.cancel();
+
+ initialSymbols.forEach((symbol) =>
+ this.symbolContainer.appendChild(new Symbol(symbol).img)
+ );
+ }
+
+ get factor() {
+ return 1 + Math.pow(this.idx / 2, 2);
+ }
+
+ renderSymbols(nextSymbols) {
+ const fragment = document.createDocumentFragment();
+
+ for (let i = 3; i < 3 + Math.floor(this.factor) * 10; i++) {
+ const icon = new Symbol(
+ i >= 10 * Math.floor(this.factor) - 2
+ ? nextSymbols[i - Math.floor(this.factor) * 10]
+ : undefined
+ );
+ fragment.appendChild(icon.img);
+ }
+
+ this.symbolContainer.appendChild(fragment);
+ }
+
+ spin() {
+ const animationPromise = new Promise(
+ (resolve) => (this.animation.onfinish = resolve)
+ );
+ const timeoutPromise = new Promise((resolve) =>
+ setTimeout(resolve, this.factor * 1000)
+ );
+
+ this.animation.play();
+
+ return Promise.race([animationPromise, timeoutPromise]).then(() => {
+ if (this.animation.playState !== "finished") this.animation.finish();
+
+ const max = this.symbolContainer.children.length - 3;
+
+ for (let i = 0; i < max; i++) {
+ this.symbolContainer.firstChild.remove();
+ }
+ });
+ }
+}
diff --git a/user_interface/src/js/Slot.js b/user_interface/src/js/Slot.js
new file mode 100644
index 0000000..034961d
--- /dev/null
+++ b/user_interface/src/js/Slot.js
@@ -0,0 +1,119 @@
+import Reel from "./Reel.js";
+import Symbol from "./Symbol.js";
+
+
+function getData() {
+ let symbols = [], won, returnCoef;
+
+ $.ajax({
+ type: "GET",
+ url: "http://127.0.0.1:5000/getState",
+ async: false,
+ // data: { param: input },
+ success: function(response) {
+ let arr = response.split(',');
+ returnCoef = parseFloat(arr.pop())
+ won = arr.pop();
+ symbols = [];
+
+ for (let i = 0, k = -1; i < arr.length; i++) {
+ if (i % 3 === 0) {
+ k++;
+ symbols[k] = [];
+ }
+
+ symbols[k].push(arr[i]);
+ }
+ }
+ });
+
+ return [symbols, won, returnCoef];
+}
+
+
+export default class Slot {
+
+ constructor(domElement, config = {}) {
+ Symbol.preload();
+
+ this.won = 0;
+ this.returnCoef = 0.0;
+
+ this.currentSymbols = [
+ ["death_star", "death_star", "death_star"],
+ ["death_star", "death_star", "death_star"],
+ ["death_star", "death_star", "death_star"],
+ ["death_star", "death_star", "death_star"],
+ // ["death_star", "death_star", "death_star"],
+ ];
+
+ this.nextSymbols = [
+ ["death_star", "death_star", "death_star"],
+ ["death_star", "death_star", "death_star"],
+ ["death_star", "death_star", "death_star"],
+ ["death_star", "death_star", "death_star"],
+ // ["death_star", "death_star", "death_star"],
+ ];
+
+ this.container = domElement;
+
+ this.reels = Array.from(this.container.getElementsByClassName("reel")).map(
+ (reelContainer, idx) =>
+ new Reel(reelContainer, idx, this.currentSymbols[idx])
+ );
+
+ this.spinButton = document.getElementById("spin");
+ this.spinButton.addEventListener("click", () => this.spin());
+
+ this.autoPlayCheckbox = document.getElementById("autoplay");
+
+ if (config.inverted) {
+ this.container.classList.add("inverted");
+ }
+
+ this.config = config;
+ }
+
+
+ spin() {
+ this.currentSymbols = this.nextSymbols;
+
+ const resp = getData();
+
+ this.nextSymbols = resp[0];
+ this.won = resp[1];
+ this.returnCoef = resp[2];
+
+ this.onSpinStart(this.nextSymbols);
+
+
+
+ return Promise.all(
+ this.reels.map((reel) => {
+ reel.renderSymbols(this.nextSymbols[reel.idx]);
+ return reel.spin();
+ })
+ ).then(() => this.onSpinEnd(this.nextSymbols));
+ }
+
+
+ onSpinStart(symbols) {
+ this.spinButton.disabled = true;
+
+ this.config.onSpinStart?.(symbols);
+ }
+
+
+ onSpinEnd(symbols) {
+ this.spinButton.disabled = false;
+
+ this.config.onSpinEnd?.(symbols);
+
+ document.getElementById("win").innerText = "WIN: " + this.won;
+ document.getElementById("returnCoef").innerText = "Return coefficient: " + this.returnCoef;
+
+ if (this.autoPlayCheckbox.checked) {
+ return window.setTimeout(() => this.spin(), 200);
+ }
+ }
+}
diff --git a/user_interface/src/js/Symbol.js b/user_interface/src/js/Symbol.js
new file mode 100644
index 0000000..7e306ef
--- /dev/null
+++ b/user_interface/src/js/Symbol.js
@@ -0,0 +1,35 @@
+const cache = {};
+
+export default class Symbol {
+ constructor(name = Symbol.random()) {
+ this.name = name;
+
+ if (cache[name]) {
+ this.img = cache[name].cloneNode();
+ } else {
+ this.img = new Image();
+ this.img.src = require(`../assets/symbols/${name}.png`);
+
+ cache[name] = this.img;
+ }
+ }
+
+ static preload() {
+ Symbol.symbols.forEach((symbol) => new Symbol(symbol));
+ }
+
+ static get symbols() {
+ return [
+ "at_at",
+ "c3po",
+ "darth_vader",
+ "death_star",
+ "falcon",
+ "r2d2",
+ ];
+ }
+
+ static random() {
+ return this.symbols[Math.floor(Math.random() * this.symbols.length)];
+ }
+}
diff --git a/user_interface/src/js/index.js b/user_interface/src/js/index.js
new file mode 100644
index 0000000..7e9d43e
--- /dev/null
+++ b/user_interface/src/js/index.js
@@ -0,0 +1,13 @@
+import Slot from "./Slot.js";
+
+const config = {
+ inverted: false, // true: reels spin from top to bottom; false: reels spin from bottom to top
+ onSpinStart: (symbols) => {
+ console.log("onSpinStart", symbols);
+ },
+ onSpinEnd: (symbols) => {
+ console.log("onSpinEnd", symbols);
+ },
+};
+
+const slot = new Slot(document.getElementById("slot"), config);
diff --git a/user_interface/webpack.config.js b/user_interface/webpack.config.js
new file mode 100644
index 0000000..7b55ac7
--- /dev/null
+++ b/user_interface/webpack.config.js
@@ -0,0 +1,42 @@
+const path = require("path");
+
+const { CleanWebpackPlugin } = require("clean-webpack-plugin");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+
+module.exports = {
+ entry: ["./src/js/index.js", "./src/css/style.css"],
+ output: {
+ filename: "bundle.[contenthash].js",
+ path: path.resolve(__dirname, "dist"),
+ },
+ performance: {
+ hints: false,
+ },
+ plugins: [
+ new CleanWebpackPlugin(),
+ new HtmlWebpackPlugin({
+ template: "./src/index.html",
+ }),
+ new MiniCssExtractPlugin({
+ filename: "main.[contenthash].css",
+ }),
+ ],
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
+ },
+ {
+ test: /\.(png|jpg|gif|svg)$/,
+ type: "asset/resource",
+ },
+ {
+ test: /\.js$/,
+ exclude: /node_modules/,
+ use: "babel-loader",
+ },
+ ],
+ },
+};