22ML_hw3/ml_hw3.ipynb

11897 lines
802 KiB
Plaintext
Raw Normal View History

2022-11-28 20:34:02 +09:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 기계학습 - 2022년 2학기"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 과제2. 다중계층 신경망을 이용한 얼굴 표정 분류기 작성\n",
"\n",
"과제 문의: 전북대학교 컴퓨터공학부 시각 및 학습 연구실 (공과대학 7호관 7619)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Requirements\n",
"- Python >= 3.6\n",
"- numpy\n",
"- matplotlib\n",
"- jupyterplot"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"이번 과제에서는 사람 얼굴의 표정 데이터셋(Toronto Faces Dataset, [TFD](http://aclab.ca/users/josh/TFD.html))을 분류하는 다중계층 신경망(Multi-Layer Neural Net)을 구현하고 테스트 합니다."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"from matplotlib import pyplot as plt\n",
"\n",
"import importlib.util\n",
"try:\n",
" importlib.util.find_spec('jupyterplot')\n",
"except ImportError:\n",
" %pip install jupyterplot\n",
" pass\n",
"\n",
"from jupyterplot import ProgressPlot\n",
"\n",
"try:\n",
" importlib.util.find_spec('tqdm')\n",
"except ImportError:\n",
" %pip install tqdm\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Toronto Faces Dataset"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"TFD는 1-Anger, 2-Disgust, 3-Fear, 4-Happy, 5-Sad, 6-Suprise, 7-Neutral의 총 7개의 클래스를 가진 데이터셋입니다.\n",
"\n",
"데이터셋은 학습, 검증, 테스트(training, validation, test)를 위해서 각각 3374, 419, 385장의 48 $\\times$ 48 크기 grayscale 이미지를 제공합니다.\n",
"\n",
"데이터셋의 예시를 확인하기 위해 아래 셀들을 실행해보시기 바랍니다."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"#### Please DO NOT DELETE this cell. ###\n",
"\n",
"def LoadData(fname):\n",
" \"\"\"Loads data from an NPZ file.\n",
"\n",
" Args:\n",
" fname: NPZ filename.\n",
"\n",
" Returns:\n",
" data: Tuple {inputs, target}_{train, valid, test}.\n",
" Row-major, outer axis to be the number of observations.\n",
" \"\"\"\n",
" npzfile = np.load(fname)\n",
"\n",
" inputs_train = npzfile['inputs_train'].T / 255.0\n",
" inputs_valid = npzfile['inputs_valid'].T / 255.0\n",
" inputs_test = npzfile['inputs_test'].T / 255.0\n",
" target_train = npzfile['target_train'].tolist()\n",
" target_valid = npzfile['target_valid'].tolist()\n",
" target_test = npzfile['target_test'].tolist()\n",
"\n",
" num_class = max(target_train + target_valid + target_test) + 1\n",
" target_train_1hot = np.zeros([num_class, len(target_train)])\n",
" target_valid_1hot = np.zeros([num_class, len(target_valid)])\n",
" target_test_1hot = np.zeros([num_class, len(target_test)])\n",
"\n",
" for ii, xx in enumerate(target_train):\n",
" target_train_1hot[xx, ii] = 1.0\n",
"\n",
" for ii, xx in enumerate(target_valid):\n",
" target_valid_1hot[xx, ii] = 1.0\n",
"\n",
" for ii, xx in enumerate(target_test):\n",
" target_test_1hot[xx, ii] = 1.0\n",
"\n",
" inputs_train = inputs_train.T\n",
" inputs_valid = inputs_valid.T\n",
" inputs_test = inputs_test.T\n",
" target_train_1hot = target_train_1hot.T\n",
" target_valid_1hot = target_valid_1hot.T\n",
" target_test_1hot = target_test_1hot.T\n",
" return inputs_train, inputs_valid, inputs_test, target_train_1hot, target_valid_1hot, target_test_1hot\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"training dataset\n",
"inputs: (3374, 2304) targets: (3374, 7)\n",
"validation dataset\n",
"inputs: (419, 2304) targets: (419, 7)\n",
"test dataset\n",
"inputs: (385, 2304) targets: (385, 7)\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABFEAAACuCAYAAAD+kpJ4AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOx9d5hkVbX9qupQVV2de/IMM+QoCA8BFSVLkCBRQJTkU54gggGzRMGIgmMWHRDGBBieARUUww9FUDEiMk+CDBN6ejpWV3d1ddX9/dHfOr3urnOrqwemGvXu7+uvqqtu3XvCPnuvvc4+5ySCIAgQSyyxxBJLLLHEEkssscQSSyyxxBJLVUnOdQFiiSWWWGKJJZZYYoklllhiiSWWWP4VJCZRYoklllhiiSWWWGKJJZZYYoklllhqkJhEiSWWWGKJJZZYYoklllhiiSWWWGKpQWISJZZYYoklllhiiSWWWGKJJZZYYomlBolJlFhiiSWWWGKJJZZYYoklllhiiSWWGiQmUWKJJZZYYoklllhiiSWWWGKJJZZYapCYRIklllhiiSWWWGKJJZZYYoklllhiqUFiEiWWWGKJJZZYYoklllhiiSWWWGKJpQaJSZRYYoklllhiiSWWWGKJJZZYYokllhokJlFiieVfQLbddluce+657v+f/exnSCQS+NnPfjZnZYollihZs2YNjjzySHR0dCCRSODb3/72XBcpln8ROeSQQ/C85z1vrosRSyzPSbn55puRSCTwxBNPzHVRYvk3l1jXYvl3kK2px3UjUR588EG88Y1vxB577IFsNovly5fjla98JR599FHv9eVyGZ/5zGew9957I5PJoKenB4cddhj++Mc/Vlz7j3/8A6961auwYMECZDIZ7LTTTnjPe97zjO4Zy7+P/PWvf8Vpp52G7bffHi0tLZg3bx4OOuggfPe73/Ve/7e//Q1HH300Wltb0d3djde85jXYtGlTxXXr16/H61//emy33XbIZDLYYYcd8Ja3vAWbN2/e2lX6l5J169bhyiuvxB/+8Ie5LspzTraWXSyXy/jwhz+M7bbbDul0GnvttRe++tWveu/5jW98Ay984QvR2dmJnp4eHHzwwfj+97//jOp1zjnn4M9//jOuvfZa3HrrrXjBC17wjO4XyzOT2djARCIR+feyl70sdO21116LE044AQsXLkQikcCVV15ZpxrF8q8uuVwOV1xxBY4++mh0d3cjkUjg5ptvjry+Vr9cq+174IEHcOGFF2LfffdFU1MTEonEs1m9WLaizEZ3ZtPPn/nMZ3Daaadh+fLlSCQSoYkrn9xzzz047LDD0NHRgba2Nuy77774+te//gxqFsu/gszWdn3yk5/EbrvthlQqhaVLl+Itb3kLRkdHQ9esW7cOr371q7HLLrugra0NnZ2d2H///XHLLbcgCILQtdtuu22kj95pp522RpW3ulx33XX/kpNtjfV60Ic+9CHcd999OO2007DXXnthw4YN+OQnP4n/+q//wv33318x83T++edj9erVOPvss/HGN74Ro6OjeOihh9Db2xu67g9/+AMOOeQQLF26FG9961vR09ODf/7zn3jqqacqylDrPWP595Inn3wSIyMjOOecc7BkyRLk83nceeedOOGEE/C5z30Or3/96921a9euxUEHHYSOjg5cd911yOVy+OhHP4o///nPeOCBB9Dc3Axgyoi+6EUvwujoKC688EJss802+OMf/4hPfvKTuPfee/G73/0OyeTW4ygPOuggjI2NufI8l2XdunW46qqrsO2222Lvvfee6+I8p2Rr2cX3vOc9+OAHP4jXve512G+//fCd73wHr3rVq5BIJHDGGWe461auXIk3velNOPbYY/HBD34Q4+PjuPnmm3HcccfhzjvvxMknnzzrOo2NjeHXv/413vOe9+CNb3zjljVMLM+qzMYG3nrrrRW//+1vf4sbb7wRRx55ZOjz9773vVi0aBH22Wcf/OhHP9rq9Yjl30f6+vpw9dVXY/ny5Xj+859fNauyVr8M1G77fvCDH+Cmm27CXnvthe233z6SuI7luSez0Z3Z9POHPvQhjIyMYP/998f69eurlmHVqlV47Wtfi5e97GW47rrr0NDQgL///e/e2ONfVV7zmtfgjDPOQCqVmuuiPKdkNvr3jne8Ax/+8Idx6qmn4pJLLsHDDz+MlStX4q9//WvIZ/b19WHt2rU49dRTsXz5chSLRdx9990499xz8fe//x3XXXedu/aGG25ALpcLPefJJ5/Ee9/73gof/a8i1113HU499VSceOKJc12U2UlQJ7nvvvuCQqEQ+uzRRx8NUqlUcNZZZ4U+//rXvx4ACL75zW9WvWepVAqe97znBQcccECQz+erXlvrPf8TpFQqBWNjY3NdjDmVycnJ4PnPf36wyy67hD5/wxveEGQymeDJJ590n919990BgOBzn/uc+2z16tUBgOB73/te6PeXX355ACD4/e9//6yWd8WKFcE555zzrN6zXvLggw8GAIJVq1bNdVGec7I17OLatWuDpqam4KKLLnKflcvl4KUvfWmwbNmyYHJy0n2+0047Bfvtt19QLpfdZ0NDQ0Fra2twwgknbFGdnnzyyQBA8JGPfGSLfv9MpFgsVrRnLH6JsoE+ee1rXxskEongqaeeCn3++OOPB0EQBJs2bQoABFdcccUzLtfBBx8c7LHHHs/4PrE8t2V8fDxYv359EAQz+4ha/fJsbN+GDRscbrzooouCOsLhZySrVq0KALix958os9Gd2fTzE0884XxhNpuNxFyPP/54kMlkgje96U1bXonnsORyubkuwnNaatW/devWBY2NjcFrXvOa0OcrV64MAAT/+7//O+OzjjvuuCCbzYZsl0+uueaaAEBw33331V6R55BUG29WZqufW9Nm1m05z4tf/OKKWfOddtoJe+yxB/72t7+FPv/Yxz6G/fffHyeddBLK5XJF2hPlxz/+Mf7yl7/giiuuQCaTQT6fR6lU8l5b6z2BqeVB//jHP2asU39/P972trdhzz33RGtrK9rb23HMMcdUpNZz/4pvfOMbuPbaa7Fs2TKk02kcfvjh+L//+7+K+37qU5/C9ttvj0wmg/333x+//OUvccghh+CQQw4JXVcoFHDFFVdgxx13RCqVwjbbbIO3v/3tKBQKoesSiQTe+MY3YvXq1dhjjz2QSqXwwx/+cMb6/TtLQ0MDttlmGwwODoY+v/POO3Hcccdh+fLl7rMjjjgCO++8M77xjW+4z4aHhwEACxcuDP1+8eLFAIBMJuM+GxoawiOPPIKhoaEZyxUEAd7//vdj2bJlaGlpwaGHHoq//vWvFdf59kRZs2YNTjnlFCxatAjpdBrLli3DGWecEXru2NgY3vSmN2HevHloa2vDCSecgKeffroiFf/cc8/FtttuW/HcK6+8siId9u6778ZLXvISdHZ2orW1Fbvssgve/e53u3Lut99+AIDzzjvPpRxWS338T5KtYRe/853voFgs4sILL3SfJRIJvOENb8DatWvx61//2n0+PDyMBQsWhPq0vb0dra2tIR0GarOLV155JVasWAEAuOyyy5BIJEJ69PTTT+P888/HwoULkUqlsMcee+BLX/pS6B4TExO4/PLLse+++6KjowPZbBYvfelLce+994aue+KJJ5BIJPDRj34UN9xwA3bYYQekUik8/PDDVcsYy5RE2UArhUIBd955Jw4++GAsW7Ys9J3PRvgkn8/jkUceQV9fX83le/jhh3HooYeipaUFS5cuxYc//OHQ91uiJx//+MexYsUKZDIZHHzwwfjLX/4Suvbcc89Fa2srHnvsMRx11FHIZrNYsmQJrr76apdSHQQBtt12W7ziFa+oKPP4+Dg6OjpwwQUX1FzP/1RJpVJYtGhRTdfW6pdnY/sWLlxYYeOi5J///CceeeSRmq5duXIl9thjD7S0tKCrqwsveMEL8JWvfMV9/+STT+LCCy/ELrvs4pZknnbaad71+n/9619x2GGHIZPJYNmyZXj/+9+PcrlcUzn+nWU2ujObfl6xYkVNy7o++9nPolQq4eqrrwYwlZk
"text/plain": [
"<Figure size 1400x700 with 7 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"data = LoadData('./toronto_face.npz')\n",
"inputs = data[:3]\n",
"targets = data[3:]\n",
"inputs = {k:v for k, v in zip(['train', 'valid', 'test'], inputs)}\n",
"targets = {k:v for k, v in zip(['train', 'valid', 'test'], targets)}\n",
"\n",
"print('training dataset')\n",
"print('inputs:', inputs['train'].shape, 'targets:', targets['train'].shape)\n",
"print('validation dataset')\n",
"print('inputs:', inputs['valid'].shape, 'targets:', targets['valid'].shape)\n",
"print('test dataset')\n",
"print('inputs:', inputs['test'].shape, 'targets:', targets['test'].shape)\n",
"\n",
"classes = ['anger', 'disgust', 'fear', 'happy', 'sad', 'suprise', 'neutral']\n",
"_, labels = np.nonzero(targets['train'])\n",
"\n",
"figs, axes = plt.subplots(nrows=1, ncols=7, figsize=(14,7))\n",
"for idx in range(7):\n",
" axis = axes[idx]\n",
" rnd_idx = np.random.choice(np.nonzero(labels == idx)[0])\n",
" axis.axis('off')\n",
" axis.imshow(inputs['train'][rnd_idx].reshape(48, 48), cmap='gray')\n",
" axis.set_title('{}: {}'.format(rnd_idx, classes[idx]))\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Training Multi-layer Neural Networks"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"1. 기본적인 일반화 (basic generalization): 코드에 주어진 hyperparameter 들을 이용하여 신경망을 학습시킨다. 학습 오차(training error)와 일반화를 위한 검증 오차(validation error) 결과가 어떻게 다른지 설명한다. 두 가지 경우(학습과 일반화 검증)에 대해 오차 커브(error curve)를 그래프로 제시하시오."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2. 최적화 (optimization): Learning rate, momentum, mini-batch size 세 가지 종류의 parameter 들을 아래와 같이 변화시키면서 다양한 조합들에 대해 신경망이 cross-entropy 관점에서 어떻게 수렴하는지 살펴본다. 가장 우수한 성능을 나타내는 hyperparameter 들의 조합이 어떤 것인지 제시하시오. (모든 경우의 수를 다 따지면 75 가지 신경망 모델을 테스트해야 하나 시간이 너무 많이 결릴 수 있으므로 이 중에서 일부분의 경우들만 테스트해도 된다. 그러나 어떤 근거로 해당 조합들만 테스트했는지 적당한 설명이 있어야 함.)\n",
" - Learning rate ( $\\epsilon$ ): 0.001 에서 1.0 사이의 5 가지 경우\n",
" - Momentum: 0.0 에서 0.9 사이의 3 가지 경우\n",
" - Mini-batch size: 1 에서 1000 까지의 5 가지 경우"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3. 신경망 모델 구조 변경: Momentum 을 0.9로 고정시킨 상태에서 신경망의 hidden unit 들의 갯수를 2 에서 100 사이의 3 가지 다른 경우에 대해 성능을 비교한다. 필요한 경우 learning rate 와 학습 기간(epochs)은 신경망 구조에 따라 적당하게 변경할 수 있다. Hidden unit 의 갯수들이 학습에서의 수렴과 신경망의 일반화 성는에 미치는 영향에 대한 데이터(표나 그래프)를 제시하고 경향을 분석하시오."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Method and Class Definitions for Neural Networks"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Utility methods"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def Save(fname: str, data):\n",
" \"\"\"Saves the model to a numpy file.\"\"\"\n",
" print('Writing to ' + fname)\n",
" np.savez_compressed(fname, **data)\n",
"\n",
"\n",
"def Load(fname: str):\n",
" \"\"\"Loads model from numpy file.\"\"\"\n",
" print('Loading from ' + fname)\n",
" return dict(np.load(fname))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Utility Classes"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"from dataclasses import dataclass, fields, asdict\n",
"from os import PathLike\n",
"from typing import List, Tuple, Dict, Any, Union, Optional, TextIO\n",
"import json\n",
"\n",
"\n",
"class BaseDataclass:\n",
" def to_dict(self):\n",
" return asdict(self)\n",
"\n",
" def to_json(self, fp: Union[str, PathLike, TextIO]):\n",
" json.dump(self.to_dict(), fp, indent=2)\n",
"\n",
" @classmethod\n",
" def from_dict(cls, d: Dict[str, Any]):\n",
" return cls(**d)\n",
"\n",
" @classmethod\n",
" def from_json_stream(cls, fp: TextIO):\n",
" return cls.from_dict(json.load(fp))\n",
" \n",
" @classmethod\n",
" def load_from_json(cls, fp_or_name: Union[str, PathLike, TextIO]):\n",
" if isinstance(fp_or_name, str) or isinstance(fp_or_name, PathLike):\n",
" with open(fp_or_name, 'r') as fp:\n",
" return cls.from_json_stream(fp)\n",
" else:\n",
" return cls.from_json_stream(fp_or_name)\n",
" \n",
" def save_json(self, fp_or_name: Union[str, PathLike, TextIO]):\n",
" if isinstance(fp_or_name, str) or isinstance(fp_or_name, PathLike):\n",
" with open(fp_or_name, 'w') as fp:\n",
" self.to_json(fp)\n",
" else:\n",
" self.to_json(fp_or_name)\n",
" \n",
" def keys(self):\n",
" return [f.name for f in fields(self)]\n",
" \n",
" def values(self):\n",
" return [getattr(self, f.name) for f in fields(self)]\n",
" \n",
" def items(self):\n",
" return [(f.name, getattr(self, f.name)) for f in fields(self)]\n",
" \n",
" def copy(self):\n",
" return self.from_dict(self.to_dict())\n",
"\n",
" def __getitem__(self, key):\n",
" return getattr(self, key)\n",
"\n",
" def __setitem__(self, key, value):\n",
" return setattr(self, key, value)\n",
"\n",
" def __iter__(self):\n",
" return iter(self.keys())\n",
"\n",
"@dataclass\n",
"class Config(BaseDataclass):\n",
" \"\"\"Configuration for the neural network.\"\"\"\n",
" num_inputs: int = 2304\n",
" num_hiddens: Tuple[int,int] = (16, 8)\n",
" num_outputs: int = 7\n",
" eps: float = 1e-3\n",
" momentum: float = 0.9\n",
" num_epochs: int = 100\n",
" batch_size: int = 128\n",
" early_stopping: bool = True\n",
" patience: int = 10\n",
"\n",
"@dataclass\n",
"class ModelWeights(BaseDataclass):\n",
" \"\"\"Model for the neural network.\"\"\"\n",
" W1: np.ndarray\n",
" b1: np.ndarray\n",
" W2: np.ndarray\n",
" b2: np.ndarray\n",
" W3: np.ndarray\n",
" b3: np.ndarray\n",
"\n",
" \n",
" def to_json(cls, fp: Union[str, PathLike, TextIO]):\n",
" raise NotImplementedError('Cannot save model weights to JSON.')\n",
" \n",
" def save_json(cls, fp_or_name: Union[str, PathLike, TextIO]):\n",
" raise NotImplementedError('Cannot save model weights to JSON.')\n",
"\n",
" @classmethod\n",
" def from_json_stream(cls, fp: TextIO):\n",
" raise NotImplementedError('Cannot load model weights from JSON.')\n",
"\n",
" @classmethod\n",
" def load_from_json(cls, fp_or_name: Union[str, PathLike, TextIO]):\n",
" raise NotImplementedError('Cannot load model weights from JSON.')\n",
" \n",
" def copy(self):\n",
" return ModelWeights(\n",
" W1=self.W1.copy(),\n",
" b1=self.b1.copy(),\n",
" W2=self.W2.copy(),\n",
" b2=self.b2.copy(),\n",
" W3=self.W3.copy(),\n",
" b3=self.b3.copy(),\n",
" )\n",
"\n",
" def save(self, fp: Union[str, PathLike, TextIO]):\n",
" \"\"\"Saves the model to a numpy file.\"\"\"\n",
" np.savez_compressed(fp, **asdict(self))\n",
"\n",
" @classmethod\n",
" def load(cls, fp: Union[str, PathLike, TextIO]):\n",
" \"\"\"Loads model from numpy file.\"\"\"\n",
" # Since the numpy version after 1.16.2, In response to CVE-2019-6446(https://nvd.nist.gov/vuln/detail/CVE-2019-6446),\n",
" # np.savez_compressed allow_pickle=False by default.\n",
" # In 1.16.2 and earlier, Arbitrary code execution can be performed by loading a maliciously crafted .npy file.\n",
" # So, I set allow_pickle=False to prevent this vulnerability.\n",
" data = dict(np.load(fp, allow_pickle=False))\n",
" \n",
" return cls(**data)\n",
"\n",
"@dataclass\n",
"class Statistic(BaseDataclass):\n",
" \"\"\"Statistics for the neural network.\"\"\"\n",
" train_ce: List[Tuple[int, float]]\n",
" valid_ce: List[Tuple[int, float]]\n",
" train_acc: List[Tuple[int, float]]\n",
" valid_acc: List[Tuple[int, float]]\n",
" test_ce: float\n",
" test_acc: float\n",
"\n",
" def keys(self):\n",
" return [f.name for f in fields(self)]\n",
" \n",
" def __getitem__(self, key):\n",
" return getattr(self, key)\n",
"\n",
" def best_valid_acc(self):\n",
" return max(self.valid_acc, key=lambda x: x[1])"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"if False:\n",
" import io\n",
" # Test the dataclass\n",
" # Config\n",
" config = Config(2304, (100, 50), 7, 0.01, 0.9, 100, 100)\n",
" fp = io.StringIO()\n",
" config.save_json(fp)\n",
" fp.seek(0)\n",
" config = Config.load_from_json(fp)\n",
" print(config)\n",
"\n",
" # ModelWeights\n",
" model = ModelWeights(np.random.randn(2304, 100), np.random.randn(100), np.random.randn(100, 50), np.random.randn(50), np.random.randn(50, 7), np.random.randn(7))\n",
" fp = io.BytesIO()\n",
" model.save(fp)\n",
" fp.seek(0)\n",
" model = ModelWeights.load(fp)\n",
" print(model.keys())\n",
" \n",
" # Statistic\n",
" stat = Statistic([(1, 0.1), (2, 0.2)], [(1, 0.3), (2, 0.4)], [(1, 0.5), (2, 0.6)], [(1, 0.7), (2, 0.8)], 0.9, 1.0)\n",
" fp = io.StringIO()\n",
" stat.save_json(fp)\n",
" fp.seek(0)\n",
" stat = Statistic.load_from_json(fp)\n",
" print(stat.keys())\n",
" print(stat.best_valid_acc())\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Neural Networks\n",
"아래는 neural networks 의 초기화 및 forward pass를 구현한 코드 입니다."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def Affine(x: np.ndarray, w: np.ndarray, b: np.ndarray) -> np.ndarray:\n",
" \"\"\"Computes the affine transformation.\n",
"\n",
" Args:\n",
" x: Inputs\n",
" w: Weights\n",
" b: Bias\n",
"\n",
" Returns:\n",
" y: Outputs\n",
" \"\"\"\n",
" # y = np.dot(w.T, x) + b\n",
" y = x.dot(w) + b\n",
" return y\n",
"\n",
"def ReLU(x: np.ndarray) -> np.ndarray:\n",
" \"\"\"Computes the ReLU activation function.\n",
"\n",
" Args:\n",
" x: Inputs\n",
"\n",
" Returns:\n",
" y: Activation\n",
" \"\"\"\n",
" return np.maximum(x, 0.0)\n",
"\n",
"def Softmax(x: np.ndarray) -> np.ndarray:\n",
" \"\"\"Computes the softmax activation function.\n",
"\n",
" Args:\n",
" x: Inputs\n",
"\n",
" Returns:\n",
" y: Activation\n",
" \"\"\"\n",
" x -= np.max(x, axis=1, keepdims=True)\n",
" return np.exp(x) / np.exp(x).sum(axis=1, keepdims=True)\n",
"\n",
"def InitMLP(num_inputs: int, num_hiddens: Tuple[int, int], num_outputs: int):\n",
" \"\"\"Initializes NN parameters.\n",
"\n",
" Args:\n",
" num_inputs: Number of input units.\n",
" num_hiddens: List of two elements, hidden size for each layer.\n",
" num_outputs: Number of output units.\n",
"\n",
" Returns:\n",
" model: Randomly initialized network weights.\n",
" \"\"\"\n",
" W1 = 0.1 * np.random.randn(num_inputs, num_hiddens[0])\n",
" W2 = 0.1 * np.random.randn(num_hiddens[0], num_hiddens[1])\n",
" W3 = 0.01 * np.random.randn(num_hiddens[1], num_outputs)\n",
" b1 = np.zeros((num_hiddens[0]))\n",
" b2 = np.zeros((num_hiddens[1]))\n",
" b3 = np.zeros((num_outputs))\n",
" model = ModelWeights(W1, b1, W2, b2, W3, b3)\n",
" return model\n",
"\n",
"def NNForward(model: ModelWeights, x: np.ndarray) -> Dict[str, np.ndarray]:\n",
" \"\"\"Runs the forward pass.\n",
"\n",
" Args:\n",
" model: Dictionary of all the weights.\n",
" x: Input to the network.\n",
"\n",
" Returns:\n",
" var: Dictionary of all intermediate variables.\n",
" \"\"\"\n",
" h1 = Affine(x, model.W1, model.b1)\n",
" h1r = ReLU(h1)\n",
" h2 = Affine(h1r, model.W2, model.b2)\n",
" h2r = ReLU(h2)\n",
" y = Affine(h2r, model.W3, model.b3)\n",
" var = {\n",
" 'x': x,\n",
" 'h1': h1,\n",
" 'h1r': h1r,\n",
" 'h2': h2,\n",
" 'h2r': h2r,\n",
" 'y': y\n",
" }\n",
" return var"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"아래는 neural networks 의 backward 구현하기 위한 코드들입니다.\n",
"아래 세 부분을 채워 코드를 완성시키기 바랍니다.\n",
"\n",
"1. Affine layer 의 backward pass equations (linear trainsformation + bias).\n",
"2. RELU activation function 의 backward pass equations.\n",
"3. Momentum 이 포함된 weight update equations."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def AffineBackward(grad_y: np.ndarray, x: np.ndarray, w: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:\n",
" \"\"\"Computes gradients of affine transformation.\n",
"\n",
" Args:\n",
" grad_y: gradient from last layer\n",
" x: inputs\n",
" w: weights\n",
"\n",
" Returns:\n",
" grad_x: Gradients wrt. the inputs.\n",
" grad_w: Gradients wrt. the weights.\n",
" grad_b: Gradients wrt. the biases.\n",
" \"\"\"\n",
" grad_x = grad_y.dot(w.T)\n",
" grad_w = x.T.dot(grad_y)\n",
" grad_b = np.sum(grad_y, axis=0)\n",
" return grad_x, grad_w, grad_b"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"def ReLUBackward(grad_y: np.ndarray, x: np.ndarray, y: np.ndarray) -> np.ndarray:\n",
" \"\"\"Computes gradients of the ReLU activation function.\n",
"\n",
" Returns:\n",
" grad_x: Gradients wrt. the inputs.\n",
" \"\"\"\n",
" grad_x = grad_y * (x > 0)\n",
" return grad_x"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"def NNBackward(model: ModelWeights, err: np.ndarray, var: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:\n",
" \"\"\"Runs the backward pass.\n",
"\n",
" Args:\n",
" model: Dictionary of all the weights.\n",
" err: Gradients to the output of the network.\n",
" var: Intermediate variables from the forward pass.\n",
" Returns:\n",
" grads: Gradients to all the weights.\n",
" \"\"\"\n",
" dE_dh2r, dE_dW3, dE_db3 = AffineBackward(err, var['h2r'], model['W3'])\n",
" dE_dh2 = ReLUBackward(dE_dh2r, var['h2'], var['h2r'])\n",
" dE_dh1r, dE_dW2, dE_db2 = AffineBackward(dE_dh2, var['h1r'], model['W2'])\n",
" dE_dh1 = ReLUBackward(dE_dh1r, var['h1'], var['h1r'])\n",
" _, dE_dW1, dE_db1 = AffineBackward(dE_dh1, var['x'], model['W1'])\n",
"\n",
" grads = {}\n",
" grads['W1'] = dE_dW1\n",
" grads['W2'] = dE_dW2\n",
" grads['W3'] = dE_dW3\n",
" grads['b1'] = dE_db1\n",
" grads['b2'] = dE_db2\n",
" grads['b3'] = dE_db3\n",
" return grads"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"def InitMomentumState(model: ModelWeights) -> Dict[str, np.ndarray]:\n",
" \"\"\"Initializes momentums for all the weights.\n",
"\n",
" Args:\n",
" model: Dictionary of all the weights.\n",
"\n",
" Returns:\n",
" momentums: Dictionary of all the momentums.\n",
" \"\"\"\n",
" momentums = {}\n",
" for key in model.keys():\n",
" momentums[key] = np.zeros_like(model[key])\n",
" return momentums\n",
"\n",
"def NNUpdate(model: ModelWeights, eps: float, momentum: float, optimizer_state: Dict[str, np.ndarray], grads: Dict[str, np.ndarray]):\n",
" \"\"\"Update NN weights.\n",
"\n",
" Args:\n",
" model: Dictionary of all the weights.\n",
" eps: Learning rate.\n",
" momentum: Momentum.\n",
" optimizer_state: State of the optimizer.\n",
" tape: Gradients to all the weights.\n",
" \"\"\"\n",
" for key in model:\n",
" # Momentum update\n",
" # optimizer state is the velocity\n",
" optimizer_state[key] = momentum * optimizer_state[key] - eps * grads[key]\n",
" model[key] += optimizer_state[key]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 훈련"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"def Train(model, forward, backward, update, eps, momentum, num_epochs,\n",
" batch_size):\n",
" \"\"\"Trains a simple MLP.\n",
"\n",
" Args:\n",
" model: Dictionary of model weights.\n",
" forward: Forward prop function.\n",
" backward: Backward prop function.\n",
" update: Update weights function.\n",
" eps: Learning rate.\n",
" momentum: Momentum.\n",
" num_epochs: Number of epochs to run training for.\n",
" batch_size: Mini-batch size, -1 for full batch.\n",
"\n",
" Returns:\n",
" stats: Dictionary of training statistics.\n",
" - train_ce: Training cross entropy.\n",
" - valid_ce: Validation cross entropy.\n",
" - train_acc: Training accuracy.\n",
" - valid_acc: Validation accuracy.\n",
" \"\"\"\n",
" inputs_train, inputs_valid, inputs_test, target_train, target_valid, \\\n",
" target_test = LoadData('./toronto_face.npz')\n",
" rnd_idx = np.arange(inputs_train.shape[0])\n",
" train_ce_list = []\n",
" valid_ce_list = []\n",
" train_acc_list = []\n",
" valid_acc_list = []\n",
" \n",
" num_train_cases = inputs_train.shape[0]\n",
" if batch_size == -1:\n",
" batch_size = num_train_cases\n",
" num_steps = int(np.ceil(num_train_cases / batch_size))\n",
"\n",
" pp = ProgressPlot(\n",
" plot_names=['Cross entropy', 'Accuracy'],\n",
" line_names=['Train', 'Validation'],\n",
" x_label='Iteration',\n",
" x_lim=[0, num_epochs*num_steps]\n",
" )\n",
" optimizer_state = InitMomentumState(model)\n",
"\n",
" valid_ce = 0\n",
" valid_acc = 0\n",
" for epoch in range(num_epochs):\n",
" np.random.shuffle(rnd_idx)\n",
" inputs_train = inputs_train[rnd_idx]\n",
" target_train = target_train[rnd_idx]\n",
" for step in range(num_steps):\n",
" # Forward prop.\n",
" start = step * batch_size\n",
" end = min(num_train_cases, (step + 1) * batch_size)\n",
" x = inputs_train[start: end]\n",
" t = target_train[start: end]\n",
"\n",
" var = forward(model, x)\n",
" prediction = Softmax(var['y'])\n",
"\n",
" train_ce = -np.sum(t * np.log(prediction)) / x.shape[0]\n",
" train_acc = (np.argmax(prediction, axis=1) ==\n",
" np.argmax(t, axis=1)).astype('float').mean()\n",
" pp.update([[train_ce, valid_ce], [train_acc, valid_acc]])\n",
"\n",
" # Compute error.\n",
" error = (prediction - t) / x.shape[0]\n",
"\n",
" # Backward prop.\n",
" grads = backward(model, error, var)\n",
"\n",
" # Update weights.\n",
" update(model, eps, momentum, optimizer_state, grads)\n",
"\n",
" valid_ce, valid_acc = Evaluate(\n",
" inputs_valid, target_valid, model, forward, batch_size=batch_size)\n",
" \n",
" pp.update([[train_ce, valid_ce], [train_acc, valid_acc]])\n",
" train_ce_list.append((epoch, train_ce))\n",
" train_acc_list.append((epoch, train_acc))\n",
" valid_ce_list.append((epoch, valid_ce))\n",
" valid_acc_list.append((epoch, valid_acc))\n",
"\n",
" # print()\n",
" train_ce, train_acc = Evaluate(\n",
" inputs_train, target_train, model, forward, batch_size=batch_size)\n",
" valid_ce, valid_acc = Evaluate(\n",
" inputs_valid, target_valid, model, forward, batch_size=batch_size)\n",
" test_ce, test_acc = Evaluate(\n",
" inputs_test, target_test, model, forward, batch_size=batch_size)\n",
" print('CE: Train %.5f Validation %.5f Test %.5f' %\n",
" (train_ce, valid_ce, test_ce))\n",
" print('Acc: Train {:.5f} Validation {:.5f} Test {:.5f}'.format(\n",
" train_acc, valid_acc, test_acc))\n",
" pp.finalize()\n",
" stats = {\n",
" 'train_ce': train_ce_list,\n",
" 'valid_ce': valid_ce_list,\n",
" 'train_acc': train_acc_list,\n",
" 'valid_acc': valid_acc_list\n",
" }\n",
"\n",
" return model, stats\n",
"\n",
"def Evaluate(inputs, target, model, forward, batch_size=-1):\n",
" \"\"\"Evaluates the model on inputs and target.\n",
"\n",
" Args:\n",
" inputs: Inputs to the network.\n",
" target: Target of the inputs.\n",
" model: Dictionary of network weights.\n",
" \"\"\"\n",
" num_cases = inputs.shape[0]\n",
" if batch_size == -1:\n",
" batch_size = num_cases\n",
" num_steps = int(np.ceil(num_cases / batch_size))\n",
" ce = 0.0\n",
" acc = 0.0\n",
" for step in range(num_steps):\n",
" start = step * batch_size\n",
" end = min(num_cases, (step + 1) * batch_size)\n",
" x = inputs[start: end]\n",
" t = target[start: end]\n",
" prediction = Softmax(forward(model, x)['y'])\n",
" ce += -np.sum(t * np.log(prediction))\n",
" acc += (np.argmax(prediction, axis=1) == np.argmax(\n",
" t, axis=1)).astype('float').sum()\n",
" ce /= num_cases\n",
" acc /= num_cases\n",
" return ce, acc"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"def CheckGrad(model, forward, backward, name, x):\n",
" \"\"\"Check the gradients\n",
"\n",
" Args:\n",
" model: Dictionary of network weights.\n",
" name: Weights name to check.\n",
" x: Fake input.\n",
" \"\"\"\n",
" np.random.seed(0)\n",
" var = forward(model, x)\n",
" loss = lambda y: 0.5 * (y ** 2).sum()\n",
" grad_y = var['y']\n",
" grads = backward(model, grad_y, var)\n",
" grad_w = grads[name].ravel()\n",
" w_ = model[name].ravel()\n",
" eps = 1e-7\n",
" grad_w_2 = np.zeros(w_.shape)\n",
" check_elem = np.arange(w_.size)\n",
" np.random.shuffle(check_elem)\n",
" # Randomly check 20 elements.\n",
" check_elem = check_elem[:20]\n",
" for ii in check_elem:\n",
" w_[ii] += eps\n",
" err_plus = loss(forward(model, x)['y'])\n",
" w_[ii] -= 2 * eps\n",
" err_minus = loss(forward(model, x)['y'])\n",
" w_[ii] += eps\n",
" grad_w_2[ii] = (err_plus - err_minus) / 2 / eps\n",
" np.testing.assert_almost_equal(grad_w[check_elem], grad_w_2[check_elem],\n",
" decimal=3)\n",
"\n",
"\n",
"def main():\n",
" \"\"\"Trains a NN.\"\"\"\n",
" model_fname = 'nn_model.npz'\n",
" stats_fname = 'nn_stats.npz'\n",
"\n",
" # Hyper-parameters. Modify them if needed.\n",
" num_hiddens = [16, 32]\n",
" eps = 0.01\n",
" momentum = 0.0\n",
" num_epochs = 1000\n",
" batch_size = 100\n",
"\n",
" # Input-output dimensions.\n",
" num_inputs = 2304\n",
" num_outputs = 7\n",
"\n",
" # Initialize model.\n",
" model = InitMLP(num_inputs, num_hiddens, num_outputs)\n",
"\n",
" # Uncomment to reload trained model here.\n",
" # model = Load(model_fname)\n",
"\n",
" # Check gradient implementation.\n",
" print('Checking gradients...')\n",
" x = np.random.rand(10, 48 * 48) * 0.1\n",
" CheckGrad(model, NNForward, NNBackward, 'W3', x)\n",
" CheckGrad(model, NNForward, NNBackward, 'b3', x)\n",
" CheckGrad(model, NNForward, NNBackward, 'W2', x)\n",
" CheckGrad(model, NNForward, NNBackward, 'b2', x)\n",
" CheckGrad(model, NNForward, NNBackward, 'W1', x)\n",
" CheckGrad(model, NNForward, NNBackward, 'b1', x)\n",
" print('Done.')\n",
" # Train model.\n",
" print('training...')\n",
" trained_model, stats = Train(model, NNForward, NNBackward, NNUpdate, eps,\n",
" momentum, num_epochs, batch_size)\n",
"\n",
" plt.figure(0)\n",
" plt.plot(np.array(stats['train_ce'])[:, 0], np.array(stats['train_ce'])[:, 1], 'b', label='Train')\n",
" plt.plot(np.array(stats['valid_ce'])[:, 0], np.array(stats['valid_ce'])[:, 1], 'orange', label='Validation')\n",
" plt.xlabel('Epoch')\n",
" plt.ylabel('Cross Entropy')\n",
" plt.legend()\n",
"\n",
" plt.figure(1)\n",
" plt.plot(np.array(stats['train_acc'])[:, 0], np.array(stats['train_acc'])[:, 1], 'b', label='Train')\n",
" plt.plot(np.array(stats['valid_acc'])[:, 0], np.array(stats['valid_acc'])[:, 1], 'orange', label='Validation')\n",
" plt.xlabel('Epoch')\n",
" plt.ylabel('Accuracy')\n",
" plt.legend()\n",
" plt.show()\n",
" # Uncomment if you wish to save the model.\n",
" Save(model_fname, model)\n",
"\n",
" # Uncomment if you wish to save the training statistics.\n",
" Save(stats_fname, stats)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Early Stopping 이 적용된 훈련"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"사양이 좋지 않은 컴퓨터에서 `ProgressPlot` 이 항목 수가 많아지면서(약 10000부터) 더 이상 그래프가 제대로 그리지 못하고 느려지는 있습니다. 이 문제는 `ProgressPlot`이 그래프를 그리는 것이 O(N)의 복잡도를 가져서 그렇습니다. 이를 해결하기 위해 `ProgressPlot`에서 `Tqdm` 으로 변경하였습니다. "
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"from tqdm import tqdm\n",
"\n",
"def TrainAdvanced(model: ModelWeights, \n",
" forward=NNForward,\n",
" backward=NNBackward,\n",
" update=NNUpdate,\n",
" eps = 0.01,\n",
" momentum = 0.0,\n",
" num_epochs = 1000,\n",
" batch_size = 100,\n",
" early_stopping: bool = True,\n",
" patience: int = 10,\n",
" verbose: bool = True,\n",
" tqdm_leave: bool = True,\n",
" pplot: bool = False,\n",
" ) -> Tuple[ModelWeights, Statistic]:\n",
" \"\"\"Trains a simple MLP.\n",
"\n",
" Args:\n",
" model: Dictionary of model weights.\n",
" forward: Forward prop function.\n",
" backward: Backward prop function.\n",
" update: Update weights function.\n",
" eps: Learning rate.\n",
" momentum: Momentum.\n",
" num_epochs: Number of epochs to run training for.\n",
" batch_size: Mini-batch size, -1 for full batch.\n",
" early_stopping: Whether to use early stopping.\n",
" patience: Number of epochs to wait before early stopping.\n",
" verbose: Whether to print training statistics.\n",
" tqdm_leave: Whether to leave tqdm progress bar.\n",
" pplot: Whether to plot training statistics.\n",
"\n",
" Returns:\n",
" model: Trained model.\n",
" stats: Dictionary of training statistics.\n",
" - train_ce: Training cross entropy.\n",
" - valid_ce: Validation cross entropy.\n",
" - train_acc: Training accuracy.\n",
" - valid_acc: Validation accuracy.\n",
" \"\"\"\n",
" # load data\n",
" inputs_train, inputs_valid, inputs_test, target_train, target_valid, \\\n",
" target_test = LoadData('./toronto_face.npz')\n",
" \n",
" rnd_idx = np.arange(inputs_train.shape[0])\n",
" train_ce_list = []\n",
" valid_ce_list = []\n",
" train_acc_list = []\n",
" valid_acc_list = []\n",
" \n",
" num_train_cases = inputs_train.shape[0]\n",
" if batch_size == -1 or batch_size > num_train_cases or batch_size == 0:\n",
" batch_size = num_train_cases\n",
" num_steps = int(np.ceil(num_train_cases / batch_size))\n",
"\n",
" try:\n",
" if pplot:\n",
" # initialize plot\n",
" pp = ProgressPlot(\n",
" plot_names=['Cross entropy', 'Accuracy'],\n",
" line_names=['Train', 'Validation'],\n",
" x_label='Iteration',\n",
" x_lim=[0, num_epochs]\n",
" )\n",
" pbar = range(num_epochs)\n",
" else:\n",
" # Tqdm progress bar.\n",
" pbar = tqdm(range(num_epochs), disable=not verbose or num_epochs == 1, leave=tqdm_leave)\n",
"\n",
" # Initialize optimizer state\n",
" optimizer_state = InitMomentumState(model)\n",
"\n",
" # Initialize stats.\n",
" valid_ce = 0\n",
" valid_acc = 0\n",
"\n",
" # Early stopping\n",
" best_valid_ce = np.inf\n",
" best_valid_acc = 0\n",
" best_epoch = 0\n",
" best_model = None\n",
"\n",
" epsilon = np.finfo(float).eps\n",
"\n",
" for epoch in pbar:\n",
" np.random.shuffle(rnd_idx)\n",
" inputs_train = inputs_train[rnd_idx]\n",
" target_train = target_train[rnd_idx]\n",
"\n",
" train_ce = 0\n",
" train_acc = 0\n",
" for step in range(num_steps):\n",
" # Get mini-batch.\n",
" start = step * batch_size\n",
" # min is used to handle the case when batch_size does not divide num_train_cases\n",
" end = min(num_train_cases, (step + 1) * batch_size)\n",
"\n",
" input_batch = inputs_train[start: end]\n",
" target_batch = target_train[start: end]\n",
"\n",
" # Forward prop.\n",
" var = forward(model, input_batch)\n",
" prediction = Softmax(var['y'])\n",
"\n",
" # Compute loss.\n",
" train_ce += -np.sum(target_batch * np.log(prediction + epsilon)) / input_batch.shape[0]\n",
" train_acc += (np.argmax(prediction, axis=1) ==\n",
" np.argmax(target_batch, axis=1)).astype('float').sum()\n",
"\n",
" # Compute error.\n",
" error = (prediction - target_batch) / input_batch.shape[0]\n",
"\n",
" # Backward prop.\n",
" grads = backward(model, error, var)\n",
"\n",
" # Update weights.\n",
" update(model, eps, momentum, optimizer_state, grads)\n",
"\n",
" train_ce /= num_steps\n",
" train_acc /= num_train_cases\n",
"\n",
" # Compute validation error.\n",
" valid_ce, valid_acc = Evaluate(\n",
" inputs_valid, target_valid, model, forward, batch_size=batch_size)\n",
"\n",
" train_ce_list.append((epoch, train_ce))\n",
" train_acc_list.append((epoch, train_acc))\n",
" valid_ce_list.append((epoch, valid_ce))\n",
" valid_acc_list.append((epoch, valid_acc))\n",
"\n",
" if pplot:\n",
" # Update plot.\n",
" pp.update([[train_ce, valid_ce], [train_acc, valid_acc]])\n",
" else:\n",
" # Tqdm progress bar.\n",
" pbar.set_description(f\"Train CE: {train_ce:.4f}, Valid CE: {valid_ce:.4f}, Train Acc: {train_acc:.4f}, Valid Acc: {valid_acc:.4f}\")\n",
"\n",
" # Early stopping.\n",
" if valid_ce < best_valid_ce:\n",
" best_valid_ce = valid_ce\n",
" best_valid_acc = valid_acc\n",
" best_epoch = epoch\n",
" best_model = model.copy()\n",
" elif early_stopping and epoch - best_epoch >= patience:\n",
" model = best_model\n",
" break\n",
" \n",
" test_ce, test_acc = Evaluate(\n",
" inputs_test, target_test, model, forward, batch_size=batch_size)\n",
"\n",
" stats = Statistic(train_ce_list, valid_ce_list, train_acc_list, valid_acc_list,\n",
" test_ce=test_ce, test_acc=test_acc)\n",
" finally:\n",
" if not pplot:\n",
" pbar.close()\n",
" else:\n",
" pp.finalize()\n",
" \n",
" return model, stats"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"def TrainMLP(conf: Config, pplot: bool = False) -> Tuple[ModelWeights, Statistic]:\n",
" \"\"\"Trains a simple MLP.\n",
"\n",
" Args:\n",
" conf: Configuration.\n",
" pplot: Whether to plot training statistics.\n",
"\n",
" Returns:\n",
" model: Trained model.\n",
" stats: Dictionary of training statistics.\n",
" - train_ce: Training cross entropy list.\n",
" - valid_ce: Validation cross entropy list.\n",
" - train_acc: Training accuracy list.\n",
" - valid_acc: Validation accuracy list.\n",
" - test_ce: Test cross entropy.\n",
" - test_acc: Test accuracy.\n",
" \"\"\"\n",
" # Initialize model.\n",
" model = InitMLP(\n",
" conf.num_inputs, conf.num_hiddens, conf.num_outputs)\n",
"\n",
" # Train model.\n",
" model, stats = TrainAdvanced(\n",
" model,\n",
" eps=conf.eps,\n",
" momentum=conf.momentum,\n",
" num_epochs=conf.num_epochs,\n",
" batch_size=conf.batch_size,\n",
" early_stopping=conf.early_stopping,\n",
" patience=conf.patience,\n",
" verbose=True,\n",
" tqdm_leave=False,\n",
" pplot=pplot)\n",
"\n",
" return model, stats"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def PlotStats(stats: Statistic, title: str = '', save_path: Optional[str] = None, show: bool = True):\n",
" \"\"\"Plots training statistics.\n",
"\n",
" Args:\n",
" stats: Dictionary of training statistics.\n",
" - train_ce: Training cross entropy list.\n",
" - valid_ce: Validation cross entropy list.\n",
" - train_acc: Training accuracy list.\n",
" - valid_acc: Validation accuracy list.\n",
" - test_ce: Test cross entropy.\n",
" - test_acc: Test accuracy.\n",
" title: Plot title.\n",
" \"\"\"\n",
" fig, ax = plt.subplots(1, 2, figsize=(12, 4))\n",
" fig.suptitle(title)\n",
" ax[0].set_title('Cross Entropy')\n",
" ax[0].set_xlabel('Epoch')\n",
" ax[0].set_ylabel('Cross Entropy')\n",
" ax[0].plot(*zip(*stats.train_ce), label='Train')\n",
" ax[0].plot(*zip(*stats.valid_ce), label='Valid')\n",
" ax[0].legend()\n",
" ax[1].set_title('Accuracy')\n",
" ax[1].set_xlabel('Epoch')\n",
" ax[1].set_ylabel('Accuracy')\n",
" ax[1].plot(*zip(*stats.train_acc), label='Train')\n",
" ax[1].plot(*zip(*stats.valid_acc), label='Valid')\n",
" ax[1].legend()\n",
" if save_path is not None:\n",
" plt.savefig(save_path)\n",
" if show:\n",
" plt.show()\n",
" # close the plot\n",
" plt.close()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"# Test PlotStats.\n",
"if False:\n",
" mock_stats = Statistic(\n",
" train_ce=[(0, 0.5), (1, 0.4), (2, 0.3)],\n",
" valid_ce=[(0, 0.6), (1, 0.5), (2, 0.4)],\n",
" train_acc=[(0, 0.7), (1, 0.8), (2, 0.9)],\n",
" valid_acc=[(0, 0.6), (1, 0.5), (2, 0.4)],\n",
" test_ce=0.3,\n",
" test_acc=0.9,\n",
" )\n",
" PlotStats(mock_stats, title='MLP')"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"def ExperimentMLP(conf: Config, title: Optional[str] = None, save_dir: Union[str, PathLike] = 'results', show: bool = True, pplot: bool = False):\n",
" \"\"\"Runs a simple MLP experiment.\n",
"\n",
" Args:\n",
" conf: Configuration.\n",
" save_dir: Directory to save results.\n",
" show: Whether to show plots.\n",
" \"\"\"\n",
" if title is None:\n",
" title = f'MLP [{\",\".join([str(s) for s in conf.num_hiddens])}] lr:{conf.eps} m:{conf.momentum} b:{conf.batch_size}'\n",
" # Create save directory.\n",
" os.makedirs(save_dir, exist_ok=True)\n",
"\n",
" # Train model.\n",
" model, stats = TrainMLP(conf, pplot=pplot)\n",
" conf.save_json(os.path.join(save_dir, 'conf.json'))\n",
" model.save(os.path.join(save_dir, 'model.npz'))\n",
" # Plot training statistics.\n",
" PlotStats(stats, title='MLP', save_path=os.path.join(save_dir, 'stats.png'), show=show)\n",
" stats.save_json(os.path.join(save_dir, 'stats.json'))\n",
"\n",
" return model, stats"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"if False:\n",
" import traceback\n",
" import time\n",
" try:\n",
" begin = time.time()\n",
" # Test ExperimentMLP.\n",
" ExperimentMLP(Config(), save_dir='test_mlp', show=True)\n",
" end = time.time()\n",
" print(f\"Time: {end - begin:.2f} seconds\")\n",
" except TypeError as e:\n",
" print('TypeError: ', e)\n",
" traceback.print_exc()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 테스트"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"def load_experiment(path: Union[str, PathLike], load_model = False) -> Tuple[Config ,Statistic, Optional[ModelWeights]]:\n",
" \"\"\"Loads experiment result\n",
"\n",
" Args:\n",
" path: Path to experiment directory.\n",
" load_model: Whether to load model.\n",
"\n",
" Returns:\n",
" conf: Configuration.\n",
" stats: Dictionary of training statistics.\n",
" - train_ce: Training cross entropy list.\n",
" - valid_ce: Validation cross entropy list.\n",
" - train_acc: Training accuracy list.\n",
" - valid_acc: Validation accuracy list.\n",
" - test_ce: Test cross entropy.\n",
" - test_acc: Test accuracy.\n",
" model: Trained model.\n",
" \"\"\"\n",
" stat = Statistic.load_from_json(os.path.join(path, 'stats.json'))\n",
" conf = Config.load_from_json(os.path.join(path, 'conf.json'))\n",
" model = None\n",
" if load_model:\n",
" model = ModelWeights.load(os.path.join(path, 'model.npz'))\n",
" return conf, stat, model"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
"if False:\n",
" if not os.path.exists('test_mlp'):\n",
" ExperimentMLP(Config(), save_dir='test_mlp', show=True)\n",
" conf, stats, model = load_experiment('test_mlp', load_model=True)\n",
" print(conf)\n",
" print(stats)\n",
" print(model)\n"
]
},
{
"cell_type": "code",
"execution_count": 95,
"metadata": {},
"outputs": [],
"source": [
"def load_experiment_metafile(path: PathLike, init_task_if_not_exists: Optional[List[Any]] = None) -> Dict[str, List[Any]]:\n",
" \"\"\"Load meta data of all experiments\n",
" \n",
" Args:\n",
" path: Path to meta file.\n",
" init_task_if_not_exists: Initialize meta file if not exists.\n",
"\n",
" Returns:\n",
" meta: Dictionary of meta data.\n",
" \"\"\"\n",
" # load previous experiments if any exist\n",
" try:\n",
" with open(path, 'r') as f:\n",
" experiments = json.load(f)\n",
" except FileNotFoundError:\n",
" experiments = {\n",
" \"remain_experiments\": [],\n",
" \"completed_experiment_results\": [] # list of completed experiment\n",
" }\n",
" if init_task_if_not_exists is not None:\n",
" experiments[\"remain_experiments\"] = init_task_if_not_exists.copy() # list of remaining experiment\n",
" return experiments\n",
"\n",
"def save_experiment_metafile(path: PathLike, experiments: Dict[str, Any]):\n",
" \"\"\"Save meta data of all experiments\n",
" \n",
" Args:\n",
" path: Path to meta file.\n",
" experiments: Dictionary of meta data. \n",
" \"\"\"\n",
" with open(path, 'w') as f:\n",
" json.dump(experiments, f, indent=4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 문제"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1. 기본적인 일반화 (basic generalization): 코드에 주어진 hyperparameter 들을 이용하여 신경망을 학습시킨다. 학습 오차(training error)와 일반화를 위한 검증 오차(validation error) 결과가 어떻게 다른지 설명한다. 두 가지 경우(학습과 일반화 검증)에 대해 오차 커브(error curve)를 그래프로 제시하시오."
]
},
{
"cell_type": "code",
"execution_count": 121,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Checking gradients...\n",
"Done.\n",
"training...\n"
]
},
{
"data": {
"text/html": [
"<style>svg.learning-curve {\n",
" width: 100%;\n",
" shape-rendering: crispEdges;\n",
" font-size: 14px;\n",
"}\n",
"\n",
"svg.learning-curve .tick text {\n",
" font-size: 14px;\n",
" fill: #505050;\n",
"}\n",
"\n",
"svg.learning-curve .tick line {\n",
" stroke-width: 2;\n",
" stroke: #505050;\n",
"}\n",
"\n",
"svg.learning-curve .background {\n",
" fill: #EBEBEB;\n",
"}\n",
"\n",
"svg.learning-curve .facet {\n",
" font-size: 14px;\n",
"}\n",
"\n",
"svg.learning-curve .facet-background {\n",
" fill: #D9D9D9;\n",
"}\n",
"\n",
"svg.learning-curve path.line {\n",
" fill: none;\n",
" shape-rendering: geometricPrecision;\n",
" stroke-width: 1;\n",
"}\n",
"\n",
"svg.learning-curve .grid line {\n",
" stroke: #fff;\n",
" stroke-width: 2;\n",
"}\n",
"\n",
"svg.learning-curve .grid .minor {\n",
" stroke-opacity: .5;\n",
"}\n",
"\n",
"svg.learning-curve .grid text {\n",
" display: none;\n",
"}\n",
"\n",
"svg.learning-curve .axis path,\n",
"svg.learning-curve .grid path {\n",
" display: none;\n",
"}\n",
"\n",
"svg.learning-curve .axis.hide-axis {\n",
" display: none;\n",
"}\n",
"\n",
"svg.learning-curve .legned rect {\n",
" fill: #EBEBEB;\n",
"}\n",
"\n",
"svg.learning-curve .legned line {\n",
" stroke-width: 2;\n",
"}\n",
"</style><script>(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n",
"window.d3 = Object.assign(\n",
" {},\n",
" // d3.select\n",
" // d3.selectAll\n",
" require('d3-selection'),\n",
" // .transition()\n",
" require('d3-transition'),\n",
" // d3.extent\n",
" require('d3-array'),\n",
" // d3.axisBottom\n",
" // d3.axisLeft\n",
" require('d3-axis'),\n",
" // d3.scaleLinear\n",
" // d3.scaleTime\n",
" require('d3-scale'),\n",
" // d3.line\n",
" require('d3-shape')\n",
");\n",
"\n",
"},{\"d3-array\":2,\"d3-axis\":3,\"d3-scale\":10,\"d3-selection\":11,\"d3-shape\":12,\"d3-transition\":16}],2:[function(require,module,exports){\n",
"// https://d3js.org/d3-array/ v2.4.0 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"function ascending(a, b) {\n",
" return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n",
"}\n",
"\n",
"function bisector(compare) {\n",
" if (compare.length === 1) compare = ascendingComparator(compare);\n",
" return {\n",
" left: function(a, x, lo, hi) {\n",
" if (lo == null) lo = 0;\n",
" if (hi == null) hi = a.length;\n",
" while (lo < hi) {\n",
" var mid = lo + hi >>> 1;\n",
" if (compare(a[mid], x) < 0) lo = mid + 1;\n",
" else hi = mid;\n",
" }\n",
" return lo;\n",
" },\n",
" right: function(a, x, lo, hi) {\n",
" if (lo == null) lo = 0;\n",
" if (hi == null) hi = a.length;\n",
" while (lo < hi) {\n",
" var mid = lo + hi >>> 1;\n",
" if (compare(a[mid], x) > 0) hi = mid;\n",
" else lo = mid + 1;\n",
" }\n",
" return lo;\n",
" }\n",
" };\n",
"}\n",
"\n",
"function ascendingComparator(f) {\n",
" return function(d, x) {\n",
" return ascending(f(d), x);\n",
" };\n",
"}\n",
"\n",
"var ascendingBisect = bisector(ascending);\n",
"var bisectRight = ascendingBisect.right;\n",
"var bisectLeft = ascendingBisect.left;\n",
"\n",
"function count(values, valueof) {\n",
" let count = 0;\n",
" if (valueof === undefined) {\n",
" for (let value of values) {\n",
" if (value != null && (value = +value) >= value) {\n",
" ++count;\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {\n",
" ++count;\n",
" }\n",
" }\n",
" }\n",
" return count;\n",
"}\n",
"\n",
"function length(array) {\n",
" return array.length | 0;\n",
"}\n",
"\n",
"function empty(length) {\n",
" return !(length > 0);\n",
"}\n",
"\n",
"function arrayify(values) {\n",
" return typeof values !== \"object\" || \"length\" in values ? values : Array.from(values);\n",
"}\n",
"\n",
"function reducer(reduce) {\n",
" return values => reduce(...values);\n",
"}\n",
"\n",
"function cross(...values) {\n",
" const reduce = typeof values[values.length - 1] === \"function\" && reducer(values.pop());\n",
" values = values.map(arrayify);\n",
" const lengths = values.map(length);\n",
" const j = values.length - 1;\n",
" const index = new Array(j + 1).fill(0);\n",
" const product = [];\n",
" if (j < 0 || lengths.some(empty)) return product;\n",
" while (true) {\n",
" product.push(index.map((j, i) => values[i][j]));\n",
" let i = j;\n",
" while (++index[i] === lengths[i]) {\n",
" if (i === 0) return reduce ? product.map(reduce) : product;\n",
" index[i--] = 0;\n",
" }\n",
" }\n",
"}\n",
"\n",
"function cumsum(values, valueof) {\n",
" var sum = 0, index = 0;\n",
" return Float64Array.from(values, valueof === undefined\n",
" ? v => (sum += +v || 0)\n",
" : v => (sum += +valueof(v, index++, values) || 0));\n",
"}\n",
"\n",
"function descending(a, b) {\n",
" return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n",
"}\n",
"\n",
"function variance(values, valueof) {\n",
" let count = 0;\n",
" let delta;\n",
" let mean = 0;\n",
" let sum = 0;\n",
" if (valueof === undefined) {\n",
" for (let value of values) {\n",
" if (value != null && (value = +value) >= value) {\n",
" delta = value - mean;\n",
" mean += delta / ++count;\n",
" sum += delta * (value - mean);\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {\n",
" delta = value - mean;\n",
" mean += delta / ++count;\n",
" sum += delta * (value - mean);\n",
" }\n",
" }\n",
" }\n",
" if (count > 1) return sum / (count - 1);\n",
"}\n",
"\n",
"function deviation(values, valueof) {\n",
" const v = variance(values, valueof);\n",
" return v ? Math.sqrt(v) : v;\n",
"}\n",
"\n",
"function extent(values, valueof) {\n",
" let min;\n",
" let max;\n",
" if (valueof === undefined) {\n",
" for (const value of values) {\n",
" if (value != null) {\n",
" if (min === undefined) {\n",
" if (value >= value) min = max = value;\n",
" } else {\n",
" if (min > value) min = value;\n",
" if (max < value) max = value;\n",
" }\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null) {\n",
" if (min === undefined) {\n",
" if (value >= value) min = max = value;\n",
" } else {\n",
" if (min > value) min = value;\n",
" if (max < value) max = value;\n",
" }\n",
" }\n",
" }\n",
" }\n",
" return [min, max];\n",
"}\n",
"\n",
"function identity(x) {\n",
" return x;\n",
"}\n",
"\n",
"function group(values, ...keys) {\n",
" return nest(values, identity, identity, keys);\n",
"}\n",
"\n",
"function groups(values, ...keys) {\n",
" return nest(values, Array.from, identity, keys);\n",
"}\n",
"\n",
"function rollup(values, reduce, ...keys) {\n",
" return nest(values, identity, reduce, keys);\n",
"}\n",
"\n",
"function rollups(values, reduce, ...keys) {\n",
" return nest(values, Array.from, reduce, keys);\n",
"}\n",
"\n",
"function nest(values, map, reduce, keys) {\n",
" return (function regroup(values, i) {\n",
" if (i >= keys.length) return reduce(values);\n",
" const groups = new Map();\n",
" const keyof = keys[i++];\n",
" let index = -1;\n",
" for (const value of values) {\n",
" const key = keyof(value, ++index, values);\n",
" const group = groups.get(key);\n",
" if (group) group.push(value);\n",
" else groups.set(key, [value]);\n",
" }\n",
" for (const [key, values] of groups) {\n",
" groups.set(key, regroup(values, i));\n",
" }\n",
" return map(groups);\n",
" })(values, 0);\n",
"}\n",
"\n",
"var array = Array.prototype;\n",
"\n",
"var slice = array.slice;\n",
"\n",
"function constant(x) {\n",
" return function() {\n",
" return x;\n",
" };\n",
"}\n",
"\n",
"function range(start, stop, step) {\n",
" start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;\n",
"\n",
" var i = -1,\n",
" n = Math.max(0, Math.ceil((stop - start) / step)) | 0,\n",
" range = new Array(n);\n",
"\n",
" while (++i < n) {\n",
" range[i] = start + i * step;\n",
" }\n",
"\n",
" return range;\n",
"}\n",
"\n",
"var e10 = Math.sqrt(50),\n",
" e5 = Math.sqrt(10),\n",
" e2 = Math.sqrt(2);\n",
"\n",
"function ticks(start, stop, count) {\n",
" var reverse,\n",
" i = -1,\n",
" n,\n",
" ticks,\n",
" step;\n",
"\n",
" stop = +stop, start = +start, count = +count;\n",
" if (start === stop && count > 0) return [start];\n",
" if (reverse = stop < start) n = start, start = stop, stop = n;\n",
" if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];\n",
"\n",
" if (step > 0) {\n",
" start = Math.ceil(start / step);\n",
" stop = Math.floor(stop / step);\n",
" ticks = new Array(n = Math.ceil(stop - start + 1));\n",
" while (++i < n) ticks[i] = (start + i) * step;\n",
" } else {\n",
" start = Math.floor(start * step);\n",
" stop = Math.ceil(stop * step);\n",
" ticks = new Array(n = Math.ceil(start - stop + 1));\n",
" while (++i < n) ticks[i] = (start - i) / step;\n",
" }\n",
"\n",
" if (reverse) ticks.reverse();\n",
"\n",
" return ticks;\n",
"}\n",
"\n",
"function tickIncrement(start, stop, count) {\n",
" var step = (stop - start) / Math.max(0, count),\n",
" power = Math.floor(Math.log(step) / Math.LN10),\n",
" error = step / Math.pow(10, power);\n",
" return power >= 0\n",
" ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)\n",
" : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);\n",
"}\n",
"\n",
"function tickStep(start, stop, count) {\n",
" var step0 = Math.abs(stop - start) / Math.max(0, count),\n",
" step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),\n",
" error = step0 / step1;\n",
" if (error >= e10) step1 *= 10;\n",
" else if (error >= e5) step1 *= 5;\n",
" else if (error >= e2) step1 *= 2;\n",
" return stop < start ? -step1 : step1;\n",
"}\n",
"\n",
"function sturges(values) {\n",
" return Math.ceil(Math.log(count(values)) / Math.LN2) + 1;\n",
"}\n",
"\n",
"function bin() {\n",
" var value = identity,\n",
" domain = extent,\n",
" threshold = sturges;\n",
"\n",
" function histogram(data) {\n",
" if (!Array.isArray(data)) data = Array.from(data);\n",
"\n",
" var i,\n",
" n = data.length,\n",
" x,\n",
" values = new Array(n);\n",
"\n",
" for (i = 0; i < n; ++i) {\n",
" values[i] = value(data[i], i, data);\n",
" }\n",
"\n",
" var xz = domain(values),\n",
" x0 = xz[0],\n",
" x1 = xz[1],\n",
" tz = threshold(values, x0, x1);\n",
"\n",
" // Convert number of thresholds into uniform thresholds.\n",
" if (!Array.isArray(tz)) {\n",
" tz = tickStep(x0, x1, tz);\n",
" tz = range(Math.ceil(x0 / tz) * tz, x1, tz); // exclusive\n",
" }\n",
"\n",
" // Remove any thresholds outside the domain.\n",
" var m = tz.length;\n",
" while (tz[0] <= x0) tz.shift(), --m;\n",
" while (tz[m - 1] > x1) tz.pop(), --m;\n",
"\n",
" var bins = new Array(m + 1),\n",
" bin;\n",
"\n",
" // Initialize bins.\n",
" for (i = 0; i <= m; ++i) {\n",
" bin = bins[i] = [];\n",
" bin.x0 = i > 0 ? tz[i - 1] : x0;\n",
" bin.x1 = i < m ? tz[i] : x1;\n",
" }\n",
"\n",
" // Assign data to bins by value, ignoring any outside the domain.\n",
" for (i = 0; i < n; ++i) {\n",
" x = values[i];\n",
" if (x0 <= x && x <= x1) {\n",
" bins[bisectRight(tz, x, 0, m)].push(data[i]);\n",
" }\n",
" }\n",
"\n",
" return bins;\n",
" }\n",
"\n",
" histogram.value = function(_) {\n",
" return arguments.length ? (value = typeof _ === \"function\" ? _ : constant(_), histogram) : value;\n",
" };\n",
"\n",
" histogram.domain = function(_) {\n",
" return arguments.length ? (domain = typeof _ === \"function\" ? _ : constant([_[0], _[1]]), histogram) : domain;\n",
" };\n",
"\n",
" histogram.thresholds = function(_) {\n",
" return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold;\n",
" };\n",
"\n",
" return histogram;\n",
"}\n",
"\n",
"function max(values, valueof) {\n",
" let max;\n",
" if (valueof === undefined) {\n",
" for (const value of values) {\n",
" if (value != null\n",
" && (max < value || (max === undefined && value >= value))) {\n",
" max = value;\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null\n",
" && (max < value || (max === undefined && value >= value))) {\n",
" max = value;\n",
" }\n",
" }\n",
" }\n",
" return max;\n",
"}\n",
"\n",
"function min(values, valueof) {\n",
" let min;\n",
" if (valueof === undefined) {\n",
" for (const value of values) {\n",
" if (value != null\n",
" && (min > value || (min === undefined && value >= value))) {\n",
" min = value;\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null\n",
" && (min > value || (min === undefined && value >= value))) {\n",
" min = value;\n",
" }\n",
" }\n",
" }\n",
" return min;\n",
"}\n",
"\n",
"// Based on https://github.com/mourner/quickselect\n",
"// ISC license, Copyright 2018 Vladimir Agafonkin.\n",
"function quickselect(array, k, left = 0, right = array.length - 1, compare = ascending) {\n",
" while (right > left) {\n",
" if (right - left > 600) {\n",
" const n = right - left + 1;\n",
" const m = k - left + 1;\n",
" const z = Math.log(n);\n",
" const s = 0.5 * Math.exp(2 * z / 3);\n",
" const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);\n",
" const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));\n",
" const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));\n",
" quickselect(array, k, newLeft, newRight, compare);\n",
" }\n",
"\n",
" const t = array[k];\n",
" let i = left;\n",
" let j = right;\n",
"\n",
" swap(array, left, k);\n",
" if (compare(array[right], t) > 0) swap(array, left, right);\n",
"\n",
" while (i < j) {\n",
" swap(array, i, j), ++i, --j;\n",
" while (compare(array[i], t) < 0) ++i;\n",
" while (compare(array[j], t) > 0) --j;\n",
" }\n",
"\n",
" if (compare(array[left], t) === 0) swap(array, left, j);\n",
" else ++j, swap(array, j, right);\n",
"\n",
" if (j <= k) left = j + 1;\n",
" if (k <= j) right = j - 1;\n",
" }\n",
" return array;\n",
"}\n",
"\n",
"function swap(array, i, j) {\n",
" const t = array[i];\n",
" array[i] = array[j];\n",
" array[j] = t;\n",
"}\n",
"\n",
"function number(x) {\n",
" return x === null ? NaN : +x;\n",
"}\n",
"\n",
"function* numbers(values, valueof) {\n",
" if (valueof === undefined) {\n",
" for (let value of values) {\n",
" if (value != null && (value = +value) >= value) {\n",
" yield value;\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {\n",
" yield value;\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"function quantile(values, p, valueof) {\n",
" values = Float64Array.from(numbers(values, valueof));\n",
" if (!(n = values.length)) return;\n",
" if ((p = +p) <= 0 || n < 2) return min(values);\n",
" if (p >= 1) return max(values);\n",
" var n,\n",
" i = (n - 1) * p,\n",
" i0 = Math.floor(i),\n",
" value0 = max(quickselect(values, i0).subarray(0, i0 + 1)),\n",
" value1 = min(values.subarray(i0 + 1));\n",
" return value0 + (value1 - value0) * (i - i0);\n",
"}\n",
"\n",
"function quantileSorted(values, p, valueof = number) {\n",
" if (!(n = values.length)) return;\n",
" if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);\n",
" if (p >= 1) return +valueof(values[n - 1], n - 1, values);\n",
" var n,\n",
" i = (n - 1) * p,\n",
" i0 = Math.floor(i),\n",
" value0 = +valueof(values[i0], i0, values),\n",
" value1 = +valueof(values[i0 + 1], i0 + 1, values);\n",
" return value0 + (value1 - value0) * (i - i0);\n",
"}\n",
"\n",
"function freedmanDiaconis(values, min, max) {\n",
" return Math.ceil((max - min) / (2 * (quantile(values, 0.75) - quantile(values, 0.25)) * Math.pow(count(values), -1 / 3)));\n",
"}\n",
"\n",
"function scott(values, min, max) {\n",
" return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(count(values), -1 / 3)));\n",
"}\n",
"\n",
"function maxIndex(values, valueof) {\n",
" let max;\n",
" let maxIndex = -1;\n",
" let index = -1;\n",
" if (valueof === undefined) {\n",
" for (const value of values) {\n",
" ++index;\n",
" if (value != null\n",
" && (max < value || (max === undefined && value >= value))) {\n",
" max = value, maxIndex = index;\n",
" }\n",
" }\n",
" } else {\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null\n",
" && (max < value || (max === undefined && value >= value))) {\n",
" max = value, maxIndex = index;\n",
" }\n",
" }\n",
" }\n",
" return maxIndex;\n",
"}\n",
"\n",
"function mean(values, valueof) {\n",
" let count = 0;\n",
" let sum = 0;\n",
" if (valueof === undefined) {\n",
" for (let value of values) {\n",
" if (value != null && (value = +value) >= value) {\n",
" ++count, sum += value;\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {\n",
" ++count, sum += value;\n",
" }\n",
" }\n",
" }\n",
" if (count) return sum / count;\n",
"}\n",
"\n",
"function median(values, valueof) {\n",
" return quantile(values, 0.5, valueof);\n",
"}\n",
"\n",
"function* flatten(arrays) {\n",
" for (const array of arrays) {\n",
" yield* array;\n",
" }\n",
"}\n",
"\n",
"function merge(arrays) {\n",
" return Array.from(flatten(arrays));\n",
"}\n",
"\n",
"function minIndex(values, valueof) {\n",
" let min;\n",
" let minIndex = -1;\n",
" let index = -1;\n",
" if (valueof === undefined) {\n",
" for (const value of values) {\n",
" ++index;\n",
" if (value != null\n",
" && (min > value || (min === undefined && value >= value))) {\n",
" min = value, minIndex = index;\n",
" }\n",
" }\n",
" } else {\n",
" for (let value of values) {\n",
" if ((value = valueof(value, ++index, values)) != null\n",
" && (min > value || (min === undefined && value >= value))) {\n",
" min = value, minIndex = index;\n",
" }\n",
" }\n",
" }\n",
" return minIndex;\n",
"}\n",
"\n",
"function pairs(values, pairof = pair) {\n",
" const pairs = [];\n",
" let previous;\n",
" let first = false;\n",
" for (const value of values) {\n",
" if (first) pairs.push(pairof(previous, value));\n",
" previous = value;\n",
" first = true;\n",
" }\n",
" return pairs;\n",
"}\n",
"\n",
"function pair(a, b) {\n",
" return [a, b];\n",
"}\n",
"\n",
"function permute(source, keys) {\n",
" return Array.from(keys, key => source[key]);\n",
"}\n",
"\n",
"function least(values, compare = ascending) {\n",
" let min;\n",
" let defined = false;\n",
" if (compare.length === 1) {\n",
" let minValue;\n",
" for (const element of values) {\n",
" const value = compare(element);\n",
" if (defined\n",
" ? ascending(value, minValue) < 0\n",
" : ascending(value, value) === 0) {\n",
" min = element;\n",
" minValue = value;\n",
" defined = true;\n",
" }\n",
" }\n",
" } else {\n",
" for (const value of values) {\n",
" if (defined\n",
" ? compare(value, min) < 0\n",
" : compare(value, value) === 0) {\n",
" min = value;\n",
" defined = true;\n",
" }\n",
" }\n",
" }\n",
" return min;\n",
"}\n",
"\n",
"function leastIndex(values, compare = ascending) {\n",
" if (compare.length === 1) return minIndex(values, compare);\n",
" let minValue;\n",
" let min = -1;\n",
" let index = -1;\n",
" for (const value of values) {\n",
" ++index;\n",
" if (min < 0\n",
" ? compare(value, value) === 0\n",
" : compare(value, minValue) < 0) {\n",
" minValue = value;\n",
" min = index;\n",
" }\n",
" }\n",
" return min;\n",
"}\n",
"\n",
"function greatest(values, compare = ascending) {\n",
" let max;\n",
" let defined = false;\n",
" if (compare.length === 1) {\n",
" let maxValue;\n",
" for (const element of values) {\n",
" const value = compare(element);\n",
" if (defined\n",
" ? ascending(value, maxValue) > 0\n",
" : ascending(value, value) === 0) {\n",
" max = element;\n",
" maxValue = value;\n",
" defined = true;\n",
" }\n",
" }\n",
" } else {\n",
" for (const value of values) {\n",
" if (defined\n",
" ? compare(value, max) > 0\n",
" : compare(value, value) === 0) {\n",
" max = value;\n",
" defined = true;\n",
" }\n",
" }\n",
" }\n",
" return max;\n",
"}\n",
"\n",
"function greatestIndex(values, compare = ascending) {\n",
" if (compare.length === 1) return maxIndex(values, compare);\n",
" let maxValue;\n",
" let max = -1;\n",
" let index = -1;\n",
" for (const value of values) {\n",
" ++index;\n",
" if (max < 0\n",
" ? compare(value, value) === 0\n",
" : compare(value, maxValue) > 0) {\n",
" maxValue = value;\n",
" max = index;\n",
" }\n",
" }\n",
" return max;\n",
"}\n",
"\n",
"function scan(values, compare) {\n",
" const index = leastIndex(values, compare);\n",
" return index < 0 ? undefined : index;\n",
"}\n",
"\n",
"function shuffle(array, i0 = 0, i1 = array.length) {\n",
" var m = i1 - (i0 = +i0),\n",
" t,\n",
" i;\n",
"\n",
" while (m) {\n",
" i = Math.random() * m-- | 0;\n",
" t = array[m + i0];\n",
" array[m + i0] = array[i + i0];\n",
" array[i + i0] = t;\n",
" }\n",
"\n",
" return array;\n",
"}\n",
"\n",
"function sum(values, valueof) {\n",
" let sum = 0;\n",
" if (valueof === undefined) {\n",
" for (let value of values) {\n",
" if (value = +value) {\n",
" sum += value;\n",
" }\n",
" }\n",
" } else {\n",
" let index = -1;\n",
" for (let value of values) {\n",
" if (value = +valueof(value, ++index, values)) {\n",
" sum += value;\n",
" }\n",
" }\n",
" }\n",
" return sum;\n",
"}\n",
"\n",
"function transpose(matrix) {\n",
" if (!(n = matrix.length)) return [];\n",
" for (var i = -1, m = min(matrix, length$1), transpose = new Array(m); ++i < m;) {\n",
" for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {\n",
" row[j] = matrix[j][i];\n",
" }\n",
" }\n",
" return transpose;\n",
"}\n",
"\n",
"function length$1(d) {\n",
" return d.length;\n",
"}\n",
"\n",
"function zip() {\n",
" return transpose(arguments);\n",
"}\n",
"\n",
"exports.ascending = ascending;\n",
"exports.bin = bin;\n",
"exports.bisect = bisectRight;\n",
"exports.bisectLeft = bisectLeft;\n",
"exports.bisectRight = bisectRight;\n",
"exports.bisector = bisector;\n",
"exports.count = count;\n",
"exports.cross = cross;\n",
"exports.cumsum = cumsum;\n",
"exports.descending = descending;\n",
"exports.deviation = deviation;\n",
"exports.extent = extent;\n",
"exports.greatest = greatest;\n",
"exports.greatestIndex = greatestIndex;\n",
"exports.group = group;\n",
"exports.groups = groups;\n",
"exports.histogram = bin;\n",
"exports.least = least;\n",
"exports.leastIndex = leastIndex;\n",
"exports.max = max;\n",
"exports.maxIndex = maxIndex;\n",
"exports.mean = mean;\n",
"exports.median = median;\n",
"exports.merge = merge;\n",
"exports.min = min;\n",
"exports.minIndex = minIndex;\n",
"exports.pairs = pairs;\n",
"exports.permute = permute;\n",
"exports.quantile = quantile;\n",
"exports.quantileSorted = quantileSorted;\n",
"exports.quickselect = quickselect;\n",
"exports.range = range;\n",
"exports.rollup = rollup;\n",
"exports.rollups = rollups;\n",
"exports.scan = scan;\n",
"exports.shuffle = shuffle;\n",
"exports.sum = sum;\n",
"exports.thresholdFreedmanDiaconis = freedmanDiaconis;\n",
"exports.thresholdScott = scott;\n",
"exports.thresholdSturges = sturges;\n",
"exports.tickIncrement = tickIncrement;\n",
"exports.tickStep = tickStep;\n",
"exports.ticks = ticks;\n",
"exports.transpose = transpose;\n",
"exports.variance = variance;\n",
"exports.zip = zip;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],3:[function(require,module,exports){\n",
"// https://d3js.org/d3-axis/ v1.0.12 Copyright 2018 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(factory((global.d3 = global.d3 || {})));\n",
"}(this, (function (exports) { 'use strict';\n",
"\n",
"var slice = Array.prototype.slice;\n",
"\n",
"function identity(x) {\n",
" return x;\n",
"}\n",
"\n",
"var top = 1,\n",
" right = 2,\n",
" bottom = 3,\n",
" left = 4,\n",
" epsilon = 1e-6;\n",
"\n",
"function translateX(x) {\n",
" return \"translate(\" + (x + 0.5) + \",0)\";\n",
"}\n",
"\n",
"function translateY(y) {\n",
" return \"translate(0,\" + (y + 0.5) + \")\";\n",
"}\n",
"\n",
"function number(scale) {\n",
" return function(d) {\n",
" return +scale(d);\n",
" };\n",
"}\n",
"\n",
"function center(scale) {\n",
" var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.\n",
" if (scale.round()) offset = Math.round(offset);\n",
" return function(d) {\n",
" return +scale(d) + offset;\n",
" };\n",
"}\n",
"\n",
"function entering() {\n",
" return !this.__axis;\n",
"}\n",
"\n",
"function axis(orient, scale) {\n",
" var tickArguments = [],\n",
" tickValues = null,\n",
" tickFormat = null,\n",
" tickSizeInner = 6,\n",
" tickSizeOuter = 6,\n",
" tickPadding = 3,\n",
" k = orient === top || orient === left ? -1 : 1,\n",
" x = orient === left || orient === right ? \"x\" : \"y\",\n",
" transform = orient === top || orient === bottom ? translateX : translateY;\n",
"\n",
" function axis(context) {\n",
" var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,\n",
" format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity) : tickFormat,\n",
" spacing = Math.max(tickSizeInner, 0) + tickPadding,\n",
" range = scale.range(),\n",
" range0 = +range[0] + 0.5,\n",
" range1 = +range[range.length - 1] + 0.5,\n",
" position = (scale.bandwidth ? center : number)(scale.copy()),\n",
" selection = context.selection ? context.selection() : context,\n",
" path = selection.selectAll(\".domain\").data([null]),\n",
" tick = selection.selectAll(\".tick\").data(values, scale).order(),\n",
" tickExit = tick.exit(),\n",
" tickEnter = tick.enter().append(\"g\").attr(\"class\", \"tick\"),\n",
" line = tick.select(\"line\"),\n",
" text = tick.select(\"text\");\n",
"\n",
" path = path.merge(path.enter().insert(\"path\", \".tick\")\n",
" .attr(\"class\", \"domain\")\n",
" .attr(\"stroke\", \"currentColor\"));\n",
"\n",
" tick = tick.merge(tickEnter);\n",
"\n",
" line = line.merge(tickEnter.append(\"line\")\n",
" .attr(\"stroke\", \"currentColor\")\n",
" .attr(x + \"2\", k * tickSizeInner));\n",
"\n",
" text = text.merge(tickEnter.append(\"text\")\n",
" .attr(\"fill\", \"currentColor\")\n",
" .attr(x, k * spacing)\n",
" .attr(\"dy\", orient === top ? \"0em\" : orient === bottom ? \"0.71em\" : \"0.32em\"));\n",
"\n",
" if (context !== selection) {\n",
" path = path.transition(context);\n",
" tick = tick.transition(context);\n",
" line = line.transition(context);\n",
" text = text.transition(context);\n",
"\n",
" tickExit = tickExit.transition(context)\n",
" .attr(\"opacity\", epsilon)\n",
" .attr(\"transform\", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute(\"transform\"); });\n",
"\n",
" tickEnter\n",
" .attr(\"opacity\", epsilon)\n",
" .attr(\"transform\", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });\n",
" }\n",
"\n",
" tickExit.remove();\n",
"\n",
" path\n",
" .attr(\"d\", orient === left || orient == right\n",
" ? (tickSizeOuter ? \"M\" + k * tickSizeOuter + \",\" + range0 + \"H0.5V\" + range1 + \"H\" + k * tickSizeOuter : \"M0.5,\" + range0 + \"V\" + range1)\n",
" : (tickSizeOuter ? \"M\" + range0 + \",\" + k * tickSizeOuter + \"V0.5H\" + range1 + \"V\" + k * tickSizeOuter : \"M\" + range0 + \",0.5H\" + range1));\n",
"\n",
" tick\n",
" .attr(\"opacity\", 1)\n",
" .attr(\"transform\", function(d) { return transform(position(d)); });\n",
"\n",
" line\n",
" .attr(x + \"2\", k * tickSizeInner);\n",
"\n",
" text\n",
" .attr(x, k * spacing)\n",
" .text(format);\n",
"\n",
" selection.filter(entering)\n",
" .attr(\"fill\", \"none\")\n",
" .attr(\"font-size\", 10)\n",
" .attr(\"font-family\", \"sans-serif\")\n",
" .attr(\"text-anchor\", orient === right ? \"start\" : orient === left ? \"end\" : \"middle\");\n",
"\n",
" selection\n",
" .each(function() { this.__axis = position; });\n",
" }\n",
"\n",
" axis.scale = function(_) {\n",
" return arguments.length ? (scale = _, axis) : scale;\n",
" };\n",
"\n",
" axis.ticks = function() {\n",
" return tickArguments = slice.call(arguments), axis;\n",
" };\n",
"\n",
" axis.tickArguments = function(_) {\n",
" return arguments.length ? (tickArguments = _ == null ? [] : slice.call(_), axis) : tickArguments.slice();\n",
" };\n",
"\n",
" axis.tickValues = function(_) {\n",
" return arguments.length ? (tickValues = _ == null ? null : slice.call(_), axis) : tickValues && tickValues.slice();\n",
" };\n",
"\n",
" axis.tickFormat = function(_) {\n",
" return arguments.length ? (tickFormat = _, axis) : tickFormat;\n",
" };\n",
"\n",
" axis.tickSize = function(_) {\n",
" return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;\n",
" };\n",
"\n",
" axis.tickSizeInner = function(_) {\n",
" return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;\n",
" };\n",
"\n",
" axis.tickSizeOuter = function(_) {\n",
" return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;\n",
" };\n",
"\n",
" axis.tickPadding = function(_) {\n",
" return arguments.length ? (tickPadding = +_, axis) : tickPadding;\n",
" };\n",
"\n",
" return axis;\n",
"}\n",
"\n",
"function axisTop(scale) {\n",
" return axis(top, scale);\n",
"}\n",
"\n",
"function axisRight(scale) {\n",
" return axis(right, scale);\n",
"}\n",
"\n",
"function axisBottom(scale) {\n",
" return axis(bottom, scale);\n",
"}\n",
"\n",
"function axisLeft(scale) {\n",
" return axis(left, scale);\n",
"}\n",
"\n",
"exports.axisTop = axisTop;\n",
"exports.axisRight = axisRight;\n",
"exports.axisBottom = axisBottom;\n",
"exports.axisLeft = axisLeft;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"})));\n",
"\n",
"},{}],4:[function(require,module,exports){\n",
"// https://d3js.org/d3-color/ v1.4.0 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"function define(constructor, factory, prototype) {\n",
" constructor.prototype = factory.prototype = prototype;\n",
" prototype.constructor = constructor;\n",
"}\n",
"\n",
"function extend(parent, definition) {\n",
" var prototype = Object.create(parent.prototype);\n",
" for (var key in definition) prototype[key] = definition[key];\n",
" return prototype;\n",
"}\n",
"\n",
"function Color() {}\n",
"\n",
"var darker = 0.7;\n",
"var brighter = 1 / darker;\n",
"\n",
"var reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n",
" reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n",
" reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n",
" reHex = /^#([0-9a-f]{3,8})$/,\n",
" reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n",
" reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n",
" reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n",
" reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n",
" reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n",
" reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n",
"\n",
"var named = {\n",
" aliceblue: 0xf0f8ff,\n",
" antiquewhite: 0xfaebd7,\n",
" aqua: 0x00ffff,\n",
" aquamarine: 0x7fffd4,\n",
" azure: 0xf0ffff,\n",
" beige: 0xf5f5dc,\n",
" bisque: 0xffe4c4,\n",
" black: 0x000000,\n",
" blanchedalmond: 0xffebcd,\n",
" blue: 0x0000ff,\n",
" blueviolet: 0x8a2be2,\n",
" brown: 0xa52a2a,\n",
" burlywood: 0xdeb887,\n",
" cadetblue: 0x5f9ea0,\n",
" chartreuse: 0x7fff00,\n",
" chocolate: 0xd2691e,\n",
" coral: 0xff7f50,\n",
" cornflowerblue: 0x6495ed,\n",
" cornsilk: 0xfff8dc,\n",
" crimson: 0xdc143c,\n",
" cyan: 0x00ffff,\n",
" darkblue: 0x00008b,\n",
" darkcyan: 0x008b8b,\n",
" darkgoldenrod: 0xb8860b,\n",
" darkgray: 0xa9a9a9,\n",
" darkgreen: 0x006400,\n",
" darkgrey: 0xa9a9a9,\n",
" darkkhaki: 0xbdb76b,\n",
" darkmagenta: 0x8b008b,\n",
" darkolivegreen: 0x556b2f,\n",
" darkorange: 0xff8c00,\n",
" darkorchid: 0x9932cc,\n",
" darkred: 0x8b0000,\n",
" darksalmon: 0xe9967a,\n",
" darkseagreen: 0x8fbc8f,\n",
" darkslateblue: 0x483d8b,\n",
" darkslategray: 0x2f4f4f,\n",
" darkslategrey: 0x2f4f4f,\n",
" darkturquoise: 0x00ced1,\n",
" darkviolet: 0x9400d3,\n",
" deeppink: 0xff1493,\n",
" deepskyblue: 0x00bfff,\n",
" dimgray: 0x696969,\n",
" dimgrey: 0x696969,\n",
" dodgerblue: 0x1e90ff,\n",
" firebrick: 0xb22222,\n",
" floralwhite: 0xfffaf0,\n",
" forestgreen: 0x228b22,\n",
" fuchsia: 0xff00ff,\n",
" gainsboro: 0xdcdcdc,\n",
" ghostwhite: 0xf8f8ff,\n",
" gold: 0xffd700,\n",
" goldenrod: 0xdaa520,\n",
" gray: 0x808080,\n",
" green: 0x008000,\n",
" greenyellow: 0xadff2f,\n",
" grey: 0x808080,\n",
" honeydew: 0xf0fff0,\n",
" hotpink: 0xff69b4,\n",
" indianred: 0xcd5c5c,\n",
" indigo: 0x4b0082,\n",
" ivory: 0xfffff0,\n",
" khaki: 0xf0e68c,\n",
" lavender: 0xe6e6fa,\n",
" lavenderblush: 0xfff0f5,\n",
" lawngreen: 0x7cfc00,\n",
" lemonchiffon: 0xfffacd,\n",
" lightblue: 0xadd8e6,\n",
" lightcoral: 0xf08080,\n",
" lightcyan: 0xe0ffff,\n",
" lightgoldenrodyellow: 0xfafad2,\n",
" lightgray: 0xd3d3d3,\n",
" lightgreen: 0x90ee90,\n",
" lightgrey: 0xd3d3d3,\n",
" lightpink: 0xffb6c1,\n",
" lightsalmon: 0xffa07a,\n",
" lightseagreen: 0x20b2aa,\n",
" lightskyblue: 0x87cefa,\n",
" lightslategray: 0x778899,\n",
" lightslategrey: 0x778899,\n",
" lightsteelblue: 0xb0c4de,\n",
" lightyellow: 0xffffe0,\n",
" lime: 0x00ff00,\n",
" limegreen: 0x32cd32,\n",
" linen: 0xfaf0e6,\n",
" magenta: 0xff00ff,\n",
" maroon: 0x800000,\n",
" mediumaquamarine: 0x66cdaa,\n",
" mediumblue: 0x0000cd,\n",
" mediumorchid: 0xba55d3,\n",
" mediumpurple: 0x9370db,\n",
" mediumseagreen: 0x3cb371,\n",
" mediumslateblue: 0x7b68ee,\n",
" mediumspringgreen: 0x00fa9a,\n",
" mediumturquoise: 0x48d1cc,\n",
" mediumvioletred: 0xc71585,\n",
" midnightblue: 0x191970,\n",
" mintcream: 0xf5fffa,\n",
" mistyrose: 0xffe4e1,\n",
" moccasin: 0xffe4b5,\n",
" navajowhite: 0xffdead,\n",
" navy: 0x000080,\n",
" oldlace: 0xfdf5e6,\n",
" olive: 0x808000,\n",
" olivedrab: 0x6b8e23,\n",
" orange: 0xffa500,\n",
" orangered: 0xff4500,\n",
" orchid: 0xda70d6,\n",
" palegoldenrod: 0xeee8aa,\n",
" palegreen: 0x98fb98,\n",
" paleturquoise: 0xafeeee,\n",
" palevioletred: 0xdb7093,\n",
" papayawhip: 0xffefd5,\n",
" peachpuff: 0xffdab9,\n",
" peru: 0xcd853f,\n",
" pink: 0xffc0cb,\n",
" plum: 0xdda0dd,\n",
" powderblue: 0xb0e0e6,\n",
" purple: 0x800080,\n",
" rebeccapurple: 0x663399,\n",
" red: 0xff0000,\n",
" rosybrown: 0xbc8f8f,\n",
" royalblue: 0x4169e1,\n",
" saddlebrown: 0x8b4513,\n",
" salmon: 0xfa8072,\n",
" sandybrown: 0xf4a460,\n",
" seagreen: 0x2e8b57,\n",
" seashell: 0xfff5ee,\n",
" sienna: 0xa0522d,\n",
" silver: 0xc0c0c0,\n",
" skyblue: 0x87ceeb,\n",
" slateblue: 0x6a5acd,\n",
" slategray: 0x708090,\n",
" slategrey: 0x708090,\n",
" snow: 0xfffafa,\n",
" springgreen: 0x00ff7f,\n",
" steelblue: 0x4682b4,\n",
" tan: 0xd2b48c,\n",
" teal: 0x008080,\n",
" thistle: 0xd8bfd8,\n",
" tomato: 0xff6347,\n",
" turquoise: 0x40e0d0,\n",
" violet: 0xee82ee,\n",
" wheat: 0xf5deb3,\n",
" white: 0xffffff,\n",
" whitesmoke: 0xf5f5f5,\n",
" yellow: 0xffff00,\n",
" yellowgreen: 0x9acd32\n",
"};\n",
"\n",
"define(Color, color, {\n",
" copy: function(channels) {\n",
" return Object.assign(new this.constructor, this, channels);\n",
" },\n",
" displayable: function() {\n",
" return this.rgb().displayable();\n",
" },\n",
" hex: color_formatHex, // Deprecated! Use color.formatHex.\n",
" formatHex: color_formatHex,\n",
" formatHsl: color_formatHsl,\n",
" formatRgb: color_formatRgb,\n",
" toString: color_formatRgb\n",
"});\n",
"\n",
"function color_formatHex() {\n",
" return this.rgb().formatHex();\n",
"}\n",
"\n",
"function color_formatHsl() {\n",
" return hslConvert(this).formatHsl();\n",
"}\n",
"\n",
"function color_formatRgb() {\n",
" return this.rgb().formatRgb();\n",
"}\n",
"\n",
"function color(format) {\n",
" var m, l;\n",
" format = (format + \"\").trim().toLowerCase();\n",
" return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000\n",
" : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00\n",
" : l === 8 ? new Rgb(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000\n",
" : l === 4 ? new Rgb((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000\n",
" : null) // invalid hex\n",
" : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n",
" : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n",
" : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n",
" : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n",
" : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n",
" : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n",
" : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins\n",
" : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n",
" : null;\n",
"}\n",
"\n",
"function rgbn(n) {\n",
" return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n",
"}\n",
"\n",
"function rgba(r, g, b, a) {\n",
" if (a <= 0) r = g = b = NaN;\n",
" return new Rgb(r, g, b, a);\n",
"}\n",
"\n",
"function rgbConvert(o) {\n",
" if (!(o instanceof Color)) o = color(o);\n",
" if (!o) return new Rgb;\n",
" o = o.rgb();\n",
" return new Rgb(o.r, o.g, o.b, o.opacity);\n",
"}\n",
"\n",
"function rgb(r, g, b, opacity) {\n",
" return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n",
"}\n",
"\n",
"function Rgb(r, g, b, opacity) {\n",
" this.r = +r;\n",
" this.g = +g;\n",
" this.b = +b;\n",
" this.opacity = +opacity;\n",
"}\n",
"\n",
"define(Rgb, rgb, extend(Color, {\n",
" brighter: function(k) {\n",
" k = k == null ? brighter : Math.pow(brighter, k);\n",
" return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n",
" },\n",
" darker: function(k) {\n",
" k = k == null ? darker : Math.pow(darker, k);\n",
" return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n",
" },\n",
" rgb: function() {\n",
" return this;\n",
" },\n",
" displayable: function() {\n",
" return (-0.5 <= this.r && this.r < 255.5)\n",
" && (-0.5 <= this.g && this.g < 255.5)\n",
" && (-0.5 <= this.b && this.b < 255.5)\n",
" && (0 <= this.opacity && this.opacity <= 1);\n",
" },\n",
" hex: rgb_formatHex, // Deprecated! Use color.formatHex.\n",
" formatHex: rgb_formatHex,\n",
" formatRgb: rgb_formatRgb,\n",
" toString: rgb_formatRgb\n",
"}));\n",
"\n",
"function rgb_formatHex() {\n",
" return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n",
"}\n",
"\n",
"function rgb_formatRgb() {\n",
" var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n",
" return (a === 1 ? \"rgb(\" : \"rgba(\")\n",
" + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n",
" + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n",
" + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n",
" + (a === 1 ? \")\" : \", \" + a + \")\");\n",
"}\n",
"\n",
"function hex(value) {\n",
" value = Math.max(0, Math.min(255, Math.round(value) || 0));\n",
" return (value < 16 ? \"0\" : \"\") + value.toString(16);\n",
"}\n",
"\n",
"function hsla(h, s, l, a) {\n",
" if (a <= 0) h = s = l = NaN;\n",
" else if (l <= 0 || l >= 1) h = s = NaN;\n",
" else if (s <= 0) h = NaN;\n",
" return new Hsl(h, s, l, a);\n",
"}\n",
"\n",
"function hslConvert(o) {\n",
" if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n",
" if (!(o instanceof Color)) o = color(o);\n",
" if (!o) return new Hsl;\n",
" if (o instanceof Hsl) return o;\n",
" o = o.rgb();\n",
" var r = o.r / 255,\n",
" g = o.g / 255,\n",
" b = o.b / 255,\n",
" min = Math.min(r, g, b),\n",
" max = Math.max(r, g, b),\n",
" h = NaN,\n",
" s = max - min,\n",
" l = (max + min) / 2;\n",
" if (s) {\n",
" if (r === max) h = (g - b) / s + (g < b) * 6;\n",
" else if (g === max) h = (b - r) / s + 2;\n",
" else h = (r - g) / s + 4;\n",
" s /= l < 0.5 ? max + min : 2 - max - min;\n",
" h *= 60;\n",
" } else {\n",
" s = l > 0 && l < 1 ? 0 : h;\n",
" }\n",
" return new Hsl(h, s, l, o.opacity);\n",
"}\n",
"\n",
"function hsl(h, s, l, opacity) {\n",
" return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n",
"}\n",
"\n",
"function Hsl(h, s, l, opacity) {\n",
" this.h = +h;\n",
" this.s = +s;\n",
" this.l = +l;\n",
" this.opacity = +opacity;\n",
"}\n",
"\n",
"define(Hsl, hsl, extend(Color, {\n",
" brighter: function(k) {\n",
" k = k == null ? brighter : Math.pow(brighter, k);\n",
" return new Hsl(this.h, this.s, this.l * k, this.opacity);\n",
" },\n",
" darker: function(k) {\n",
" k = k == null ? darker : Math.pow(darker, k);\n",
" return new Hsl(this.h, this.s, this.l * k, this.opacity);\n",
" },\n",
" rgb: function() {\n",
" var h = this.h % 360 + (this.h < 0) * 360,\n",
" s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n",
" l = this.l,\n",
" m2 = l + (l < 0.5 ? l : 1 - l) * s,\n",
" m1 = 2 * l - m2;\n",
" return new Rgb(\n",
" hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n",
" hsl2rgb(h, m1, m2),\n",
" hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n",
" this.opacity\n",
" );\n",
" },\n",
" displayable: function() {\n",
" return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n",
" && (0 <= this.l && this.l <= 1)\n",
" && (0 <= this.opacity && this.opacity <= 1);\n",
" },\n",
" formatHsl: function() {\n",
" var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n",
" return (a === 1 ? \"hsl(\" : \"hsla(\")\n",
" + (this.h || 0) + \", \"\n",
" + (this.s || 0) * 100 + \"%, \"\n",
" + (this.l || 0) * 100 + \"%\"\n",
" + (a === 1 ? \")\" : \", \" + a + \")\");\n",
" }\n",
"}));\n",
"\n",
"/* From FvD 13.37, CSS Color Module Level 3 */\n",
"function hsl2rgb(h, m1, m2) {\n",
" return (h < 60 ? m1 + (m2 - m1) * h / 60\n",
" : h < 180 ? m2\n",
" : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n",
" : m1) * 255;\n",
"}\n",
"\n",
"var deg2rad = Math.PI / 180;\n",
"var rad2deg = 180 / Math.PI;\n",
"\n",
"// https://observablehq.com/@mbostock/lab-and-rgb\n",
"var K = 18,\n",
" Xn = 0.96422,\n",
" Yn = 1,\n",
" Zn = 0.82521,\n",
" t0 = 4 / 29,\n",
" t1 = 6 / 29,\n",
" t2 = 3 * t1 * t1,\n",
" t3 = t1 * t1 * t1;\n",
"\n",
"function labConvert(o) {\n",
" if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n",
" if (o instanceof Hcl) return hcl2lab(o);\n",
" if (!(o instanceof Rgb)) o = rgbConvert(o);\n",
" var r = rgb2lrgb(o.r),\n",
" g = rgb2lrgb(o.g),\n",
" b = rgb2lrgb(o.b),\n",
" y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n",
" if (r === g && g === b) x = z = y; else {\n",
" x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n",
" z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n",
" }\n",
" return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n",
"}\n",
"\n",
"function gray(l, opacity) {\n",
" return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n",
"}\n",
"\n",
"function lab(l, a, b, opacity) {\n",
" return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n",
"}\n",
"\n",
"function Lab(l, a, b, opacity) {\n",
" this.l = +l;\n",
" this.a = +a;\n",
" this.b = +b;\n",
" this.opacity = +opacity;\n",
"}\n",
"\n",
"define(Lab, lab, extend(Color, {\n",
" brighter: function(k) {\n",
" return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n",
" },\n",
" darker: function(k) {\n",
" return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n",
" },\n",
" rgb: function() {\n",
" var y = (this.l + 16) / 116,\n",
" x = isNaN(this.a) ? y : y + this.a / 500,\n",
" z = isNaN(this.b) ? y : y - this.b / 200;\n",
" x = Xn * lab2xyz(x);\n",
" y = Yn * lab2xyz(y);\n",
" z = Zn * lab2xyz(z);\n",
" return new Rgb(\n",
" lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n",
" lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n",
" lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n",
" this.opacity\n",
" );\n",
" }\n",
"}));\n",
"\n",
"function xyz2lab(t) {\n",
" return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n",
"}\n",
"\n",
"function lab2xyz(t) {\n",
" return t > t1 ? t * t * t : t2 * (t - t0);\n",
"}\n",
"\n",
"function lrgb2rgb(x) {\n",
" return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n",
"}\n",
"\n",
"function rgb2lrgb(x) {\n",
" return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n",
"}\n",
"\n",
"function hclConvert(o) {\n",
" if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n",
" if (!(o instanceof Lab)) o = labConvert(o);\n",
" if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0 < o.l && o.l < 100 ? 0 : NaN, o.l, o.opacity);\n",
" var h = Math.atan2(o.b, o.a) * rad2deg;\n",
" return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n",
"}\n",
"\n",
"function lch(l, c, h, opacity) {\n",
" return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n",
"}\n",
"\n",
"function hcl(h, c, l, opacity) {\n",
" return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n",
"}\n",
"\n",
"function Hcl(h, c, l, opacity) {\n",
" this.h = +h;\n",
" this.c = +c;\n",
" this.l = +l;\n",
" this.opacity = +opacity;\n",
"}\n",
"\n",
"function hcl2lab(o) {\n",
" if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n",
" var h = o.h * deg2rad;\n",
" return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n",
"}\n",
"\n",
"define(Hcl, hcl, extend(Color, {\n",
" brighter: function(k) {\n",
" return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n",
" },\n",
" darker: function(k) {\n",
" return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n",
" },\n",
" rgb: function() {\n",
" return hcl2lab(this).rgb();\n",
" }\n",
"}));\n",
"\n",
"var A = -0.14861,\n",
" B = +1.78277,\n",
" C = -0.29227,\n",
" D = -0.90649,\n",
" E = +1.97294,\n",
" ED = E * D,\n",
" EB = E * B,\n",
" BC_DA = B * C - D * A;\n",
"\n",
"function cubehelixConvert(o) {\n",
" if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n",
" if (!(o instanceof Rgb)) o = rgbConvert(o);\n",
" var r = o.r / 255,\n",
" g = o.g / 255,\n",
" b = o.b / 255,\n",
" l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n",
" bl = b - l,\n",
" k = (E * (g - l) - C * bl) / D,\n",
" s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n",
" h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;\n",
" return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n",
"}\n",
"\n",
"function cubehelix(h, s, l, opacity) {\n",
" return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n",
"}\n",
"\n",
"function Cubehelix(h, s, l, opacity) {\n",
" this.h = +h;\n",
" this.s = +s;\n",
" this.l = +l;\n",
" this.opacity = +opacity;\n",
"}\n",
"\n",
"define(Cubehelix, cubehelix, extend(Color, {\n",
" brighter: function(k) {\n",
" k = k == null ? brighter : Math.pow(brighter, k);\n",
" return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n",
" },\n",
" darker: function(k) {\n",
" k = k == null ? darker : Math.pow(darker, k);\n",
" return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n",
" },\n",
" rgb: function() {\n",
" var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,\n",
" l = +this.l,\n",
" a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n",
" cosh = Math.cos(h),\n",
" sinh = Math.sin(h);\n",
" return new Rgb(\n",
" 255 * (l + a * (A * cosh + B * sinh)),\n",
" 255 * (l + a * (C * cosh + D * sinh)),\n",
" 255 * (l + a * (E * cosh)),\n",
" this.opacity\n",
" );\n",
" }\n",
"}));\n",
"\n",
"exports.color = color;\n",
"exports.cubehelix = cubehelix;\n",
"exports.gray = gray;\n",
"exports.hcl = hcl;\n",
"exports.hsl = hsl;\n",
"exports.lab = lab;\n",
"exports.lch = lch;\n",
"exports.rgb = rgb;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],5:[function(require,module,exports){\n",
"// https://d3js.org/d3-dispatch/ v1.0.6 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"var noop = {value: function() {}};\n",
"\n",
"function dispatch() {\n",
" for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n",
" if (!(t = arguments[i] + \"\") || (t in _) || /[\\s.]/.test(t)) throw new Error(\"illegal type: \" + t);\n",
" _[t] = [];\n",
" }\n",
" return new Dispatch(_);\n",
"}\n",
"\n",
"function Dispatch(_) {\n",
" this._ = _;\n",
"}\n",
"\n",
"function parseTypenames(typenames, types) {\n",
" return typenames.trim().split(/^|\\s+/).map(function(t) {\n",
" var name = \"\", i = t.indexOf(\".\");\n",
" if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n",
" if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n",
" return {type: t, name: name};\n",
" });\n",
"}\n",
"\n",
"Dispatch.prototype = dispatch.prototype = {\n",
" constructor: Dispatch,\n",
" on: function(typename, callback) {\n",
" var _ = this._,\n",
" T = parseTypenames(typename + \"\", _),\n",
" t,\n",
" i = -1,\n",
" n = T.length;\n",
"\n",
" // If no callback was specified, return the callback of the given type and name.\n",
" if (arguments.length < 2) {\n",
" while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n",
" return;\n",
" }\n",
"\n",
" // If a type was specified, set the callback for the given type and name.\n",
" // Otherwise, if a null callback was specified, remove callbacks of the given name.\n",
" if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n",
" while (++i < n) {\n",
" if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n",
" else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n",
" }\n",
"\n",
" return this;\n",
" },\n",
" copy: function() {\n",
" var copy = {}, _ = this._;\n",
" for (var t in _) copy[t] = _[t].slice();\n",
" return new Dispatch(copy);\n",
" },\n",
" call: function(type, that) {\n",
" if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n",
" if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n",
" for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n",
" },\n",
" apply: function(type, that, args) {\n",
" if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n",
" for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n",
" }\n",
"};\n",
"\n",
"function get(type, name) {\n",
" for (var i = 0, n = type.length, c; i < n; ++i) {\n",
" if ((c = type[i]).name === name) {\n",
" return c.value;\n",
" }\n",
" }\n",
"}\n",
"\n",
"function set(type, name, callback) {\n",
" for (var i = 0, n = type.length; i < n; ++i) {\n",
" if (type[i].name === name) {\n",
" type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n",
" break;\n",
" }\n",
" }\n",
" if (callback != null) type.push({name: name, value: callback});\n",
" return type;\n",
"}\n",
"\n",
"exports.dispatch = dispatch;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],6:[function(require,module,exports){\n",
"// https://d3js.org/d3-ease/ v1.0.6 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"function linear(t) {\n",
" return +t;\n",
"}\n",
"\n",
"function quadIn(t) {\n",
" return t * t;\n",
"}\n",
"\n",
"function quadOut(t) {\n",
" return t * (2 - t);\n",
"}\n",
"\n",
"function quadInOut(t) {\n",
" return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;\n",
"}\n",
"\n",
"function cubicIn(t) {\n",
" return t * t * t;\n",
"}\n",
"\n",
"function cubicOut(t) {\n",
" return --t * t * t + 1;\n",
"}\n",
"\n",
"function cubicInOut(t) {\n",
" return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;\n",
"}\n",
"\n",
"var exponent = 3;\n",
"\n",
"var polyIn = (function custom(e) {\n",
" e = +e;\n",
"\n",
" function polyIn(t) {\n",
" return Math.pow(t, e);\n",
" }\n",
"\n",
" polyIn.exponent = custom;\n",
"\n",
" return polyIn;\n",
"})(exponent);\n",
"\n",
"var polyOut = (function custom(e) {\n",
" e = +e;\n",
"\n",
" function polyOut(t) {\n",
" return 1 - Math.pow(1 - t, e);\n",
" }\n",
"\n",
" polyOut.exponent = custom;\n",
"\n",
" return polyOut;\n",
"})(exponent);\n",
"\n",
"var polyInOut = (function custom(e) {\n",
" e = +e;\n",
"\n",
" function polyInOut(t) {\n",
" return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;\n",
" }\n",
"\n",
" polyInOut.exponent = custom;\n",
"\n",
" return polyInOut;\n",
"})(exponent);\n",
"\n",
"var pi = Math.PI,\n",
" halfPi = pi / 2;\n",
"\n",
"function sinIn(t) {\n",
" return 1 - Math.cos(t * halfPi);\n",
"}\n",
"\n",
"function sinOut(t) {\n",
" return Math.sin(t * halfPi);\n",
"}\n",
"\n",
"function sinInOut(t) {\n",
" return (1 - Math.cos(pi * t)) / 2;\n",
"}\n",
"\n",
"function expIn(t) {\n",
" return Math.pow(2, 10 * t - 10);\n",
"}\n",
"\n",
"function expOut(t) {\n",
" return 1 - Math.pow(2, -10 * t);\n",
"}\n",
"\n",
"function expInOut(t) {\n",
" return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;\n",
"}\n",
"\n",
"function circleIn(t) {\n",
" return 1 - Math.sqrt(1 - t * t);\n",
"}\n",
"\n",
"function circleOut(t) {\n",
" return Math.sqrt(1 - --t * t);\n",
"}\n",
"\n",
"function circleInOut(t) {\n",
" return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;\n",
"}\n",
"\n",
"var b1 = 4 / 11,\n",
" b2 = 6 / 11,\n",
" b3 = 8 / 11,\n",
" b4 = 3 / 4,\n",
" b5 = 9 / 11,\n",
" b6 = 10 / 11,\n",
" b7 = 15 / 16,\n",
" b8 = 21 / 22,\n",
" b9 = 63 / 64,\n",
" b0 = 1 / b1 / b1;\n",
"\n",
"function bounceIn(t) {\n",
" return 1 - bounceOut(1 - t);\n",
"}\n",
"\n",
"function bounceOut(t) {\n",
" return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;\n",
"}\n",
"\n",
"function bounceInOut(t) {\n",
" return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;\n",
"}\n",
"\n",
"var overshoot = 1.70158;\n",
"\n",
"var backIn = (function custom(s) {\n",
" s = +s;\n",
"\n",
" function backIn(t) {\n",
" return t * t * ((s + 1) * t - s);\n",
" }\n",
"\n",
" backIn.overshoot = custom;\n",
"\n",
" return backIn;\n",
"})(overshoot);\n",
"\n",
"var backOut = (function custom(s) {\n",
" s = +s;\n",
"\n",
" function backOut(t) {\n",
" return --t * t * ((s + 1) * t + s) + 1;\n",
" }\n",
"\n",
" backOut.overshoot = custom;\n",
"\n",
" return backOut;\n",
"})(overshoot);\n",
"\n",
"var backInOut = (function custom(s) {\n",
" s = +s;\n",
"\n",
" function backInOut(t) {\n",
" return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;\n",
" }\n",
"\n",
" backInOut.overshoot = custom;\n",
"\n",
" return backInOut;\n",
"})(overshoot);\n",
"\n",
"var tau = 2 * Math.PI,\n",
" amplitude = 1,\n",
" period = 0.3;\n",
"\n",
"var elasticIn = (function custom(a, p) {\n",
" var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n",
"\n",
" function elasticIn(t) {\n",
" return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);\n",
" }\n",
"\n",
" elasticIn.amplitude = function(a) { return custom(a, p * tau); };\n",
" elasticIn.period = function(p) { return custom(a, p); };\n",
"\n",
" return elasticIn;\n",
"})(amplitude, period);\n",
"\n",
"var elasticOut = (function custom(a, p) {\n",
" var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n",
"\n",
" function elasticOut(t) {\n",
" return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);\n",
" }\n",
"\n",
" elasticOut.amplitude = function(a) { return custom(a, p * tau); };\n",
" elasticOut.period = function(p) { return custom(a, p); };\n",
"\n",
" return elasticOut;\n",
"})(amplitude, period);\n",
"\n",
"var elasticInOut = (function custom(a, p) {\n",
" var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n",
"\n",
" function elasticInOut(t) {\n",
" return ((t = t * 2 - 1) < 0\n",
" ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)\n",
" : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;\n",
" }\n",
"\n",
" elasticInOut.amplitude = function(a) { return custom(a, p * tau); };\n",
" elasticInOut.period = function(p) { return custom(a, p); };\n",
"\n",
" return elasticInOut;\n",
"})(amplitude, period);\n",
"\n",
"exports.easeBack = backInOut;\n",
"exports.easeBackIn = backIn;\n",
"exports.easeBackInOut = backInOut;\n",
"exports.easeBackOut = backOut;\n",
"exports.easeBounce = bounceOut;\n",
"exports.easeBounceIn = bounceIn;\n",
"exports.easeBounceInOut = bounceInOut;\n",
"exports.easeBounceOut = bounceOut;\n",
"exports.easeCircle = circleInOut;\n",
"exports.easeCircleIn = circleIn;\n",
"exports.easeCircleInOut = circleInOut;\n",
"exports.easeCircleOut = circleOut;\n",
"exports.easeCubic = cubicInOut;\n",
"exports.easeCubicIn = cubicIn;\n",
"exports.easeCubicInOut = cubicInOut;\n",
"exports.easeCubicOut = cubicOut;\n",
"exports.easeElastic = elasticOut;\n",
"exports.easeElasticIn = elasticIn;\n",
"exports.easeElasticInOut = elasticInOut;\n",
"exports.easeElasticOut = elasticOut;\n",
"exports.easeExp = expInOut;\n",
"exports.easeExpIn = expIn;\n",
"exports.easeExpInOut = expInOut;\n",
"exports.easeExpOut = expOut;\n",
"exports.easeLinear = linear;\n",
"exports.easePoly = polyInOut;\n",
"exports.easePolyIn = polyIn;\n",
"exports.easePolyInOut = polyInOut;\n",
"exports.easePolyOut = polyOut;\n",
"exports.easeQuad = quadInOut;\n",
"exports.easeQuadIn = quadIn;\n",
"exports.easeQuadInOut = quadInOut;\n",
"exports.easeQuadOut = quadOut;\n",
"exports.easeSin = sinInOut;\n",
"exports.easeSinIn = sinIn;\n",
"exports.easeSinInOut = sinInOut;\n",
"exports.easeSinOut = sinOut;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],7:[function(require,module,exports){\n",
"// https://d3js.org/d3-format/ v1.4.2 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"// Computes the decimal coefficient and exponent of the specified number x with\n",
"// significant digits p, where x is positive and p is in [1, 21] or undefined.\n",
"// For example, formatDecimal(1.23) returns [\"123\", 0].\n",
"function formatDecimal(x, p) {\n",
" if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf(\"e\")) < 0) return null; // NaN, +/-Infinity\n",
" var i, coefficient = x.slice(0, i);\n",
"\n",
" // The string returned by toExponential either has the form \\d\\.\\d+e[-+]\\d+\n",
" // (e.g., 1.2e+3) or the form \\de[-+]\\d+ (e.g., 1e+3).\n",
" return [\n",
" coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,\n",
" +x.slice(i + 1)\n",
" ];\n",
"}\n",
"\n",
"function exponent(x) {\n",
" return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;\n",
"}\n",
"\n",
"function formatGroup(grouping, thousands) {\n",
" return function(value, width) {\n",
" var i = value.length,\n",
" t = [],\n",
" j = 0,\n",
" g = grouping[0],\n",
" length = 0;\n",
"\n",
" while (i > 0 && g > 0) {\n",
" if (length + g + 1 > width) g = Math.max(1, width - length);\n",
" t.push(value.substring(i -= g, i + g));\n",
" if ((length += g + 1) > width) break;\n",
" g = grouping[j = (j + 1) % grouping.length];\n",
" }\n",
"\n",
" return t.reverse().join(thousands);\n",
" };\n",
"}\n",
"\n",
"function formatNumerals(numerals) {\n",
" return function(value) {\n",
" return value.replace(/[0-9]/g, function(i) {\n",
" return numerals[+i];\n",
" });\n",
" };\n",
"}\n",
"\n",
"// [[fill]align][sign][symbol][0][width][,][.precision][~][type]\n",
"var re = /^(?:(.)?([<>=^]))?([+\\-( ])?([$#])?(0)?(\\d+)?(,)?(\\.\\d+)?(~)?([a-z%])?$/i;\n",
"\n",
"function formatSpecifier(specifier) {\n",
" if (!(match = re.exec(specifier))) throw new Error(\"invalid format: \" + specifier);\n",
" var match;\n",
" return new FormatSpecifier({\n",
" fill: match[1],\n",
" align: match[2],\n",
" sign: match[3],\n",
" symbol: match[4],\n",
" zero: match[5],\n",
" width: match[6],\n",
" comma: match[7],\n",
" precision: match[8] && match[8].slice(1),\n",
" trim: match[9],\n",
" type: match[10]\n",
" });\n",
"}\n",
"\n",
"formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof\n",
"\n",
"function FormatSpecifier(specifier) {\n",
" this.fill = specifier.fill === undefined ? \" \" : specifier.fill + \"\";\n",
" this.align = specifier.align === undefined ? \">\" : specifier.align + \"\";\n",
" this.sign = specifier.sign === undefined ? \"-\" : specifier.sign + \"\";\n",
" this.symbol = specifier.symbol === undefined ? \"\" : specifier.symbol + \"\";\n",
" this.zero = !!specifier.zero;\n",
" this.width = specifier.width === undefined ? undefined : +specifier.width;\n",
" this.comma = !!specifier.comma;\n",
" this.precision = specifier.precision === undefined ? undefined : +specifier.precision;\n",
" this.trim = !!specifier.trim;\n",
" this.type = specifier.type === undefined ? \"\" : specifier.type + \"\";\n",
"}\n",
"\n",
"FormatSpecifier.prototype.toString = function() {\n",
" return this.fill\n",
" + this.align\n",
" + this.sign\n",
" + this.symbol\n",
" + (this.zero ? \"0\" : \"\")\n",
" + (this.width === undefined ? \"\" : Math.max(1, this.width | 0))\n",
" + (this.comma ? \",\" : \"\")\n",
" + (this.precision === undefined ? \"\" : \".\" + Math.max(0, this.precision | 0))\n",
" + (this.trim ? \"~\" : \"\")\n",
" + this.type;\n",
"};\n",
"\n",
"// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.\n",
"function formatTrim(s) {\n",
" out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {\n",
" switch (s[i]) {\n",
" case \".\": i0 = i1 = i; break;\n",
" case \"0\": if (i0 === 0) i0 = i; i1 = i; break;\n",
" default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;\n",
" }\n",
" }\n",
" return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;\n",
"}\n",
"\n",
"var prefixExponent;\n",
"\n",
"function formatPrefixAuto(x, p) {\n",
" var d = formatDecimal(x, p);\n",
" if (!d) return x + \"\";\n",
" var coefficient = d[0],\n",
" exponent = d[1],\n",
" i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,\n",
" n = coefficient.length;\n",
" return i === n ? coefficient\n",
" : i > n ? coefficient + new Array(i - n + 1).join(\"0\")\n",
" : i > 0 ? coefficient.slice(0, i) + \".\" + coefficient.slice(i)\n",
" : \"0.\" + new Array(1 - i).join(\"0\") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!\n",
"}\n",
"\n",
"function formatRounded(x, p) {\n",
" var d = formatDecimal(x, p);\n",
" if (!d) return x + \"\";\n",
" var coefficient = d[0],\n",
" exponent = d[1];\n",
" return exponent < 0 ? \"0.\" + new Array(-exponent).join(\"0\") + coefficient\n",
" : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + \".\" + coefficient.slice(exponent + 1)\n",
" : coefficient + new Array(exponent - coefficient.length + 2).join(\"0\");\n",
"}\n",
"\n",
"var formatTypes = {\n",
" \"%\": function(x, p) { return (x * 100).toFixed(p); },\n",
" \"b\": function(x) { return Math.round(x).toString(2); },\n",
" \"c\": function(x) { return x + \"\"; },\n",
" \"d\": function(x) { return Math.round(x).toString(10); },\n",
" \"e\": function(x, p) { return x.toExponential(p); },\n",
" \"f\": function(x, p) { return x.toFixed(p); },\n",
" \"g\": function(x, p) { return x.toPrecision(p); },\n",
" \"o\": function(x) { return Math.round(x).toString(8); },\n",
" \"p\": function(x, p) { return formatRounded(x * 100, p); },\n",
" \"r\": formatRounded,\n",
" \"s\": formatPrefixAuto,\n",
" \"X\": function(x) { return Math.round(x).toString(16).toUpperCase(); },\n",
" \"x\": function(x) { return Math.round(x).toString(16); }\n",
"};\n",
"\n",
"function identity(x) {\n",
" return x;\n",
"}\n",
"\n",
"var map = Array.prototype.map,\n",
" prefixes = [\"y\",\"z\",\"a\",\"f\",\"p\",\"n\",\"u\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\",\"Y\"];\n",
"\n",
"function formatLocale(locale) {\n",
" var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map.call(locale.grouping, Number), locale.thousands + \"\"),\n",
" currencyPrefix = locale.currency === undefined ? \"\" : locale.currency[0] + \"\",\n",
" currencySuffix = locale.currency === undefined ? \"\" : locale.currency[1] + \"\",\n",
" decimal = locale.decimal === undefined ? \".\" : locale.decimal + \"\",\n",
" numerals = locale.numerals === undefined ? identity : formatNumerals(map.call(locale.numerals, String)),\n",
" percent = locale.percent === undefined ? \"%\" : locale.percent + \"\",\n",
" minus = locale.minus === undefined ? \"-\" : locale.minus + \"\",\n",
" nan = locale.nan === undefined ? \"NaN\" : locale.nan + \"\";\n",
"\n",
" function newFormat(specifier) {\n",
" specifier = formatSpecifier(specifier);\n",
"\n",
" var fill = specifier.fill,\n",
" align = specifier.align,\n",
" sign = specifier.sign,\n",
" symbol = specifier.symbol,\n",
" zero = specifier.zero,\n",
" width = specifier.width,\n",
" comma = specifier.comma,\n",
" precision = specifier.precision,\n",
" trim = specifier.trim,\n",
" type = specifier.type;\n",
"\n",
" // The \"n\" type is an alias for \",g\".\n",
" if (type === \"n\") comma = true, type = \"g\";\n",
"\n",
" // The \"\" type, and any invalid type, is an alias for \".12~g\".\n",
" else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = \"g\";\n",
"\n",
" // If zero fill is specified, padding goes after sign and before digits.\n",
" if (zero || (fill === \"0\" && align === \"=\")) zero = true, fill = \"0\", align = \"=\";\n",
"\n",
" // Compute the prefix and suffix.\n",
" // For SI-prefix, the suffix is lazily computed.\n",
" var prefix = symbol === \"$\" ? currencyPrefix : symbol === \"#\" && /[boxX]/.test(type) ? \"0\" + type.toLowerCase() : \"\",\n",
" suffix = symbol === \"$\" ? currencySuffix : /[%p]/.test(type) ? percent : \"\";\n",
"\n",
" // What format function should we use?\n",
" // Is this an integer type?\n",
" // Can this type generate exponential notation?\n",
" var formatType = formatTypes[type],\n",
" maybeSuffix = /[defgprs%]/.test(type);\n",
"\n",
" // Set the default precision if not specified,\n",
" // or clamp the specified precision to the supported range.\n",
" // For significant precision, it must be in [1, 21].\n",
" // For fixed precision, it must be in [0, 20].\n",
" precision = precision === undefined ? 6\n",
" : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))\n",
" : Math.max(0, Math.min(20, precision));\n",
"\n",
" function format(value) {\n",
" var valuePrefix = prefix,\n",
" valueSuffix = suffix,\n",
" i, n, c;\n",
"\n",
" if (type === \"c\") {\n",
" valueSuffix = formatType(value) + valueSuffix;\n",
" value = \"\";\n",
" } else {\n",
" value = +value;\n",
"\n",
" // Perform the initial formatting.\n",
" var valueNegative = value < 0;\n",
" value = isNaN(value) ? nan : formatType(Math.abs(value), precision);\n",
"\n",
" // Trim insignificant zeros.\n",
" if (trim) value = formatTrim(value);\n",
"\n",
" // If a negative value rounds to zero during formatting, treat as positive.\n",
" if (valueNegative && +value === 0) valueNegative = false;\n",
"\n",
" // Compute the prefix and suffix.\n",
" valuePrefix = (valueNegative ? (sign === \"(\" ? sign : minus) : sign === \"-\" || sign === \"(\" ? \"\" : sign) + valuePrefix;\n",
"\n",
" valueSuffix = (type === \"s\" ? prefixes[8 + prefixExponent / 3] : \"\") + valueSuffix + (valueNegative && sign === \"(\" ? \")\" : \"\");\n",
"\n",
" // Break the formatted value into the integer \"value\" part that can be\n",
" // grouped, and fractional or exponential \"suffix\" part that is not.\n",
" if (maybeSuffix) {\n",
" i = -1, n = value.length;\n",
" while (++i < n) {\n",
" if (c = value.charCodeAt(i), 48 > c || c > 57) {\n",
" valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;\n",
" value = value.slice(0, i);\n",
" break;\n",
" }\n",
" }\n",
" }\n",
" }\n",
"\n",
" // If the fill character is not \"0\", grouping is applied before padding.\n",
" if (comma && !zero) value = group(value, Infinity);\n",
"\n",
" // Compute the padding.\n",
" var length = valuePrefix.length + value.length + valueSuffix.length,\n",
" padding = length < width ? new Array(width - length + 1).join(fill) : \"\";\n",
"\n",
" // If the fill character is \"0\", grouping is applied after padding.\n",
" if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = \"\";\n",
"\n",
" // Reconstruct the final output based on the desired alignment.\n",
" switch (align) {\n",
" case \"<\": value = valuePrefix + value + valueSuffix + padding; break;\n",
" case \"=\": value = valuePrefix + padding + value + valueSuffix; break;\n",
" case \"^\": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;\n",
" default: value = padding + valuePrefix + value + valueSuffix; break;\n",
" }\n",
"\n",
" return numerals(value);\n",
" }\n",
"\n",
" format.toString = function() {\n",
" return specifier + \"\";\n",
" };\n",
"\n",
" return format;\n",
" }\n",
"\n",
" function formatPrefix(specifier, value) {\n",
" var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = \"f\", specifier)),\n",
" e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,\n",
" k = Math.pow(10, -e),\n",
" prefix = prefixes[8 + e / 3];\n",
" return function(value) {\n",
" return f(k * value) + prefix;\n",
" };\n",
" }\n",
"\n",
" return {\n",
" format: newFormat,\n",
" formatPrefix: formatPrefix\n",
" };\n",
"}\n",
"\n",
"var locale;\n",
"\n",
"defaultLocale({\n",
" decimal: \".\",\n",
" thousands: \",\",\n",
" grouping: [3],\n",
" currency: [\"$\", \"\"],\n",
" minus: \"-\"\n",
"});\n",
"\n",
"function defaultLocale(definition) {\n",
" locale = formatLocale(definition);\n",
" exports.format = locale.format;\n",
" exports.formatPrefix = locale.formatPrefix;\n",
" return locale;\n",
"}\n",
"\n",
"function precisionFixed(step) {\n",
" return Math.max(0, -exponent(Math.abs(step)));\n",
"}\n",
"\n",
"function precisionPrefix(step, value) {\n",
" return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));\n",
"}\n",
"\n",
"function precisionRound(step, max) {\n",
" step = Math.abs(step), max = Math.abs(max) - step;\n",
" return Math.max(0, exponent(max) - exponent(step)) + 1;\n",
"}\n",
"\n",
"exports.FormatSpecifier = FormatSpecifier;\n",
"exports.formatDefaultLocale = defaultLocale;\n",
"exports.formatLocale = formatLocale;\n",
"exports.formatSpecifier = formatSpecifier;\n",
"exports.precisionFixed = precisionFixed;\n",
"exports.precisionPrefix = precisionPrefix;\n",
"exports.precisionRound = precisionRound;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],8:[function(require,module,exports){\n",
"// https://d3js.org/d3-interpolate/ v1.4.0 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-color')) :\n",
"typeof define === 'function' && define.amd ? define(['exports', 'd3-color'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}, global.d3));\n",
"}(this, function (exports, d3Color) { 'use strict';\n",
"\n",
"function basis(t1, v0, v1, v2, v3) {\n",
" var t2 = t1 * t1, t3 = t2 * t1;\n",
" return ((1 - 3 * t1 + 3 * t2 - t3) * v0\n",
" + (4 - 6 * t2 + 3 * t3) * v1\n",
" + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2\n",
" + t3 * v3) / 6;\n",
"}\n",
"\n",
"function basis$1(values) {\n",
" var n = values.length - 1;\n",
" return function(t) {\n",
" var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),\n",
" v1 = values[i],\n",
" v2 = values[i + 1],\n",
" v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,\n",
" v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;\n",
" return basis((t - i / n) * n, v0, v1, v2, v3);\n",
" };\n",
"}\n",
"\n",
"function basisClosed(values) {\n",
" var n = values.length;\n",
" return function(t) {\n",
" var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),\n",
" v0 = values[(i + n - 1) % n],\n",
" v1 = values[i % n],\n",
" v2 = values[(i + 1) % n],\n",
" v3 = values[(i + 2) % n];\n",
" return basis((t - i / n) * n, v0, v1, v2, v3);\n",
" };\n",
"}\n",
"\n",
"function constant(x) {\n",
" return function() {\n",
" return x;\n",
" };\n",
"}\n",
"\n",
"function linear(a, d) {\n",
" return function(t) {\n",
" return a + t * d;\n",
" };\n",
"}\n",
"\n",
"function exponential(a, b, y) {\n",
" return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n",
" return Math.pow(a + t * b, y);\n",
" };\n",
"}\n",
"\n",
"function hue(a, b) {\n",
" var d = b - a;\n",
" return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant(isNaN(a) ? b : a);\n",
"}\n",
"\n",
"function gamma(y) {\n",
" return (y = +y) === 1 ? nogamma : function(a, b) {\n",
" return b - a ? exponential(a, b, y) : constant(isNaN(a) ? b : a);\n",
" };\n",
"}\n",
"\n",
"function nogamma(a, b) {\n",
" var d = b - a;\n",
" return d ? linear(a, d) : constant(isNaN(a) ? b : a);\n",
"}\n",
"\n",
"var rgb = (function rgbGamma(y) {\n",
" var color = gamma(y);\n",
"\n",
" function rgb(start, end) {\n",
" var r = color((start = d3Color.rgb(start)).r, (end = d3Color.rgb(end)).r),\n",
" g = color(start.g, end.g),\n",
" b = color(start.b, end.b),\n",
" opacity = nogamma(start.opacity, end.opacity);\n",
" return function(t) {\n",
" start.r = r(t);\n",
" start.g = g(t);\n",
" start.b = b(t);\n",
" start.opacity = opacity(t);\n",
" return start + \"\";\n",
" };\n",
" }\n",
"\n",
" rgb.gamma = rgbGamma;\n",
"\n",
" return rgb;\n",
"})(1);\n",
"\n",
"function rgbSpline(spline) {\n",
" return function(colors) {\n",
" var n = colors.length,\n",
" r = new Array(n),\n",
" g = new Array(n),\n",
" b = new Array(n),\n",
" i, color;\n",
" for (i = 0; i < n; ++i) {\n",
" color = d3Color.rgb(colors[i]);\n",
" r[i] = color.r || 0;\n",
" g[i] = color.g || 0;\n",
" b[i] = color.b || 0;\n",
" }\n",
" r = spline(r);\n",
" g = spline(g);\n",
" b = spline(b);\n",
" color.opacity = 1;\n",
" return function(t) {\n",
" color.r = r(t);\n",
" color.g = g(t);\n",
" color.b = b(t);\n",
" return color + \"\";\n",
" };\n",
" };\n",
"}\n",
"\n",
"var rgbBasis = rgbSpline(basis$1);\n",
"var rgbBasisClosed = rgbSpline(basisClosed);\n",
"\n",
"function numberArray(a, b) {\n",
" if (!b) b = [];\n",
" var n = a ? Math.min(b.length, a.length) : 0,\n",
" c = b.slice(),\n",
" i;\n",
" return function(t) {\n",
" for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t;\n",
" return c;\n",
" };\n",
"}\n",
"\n",
"function isNumberArray(x) {\n",
" return ArrayBuffer.isView(x) && !(x instanceof DataView);\n",
"}\n",
"\n",
"function array(a, b) {\n",
" return (isNumberArray(b) ? numberArray : genericArray)(a, b);\n",
"}\n",
"\n",
"function genericArray(a, b) {\n",
" var nb = b ? b.length : 0,\n",
" na = a ? Math.min(nb, a.length) : 0,\n",
" x = new Array(na),\n",
" c = new Array(nb),\n",
" i;\n",
"\n",
" for (i = 0; i < na; ++i) x[i] = value(a[i], b[i]);\n",
" for (; i < nb; ++i) c[i] = b[i];\n",
"\n",
" return function(t) {\n",
" for (i = 0; i < na; ++i) c[i] = x[i](t);\n",
" return c;\n",
" };\n",
"}\n",
"\n",
"function date(a, b) {\n",
" var d = new Date;\n",
" return a = +a, b = +b, function(t) {\n",
" return d.setTime(a * (1 - t) + b * t), d;\n",
" };\n",
"}\n",
"\n",
"function number(a, b) {\n",
" return a = +a, b = +b, function(t) {\n",
" return a * (1 - t) + b * t;\n",
" };\n",
"}\n",
"\n",
"function object(a, b) {\n",
" var i = {},\n",
" c = {},\n",
" k;\n",
"\n",
" if (a === null || typeof a !== \"object\") a = {};\n",
" if (b === null || typeof b !== \"object\") b = {};\n",
"\n",
" for (k in b) {\n",
" if (k in a) {\n",
" i[k] = value(a[k], b[k]);\n",
" } else {\n",
" c[k] = b[k];\n",
" }\n",
" }\n",
"\n",
" return function(t) {\n",
" for (k in i) c[k] = i[k](t);\n",
" return c;\n",
" };\n",
"}\n",
"\n",
"var reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n",
" reB = new RegExp(reA.source, \"g\");\n",
"\n",
"function zero(b) {\n",
" return function() {\n",
" return b;\n",
" };\n",
"}\n",
"\n",
"function one(b) {\n",
" return function(t) {\n",
" return b(t) + \"\";\n",
" };\n",
"}\n",
"\n",
"function string(a, b) {\n",
" var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n",
" am, // current match in a\n",
" bm, // current match in b\n",
" bs, // string preceding current number in b, if any\n",
" i = -1, // index in s\n",
" s = [], // string constants and placeholders\n",
" q = []; // number interpolators\n",
"\n",
" // Coerce inputs to strings.\n",
" a = a + \"\", b = b + \"\";\n",
"\n",
" // Interpolate pairs of numbers in a & b.\n",
" while ((am = reA.exec(a))\n",
" && (bm = reB.exec(b))) {\n",
" if ((bs = bm.index) > bi) { // a string precedes the next number in b\n",
" bs = b.slice(bi, bs);\n",
" if (s[i]) s[i] += bs; // coalesce with previous string\n",
" else s[++i] = bs;\n",
" }\n",
" if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n",
" if (s[i]) s[i] += bm; // coalesce with previous string\n",
" else s[++i] = bm;\n",
" } else { // interpolate non-matching numbers\n",
" s[++i] = null;\n",
" q.push({i: i, x: number(am, bm)});\n",
" }\n",
" bi = reB.lastIndex;\n",
" }\n",
"\n",
" // Add remains of b.\n",
" if (bi < b.length) {\n",
" bs = b.slice(bi);\n",
" if (s[i]) s[i] += bs; // coalesce with previous string\n",
" else s[++i] = bs;\n",
" }\n",
"\n",
" // Special optimization for only a single match.\n",
" // Otherwise, interpolate each of the numbers and rejoin the string.\n",
" return s.length < 2 ? (q[0]\n",
" ? one(q[0].x)\n",
" : zero(b))\n",
" : (b = q.length, function(t) {\n",
" for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n",
" return s.join(\"\");\n",
" });\n",
"}\n",
"\n",
"function value(a, b) {\n",
" var t = typeof b, c;\n",
" return b == null || t === \"boolean\" ? constant(b)\n",
" : (t === \"number\" ? number\n",
" : t === \"string\" ? ((c = d3Color.color(b)) ? (b = c, rgb) : string)\n",
" : b instanceof d3Color.color ? rgb\n",
" : b instanceof Date ? date\n",
" : isNumberArray(b) ? numberArray\n",
" : Array.isArray(b) ? genericArray\n",
" : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? object\n",
" : number)(a, b);\n",
"}\n",
"\n",
"function discrete(range) {\n",
" var n = range.length;\n",
" return function(t) {\n",
" return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n",
" };\n",
"}\n",
"\n",
"function hue$1(a, b) {\n",
" var i = hue(+a, +b);\n",
" return function(t) {\n",
" var x = i(t);\n",
" return x - 360 * Math.floor(x / 360);\n",
" };\n",
"}\n",
"\n",
"function round(a, b) {\n",
" return a = +a, b = +b, function(t) {\n",
" return Math.round(a * (1 - t) + b * t);\n",
" };\n",
"}\n",
"\n",
"var degrees = 180 / Math.PI;\n",
"\n",
"var identity = {\n",
" translateX: 0,\n",
" translateY: 0,\n",
" rotate: 0,\n",
" skewX: 0,\n",
" scaleX: 1,\n",
" scaleY: 1\n",
"};\n",
"\n",
"function decompose(a, b, c, d, e, f) {\n",
" var scaleX, scaleY, skewX;\n",
" if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n",
" if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n",
" if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n",
" if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n",
" return {\n",
" translateX: e,\n",
" translateY: f,\n",
" rotate: Math.atan2(b, a) * degrees,\n",
" skewX: Math.atan(skewX) * degrees,\n",
" scaleX: scaleX,\n",
" scaleY: scaleY\n",
" };\n",
"}\n",
"\n",
"var cssNode,\n",
" cssRoot,\n",
" cssView,\n",
" svgNode;\n",
"\n",
"function parseCss(value) {\n",
" if (value === \"none\") return identity;\n",
" if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n",
" cssNode.style.transform = value;\n",
" value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n",
" cssRoot.removeChild(cssNode);\n",
" value = value.slice(7, -1).split(\",\");\n",
" return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n",
"}\n",
"\n",
"function parseSvg(value) {\n",
" if (value == null) return identity;\n",
" if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n",
" svgNode.setAttribute(\"transform\", value);\n",
" if (!(value = svgNode.transform.baseVal.consolidate())) return identity;\n",
" value = value.matrix;\n",
" return decompose(value.a, value.b, value.c, value.d, value.e, value.f);\n",
"}\n",
"\n",
"function interpolateTransform(parse, pxComma, pxParen, degParen) {\n",
"\n",
" function pop(s) {\n",
" return s.length ? s.pop() + \" \" : \"\";\n",
" }\n",
"\n",
" function translate(xa, ya, xb, yb, s, q) {\n",
" if (xa !== xb || ya !== yb) {\n",
" var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n",
" q.push({i: i - 4, x: number(xa, xb)}, {i: i - 2, x: number(ya, yb)});\n",
" } else if (xb || yb) {\n",
" s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n",
" }\n",
" }\n",
"\n",
" function rotate(a, b, s, q) {\n",
" if (a !== b) {\n",
" if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n",
" q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: number(a, b)});\n",
" } else if (b) {\n",
" s.push(pop(s) + \"rotate(\" + b + degParen);\n",
" }\n",
" }\n",
"\n",
" function skewX(a, b, s, q) {\n",
" if (a !== b) {\n",
" q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: number(a, b)});\n",
" } else if (b) {\n",
" s.push(pop(s) + \"skewX(\" + b + degParen);\n",
" }\n",
" }\n",
"\n",
" function scale(xa, ya, xb, yb, s, q) {\n",
" if (xa !== xb || ya !== yb) {\n",
" var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n",
" q.push({i: i - 4, x: number(xa, xb)}, {i: i - 2, x: number(ya, yb)});\n",
" } else if (xb !== 1 || yb !== 1) {\n",
" s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n",
" }\n",
" }\n",
"\n",
" return function(a, b) {\n",
" var s = [], // string constants and placeholders\n",
" q = []; // number interpolators\n",
" a = parse(a), b = parse(b);\n",
" translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n",
" rotate(a.rotate, b.rotate, s, q);\n",
" skewX(a.skewX, b.skewX, s, q);\n",
" scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n",
" a = b = null; // gc\n",
" return function(t) {\n",
" var i = -1, n = q.length, o;\n",
" while (++i < n) s[(o = q[i]).i] = o.x(t);\n",
" return s.join(\"\");\n",
" };\n",
" };\n",
"}\n",
"\n",
"var interpolateTransformCss = interpolateTransform(parseCss, \"px, \", \"px)\", \"deg)\");\n",
"var interpolateTransformSvg = interpolateTransform(parseSvg, \", \", \")\", \")\");\n",
"\n",
"var rho = Math.SQRT2,\n",
" rho2 = 2,\n",
" rho4 = 4,\n",
" epsilon2 = 1e-12;\n",
"\n",
"function cosh(x) {\n",
" return ((x = Math.exp(x)) + 1 / x) / 2;\n",
"}\n",
"\n",
"function sinh(x) {\n",
" return ((x = Math.exp(x)) - 1 / x) / 2;\n",
"}\n",
"\n",
"function tanh(x) {\n",
" return ((x = Math.exp(2 * x)) - 1) / (x + 1);\n",
"}\n",
"\n",
"// p0 = [ux0, uy0, w0]\n",
"// p1 = [ux1, uy1, w1]\n",
"function zoom(p0, p1) {\n",
" var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],\n",
" ux1 = p1[0], uy1 = p1[1], w1 = p1[2],\n",
" dx = ux1 - ux0,\n",
" dy = uy1 - uy0,\n",
" d2 = dx * dx + dy * dy,\n",
" i,\n",
" S;\n",
"\n",
" // Special case for u0 u1.\n",
" if (d2 < epsilon2) {\n",
" S = Math.log(w1 / w0) / rho;\n",
" i = function(t) {\n",
" return [\n",
" ux0 + t * dx,\n",
" uy0 + t * dy,\n",
" w0 * Math.exp(rho * t * S)\n",
" ];\n",
" };\n",
" }\n",
"\n",
" // General case.\n",
" else {\n",
" var d1 = Math.sqrt(d2),\n",
" b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),\n",
" b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),\n",
" r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),\n",
" r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);\n",
" S = (r1 - r0) / rho;\n",
" i = function(t) {\n",
" var s = t * S,\n",
" coshr0 = cosh(r0),\n",
" u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));\n",
" return [\n",
" ux0 + u * dx,\n",
" uy0 + u * dy,\n",
" w0 * coshr0 / cosh(rho * s + r0)\n",
" ];\n",
" };\n",
" }\n",
"\n",
" i.duration = S * 1000;\n",
"\n",
" return i;\n",
"}\n",
"\n",
"function hsl(hue) {\n",
" return function(start, end) {\n",
" var h = hue((start = d3Color.hsl(start)).h, (end = d3Color.hsl(end)).h),\n",
" s = nogamma(start.s, end.s),\n",
" l = nogamma(start.l, end.l),\n",
" opacity = nogamma(start.opacity, end.opacity);\n",
" return function(t) {\n",
" start.h = h(t);\n",
" start.s = s(t);\n",
" start.l = l(t);\n",
" start.opacity = opacity(t);\n",
" return start + \"\";\n",
" };\n",
" }\n",
"}\n",
"\n",
"var hsl$1 = hsl(hue);\n",
"var hslLong = hsl(nogamma);\n",
"\n",
"function lab(start, end) {\n",
" var l = nogamma((start = d3Color.lab(start)).l, (end = d3Color.lab(end)).l),\n",
" a = nogamma(start.a, end.a),\n",
" b = nogamma(start.b, end.b),\n",
" opacity = nogamma(start.opacity, end.opacity);\n",
" return function(t) {\n",
" start.l = l(t);\n",
" start.a = a(t);\n",
" start.b = b(t);\n",
" start.opacity = opacity(t);\n",
" return start + \"\";\n",
" };\n",
"}\n",
"\n",
"function hcl(hue) {\n",
" return function(start, end) {\n",
" var h = hue((start = d3Color.hcl(start)).h, (end = d3Color.hcl(end)).h),\n",
" c = nogamma(start.c, end.c),\n",
" l = nogamma(start.l, end.l),\n",
" opacity = nogamma(start.opacity, end.opacity);\n",
" return function(t) {\n",
" start.h = h(t);\n",
" start.c = c(t);\n",
" start.l = l(t);\n",
" start.opacity = opacity(t);\n",
" return start + \"\";\n",
" };\n",
" }\n",
"}\n",
"\n",
"var hcl$1 = hcl(hue);\n",
"var hclLong = hcl(nogamma);\n",
"\n",
"function cubehelix(hue) {\n",
" return (function cubehelixGamma(y) {\n",
" y = +y;\n",
"\n",
" function cubehelix(start, end) {\n",
" var h = hue((start = d3Color.cubehelix(start)).h, (end = d3Color.cubehelix(end)).h),\n",
" s = nogamma(start.s, end.s),\n",
" l = nogamma(start.l, end.l),\n",
" opacity = nogamma(start.opacity, end.opacity);\n",
" return function(t) {\n",
" start.h = h(t);\n",
" start.s = s(t);\n",
" start.l = l(Math.pow(t, y));\n",
" start.opacity = opacity(t);\n",
" return start + \"\";\n",
" };\n",
" }\n",
"\n",
" cubehelix.gamma = cubehelixGamma;\n",
"\n",
" return cubehelix;\n",
" })(1);\n",
"}\n",
"\n",
"var cubehelix$1 = cubehelix(hue);\n",
"var cubehelixLong = cubehelix(nogamma);\n",
"\n",
"function piecewise(interpolate, values) {\n",
" var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);\n",
" while (i < n) I[i] = interpolate(v, v = values[++i]);\n",
" return function(t) {\n",
" var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));\n",
" return I[i](t - i);\n",
" };\n",
"}\n",
"\n",
"function quantize(interpolator, n) {\n",
" var samples = new Array(n);\n",
" for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));\n",
" return samples;\n",
"}\n",
"\n",
"exports.interpolate = value;\n",
"exports.interpolateArray = array;\n",
"exports.interpolateBasis = basis$1;\n",
"exports.interpolateBasisClosed = basisClosed;\n",
"exports.interpolateCubehelix = cubehelix$1;\n",
"exports.interpolateCubehelixLong = cubehelixLong;\n",
"exports.interpolateDate = date;\n",
"exports.interpolateDiscrete = discrete;\n",
"exports.interpolateHcl = hcl$1;\n",
"exports.interpolateHclLong = hclLong;\n",
"exports.interpolateHsl = hsl$1;\n",
"exports.interpolateHslLong = hslLong;\n",
"exports.interpolateHue = hue$1;\n",
"exports.interpolateLab = lab;\n",
"exports.interpolateNumber = number;\n",
"exports.interpolateNumberArray = numberArray;\n",
"exports.interpolateObject = object;\n",
"exports.interpolateRgb = rgb;\n",
"exports.interpolateRgbBasis = rgbBasis;\n",
"exports.interpolateRgbBasisClosed = rgbBasisClosed;\n",
"exports.interpolateRound = round;\n",
"exports.interpolateString = string;\n",
"exports.interpolateTransformCss = interpolateTransformCss;\n",
"exports.interpolateTransformSvg = interpolateTransformSvg;\n",
"exports.interpolateZoom = zoom;\n",
"exports.piecewise = piecewise;\n",
"exports.quantize = quantize;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{\"d3-color\":4}],9:[function(require,module,exports){\n",
"// https://d3js.org/d3-path/ v1.0.9 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"var pi = Math.PI,\n",
" tau = 2 * pi,\n",
" epsilon = 1e-6,\n",
" tauEpsilon = tau - epsilon;\n",
"\n",
"function Path() {\n",
" this._x0 = this._y0 = // start of current subpath\n",
" this._x1 = this._y1 = null; // end of current subpath\n",
" this._ = \"\";\n",
"}\n",
"\n",
"function path() {\n",
" return new Path;\n",
"}\n",
"\n",
"Path.prototype = path.prototype = {\n",
" constructor: Path,\n",
" moveTo: function(x, y) {\n",
" this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n",
" },\n",
" closePath: function() {\n",
" if (this._x1 !== null) {\n",
" this._x1 = this._x0, this._y1 = this._y0;\n",
" this._ += \"Z\";\n",
" }\n",
" },\n",
" lineTo: function(x, y) {\n",
" this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n",
" },\n",
" quadraticCurveTo: function(x1, y1, x, y) {\n",
" this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n",
" },\n",
" bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n",
" this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n",
" },\n",
" arcTo: function(x1, y1, x2, y2, r) {\n",
" x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n",
" var x0 = this._x1,\n",
" y0 = this._y1,\n",
" x21 = x2 - x1,\n",
" y21 = y2 - y1,\n",
" x01 = x0 - x1,\n",
" y01 = y0 - y1,\n",
" l01_2 = x01 * x01 + y01 * y01;\n",
"\n",
" // Is the radius negative? Error.\n",
" if (r < 0) throw new Error(\"negative radius: \" + r);\n",
"\n",
" // Is this path empty? Move to (x1,y1).\n",
" if (this._x1 === null) {\n",
" this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n",
" }\n",
"\n",
" // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n",
" else if (!(l01_2 > epsilon));\n",
"\n",
" // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n",
" // Equivalently, is (x1,y1) coincident with (x2,y2)?\n",
" // Or, is the radius zero? Line to (x1,y1).\n",
" else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n",
" this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n",
" }\n",
"\n",
" // Otherwise, draw an arc!\n",
" else {\n",
" var x20 = x2 - x0,\n",
" y20 = y2 - y0,\n",
" l21_2 = x21 * x21 + y21 * y21,\n",
" l20_2 = x20 * x20 + y20 * y20,\n",
" l21 = Math.sqrt(l21_2),\n",
" l01 = Math.sqrt(l01_2),\n",
" l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n",
" t01 = l / l01,\n",
" t21 = l / l21;\n",
"\n",
" // If the start tangent is not coincident with (x0,y0), line to.\n",
" if (Math.abs(t01 - 1) > epsilon) {\n",
" this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n",
" }\n",
"\n",
" this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n",
" }\n",
" },\n",
" arc: function(x, y, r, a0, a1, ccw) {\n",
" x = +x, y = +y, r = +r, ccw = !!ccw;\n",
" var dx = r * Math.cos(a0),\n",
" dy = r * Math.sin(a0),\n",
" x0 = x + dx,\n",
" y0 = y + dy,\n",
" cw = 1 ^ ccw,\n",
" da = ccw ? a0 - a1 : a1 - a0;\n",
"\n",
" // Is the radius negative? Error.\n",
" if (r < 0) throw new Error(\"negative radius: \" + r);\n",
"\n",
" // Is this path empty? Move to (x0,y0).\n",
" if (this._x1 === null) {\n",
" this._ += \"M\" + x0 + \",\" + y0;\n",
" }\n",
"\n",
" // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n",
" else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n",
" this._ += \"L\" + x0 + \",\" + y0;\n",
" }\n",
"\n",
" // Is this arc empty? We're done.\n",
" if (!r) return;\n",
"\n",
" // Does the angle go the wrong way? Flip the direction.\n",
" if (da < 0) da = da % tau + tau;\n",
"\n",
" // Is this a complete circle? Draw two arcs to complete the circle.\n",
" if (da > tauEpsilon) {\n",
" this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n",
" }\n",
"\n",
" // Is this arc non-empty? Draw an arc!\n",
" else if (da > epsilon) {\n",
" this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n",
" }\n",
" },\n",
" rect: function(x, y, w, h) {\n",
" this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n",
" },\n",
" toString: function() {\n",
" return this._;\n",
" }\n",
"};\n",
"\n",
"exports.path = path;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],10:[function(require,module,exports){\n",
"// https://d3js.org/d3-scale/ v3.2.1 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-interpolate'), require('d3-format'), require('d3-time'), require('d3-time-format')) :\n",
"typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-interpolate', 'd3-format', 'd3-time', 'd3-time-format'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3, global.d3, global.d3, global.d3));\n",
"}(this, function (exports, d3Array, d3Interpolate, d3Format, d3Time, d3TimeFormat) { 'use strict';\n",
"\n",
"function initRange(domain, range) {\n",
" switch (arguments.length) {\n",
" case 0: break;\n",
" case 1: this.range(domain); break;\n",
" default: this.range(range).domain(domain); break;\n",
" }\n",
" return this;\n",
"}\n",
"\n",
"function initInterpolator(domain, interpolator) {\n",
" switch (arguments.length) {\n",
" case 0: break;\n",
" case 1: {\n",
" if (typeof domain === \"function\") this.interpolator(domain);\n",
" else this.range(domain);\n",
" break;\n",
" }\n",
" default: {\n",
" this.domain(domain);\n",
" if (typeof interpolator === \"function\") this.interpolator(interpolator);\n",
" else this.range(interpolator);\n",
" break;\n",
" }\n",
" }\n",
" return this;\n",
"}\n",
"\n",
"const implicit = Symbol(\"implicit\");\n",
"\n",
"function ordinal() {\n",
" var index = new Map(),\n",
" domain = [],\n",
" range = [],\n",
" unknown = implicit;\n",
"\n",
" function scale(d) {\n",
" var key = d + \"\", i = index.get(key);\n",
" if (!i) {\n",
" if (unknown !== implicit) return unknown;\n",
" index.set(key, i = domain.push(d));\n",
" }\n",
" return range[(i - 1) % range.length];\n",
" }\n",
"\n",
" scale.domain = function(_) {\n",
" if (!arguments.length) return domain.slice();\n",
" domain = [], index = new Map();\n",
" for (const value of _) {\n",
" const key = value + \"\";\n",
" if (index.has(key)) continue;\n",
" index.set(key, domain.push(value));\n",
" }\n",
" return scale;\n",
" };\n",
"\n",
" scale.range = function(_) {\n",
" return arguments.length ? (range = Array.from(_), scale) : range.slice();\n",
" };\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return ordinal(domain, range).unknown(unknown);\n",
" };\n",
"\n",
" initRange.apply(scale, arguments);\n",
"\n",
" return scale;\n",
"}\n",
"\n",
"function band() {\n",
" var scale = ordinal().unknown(undefined),\n",
" domain = scale.domain,\n",
" ordinalRange = scale.range,\n",
" r0 = 0,\n",
" r1 = 1,\n",
" step,\n",
" bandwidth,\n",
" round = false,\n",
" paddingInner = 0,\n",
" paddingOuter = 0,\n",
" align = 0.5;\n",
"\n",
" delete scale.unknown;\n",
"\n",
" function rescale() {\n",
" var n = domain().length,\n",
" reverse = r1 < r0,\n",
" start = reverse ? r1 : r0,\n",
" stop = reverse ? r0 : r1;\n",
" step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);\n",
" if (round) step = Math.floor(step);\n",
" start += (stop - start - step * (n - paddingInner)) * align;\n",
" bandwidth = step * (1 - paddingInner);\n",
" if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);\n",
" var values = d3Array.range(n).map(function(i) { return start + step * i; });\n",
" return ordinalRange(reverse ? values.reverse() : values);\n",
" }\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? (domain(_), rescale()) : domain();\n",
" };\n",
"\n",
" scale.range = function(_) {\n",
" return arguments.length ? ([r0, r1] = _, r0 = +r0, r1 = +r1, rescale()) : [r0, r1];\n",
" };\n",
"\n",
" scale.rangeRound = function(_) {\n",
" return [r0, r1] = _, r0 = +r0, r1 = +r1, round = true, rescale();\n",
" };\n",
"\n",
" scale.bandwidth = function() {\n",
" return bandwidth;\n",
" };\n",
"\n",
" scale.step = function() {\n",
" return step;\n",
" };\n",
"\n",
" scale.round = function(_) {\n",
" return arguments.length ? (round = !!_, rescale()) : round;\n",
" };\n",
"\n",
" scale.padding = function(_) {\n",
" return arguments.length ? (paddingInner = Math.min(1, paddingOuter = +_), rescale()) : paddingInner;\n",
" };\n",
"\n",
" scale.paddingInner = function(_) {\n",
" return arguments.length ? (paddingInner = Math.min(1, _), rescale()) : paddingInner;\n",
" };\n",
"\n",
" scale.paddingOuter = function(_) {\n",
" return arguments.length ? (paddingOuter = +_, rescale()) : paddingOuter;\n",
" };\n",
"\n",
" scale.align = function(_) {\n",
" return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return band(domain(), [r0, r1])\n",
" .round(round)\n",
" .paddingInner(paddingInner)\n",
" .paddingOuter(paddingOuter)\n",
" .align(align);\n",
" };\n",
"\n",
" return initRange.apply(rescale(), arguments);\n",
"}\n",
"\n",
"function pointish(scale) {\n",
" var copy = scale.copy;\n",
"\n",
" scale.padding = scale.paddingOuter;\n",
" delete scale.paddingInner;\n",
" delete scale.paddingOuter;\n",
"\n",
" scale.copy = function() {\n",
" return pointish(copy());\n",
" };\n",
"\n",
" return scale;\n",
"}\n",
"\n",
"function point() {\n",
" return pointish(band.apply(null, arguments).paddingInner(1));\n",
"}\n",
"\n",
"function constant(x) {\n",
" return function() {\n",
" return x;\n",
" };\n",
"}\n",
"\n",
"function number(x) {\n",
" return +x;\n",
"}\n",
"\n",
"var unit = [0, 1];\n",
"\n",
"function identity(x) {\n",
" return x;\n",
"}\n",
"\n",
"function normalize(a, b) {\n",
" return (b -= (a = +a))\n",
" ? function(x) { return (x - a) / b; }\n",
" : constant(isNaN(b) ? NaN : 0.5);\n",
"}\n",
"\n",
"function clamper(a, b) {\n",
" var t;\n",
" if (a > b) t = a, a = b, b = t;\n",
" return function(x) { return Math.max(a, Math.min(b, x)); };\n",
"}\n",
"\n",
"// normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].\n",
"// interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].\n",
"function bimap(domain, range, interpolate) {\n",
" var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];\n",
" if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);\n",
" else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);\n",
" return function(x) { return r0(d0(x)); };\n",
"}\n",
"\n",
"function polymap(domain, range, interpolate) {\n",
" var j = Math.min(domain.length, range.length) - 1,\n",
" d = new Array(j),\n",
" r = new Array(j),\n",
" i = -1;\n",
"\n",
" // Reverse descending domains.\n",
" if (domain[j] < domain[0]) {\n",
" domain = domain.slice().reverse();\n",
" range = range.slice().reverse();\n",
" }\n",
"\n",
" while (++i < j) {\n",
" d[i] = normalize(domain[i], domain[i + 1]);\n",
" r[i] = interpolate(range[i], range[i + 1]);\n",
" }\n",
"\n",
" return function(x) {\n",
" var i = d3Array.bisect(domain, x, 1, j) - 1;\n",
" return r[i](d[i](x));\n",
" };\n",
"}\n",
"\n",
"function copy(source, target) {\n",
" return target\n",
" .domain(source.domain())\n",
" .range(source.range())\n",
" .interpolate(source.interpolate())\n",
" .clamp(source.clamp())\n",
" .unknown(source.unknown());\n",
"}\n",
"\n",
"function transformer() {\n",
" var domain = unit,\n",
" range = unit,\n",
" interpolate = d3Interpolate.interpolate,\n",
" transform,\n",
" untransform,\n",
" unknown,\n",
" clamp = identity,\n",
" piecewise,\n",
" output,\n",
" input;\n",
"\n",
" function rescale() {\n",
" var n = Math.min(domain.length, range.length);\n",
" if (clamp !== identity) clamp = clamper(domain[0], domain[n - 1]);\n",
" piecewise = n > 2 ? polymap : bimap;\n",
" output = input = null;\n",
" return scale;\n",
" }\n",
"\n",
" function scale(x) {\n",
" return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));\n",
" }\n",
"\n",
" scale.invert = function(y) {\n",
" return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3Interpolate.interpolateNumber)))(y)));\n",
" };\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? (domain = Array.from(_, number), rescale()) : domain.slice();\n",
" };\n",
"\n",
" scale.range = function(_) {\n",
" return arguments.length ? (range = Array.from(_), rescale()) : range.slice();\n",
" };\n",
"\n",
" scale.rangeRound = function(_) {\n",
" return range = Array.from(_), interpolate = d3Interpolate.interpolateRound, rescale();\n",
" };\n",
"\n",
" scale.clamp = function(_) {\n",
" return arguments.length ? (clamp = _ ? true : identity, rescale()) : clamp !== identity;\n",
" };\n",
"\n",
" scale.interpolate = function(_) {\n",
" return arguments.length ? (interpolate = _, rescale()) : interpolate;\n",
" };\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" return function(t, u) {\n",
" transform = t, untransform = u;\n",
" return rescale();\n",
" };\n",
"}\n",
"\n",
"function continuous() {\n",
" return transformer()(identity, identity);\n",
"}\n",
"\n",
"function tickFormat(start, stop, count, specifier) {\n",
" var step = d3Array.tickStep(start, stop, count),\n",
" precision;\n",
" specifier = d3Format.formatSpecifier(specifier == null ? \",f\" : specifier);\n",
" switch (specifier.type) {\n",
" case \"s\": {\n",
" var value = Math.max(Math.abs(start), Math.abs(stop));\n",
" if (specifier.precision == null && !isNaN(precision = d3Format.precisionPrefix(step, value))) specifier.precision = precision;\n",
" return d3Format.formatPrefix(specifier, value);\n",
" }\n",
" case \"\":\n",
" case \"e\":\n",
" case \"g\":\n",
" case \"p\":\n",
" case \"r\": {\n",
" if (specifier.precision == null && !isNaN(precision = d3Format.precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === \"e\");\n",
" break;\n",
" }\n",
" case \"f\":\n",
" case \"%\": {\n",
" if (specifier.precision == null && !isNaN(precision = d3Format.precisionFixed(step))) specifier.precision = precision - (specifier.type === \"%\") * 2;\n",
" break;\n",
" }\n",
" }\n",
" return d3Format.format(specifier);\n",
"}\n",
"\n",
"function linearish(scale) {\n",
" var domain = scale.domain;\n",
"\n",
" scale.ticks = function(count) {\n",
" var d = domain();\n",
" return d3Array.ticks(d[0], d[d.length - 1], count == null ? 10 : count);\n",
" };\n",
"\n",
" scale.tickFormat = function(count, specifier) {\n",
" var d = domain();\n",
" return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);\n",
" };\n",
"\n",
" scale.nice = function(count) {\n",
" if (count == null) count = 10;\n",
"\n",
" var d = domain(),\n",
" i0 = 0,\n",
" i1 = d.length - 1,\n",
" start = d[i0],\n",
" stop = d[i1],\n",
" step;\n",
"\n",
" if (stop < start) {\n",
" step = start, start = stop, stop = step;\n",
" step = i0, i0 = i1, i1 = step;\n",
" }\n",
"\n",
" step = d3Array.tickIncrement(start, stop, count);\n",
"\n",
" if (step > 0) {\n",
" start = Math.floor(start / step) * step;\n",
" stop = Math.ceil(stop / step) * step;\n",
" step = d3Array.tickIncrement(start, stop, count);\n",
" } else if (step < 0) {\n",
" start = Math.ceil(start * step) / step;\n",
" stop = Math.floor(stop * step) / step;\n",
" step = d3Array.tickIncrement(start, stop, count);\n",
" }\n",
"\n",
" if (step > 0) {\n",
" d[i0] = Math.floor(start / step) * step;\n",
" d[i1] = Math.ceil(stop / step) * step;\n",
" domain(d);\n",
" } else if (step < 0) {\n",
" d[i0] = Math.ceil(start * step) / step;\n",
" d[i1] = Math.floor(stop * step) / step;\n",
" domain(d);\n",
" }\n",
"\n",
" return scale;\n",
" };\n",
"\n",
" return scale;\n",
"}\n",
"\n",
"function linear() {\n",
" var scale = continuous();\n",
"\n",
" scale.copy = function() {\n",
" return copy(scale, linear());\n",
" };\n",
"\n",
" initRange.apply(scale, arguments);\n",
"\n",
" return linearish(scale);\n",
"}\n",
"\n",
"function identity$1(domain) {\n",
" var unknown;\n",
"\n",
" function scale(x) {\n",
" return isNaN(x = +x) ? unknown : x;\n",
" }\n",
"\n",
" scale.invert = scale;\n",
"\n",
" scale.domain = scale.range = function(_) {\n",
" return arguments.length ? (domain = Array.from(_, number), scale) : domain.slice();\n",
" };\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return identity$1(domain).unknown(unknown);\n",
" };\n",
"\n",
" domain = arguments.length ? Array.from(domain, number) : [0, 1];\n",
"\n",
" return linearish(scale);\n",
"}\n",
"\n",
"function nice(domain, interval) {\n",
" domain = domain.slice();\n",
"\n",
" var i0 = 0,\n",
" i1 = domain.length - 1,\n",
" x0 = domain[i0],\n",
" x1 = domain[i1],\n",
" t;\n",
"\n",
" if (x1 < x0) {\n",
" t = i0, i0 = i1, i1 = t;\n",
" t = x0, x0 = x1, x1 = t;\n",
" }\n",
"\n",
" domain[i0] = interval.floor(x0);\n",
" domain[i1] = interval.ceil(x1);\n",
" return domain;\n",
"}\n",
"\n",
"function transformLog(x) {\n",
" return Math.log(x);\n",
"}\n",
"\n",
"function transformExp(x) {\n",
" return Math.exp(x);\n",
"}\n",
"\n",
"function transformLogn(x) {\n",
" return -Math.log(-x);\n",
"}\n",
"\n",
"function transformExpn(x) {\n",
" return -Math.exp(-x);\n",
"}\n",
"\n",
"function pow10(x) {\n",
" return isFinite(x) ? +(\"1e\" + x) : x < 0 ? 0 : x;\n",
"}\n",
"\n",
"function powp(base) {\n",
" return base === 10 ? pow10\n",
" : base === Math.E ? Math.exp\n",
" : function(x) { return Math.pow(base, x); };\n",
"}\n",
"\n",
"function logp(base) {\n",
" return base === Math.E ? Math.log\n",
" : base === 10 && Math.log10\n",
" || base === 2 && Math.log2\n",
" || (base = Math.log(base), function(x) { return Math.log(x) / base; });\n",
"}\n",
"\n",
"function reflect(f) {\n",
" return function(x) {\n",
" return -f(-x);\n",
" };\n",
"}\n",
"\n",
"function loggish(transform) {\n",
" var scale = transform(transformLog, transformExp),\n",
" domain = scale.domain,\n",
" base = 10,\n",
" logs,\n",
" pows;\n",
"\n",
" function rescale() {\n",
" logs = logp(base), pows = powp(base);\n",
" if (domain()[0] < 0) {\n",
" logs = reflect(logs), pows = reflect(pows);\n",
" transform(transformLogn, transformExpn);\n",
" } else {\n",
" transform(transformLog, transformExp);\n",
" }\n",
" return scale;\n",
" }\n",
"\n",
" scale.base = function(_) {\n",
" return arguments.length ? (base = +_, rescale()) : base;\n",
" };\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? (domain(_), rescale()) : domain();\n",
" };\n",
"\n",
" scale.ticks = function(count) {\n",
" var d = domain(),\n",
" u = d[0],\n",
" v = d[d.length - 1],\n",
" r;\n",
"\n",
" if (r = v < u) i = u, u = v, v = i;\n",
"\n",
" var i = logs(u),\n",
" j = logs(v),\n",
" p,\n",
" k,\n",
" t,\n",
" n = count == null ? 10 : +count,\n",
" z = [];\n",
"\n",
" if (!(base % 1) && j - i < n) {\n",
" i = Math.floor(i), j = Math.ceil(j);\n",
" if (u > 0) for (; i <= j; ++i) {\n",
" for (k = 1, p = pows(i); k < base; ++k) {\n",
" t = p * k;\n",
" if (t < u) continue;\n",
" if (t > v) break;\n",
" z.push(t);\n",
" }\n",
" } else for (; i <= j; ++i) {\n",
" for (k = base - 1, p = pows(i); k >= 1; --k) {\n",
" t = p * k;\n",
" if (t < u) continue;\n",
" if (t > v) break;\n",
" z.push(t);\n",
" }\n",
" }\n",
" if (z.length * 2 < n) z = d3Array.ticks(u, v, n);\n",
" } else {\n",
" z = d3Array.ticks(i, j, Math.min(j - i, n)).map(pows);\n",
" }\n",
"\n",
" return r ? z.reverse() : z;\n",
" };\n",
"\n",
" scale.tickFormat = function(count, specifier) {\n",
" if (specifier == null) specifier = base === 10 ? \".0e\" : \",\";\n",
" if (typeof specifier !== \"function\") specifier = d3Format.format(specifier);\n",
" if (count === Infinity) return specifier;\n",
" if (count == null) count = 10;\n",
" var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?\n",
" return function(d) {\n",
" var i = d / pows(Math.round(logs(d)));\n",
" if (i * base < base - 0.5) i *= base;\n",
" return i <= k ? specifier(d) : \"\";\n",
" };\n",
" };\n",
"\n",
" scale.nice = function() {\n",
" return domain(nice(domain(), {\n",
" floor: function(x) { return pows(Math.floor(logs(x))); },\n",
" ceil: function(x) { return pows(Math.ceil(logs(x))); }\n",
" }));\n",
" };\n",
"\n",
" return scale;\n",
"}\n",
"\n",
"function log() {\n",
" var scale = loggish(transformer()).domain([1, 10]);\n",
"\n",
" scale.copy = function() {\n",
" return copy(scale, log()).base(scale.base());\n",
" };\n",
"\n",
" initRange.apply(scale, arguments);\n",
"\n",
" return scale;\n",
"}\n",
"\n",
"function transformSymlog(c) {\n",
" return function(x) {\n",
" return Math.sign(x) * Math.log1p(Math.abs(x / c));\n",
" };\n",
"}\n",
"\n",
"function transformSymexp(c) {\n",
" return function(x) {\n",
" return Math.sign(x) * Math.expm1(Math.abs(x)) * c;\n",
" };\n",
"}\n",
"\n",
"function symlogish(transform) {\n",
" var c = 1, scale = transform(transformSymlog(c), transformSymexp(c));\n",
"\n",
" scale.constant = function(_) {\n",
" return arguments.length ? transform(transformSymlog(c = +_), transformSymexp(c)) : c;\n",
" };\n",
"\n",
" return linearish(scale);\n",
"}\n",
"\n",
"function symlog() {\n",
" var scale = symlogish(transformer());\n",
"\n",
" scale.copy = function() {\n",
" return copy(scale, symlog()).constant(scale.constant());\n",
" };\n",
"\n",
" return initRange.apply(scale, arguments);\n",
"}\n",
"\n",
"function transformPow(exponent) {\n",
" return function(x) {\n",
" return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);\n",
" };\n",
"}\n",
"\n",
"function transformSqrt(x) {\n",
" return x < 0 ? -Math.sqrt(-x) : Math.sqrt(x);\n",
"}\n",
"\n",
"function transformSquare(x) {\n",
" return x < 0 ? -x * x : x * x;\n",
"}\n",
"\n",
"function powish(transform) {\n",
" var scale = transform(identity, identity),\n",
" exponent = 1;\n",
"\n",
" function rescale() {\n",
" return exponent === 1 ? transform(identity, identity)\n",
" : exponent === 0.5 ? transform(transformSqrt, transformSquare)\n",
" : transform(transformPow(exponent), transformPow(1 / exponent));\n",
" }\n",
"\n",
" scale.exponent = function(_) {\n",
" return arguments.length ? (exponent = +_, rescale()) : exponent;\n",
" };\n",
"\n",
" return linearish(scale);\n",
"}\n",
"\n",
"function pow() {\n",
" var scale = powish(transformer());\n",
"\n",
" scale.copy = function() {\n",
" return copy(scale, pow()).exponent(scale.exponent());\n",
" };\n",
"\n",
" initRange.apply(scale, arguments);\n",
"\n",
" return scale;\n",
"}\n",
"\n",
"function sqrt() {\n",
" return pow.apply(null, arguments).exponent(0.5);\n",
"}\n",
"\n",
"function square(x) {\n",
" return Math.sign(x) * x * x;\n",
"}\n",
"\n",
"function unsquare(x) {\n",
" return Math.sign(x) * Math.sqrt(Math.abs(x));\n",
"}\n",
"\n",
"function radial() {\n",
" var squared = continuous(),\n",
" range = [0, 1],\n",
" round = false,\n",
" unknown;\n",
"\n",
" function scale(x) {\n",
" var y = unsquare(squared(x));\n",
" return isNaN(y) ? unknown : round ? Math.round(y) : y;\n",
" }\n",
"\n",
" scale.invert = function(y) {\n",
" return squared.invert(square(y));\n",
" };\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? (squared.domain(_), scale) : squared.domain();\n",
" };\n",
"\n",
" scale.range = function(_) {\n",
" return arguments.length ? (squared.range((range = Array.from(_, number)).map(square)), scale) : range.slice();\n",
" };\n",
"\n",
" scale.rangeRound = function(_) {\n",
" return scale.range(_).round(true);\n",
" };\n",
"\n",
" scale.round = function(_) {\n",
" return arguments.length ? (round = !!_, scale) : round;\n",
" };\n",
"\n",
" scale.clamp = function(_) {\n",
" return arguments.length ? (squared.clamp(_), scale) : squared.clamp();\n",
" };\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return radial(squared.domain(), range)\n",
" .round(round)\n",
" .clamp(squared.clamp())\n",
" .unknown(unknown);\n",
" };\n",
"\n",
" initRange.apply(scale, arguments);\n",
"\n",
" return linearish(scale);\n",
"}\n",
"\n",
"function quantile() {\n",
" var domain = [],\n",
" range = [],\n",
" thresholds = [],\n",
" unknown;\n",
"\n",
" function rescale() {\n",
" var i = 0, n = Math.max(1, range.length);\n",
" thresholds = new Array(n - 1);\n",
" while (++i < n) thresholds[i - 1] = d3Array.quantile(domain, i / n);\n",
" return scale;\n",
" }\n",
"\n",
" function scale(x) {\n",
" return isNaN(x = +x) ? unknown : range[d3Array.bisect(thresholds, x)];\n",
" }\n",
"\n",
" scale.invertExtent = function(y) {\n",
" var i = range.indexOf(y);\n",
" return i < 0 ? [NaN, NaN] : [\n",
" i > 0 ? thresholds[i - 1] : domain[0],\n",
" i < thresholds.length ? thresholds[i] : domain[domain.length - 1]\n",
" ];\n",
" };\n",
"\n",
" scale.domain = function(_) {\n",
" if (!arguments.length) return domain.slice();\n",
" domain = [];\n",
" for (let d of _) if (d != null && !isNaN(d = +d)) domain.push(d);\n",
" domain.sort(d3Array.ascending);\n",
" return rescale();\n",
" };\n",
"\n",
" scale.range = function(_) {\n",
" return arguments.length ? (range = Array.from(_), rescale()) : range.slice();\n",
" };\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" scale.quantiles = function() {\n",
" return thresholds.slice();\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return quantile()\n",
" .domain(domain)\n",
" .range(range)\n",
" .unknown(unknown);\n",
" };\n",
"\n",
" return initRange.apply(scale, arguments);\n",
"}\n",
"\n",
"function quantize() {\n",
" var x0 = 0,\n",
" x1 = 1,\n",
" n = 1,\n",
" domain = [0.5],\n",
" range = [0, 1],\n",
" unknown;\n",
"\n",
" function scale(x) {\n",
" return x <= x ? range[d3Array.bisect(domain, x, 0, n)] : unknown;\n",
" }\n",
"\n",
" function rescale() {\n",
" var i = -1;\n",
" domain = new Array(n);\n",
" while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);\n",
" return scale;\n",
" }\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? ([x0, x1] = _, x0 = +x0, x1 = +x1, rescale()) : [x0, x1];\n",
" };\n",
"\n",
" scale.range = function(_) {\n",
" return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();\n",
" };\n",
"\n",
" scale.invertExtent = function(y) {\n",
" var i = range.indexOf(y);\n",
" return i < 0 ? [NaN, NaN]\n",
" : i < 1 ? [x0, domain[0]]\n",
" : i >= n ? [domain[n - 1], x1]\n",
" : [domain[i - 1], domain[i]];\n",
" };\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : scale;\n",
" };\n",
"\n",
" scale.thresholds = function() {\n",
" return domain.slice();\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return quantize()\n",
" .domain([x0, x1])\n",
" .range(range)\n",
" .unknown(unknown);\n",
" };\n",
"\n",
" return initRange.apply(linearish(scale), arguments);\n",
"}\n",
"\n",
"function threshold() {\n",
" var domain = [0.5],\n",
" range = [0, 1],\n",
" unknown,\n",
" n = 1;\n",
"\n",
" function scale(x) {\n",
" return x <= x ? range[d3Array.bisect(domain, x, 0, n)] : unknown;\n",
" }\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? (domain = Array.from(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice();\n",
" };\n",
"\n",
" scale.range = function(_) {\n",
" return arguments.length ? (range = Array.from(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice();\n",
" };\n",
"\n",
" scale.invertExtent = function(y) {\n",
" var i = range.indexOf(y);\n",
" return [domain[i - 1], domain[i]];\n",
" };\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return threshold()\n",
" .domain(domain)\n",
" .range(range)\n",
" .unknown(unknown);\n",
" };\n",
"\n",
" return initRange.apply(scale, arguments);\n",
"}\n",
"\n",
"var durationSecond = 1000,\n",
" durationMinute = durationSecond * 60,\n",
" durationHour = durationMinute * 60,\n",
" durationDay = durationHour * 24,\n",
" durationWeek = durationDay * 7,\n",
" durationMonth = durationDay * 30,\n",
" durationYear = durationDay * 365;\n",
"\n",
"function date(t) {\n",
" return new Date(t);\n",
"}\n",
"\n",
"function number$1(t) {\n",
" return t instanceof Date ? +t : +new Date(+t);\n",
"}\n",
"\n",
"function calendar(year, month, week, day, hour, minute, second, millisecond, format) {\n",
" var scale = continuous(),\n",
" invert = scale.invert,\n",
" domain = scale.domain;\n",
"\n",
" var formatMillisecond = format(\".%L\"),\n",
" formatSecond = format(\":%S\"),\n",
" formatMinute = format(\"%I:%M\"),\n",
" formatHour = format(\"%I %p\"),\n",
" formatDay = format(\"%a %d\"),\n",
" formatWeek = format(\"%b %d\"),\n",
" formatMonth = format(\"%B\"),\n",
" formatYear = format(\"%Y\");\n",
"\n",
" var tickIntervals = [\n",
" [second, 1, durationSecond],\n",
" [second, 5, 5 * durationSecond],\n",
" [second, 15, 15 * durationSecond],\n",
" [second, 30, 30 * durationSecond],\n",
" [minute, 1, durationMinute],\n",
" [minute, 5, 5 * durationMinute],\n",
" [minute, 15, 15 * durationMinute],\n",
" [minute, 30, 30 * durationMinute],\n",
" [ hour, 1, durationHour ],\n",
" [ hour, 3, 3 * durationHour ],\n",
" [ hour, 6, 6 * durationHour ],\n",
" [ hour, 12, 12 * durationHour ],\n",
" [ day, 1, durationDay ],\n",
" [ day, 2, 2 * durationDay ],\n",
" [ week, 1, durationWeek ],\n",
" [ month, 1, durationMonth ],\n",
" [ month, 3, 3 * durationMonth ],\n",
" [ year, 1, durationYear ]\n",
" ];\n",
"\n",
" function tickFormat(date) {\n",
" return (second(date) < date ? formatMillisecond\n",
" : minute(date) < date ? formatSecond\n",
" : hour(date) < date ? formatMinute\n",
" : day(date) < date ? formatHour\n",
" : month(date) < date ? (week(date) < date ? formatDay : formatWeek)\n",
" : year(date) < date ? formatMonth\n",
" : formatYear)(date);\n",
" }\n",
"\n",
" function tickInterval(interval, start, stop) {\n",
" if (interval == null) interval = 10;\n",
"\n",
" // If a desired tick count is specified, pick a reasonable tick interval\n",
" // based on the extent of the domain and a rough estimate of tick size.\n",
" // Otherwise, assume interval is already a time interval and use it.\n",
" if (typeof interval === \"number\") {\n",
" var target = Math.abs(stop - start) / interval,\n",
" i = d3Array.bisector(function(i) { return i[2]; }).right(tickIntervals, target),\n",
" step;\n",
" if (i === tickIntervals.length) {\n",
" step = d3Array.tickStep(start / durationYear, stop / durationYear, interval);\n",
" interval = year;\n",
" } else if (i) {\n",
" i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];\n",
" step = i[1];\n",
" interval = i[0];\n",
" } else {\n",
" step = Math.max(d3Array.tickStep(start, stop, interval), 1);\n",
" interval = millisecond;\n",
" }\n",
" return interval.every(step);\n",
" }\n",
"\n",
" return interval;\n",
" }\n",
"\n",
" scale.invert = function(y) {\n",
" return new Date(invert(y));\n",
" };\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? domain(Array.from(_, number$1)) : domain().map(date);\n",
" };\n",
"\n",
" scale.ticks = function(interval) {\n",
" var d = domain(),\n",
" t0 = d[0],\n",
" t1 = d[d.length - 1],\n",
" r = t1 < t0,\n",
" t;\n",
" if (r) t = t0, t0 = t1, t1 = t;\n",
" t = tickInterval(interval, t0, t1);\n",
" t = t ? t.range(t0, t1 + 1) : []; // inclusive stop\n",
" return r ? t.reverse() : t;\n",
" };\n",
"\n",
" scale.tickFormat = function(count, specifier) {\n",
" return specifier == null ? tickFormat : format(specifier);\n",
" };\n",
"\n",
" scale.nice = function(interval) {\n",
" var d = domain();\n",
" return (interval = tickInterval(interval, d[0], d[d.length - 1]))\n",
" ? domain(nice(d, interval))\n",
" : scale;\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return copy(scale, calendar(year, month, week, day, hour, minute, second, millisecond, format));\n",
" };\n",
"\n",
" return scale;\n",
"}\n",
"\n",
"function time() {\n",
" return initRange.apply(calendar(d3Time.timeYear, d3Time.timeMonth, d3Time.timeWeek, d3Time.timeDay, d3Time.timeHour, d3Time.timeMinute, d3Time.timeSecond, d3Time.timeMillisecond, d3TimeFormat.timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]), arguments);\n",
"}\n",
"\n",
"function utcTime() {\n",
" return initRange.apply(calendar(d3Time.utcYear, d3Time.utcMonth, d3Time.utcWeek, d3Time.utcDay, d3Time.utcHour, d3Time.utcMinute, d3Time.utcSecond, d3Time.utcMillisecond, d3TimeFormat.utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]), arguments);\n",
"}\n",
"\n",
"function transformer$1() {\n",
" var x0 = 0,\n",
" x1 = 1,\n",
" t0,\n",
" t1,\n",
" k10,\n",
" transform,\n",
" interpolator = identity,\n",
" clamp = false,\n",
" unknown;\n",
"\n",
" function scale(x) {\n",
" return isNaN(x = +x) ? unknown : interpolator(k10 === 0 ? 0.5 : (x = (transform(x) - t0) * k10, clamp ? Math.max(0, Math.min(1, x)) : x));\n",
" }\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? ([x0, x1] = _, t0 = transform(x0 = +x0), t1 = transform(x1 = +x1), k10 = t0 === t1 ? 0 : 1 / (t1 - t0), scale) : [x0, x1];\n",
" };\n",
"\n",
" scale.clamp = function(_) {\n",
" return arguments.length ? (clamp = !!_, scale) : clamp;\n",
" };\n",
"\n",
" scale.interpolator = function(_) {\n",
" return arguments.length ? (interpolator = _, scale) : interpolator;\n",
" };\n",
"\n",
" function range(interpolate) {\n",
" return function(_) {\n",
" var r0, r1;\n",
" return arguments.length ? ([r0, r1] = _, interpolator = interpolate(r0, r1), scale) : [interpolator(0), interpolator(1)];\n",
" };\n",
" }\n",
"\n",
" scale.range = range(d3Interpolate.interpolate);\n",
"\n",
" scale.rangeRound = range(d3Interpolate.interpolateRound);\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" return function(t) {\n",
" transform = t, t0 = t(x0), t1 = t(x1), k10 = t0 === t1 ? 0 : 1 / (t1 - t0);\n",
" return scale;\n",
" };\n",
"}\n",
"\n",
"function copy$1(source, target) {\n",
" return target\n",
" .domain(source.domain())\n",
" .interpolator(source.interpolator())\n",
" .clamp(source.clamp())\n",
" .unknown(source.unknown());\n",
"}\n",
"\n",
"function sequential() {\n",
" var scale = linearish(transformer$1()(identity));\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, sequential());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function sequentialLog() {\n",
" var scale = loggish(transformer$1()).domain([1, 10]);\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, sequentialLog()).base(scale.base());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function sequentialSymlog() {\n",
" var scale = symlogish(transformer$1());\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, sequentialSymlog()).constant(scale.constant());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function sequentialPow() {\n",
" var scale = powish(transformer$1());\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, sequentialPow()).exponent(scale.exponent());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function sequentialSqrt() {\n",
" return sequentialPow.apply(null, arguments).exponent(0.5);\n",
"}\n",
"\n",
"function sequentialQuantile() {\n",
" var domain = [],\n",
" interpolator = identity;\n",
"\n",
" function scale(x) {\n",
" if (!isNaN(x = +x)) return interpolator((d3Array.bisect(domain, x, 1) - 1) / (domain.length - 1));\n",
" }\n",
"\n",
" scale.domain = function(_) {\n",
" if (!arguments.length) return domain.slice();\n",
" domain = [];\n",
" for (let d of _) if (d != null && !isNaN(d = +d)) domain.push(d);\n",
" domain.sort(d3Array.ascending);\n",
" return scale;\n",
" };\n",
"\n",
" scale.interpolator = function(_) {\n",
" return arguments.length ? (interpolator = _, scale) : interpolator;\n",
" };\n",
"\n",
" scale.range = function() {\n",
" return domain.map((d, i) => interpolator(i / (domain.length - 1)));\n",
" };\n",
"\n",
" scale.quantiles = function(n) {\n",
" return Array.from({length: n + 1}, (_, i) => d3Array.quantile(domain, i / n));\n",
" };\n",
"\n",
" scale.copy = function() {\n",
" return sequentialQuantile(interpolator).domain(domain);\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function transformer$2() {\n",
" var x0 = 0,\n",
" x1 = 0.5,\n",
" x2 = 1,\n",
" s = 1,\n",
" t0,\n",
" t1,\n",
" t2,\n",
" k10,\n",
" k21,\n",
" interpolator = identity,\n",
" transform,\n",
" clamp = false,\n",
" unknown;\n",
"\n",
" function scale(x) {\n",
" return isNaN(x = +x) ? unknown : (x = 0.5 + ((x = +transform(x)) - t1) * (s * x < s * t1 ? k10 : k21), interpolator(clamp ? Math.max(0, Math.min(1, x)) : x));\n",
" }\n",
"\n",
" scale.domain = function(_) {\n",
" return arguments.length ? ([x0, x1, x2] = _, t0 = transform(x0 = +x0), t1 = transform(x1 = +x1), t2 = transform(x2 = +x2), k10 = t0 === t1 ? 0 : 0.5 / (t1 - t0), k21 = t1 === t2 ? 0 : 0.5 / (t2 - t1), s = t1 < t0 ? -1 : 1, scale) : [x0, x1, x2];\n",
" };\n",
"\n",
" scale.clamp = function(_) {\n",
" return arguments.length ? (clamp = !!_, scale) : clamp;\n",
" };\n",
"\n",
" scale.interpolator = function(_) {\n",
" return arguments.length ? (interpolator = _, scale) : interpolator;\n",
" };\n",
"\n",
" function range(interpolate) {\n",
" return function(_) {\n",
" var r0, r1, r2;\n",
" return arguments.length ? ([r0, r1, r2] = _, interpolator = d3Interpolate.piecewise(interpolate, [r0, r1, r2]), scale) : [interpolator(0), interpolator(0.5), interpolator(1)];\n",
" };\n",
" }\n",
"\n",
" scale.range = range(d3Interpolate.interpolate);\n",
"\n",
" scale.rangeRound = range(d3Interpolate.interpolateRound);\n",
"\n",
" scale.unknown = function(_) {\n",
" return arguments.length ? (unknown = _, scale) : unknown;\n",
" };\n",
"\n",
" return function(t) {\n",
" transform = t, t0 = t(x0), t1 = t(x1), t2 = t(x2), k10 = t0 === t1 ? 0 : 0.5 / (t1 - t0), k21 = t1 === t2 ? 0 : 0.5 / (t2 - t1), s = t1 < t0 ? -1 : 1;\n",
" return scale;\n",
" };\n",
"}\n",
"\n",
"function diverging() {\n",
" var scale = linearish(transformer$2()(identity));\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, diverging());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function divergingLog() {\n",
" var scale = loggish(transformer$2()).domain([0.1, 1, 10]);\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, divergingLog()).base(scale.base());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function divergingSymlog() {\n",
" var scale = symlogish(transformer$2());\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, divergingSymlog()).constant(scale.constant());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function divergingPow() {\n",
" var scale = powish(transformer$2());\n",
"\n",
" scale.copy = function() {\n",
" return copy$1(scale, divergingPow()).exponent(scale.exponent());\n",
" };\n",
"\n",
" return initInterpolator.apply(scale, arguments);\n",
"}\n",
"\n",
"function divergingSqrt() {\n",
" return divergingPow.apply(null, arguments).exponent(0.5);\n",
"}\n",
"\n",
"exports.scaleBand = band;\n",
"exports.scaleDiverging = diverging;\n",
"exports.scaleDivergingLog = divergingLog;\n",
"exports.scaleDivergingPow = divergingPow;\n",
"exports.scaleDivergingSqrt = divergingSqrt;\n",
"exports.scaleDivergingSymlog = divergingSymlog;\n",
"exports.scaleIdentity = identity$1;\n",
"exports.scaleImplicit = implicit;\n",
"exports.scaleLinear = linear;\n",
"exports.scaleLog = log;\n",
"exports.scaleOrdinal = ordinal;\n",
"exports.scalePoint = point;\n",
"exports.scalePow = pow;\n",
"exports.scaleQuantile = quantile;\n",
"exports.scaleQuantize = quantize;\n",
"exports.scaleRadial = radial;\n",
"exports.scaleSequential = sequential;\n",
"exports.scaleSequentialLog = sequentialLog;\n",
"exports.scaleSequentialPow = sequentialPow;\n",
"exports.scaleSequentialQuantile = sequentialQuantile;\n",
"exports.scaleSequentialSqrt = sequentialSqrt;\n",
"exports.scaleSequentialSymlog = sequentialSymlog;\n",
"exports.scaleSqrt = sqrt;\n",
"exports.scaleSymlog = symlog;\n",
"exports.scaleThreshold = threshold;\n",
"exports.scaleTime = time;\n",
"exports.scaleUtc = utcTime;\n",
"exports.tickFormat = tickFormat;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{\"d3-array\":2,\"d3-format\":7,\"d3-interpolate\":8,\"d3-time\":14,\"d3-time-format\":13}],11:[function(require,module,exports){\n",
"// https://d3js.org/d3-selection/ v1.4.1 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"var xhtml = \"http://www.w3.org/1999/xhtml\";\n",
"\n",
"var namespaces = {\n",
" svg: \"http://www.w3.org/2000/svg\",\n",
" xhtml: xhtml,\n",
" xlink: \"http://www.w3.org/1999/xlink\",\n",
" xml: \"http://www.w3.org/XML/1998/namespace\",\n",
" xmlns: \"http://www.w3.org/2000/xmlns/\"\n",
"};\n",
"\n",
"function namespace(name) {\n",
" var prefix = name += \"\", i = prefix.indexOf(\":\");\n",
" if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n",
" return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;\n",
"}\n",
"\n",
"function creatorInherit(name) {\n",
" return function() {\n",
" var document = this.ownerDocument,\n",
" uri = this.namespaceURI;\n",
" return uri === xhtml && document.documentElement.namespaceURI === xhtml\n",
" ? document.createElement(name)\n",
" : document.createElementNS(uri, name);\n",
" };\n",
"}\n",
"\n",
"function creatorFixed(fullname) {\n",
" return function() {\n",
" return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n",
" };\n",
"}\n",
"\n",
"function creator(name) {\n",
" var fullname = namespace(name);\n",
" return (fullname.local\n",
" ? creatorFixed\n",
" : creatorInherit)(fullname);\n",
"}\n",
"\n",
"function none() {}\n",
"\n",
"function selector(selector) {\n",
" return selector == null ? none : function() {\n",
" return this.querySelector(selector);\n",
" };\n",
"}\n",
"\n",
"function selection_select(select) {\n",
" if (typeof select !== \"function\") select = selector(select);\n",
"\n",
" for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n",
" if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n",
" if (\"__data__\" in node) subnode.__data__ = node.__data__;\n",
" subgroup[i] = subnode;\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Selection(subgroups, this._parents);\n",
"}\n",
"\n",
"function empty() {\n",
" return [];\n",
"}\n",
"\n",
"function selectorAll(selector) {\n",
" return selector == null ? empty : function() {\n",
" return this.querySelectorAll(selector);\n",
" };\n",
"}\n",
"\n",
"function selection_selectAll(select) {\n",
" if (typeof select !== \"function\") select = selectorAll(select);\n",
"\n",
" for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n",
" if (node = group[i]) {\n",
" subgroups.push(select.call(node, node.__data__, i, group));\n",
" parents.push(node);\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Selection(subgroups, parents);\n",
"}\n",
"\n",
"function matcher(selector) {\n",
" return function() {\n",
" return this.matches(selector);\n",
" };\n",
"}\n",
"\n",
"function selection_filter(match) {\n",
" if (typeof match !== \"function\") match = matcher(match);\n",
"\n",
" for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n",
" if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n",
" subgroup.push(node);\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Selection(subgroups, this._parents);\n",
"}\n",
"\n",
"function sparse(update) {\n",
" return new Array(update.length);\n",
"}\n",
"\n",
"function selection_enter() {\n",
" return new Selection(this._enter || this._groups.map(sparse), this._parents);\n",
"}\n",
"\n",
"function EnterNode(parent, datum) {\n",
" this.ownerDocument = parent.ownerDocument;\n",
" this.namespaceURI = parent.namespaceURI;\n",
" this._next = null;\n",
" this._parent = parent;\n",
" this.__data__ = datum;\n",
"}\n",
"\n",
"EnterNode.prototype = {\n",
" constructor: EnterNode,\n",
" appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n",
" insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n",
" querySelector: function(selector) { return this._parent.querySelector(selector); },\n",
" querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n",
"};\n",
"\n",
"function constant(x) {\n",
" return function() {\n",
" return x;\n",
" };\n",
"}\n",
"\n",
"var keyPrefix = \"$\"; // Protect against keys like \"__proto__\".\n",
"\n",
"function bindIndex(parent, group, enter, update, exit, data) {\n",
" var i = 0,\n",
" node,\n",
" groupLength = group.length,\n",
" dataLength = data.length;\n",
"\n",
" // Put any non-null nodes that fit into update.\n",
" // Put any null nodes into enter.\n",
" // Put any remaining data into enter.\n",
" for (; i < dataLength; ++i) {\n",
" if (node = group[i]) {\n",
" node.__data__ = data[i];\n",
" update[i] = node;\n",
" } else {\n",
" enter[i] = new EnterNode(parent, data[i]);\n",
" }\n",
" }\n",
"\n",
" // Put any non-null nodes that don't fit into exit.\n",
" for (; i < groupLength; ++i) {\n",
" if (node = group[i]) {\n",
" exit[i] = node;\n",
" }\n",
" }\n",
"}\n",
"\n",
"function bindKey(parent, group, enter, update, exit, data, key) {\n",
" var i,\n",
" node,\n",
" nodeByKeyValue = {},\n",
" groupLength = group.length,\n",
" dataLength = data.length,\n",
" keyValues = new Array(groupLength),\n",
" keyValue;\n",
"\n",
" // Compute the key for each node.\n",
" // If multiple nodes have the same key, the duplicates are added to exit.\n",
" for (i = 0; i < groupLength; ++i) {\n",
" if (node = group[i]) {\n",
" keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n",
" if (keyValue in nodeByKeyValue) {\n",
" exit[i] = node;\n",
" } else {\n",
" nodeByKeyValue[keyValue] = node;\n",
" }\n",
" }\n",
" }\n",
"\n",
" // Compute the key for each datum.\n",
" // If there a node associated with this key, join and add it to update.\n",
" // If there is not (or the key is a duplicate), add it to enter.\n",
" for (i = 0; i < dataLength; ++i) {\n",
" keyValue = keyPrefix + key.call(parent, data[i], i, data);\n",
" if (node = nodeByKeyValue[keyValue]) {\n",
" update[i] = node;\n",
" node.__data__ = data[i];\n",
" nodeByKeyValue[keyValue] = null;\n",
" } else {\n",
" enter[i] = new EnterNode(parent, data[i]);\n",
" }\n",
" }\n",
"\n",
" // Add any remaining nodes that were not bound to data to exit.\n",
" for (i = 0; i < groupLength; ++i) {\n",
" if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n",
" exit[i] = node;\n",
" }\n",
" }\n",
"}\n",
"\n",
"function selection_data(value, key) {\n",
" if (!value) {\n",
" data = new Array(this.size()), j = -1;\n",
" this.each(function(d) { data[++j] = d; });\n",
" return data;\n",
" }\n",
"\n",
" var bind = key ? bindKey : bindIndex,\n",
" parents = this._parents,\n",
" groups = this._groups;\n",
"\n",
" if (typeof value !== \"function\") value = constant(value);\n",
"\n",
" for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n",
" var parent = parents[j],\n",
" group = groups[j],\n",
" groupLength = group.length,\n",
" data = value.call(parent, parent && parent.__data__, j, parents),\n",
" dataLength = data.length,\n",
" enterGroup = enter[j] = new Array(dataLength),\n",
" updateGroup = update[j] = new Array(dataLength),\n",
" exitGroup = exit[j] = new Array(groupLength);\n",
"\n",
" bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n",
"\n",
" // Now connect the enter nodes to their following update node, such that\n",
" // appendChild can insert the materialized enter node before this node,\n",
" // rather than at the end of the parent node.\n",
" for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n",
" if (previous = enterGroup[i0]) {\n",
" if (i0 >= i1) i1 = i0 + 1;\n",
" while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n",
" previous._next = next || null;\n",
" }\n",
" }\n",
" }\n",
"\n",
" update = new Selection(update, parents);\n",
" update._enter = enter;\n",
" update._exit = exit;\n",
" return update;\n",
"}\n",
"\n",
"function selection_exit() {\n",
" return new Selection(this._exit || this._groups.map(sparse), this._parents);\n",
"}\n",
"\n",
"function selection_join(onenter, onupdate, onexit) {\n",
" var enter = this.enter(), update = this, exit = this.exit();\n",
" enter = typeof onenter === \"function\" ? onenter(enter) : enter.append(onenter + \"\");\n",
" if (onupdate != null) update = onupdate(update);\n",
" if (onexit == null) exit.remove(); else onexit(exit);\n",
" return enter && update ? enter.merge(update).order() : update;\n",
"}\n",
"\n",
"function selection_merge(selection) {\n",
"\n",
" for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n",
" for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n",
" if (node = group0[i] || group1[i]) {\n",
" merge[i] = node;\n",
" }\n",
" }\n",
" }\n",
"\n",
" for (; j < m0; ++j) {\n",
" merges[j] = groups0[j];\n",
" }\n",
"\n",
" return new Selection(merges, this._parents);\n",
"}\n",
"\n",
"function selection_order() {\n",
"\n",
" for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n",
" for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n",
" if (node = group[i]) {\n",
" if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);\n",
" next = node;\n",
" }\n",
" }\n",
" }\n",
"\n",
" return this;\n",
"}\n",
"\n",
"function selection_sort(compare) {\n",
" if (!compare) compare = ascending;\n",
"\n",
" function compareNode(a, b) {\n",
" return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n",
" }\n",
"\n",
" for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n",
" if (node = group[i]) {\n",
" sortgroup[i] = node;\n",
" }\n",
" }\n",
" sortgroup.sort(compareNode);\n",
" }\n",
"\n",
" return new Selection(sortgroups, this._parents).order();\n",
"}\n",
"\n",
"function ascending(a, b) {\n",
" return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n",
"}\n",
"\n",
"function selection_call() {\n",
" var callback = arguments[0];\n",
" arguments[0] = this;\n",
" callback.apply(null, arguments);\n",
" return this;\n",
"}\n",
"\n",
"function selection_nodes() {\n",
" var nodes = new Array(this.size()), i = -1;\n",
" this.each(function() { nodes[++i] = this; });\n",
" return nodes;\n",
"}\n",
"\n",
"function selection_node() {\n",
"\n",
" for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n",
" for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n",
" var node = group[i];\n",
" if (node) return node;\n",
" }\n",
" }\n",
"\n",
" return null;\n",
"}\n",
"\n",
"function selection_size() {\n",
" var size = 0;\n",
" this.each(function() { ++size; });\n",
" return size;\n",
"}\n",
"\n",
"function selection_empty() {\n",
" return !this.node();\n",
"}\n",
"\n",
"function selection_each(callback) {\n",
"\n",
" for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n",
" for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n",
" if (node = group[i]) callback.call(node, node.__data__, i, group);\n",
" }\n",
" }\n",
"\n",
" return this;\n",
"}\n",
"\n",
"function attrRemove(name) {\n",
" return function() {\n",
" this.removeAttribute(name);\n",
" };\n",
"}\n",
"\n",
"function attrRemoveNS(fullname) {\n",
" return function() {\n",
" this.removeAttributeNS(fullname.space, fullname.local);\n",
" };\n",
"}\n",
"\n",
"function attrConstant(name, value) {\n",
" return function() {\n",
" this.setAttribute(name, value);\n",
" };\n",
"}\n",
"\n",
"function attrConstantNS(fullname, value) {\n",
" return function() {\n",
" this.setAttributeNS(fullname.space, fullname.local, value);\n",
" };\n",
"}\n",
"\n",
"function attrFunction(name, value) {\n",
" return function() {\n",
" var v = value.apply(this, arguments);\n",
" if (v == null) this.removeAttribute(name);\n",
" else this.setAttribute(name, v);\n",
" };\n",
"}\n",
"\n",
"function attrFunctionNS(fullname, value) {\n",
" return function() {\n",
" var v = value.apply(this, arguments);\n",
" if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n",
" else this.setAttributeNS(fullname.space, fullname.local, v);\n",
" };\n",
"}\n",
"\n",
"function selection_attr(name, value) {\n",
" var fullname = namespace(name);\n",
"\n",
" if (arguments.length < 2) {\n",
" var node = this.node();\n",
" return fullname.local\n",
" ? node.getAttributeNS(fullname.space, fullname.local)\n",
" : node.getAttribute(fullname);\n",
" }\n",
"\n",
" return this.each((value == null\n",
" ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n",
" ? (fullname.local ? attrFunctionNS : attrFunction)\n",
" : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n",
"}\n",
"\n",
"function defaultView(node) {\n",
" return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n",
" || (node.document && node) // node is a Window\n",
" || node.defaultView; // node is a Document\n",
"}\n",
"\n",
"function styleRemove(name) {\n",
" return function() {\n",
" this.style.removeProperty(name);\n",
" };\n",
"}\n",
"\n",
"function styleConstant(name, value, priority) {\n",
" return function() {\n",
" this.style.setProperty(name, value, priority);\n",
" };\n",
"}\n",
"\n",
"function styleFunction(name, value, priority) {\n",
" return function() {\n",
" var v = value.apply(this, arguments);\n",
" if (v == null) this.style.removeProperty(name);\n",
" else this.style.setProperty(name, v, priority);\n",
" };\n",
"}\n",
"\n",
"function selection_style(name, value, priority) {\n",
" return arguments.length > 1\n",
" ? this.each((value == null\n",
" ? styleRemove : typeof value === \"function\"\n",
" ? styleFunction\n",
" : styleConstant)(name, value, priority == null ? \"\" : priority))\n",
" : styleValue(this.node(), name);\n",
"}\n",
"\n",
"function styleValue(node, name) {\n",
" return node.style.getPropertyValue(name)\n",
" || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);\n",
"}\n",
"\n",
"function propertyRemove(name) {\n",
" return function() {\n",
" delete this[name];\n",
" };\n",
"}\n",
"\n",
"function propertyConstant(name, value) {\n",
" return function() {\n",
" this[name] = value;\n",
" };\n",
"}\n",
"\n",
"function propertyFunction(name, value) {\n",
" return function() {\n",
" var v = value.apply(this, arguments);\n",
" if (v == null) delete this[name];\n",
" else this[name] = v;\n",
" };\n",
"}\n",
"\n",
"function selection_property(name, value) {\n",
" return arguments.length > 1\n",
" ? this.each((value == null\n",
" ? propertyRemove : typeof value === \"function\"\n",
" ? propertyFunction\n",
" : propertyConstant)(name, value))\n",
" : this.node()[name];\n",
"}\n",
"\n",
"function classArray(string) {\n",
" return string.trim().split(/^|\\s+/);\n",
"}\n",
"\n",
"function classList(node) {\n",
" return node.classList || new ClassList(node);\n",
"}\n",
"\n",
"function ClassList(node) {\n",
" this._node = node;\n",
" this._names = classArray(node.getAttribute(\"class\") || \"\");\n",
"}\n",
"\n",
"ClassList.prototype = {\n",
" add: function(name) {\n",
" var i = this._names.indexOf(name);\n",
" if (i < 0) {\n",
" this._names.push(name);\n",
" this._node.setAttribute(\"class\", this._names.join(\" \"));\n",
" }\n",
" },\n",
" remove: function(name) {\n",
" var i = this._names.indexOf(name);\n",
" if (i >= 0) {\n",
" this._names.splice(i, 1);\n",
" this._node.setAttribute(\"class\", this._names.join(\" \"));\n",
" }\n",
" },\n",
" contains: function(name) {\n",
" return this._names.indexOf(name) >= 0;\n",
" }\n",
"};\n",
"\n",
"function classedAdd(node, names) {\n",
" var list = classList(node), i = -1, n = names.length;\n",
" while (++i < n) list.add(names[i]);\n",
"}\n",
"\n",
"function classedRemove(node, names) {\n",
" var list = classList(node), i = -1, n = names.length;\n",
" while (++i < n) list.remove(names[i]);\n",
"}\n",
"\n",
"function classedTrue(names) {\n",
" return function() {\n",
" classedAdd(this, names);\n",
" };\n",
"}\n",
"\n",
"function classedFalse(names) {\n",
" return function() {\n",
" classedRemove(this, names);\n",
" };\n",
"}\n",
"\n",
"function classedFunction(names, value) {\n",
" return function() {\n",
" (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n",
" };\n",
"}\n",
"\n",
"function selection_classed(name, value) {\n",
" var names = classArray(name + \"\");\n",
"\n",
" if (arguments.length < 2) {\n",
" var list = classList(this.node()), i = -1, n = names.length;\n",
" while (++i < n) if (!list.contains(names[i])) return false;\n",
" return true;\n",
" }\n",
"\n",
" return this.each((typeof value === \"function\"\n",
" ? classedFunction : value\n",
" ? classedTrue\n",
" : classedFalse)(names, value));\n",
"}\n",
"\n",
"function textRemove() {\n",
" this.textContent = \"\";\n",
"}\n",
"\n",
"function textConstant(value) {\n",
" return function() {\n",
" this.textContent = value;\n",
" };\n",
"}\n",
"\n",
"function textFunction(value) {\n",
" return function() {\n",
" var v = value.apply(this, arguments);\n",
" this.textContent = v == null ? \"\" : v;\n",
" };\n",
"}\n",
"\n",
"function selection_text(value) {\n",
" return arguments.length\n",
" ? this.each(value == null\n",
" ? textRemove : (typeof value === \"function\"\n",
" ? textFunction\n",
" : textConstant)(value))\n",
" : this.node().textContent;\n",
"}\n",
"\n",
"function htmlRemove() {\n",
" this.innerHTML = \"\";\n",
"}\n",
"\n",
"function htmlConstant(value) {\n",
" return function() {\n",
" this.innerHTML = value;\n",
" };\n",
"}\n",
"\n",
"function htmlFunction(value) {\n",
" return function() {\n",
" var v = value.apply(this, arguments);\n",
" this.innerHTML = v == null ? \"\" : v;\n",
" };\n",
"}\n",
"\n",
"function selection_html(value) {\n",
" return arguments.length\n",
" ? this.each(value == null\n",
" ? htmlRemove : (typeof value === \"function\"\n",
" ? htmlFunction\n",
" : htmlConstant)(value))\n",
" : this.node().innerHTML;\n",
"}\n",
"\n",
"function raise() {\n",
" if (this.nextSibling) this.parentNode.appendChild(this);\n",
"}\n",
"\n",
"function selection_raise() {\n",
" return this.each(raise);\n",
"}\n",
"\n",
"function lower() {\n",
" if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n",
"}\n",
"\n",
"function selection_lower() {\n",
" return this.each(lower);\n",
"}\n",
"\n",
"function selection_append(name) {\n",
" var create = typeof name === \"function\" ? name : creator(name);\n",
" return this.select(function() {\n",
" return this.appendChild(create.apply(this, arguments));\n",
" });\n",
"}\n",
"\n",
"function constantNull() {\n",
" return null;\n",
"}\n",
"\n",
"function selection_insert(name, before) {\n",
" var create = typeof name === \"function\" ? name : creator(name),\n",
" select = before == null ? constantNull : typeof before === \"function\" ? before : selector(before);\n",
" return this.select(function() {\n",
" return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n",
" });\n",
"}\n",
"\n",
"function remove() {\n",
" var parent = this.parentNode;\n",
" if (parent) parent.removeChild(this);\n",
"}\n",
"\n",
"function selection_remove() {\n",
" return this.each(remove);\n",
"}\n",
"\n",
"function selection_cloneShallow() {\n",
" var clone = this.cloneNode(false), parent = this.parentNode;\n",
" return parent ? parent.insertBefore(clone, this.nextSibling) : clone;\n",
"}\n",
"\n",
"function selection_cloneDeep() {\n",
" var clone = this.cloneNode(true), parent = this.parentNode;\n",
" return parent ? parent.insertBefore(clone, this.nextSibling) : clone;\n",
"}\n",
"\n",
"function selection_clone(deep) {\n",
" return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n",
"}\n",
"\n",
"function selection_datum(value) {\n",
" return arguments.length\n",
" ? this.property(\"__data__\", value)\n",
" : this.node().__data__;\n",
"}\n",
"\n",
"var filterEvents = {};\n",
"\n",
"exports.event = null;\n",
"\n",
"if (typeof document !== \"undefined\") {\n",
" var element = document.documentElement;\n",
" if (!(\"onmouseenter\" in element)) {\n",
" filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n",
" }\n",
"}\n",
"\n",
"function filterContextListener(listener, index, group) {\n",
" listener = contextListener(listener, index, group);\n",
" return function(event) {\n",
" var related = event.relatedTarget;\n",
" if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n",
" listener.call(this, event);\n",
" }\n",
" };\n",
"}\n",
"\n",
"function contextListener(listener, index, group) {\n",
" return function(event1) {\n",
" var event0 = exports.event; // Events can be reentrant (e.g., focus).\n",
" exports.event = event1;\n",
" try {\n",
" listener.call(this, this.__data__, index, group);\n",
" } finally {\n",
" exports.event = event0;\n",
" }\n",
" };\n",
"}\n",
"\n",
"function parseTypenames(typenames) {\n",
" return typenames.trim().split(/^|\\s+/).map(function(t) {\n",
" var name = \"\", i = t.indexOf(\".\");\n",
" if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n",
" return {type: t, name: name};\n",
" });\n",
"}\n",
"\n",
"function onRemove(typename) {\n",
" return function() {\n",
" var on = this.__on;\n",
" if (!on) return;\n",
" for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n",
" if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n",
" this.removeEventListener(o.type, o.listener, o.capture);\n",
" } else {\n",
" on[++i] = o;\n",
" }\n",
" }\n",
" if (++i) on.length = i;\n",
" else delete this.__on;\n",
" };\n",
"}\n",
"\n",
"function onAdd(typename, value, capture) {\n",
" var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n",
" return function(d, i, group) {\n",
" var on = this.__on, o, listener = wrap(value, i, group);\n",
" if (on) for (var j = 0, m = on.length; j < m; ++j) {\n",
" if ((o = on[j]).type === typename.type && o.name === typename.name) {\n",
" this.removeEventListener(o.type, o.listener, o.capture);\n",
" this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n",
" o.value = value;\n",
" return;\n",
" }\n",
" }\n",
" this.addEventListener(typename.type, listener, capture);\n",
" o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n",
" if (!on) this.__on = [o];\n",
" else on.push(o);\n",
" };\n",
"}\n",
"\n",
"function selection_on(typename, value, capture) {\n",
" var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n",
"\n",
" if (arguments.length < 2) {\n",
" var on = this.node().__on;\n",
" if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n",
" for (i = 0, o = on[j]; i < n; ++i) {\n",
" if ((t = typenames[i]).type === o.type && t.name === o.name) {\n",
" return o.value;\n",
" }\n",
" }\n",
" }\n",
" return;\n",
" }\n",
"\n",
" on = value ? onAdd : onRemove;\n",
" if (capture == null) capture = false;\n",
" for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n",
" return this;\n",
"}\n",
"\n",
"function customEvent(event1, listener, that, args) {\n",
" var event0 = exports.event;\n",
" event1.sourceEvent = exports.event;\n",
" exports.event = event1;\n",
" try {\n",
" return listener.apply(that, args);\n",
" } finally {\n",
" exports.event = event0;\n",
" }\n",
"}\n",
"\n",
"function dispatchEvent(node, type, params) {\n",
" var window = defaultView(node),\n",
" event = window.CustomEvent;\n",
"\n",
" if (typeof event === \"function\") {\n",
" event = new event(type, params);\n",
" } else {\n",
" event = window.document.createEvent(\"Event\");\n",
" if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n",
" else event.initEvent(type, false, false);\n",
" }\n",
"\n",
" node.dispatchEvent(event);\n",
"}\n",
"\n",
"function dispatchConstant(type, params) {\n",
" return function() {\n",
" return dispatchEvent(this, type, params);\n",
" };\n",
"}\n",
"\n",
"function dispatchFunction(type, params) {\n",
" return function() {\n",
" return dispatchEvent(this, type, params.apply(this, arguments));\n",
" };\n",
"}\n",
"\n",
"function selection_dispatch(type, params) {\n",
" return this.each((typeof params === \"function\"\n",
" ? dispatchFunction\n",
" : dispatchConstant)(type, params));\n",
"}\n",
"\n",
"var root = [null];\n",
"\n",
"function Selection(groups, parents) {\n",
" this._groups = groups;\n",
" this._parents = parents;\n",
"}\n",
"\n",
"function selection() {\n",
" return new Selection([[document.documentElement]], root);\n",
"}\n",
"\n",
"Selection.prototype = selection.prototype = {\n",
" constructor: Selection,\n",
" select: selection_select,\n",
" selectAll: selection_selectAll,\n",
" filter: selection_filter,\n",
" data: selection_data,\n",
" enter: selection_enter,\n",
" exit: selection_exit,\n",
" join: selection_join,\n",
" merge: selection_merge,\n",
" order: selection_order,\n",
" sort: selection_sort,\n",
" call: selection_call,\n",
" nodes: selection_nodes,\n",
" node: selection_node,\n",
" size: selection_size,\n",
" empty: selection_empty,\n",
" each: selection_each,\n",
" attr: selection_attr,\n",
" style: selection_style,\n",
" property: selection_property,\n",
" classed: selection_classed,\n",
" text: selection_text,\n",
" html: selection_html,\n",
" raise: selection_raise,\n",
" lower: selection_lower,\n",
" append: selection_append,\n",
" insert: selection_insert,\n",
" remove: selection_remove,\n",
" clone: selection_clone,\n",
" datum: selection_datum,\n",
" on: selection_on,\n",
" dispatch: selection_dispatch\n",
"};\n",
"\n",
"function select(selector) {\n",
" return typeof selector === \"string\"\n",
" ? new Selection([[document.querySelector(selector)]], [document.documentElement])\n",
" : new Selection([[selector]], root);\n",
"}\n",
"\n",
"function create(name) {\n",
" return select(creator(name).call(document.documentElement));\n",
"}\n",
"\n",
"var nextId = 0;\n",
"\n",
"function local() {\n",
" return new Local;\n",
"}\n",
"\n",
"function Local() {\n",
" this._ = \"@\" + (++nextId).toString(36);\n",
"}\n",
"\n",
"Local.prototype = local.prototype = {\n",
" constructor: Local,\n",
" get: function(node) {\n",
" var id = this._;\n",
" while (!(id in node)) if (!(node = node.parentNode)) return;\n",
" return node[id];\n",
" },\n",
" set: function(node, value) {\n",
" return node[this._] = value;\n",
" },\n",
" remove: function(node) {\n",
" return this._ in node && delete node[this._];\n",
" },\n",
" toString: function() {\n",
" return this._;\n",
" }\n",
"};\n",
"\n",
"function sourceEvent() {\n",
" var current = exports.event, source;\n",
" while (source = current.sourceEvent) current = source;\n",
" return current;\n",
"}\n",
"\n",
"function point(node, event) {\n",
" var svg = node.ownerSVGElement || node;\n",
"\n",
" if (svg.createSVGPoint) {\n",
" var point = svg.createSVGPoint();\n",
" point.x = event.clientX, point.y = event.clientY;\n",
" point = point.matrixTransform(node.getScreenCTM().inverse());\n",
" return [point.x, point.y];\n",
" }\n",
"\n",
" var rect = node.getBoundingClientRect();\n",
" return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n",
"}\n",
"\n",
"function mouse(node) {\n",
" var event = sourceEvent();\n",
" if (event.changedTouches) event = event.changedTouches[0];\n",
" return point(node, event);\n",
"}\n",
"\n",
"function selectAll(selector) {\n",
" return typeof selector === \"string\"\n",
" ? new Selection([document.querySelectorAll(selector)], [document.documentElement])\n",
" : new Selection([selector == null ? [] : selector], root);\n",
"}\n",
"\n",
"function touch(node, touches, identifier) {\n",
" if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches;\n",
"\n",
" for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {\n",
" if ((touch = touches[i]).identifier === identifier) {\n",
" return point(node, touch);\n",
" }\n",
" }\n",
"\n",
" return null;\n",
"}\n",
"\n",
"function touches(node, touches) {\n",
" if (touches == null) touches = sourceEvent().touches;\n",
"\n",
" for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {\n",
" points[i] = point(node, touches[i]);\n",
" }\n",
"\n",
" return points;\n",
"}\n",
"\n",
"exports.clientPoint = point;\n",
"exports.create = create;\n",
"exports.creator = creator;\n",
"exports.customEvent = customEvent;\n",
"exports.local = local;\n",
"exports.matcher = matcher;\n",
"exports.mouse = mouse;\n",
"exports.namespace = namespace;\n",
"exports.namespaces = namespaces;\n",
"exports.select = select;\n",
"exports.selectAll = selectAll;\n",
"exports.selection = selection;\n",
"exports.selector = selector;\n",
"exports.selectorAll = selectorAll;\n",
"exports.style = styleValue;\n",
"exports.touch = touch;\n",
"exports.touches = touches;\n",
"exports.window = defaultView;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],12:[function(require,module,exports){\n",
"// https://d3js.org/d3-shape/ v1.3.7 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-path')) :\n",
"typeof define === 'function' && define.amd ? define(['exports', 'd3-path'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}, global.d3));\n",
"}(this, function (exports, d3Path) { 'use strict';\n",
"\n",
"function constant(x) {\n",
" return function constant() {\n",
" return x;\n",
" };\n",
"}\n",
"\n",
"var abs = Math.abs;\n",
"var atan2 = Math.atan2;\n",
"var cos = Math.cos;\n",
"var max = Math.max;\n",
"var min = Math.min;\n",
"var sin = Math.sin;\n",
"var sqrt = Math.sqrt;\n",
"\n",
"var epsilon = 1e-12;\n",
"var pi = Math.PI;\n",
"var halfPi = pi / 2;\n",
"var tau = 2 * pi;\n",
"\n",
"function acos(x) {\n",
" return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n",
"}\n",
"\n",
"function asin(x) {\n",
" return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);\n",
"}\n",
"\n",
"function arcInnerRadius(d) {\n",
" return d.innerRadius;\n",
"}\n",
"\n",
"function arcOuterRadius(d) {\n",
" return d.outerRadius;\n",
"}\n",
"\n",
"function arcStartAngle(d) {\n",
" return d.startAngle;\n",
"}\n",
"\n",
"function arcEndAngle(d) {\n",
" return d.endAngle;\n",
"}\n",
"\n",
"function arcPadAngle(d) {\n",
" return d && d.padAngle; // Note: optional!\n",
"}\n",
"\n",
"function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n",
" var x10 = x1 - x0, y10 = y1 - y0,\n",
" x32 = x3 - x2, y32 = y3 - y2,\n",
" t = y32 * x10 - x32 * y10;\n",
" if (t * t < epsilon) return;\n",
" t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;\n",
" return [x0 + t * x10, y0 + t * y10];\n",
"}\n",
"\n",
"// Compute perpendicular offset line of length rc.\n",
"// http://mathworld.wolfram.com/Circle-LineIntersection.html\n",
"function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {\n",
" var x01 = x0 - x1,\n",
" y01 = y0 - y1,\n",
" lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),\n",
" ox = lo * y01,\n",
" oy = -lo * x01,\n",
" x11 = x0 + ox,\n",
" y11 = y0 + oy,\n",
" x10 = x1 + ox,\n",
" y10 = y1 + oy,\n",
" x00 = (x11 + x10) / 2,\n",
" y00 = (y11 + y10) / 2,\n",
" dx = x10 - x11,\n",
" dy = y10 - y11,\n",
" d2 = dx * dx + dy * dy,\n",
" r = r1 - rc,\n",
" D = x11 * y10 - x10 * y11,\n",
" d = (dy < 0 ? -1 : 1) * sqrt(max(0, r * r * d2 - D * D)),\n",
" cx0 = (D * dy - dx * d) / d2,\n",
" cy0 = (-D * dx - dy * d) / d2,\n",
" cx1 = (D * dy + dx * d) / d2,\n",
" cy1 = (-D * dx + dy * d) / d2,\n",
" dx0 = cx0 - x00,\n",
" dy0 = cy0 - y00,\n",
" dx1 = cx1 - x00,\n",
" dy1 = cy1 - y00;\n",
"\n",
" // Pick the closer of the two intersection points.\n",
" // TODO Is there a faster way to determine which intersection to use?\n",
" if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;\n",
"\n",
" return {\n",
" cx: cx0,\n",
" cy: cy0,\n",
" x01: -ox,\n",
" y01: -oy,\n",
" x11: cx0 * (r1 / r - 1),\n",
" y11: cy0 * (r1 / r - 1)\n",
" };\n",
"}\n",
"\n",
"function arc() {\n",
" var innerRadius = arcInnerRadius,\n",
" outerRadius = arcOuterRadius,\n",
" cornerRadius = constant(0),\n",
" padRadius = null,\n",
" startAngle = arcStartAngle,\n",
" endAngle = arcEndAngle,\n",
" padAngle = arcPadAngle,\n",
" context = null;\n",
"\n",
" function arc() {\n",
" var buffer,\n",
" r,\n",
" r0 = +innerRadius.apply(this, arguments),\n",
" r1 = +outerRadius.apply(this, arguments),\n",
" a0 = startAngle.apply(this, arguments) - halfPi,\n",
" a1 = endAngle.apply(this, arguments) - halfPi,\n",
" da = abs(a1 - a0),\n",
" cw = a1 > a0;\n",
"\n",
" if (!context) context = buffer = d3Path.path();\n",
"\n",
" // Ensure that the outer radius is always larger than the inner radius.\n",
" if (r1 < r0) r = r1, r1 = r0, r0 = r;\n",
"\n",
" // Is it a point?\n",
" if (!(r1 > epsilon)) context.moveTo(0, 0);\n",
"\n",
" // Or is it a circle or annulus?\n",
" else if (da > tau - epsilon) {\n",
" context.moveTo(r1 * cos(a0), r1 * sin(a0));\n",
" context.arc(0, 0, r1, a0, a1, !cw);\n",
" if (r0 > epsilon) {\n",
" context.moveTo(r0 * cos(a1), r0 * sin(a1));\n",
" context.arc(0, 0, r0, a1, a0, cw);\n",
" }\n",
" }\n",
"\n",
" // Or is it a circular or annular sector?\n",
" else {\n",
" var a01 = a0,\n",
" a11 = a1,\n",
" a00 = a0,\n",
" a10 = a1,\n",
" da0 = da,\n",
" da1 = da,\n",
" ap = padAngle.apply(this, arguments) / 2,\n",
" rp = (ap > epsilon) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),\n",
" rc = min(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),\n",
" rc0 = rc,\n",
" rc1 = rc,\n",
" t0,\n",
" t1;\n",
"\n",
" // Apply padding? Note that since r1 >= r0, da1 >= da0.\n",
" if (rp > epsilon) {\n",
" var p0 = asin(rp / r0 * sin(ap)),\n",
" p1 = asin(rp / r1 * sin(ap));\n",
" if ((da0 -= p0 * 2) > epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;\n",
" else da0 = 0, a00 = a10 = (a0 + a1) / 2;\n",
" if ((da1 -= p1 * 2) > epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;\n",
" else da1 = 0, a01 = a11 = (a0 + a1) / 2;\n",
" }\n",
"\n",
" var x01 = r1 * cos(a01),\n",
" y01 = r1 * sin(a01),\n",
" x10 = r0 * cos(a10),\n",
" y10 = r0 * sin(a10);\n",
"\n",
" // Apply rounded corners?\n",
" if (rc > epsilon) {\n",
" var x11 = r1 * cos(a11),\n",
" y11 = r1 * sin(a11),\n",
" x00 = r0 * cos(a00),\n",
" y00 = r0 * sin(a00),\n",
" oc;\n",
"\n",
" // Restrict the corner radius according to the sector angle.\n",
" if (da < pi && (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10))) {\n",
" var ax = x01 - oc[0],\n",
" ay = y01 - oc[1],\n",
" bx = x11 - oc[0],\n",
" by = y11 - oc[1],\n",
" kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),\n",
" lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);\n",
" rc0 = min(rc, (r0 - lc) / (kc - 1));\n",
" rc1 = min(rc, (r1 - lc) / (kc + 1));\n",
" }\n",
" }\n",
"\n",
" // Is the sector collapsed to a line?\n",
" if (!(da1 > epsilon)) context.moveTo(x01, y01);\n",
"\n",
" // Does the sector's outer ring have rounded corners?\n",
" else if (rc1 > epsilon) {\n",
" t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);\n",
" t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);\n",
"\n",
" context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);\n",
"\n",
" // Have the corners merged?\n",
" if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n",
"\n",
" // Otherwise, draw the two corners and the ring.\n",
" else {\n",
" context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n",
" context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);\n",
" context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n",
" }\n",
" }\n",
"\n",
" // Or is the outer ring just a circular arc?\n",
" else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);\n",
"\n",
" // Is there no inner ring, and it's a circular sector?\n",
" // Or perhaps it's an annular sector collapsed due to padding?\n",
" if (!(r0 > epsilon) || !(da0 > epsilon)) context.lineTo(x10, y10);\n",
"\n",
" // Does the sector's inner ring (or point) have rounded corners?\n",
" else if (rc0 > epsilon) {\n",
" t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);\n",
" t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);\n",
"\n",
" context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);\n",
"\n",
" // Have the corners merged?\n",
" if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n",
"\n",
" // Otherwise, draw the two corners and the ring.\n",
" else {\n",
" context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n",
" context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);\n",
" context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n",
" }\n",
" }\n",
"\n",
" // Or is the inner ring just a circular arc?\n",
" else context.arc(0, 0, r0, a10, a00, cw);\n",
" }\n",
"\n",
" context.closePath();\n",
"\n",
" if (buffer) return context = null, buffer + \"\" || null;\n",
" }\n",
"\n",
" arc.centroid = function() {\n",
" var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,\n",
" a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2;\n",
" return [cos(a) * r, sin(a) * r];\n",
" };\n",
"\n",
" arc.innerRadius = function(_) {\n",
" return arguments.length ? (innerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : innerRadius;\n",
" };\n",
"\n",
" arc.outerRadius = function(_) {\n",
" return arguments.length ? (outerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : outerRadius;\n",
" };\n",
"\n",
" arc.cornerRadius = function(_) {\n",
" return arguments.length ? (cornerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : cornerRadius;\n",
" };\n",
"\n",
" arc.padRadius = function(_) {\n",
" return arguments.length ? (padRadius = _ == null ? null : typeof _ === \"function\" ? _ : constant(+_), arc) : padRadius;\n",
" };\n",
"\n",
" arc.startAngle = function(_) {\n",
" return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : startAngle;\n",
" };\n",
"\n",
" arc.endAngle = function(_) {\n",
" return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : endAngle;\n",
" };\n",
"\n",
" arc.padAngle = function(_) {\n",
" return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : padAngle;\n",
" };\n",
"\n",
" arc.context = function(_) {\n",
" return arguments.length ? ((context = _ == null ? null : _), arc) : context;\n",
" };\n",
"\n",
" return arc;\n",
"}\n",
"\n",
"function Linear(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"Linear.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n",
" case 1: this._point = 2; // proceed\n",
" default: this._context.lineTo(x, y); break;\n",
" }\n",
" }\n",
"};\n",
"\n",
"function curveLinear(context) {\n",
" return new Linear(context);\n",
"}\n",
"\n",
"function x(p) {\n",
" return p[0];\n",
"}\n",
"\n",
"function y(p) {\n",
" return p[1];\n",
"}\n",
"\n",
"function line() {\n",
" var x$1 = x,\n",
" y$1 = y,\n",
" defined = constant(true),\n",
" context = null,\n",
" curve = curveLinear,\n",
" output = null;\n",
"\n",
" function line(data) {\n",
" var i,\n",
" n = data.length,\n",
" d,\n",
" defined0 = false,\n",
" buffer;\n",
"\n",
" if (context == null) output = curve(buffer = d3Path.path());\n",
"\n",
" for (i = 0; i <= n; ++i) {\n",
" if (!(i < n && defined(d = data[i], i, data)) === defined0) {\n",
" if (defined0 = !defined0) output.lineStart();\n",
" else output.lineEnd();\n",
" }\n",
" if (defined0) output.point(+x$1(d, i, data), +y$1(d, i, data));\n",
" }\n",
"\n",
" if (buffer) return output = null, buffer + \"\" || null;\n",
" }\n",
"\n",
" line.x = function(_) {\n",
" return arguments.length ? (x$1 = typeof _ === \"function\" ? _ : constant(+_), line) : x$1;\n",
" };\n",
"\n",
" line.y = function(_) {\n",
" return arguments.length ? (y$1 = typeof _ === \"function\" ? _ : constant(+_), line) : y$1;\n",
" };\n",
"\n",
" line.defined = function(_) {\n",
" return arguments.length ? (defined = typeof _ === \"function\" ? _ : constant(!!_), line) : defined;\n",
" };\n",
"\n",
" line.curve = function(_) {\n",
" return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;\n",
" };\n",
"\n",
" line.context = function(_) {\n",
" return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;\n",
" };\n",
"\n",
" return line;\n",
"}\n",
"\n",
"function area() {\n",
" var x0 = x,\n",
" x1 = null,\n",
" y0 = constant(0),\n",
" y1 = y,\n",
" defined = constant(true),\n",
" context = null,\n",
" curve = curveLinear,\n",
" output = null;\n",
"\n",
" function area(data) {\n",
" var i,\n",
" j,\n",
" k,\n",
" n = data.length,\n",
" d,\n",
" defined0 = false,\n",
" buffer,\n",
" x0z = new Array(n),\n",
" y0z = new Array(n);\n",
"\n",
" if (context == null) output = curve(buffer = d3Path.path());\n",
"\n",
" for (i = 0; i <= n; ++i) {\n",
" if (!(i < n && defined(d = data[i], i, data)) === defined0) {\n",
" if (defined0 = !defined0) {\n",
" j = i;\n",
" output.areaStart();\n",
" output.lineStart();\n",
" } else {\n",
" output.lineEnd();\n",
" output.lineStart();\n",
" for (k = i - 1; k >= j; --k) {\n",
" output.point(x0z[k], y0z[k]);\n",
" }\n",
" output.lineEnd();\n",
" output.areaEnd();\n",
" }\n",
" }\n",
" if (defined0) {\n",
" x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);\n",
" output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);\n",
" }\n",
" }\n",
"\n",
" if (buffer) return output = null, buffer + \"\" || null;\n",
" }\n",
"\n",
" function arealine() {\n",
" return line().defined(defined).curve(curve).context(context);\n",
" }\n",
"\n",
" area.x = function(_) {\n",
" return arguments.length ? (x0 = typeof _ === \"function\" ? _ : constant(+_), x1 = null, area) : x0;\n",
" };\n",
"\n",
" area.x0 = function(_) {\n",
" return arguments.length ? (x0 = typeof _ === \"function\" ? _ : constant(+_), area) : x0;\n",
" };\n",
"\n",
" area.x1 = function(_) {\n",
" return arguments.length ? (x1 = _ == null ? null : typeof _ === \"function\" ? _ : constant(+_), area) : x1;\n",
" };\n",
"\n",
" area.y = function(_) {\n",
" return arguments.length ? (y0 = typeof _ === \"function\" ? _ : constant(+_), y1 = null, area) : y0;\n",
" };\n",
"\n",
" area.y0 = function(_) {\n",
" return arguments.length ? (y0 = typeof _ === \"function\" ? _ : constant(+_), area) : y0;\n",
" };\n",
"\n",
" area.y1 = function(_) {\n",
" return arguments.length ? (y1 = _ == null ? null : typeof _ === \"function\" ? _ : constant(+_), area) : y1;\n",
" };\n",
"\n",
" area.lineX0 =\n",
" area.lineY0 = function() {\n",
" return arealine().x(x0).y(y0);\n",
" };\n",
"\n",
" area.lineY1 = function() {\n",
" return arealine().x(x0).y(y1);\n",
" };\n",
"\n",
" area.lineX1 = function() {\n",
" return arealine().x(x1).y(y0);\n",
" };\n",
"\n",
" area.defined = function(_) {\n",
" return arguments.length ? (defined = typeof _ === \"function\" ? _ : constant(!!_), area) : defined;\n",
" };\n",
"\n",
" area.curve = function(_) {\n",
" return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;\n",
" };\n",
"\n",
" area.context = function(_) {\n",
" return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;\n",
" };\n",
"\n",
" return area;\n",
"}\n",
"\n",
"function descending(a, b) {\n",
" return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n",
"}\n",
"\n",
"function identity(d) {\n",
" return d;\n",
"}\n",
"\n",
"function pie() {\n",
" var value = identity,\n",
" sortValues = descending,\n",
" sort = null,\n",
" startAngle = constant(0),\n",
" endAngle = constant(tau),\n",
" padAngle = constant(0);\n",
"\n",
" function pie(data) {\n",
" var i,\n",
" n = data.length,\n",
" j,\n",
" k,\n",
" sum = 0,\n",
" index = new Array(n),\n",
" arcs = new Array(n),\n",
" a0 = +startAngle.apply(this, arguments),\n",
" da = Math.min(tau, Math.max(-tau, endAngle.apply(this, arguments) - a0)),\n",
" a1,\n",
" p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),\n",
" pa = p * (da < 0 ? -1 : 1),\n",
" v;\n",
"\n",
" for (i = 0; i < n; ++i) {\n",
" if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {\n",
" sum += v;\n",
" }\n",
" }\n",
"\n",
" // Optionally sort the arcs by previously-computed values or by data.\n",
" if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });\n",
" else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });\n",
"\n",
" // Compute the arcs! They are stored in the original data's order.\n",
" for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {\n",
" j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {\n",
" data: data[j],\n",
" index: i,\n",
" value: v,\n",
" startAngle: a0,\n",
" endAngle: a1,\n",
" padAngle: p\n",
" };\n",
" }\n",
"\n",
" return arcs;\n",
" }\n",
"\n",
" pie.value = function(_) {\n",
" return arguments.length ? (value = typeof _ === \"function\" ? _ : constant(+_), pie) : value;\n",
" };\n",
"\n",
" pie.sortValues = function(_) {\n",
" return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;\n",
" };\n",
"\n",
" pie.sort = function(_) {\n",
" return arguments.length ? (sort = _, sortValues = null, pie) : sort;\n",
" };\n",
"\n",
" pie.startAngle = function(_) {\n",
" return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : constant(+_), pie) : startAngle;\n",
" };\n",
"\n",
" pie.endAngle = function(_) {\n",
" return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : constant(+_), pie) : endAngle;\n",
" };\n",
"\n",
" pie.padAngle = function(_) {\n",
" return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : constant(+_), pie) : padAngle;\n",
" };\n",
"\n",
" return pie;\n",
"}\n",
"\n",
"var curveRadialLinear = curveRadial(curveLinear);\n",
"\n",
"function Radial(curve) {\n",
" this._curve = curve;\n",
"}\n",
"\n",
"Radial.prototype = {\n",
" areaStart: function() {\n",
" this._curve.areaStart();\n",
" },\n",
" areaEnd: function() {\n",
" this._curve.areaEnd();\n",
" },\n",
" lineStart: function() {\n",
" this._curve.lineStart();\n",
" },\n",
" lineEnd: function() {\n",
" this._curve.lineEnd();\n",
" },\n",
" point: function(a, r) {\n",
" this._curve.point(r * Math.sin(a), r * -Math.cos(a));\n",
" }\n",
"};\n",
"\n",
"function curveRadial(curve) {\n",
"\n",
" function radial(context) {\n",
" return new Radial(curve(context));\n",
" }\n",
"\n",
" radial._curve = curve;\n",
"\n",
" return radial;\n",
"}\n",
"\n",
"function lineRadial(l) {\n",
" var c = l.curve;\n",
"\n",
" l.angle = l.x, delete l.x;\n",
" l.radius = l.y, delete l.y;\n",
"\n",
" l.curve = function(_) {\n",
" return arguments.length ? c(curveRadial(_)) : c()._curve;\n",
" };\n",
"\n",
" return l;\n",
"}\n",
"\n",
"function lineRadial$1() {\n",
" return lineRadial(line().curve(curveRadialLinear));\n",
"}\n",
"\n",
"function areaRadial() {\n",
" var a = area().curve(curveRadialLinear),\n",
" c = a.curve,\n",
" x0 = a.lineX0,\n",
" x1 = a.lineX1,\n",
" y0 = a.lineY0,\n",
" y1 = a.lineY1;\n",
"\n",
" a.angle = a.x, delete a.x;\n",
" a.startAngle = a.x0, delete a.x0;\n",
" a.endAngle = a.x1, delete a.x1;\n",
" a.radius = a.y, delete a.y;\n",
" a.innerRadius = a.y0, delete a.y0;\n",
" a.outerRadius = a.y1, delete a.y1;\n",
" a.lineStartAngle = function() { return lineRadial(x0()); }, delete a.lineX0;\n",
" a.lineEndAngle = function() { return lineRadial(x1()); }, delete a.lineX1;\n",
" a.lineInnerRadius = function() { return lineRadial(y0()); }, delete a.lineY0;\n",
" a.lineOuterRadius = function() { return lineRadial(y1()); }, delete a.lineY1;\n",
"\n",
" a.curve = function(_) {\n",
" return arguments.length ? c(curveRadial(_)) : c()._curve;\n",
" };\n",
"\n",
" return a;\n",
"}\n",
"\n",
"function pointRadial(x, y) {\n",
" return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];\n",
"}\n",
"\n",
"var slice = Array.prototype.slice;\n",
"\n",
"function linkSource(d) {\n",
" return d.source;\n",
"}\n",
"\n",
"function linkTarget(d) {\n",
" return d.target;\n",
"}\n",
"\n",
"function link(curve) {\n",
" var source = linkSource,\n",
" target = linkTarget,\n",
" x$1 = x,\n",
" y$1 = y,\n",
" context = null;\n",
"\n",
" function link() {\n",
" var buffer, argv = slice.call(arguments), s = source.apply(this, argv), t = target.apply(this, argv);\n",
" if (!context) context = buffer = d3Path.path();\n",
" curve(context, +x$1.apply(this, (argv[0] = s, argv)), +y$1.apply(this, argv), +x$1.apply(this, (argv[0] = t, argv)), +y$1.apply(this, argv));\n",
" if (buffer) return context = null, buffer + \"\" || null;\n",
" }\n",
"\n",
" link.source = function(_) {\n",
" return arguments.length ? (source = _, link) : source;\n",
" };\n",
"\n",
" link.target = function(_) {\n",
" return arguments.length ? (target = _, link) : target;\n",
" };\n",
"\n",
" link.x = function(_) {\n",
" return arguments.length ? (x$1 = typeof _ === \"function\" ? _ : constant(+_), link) : x$1;\n",
" };\n",
"\n",
" link.y = function(_) {\n",
" return arguments.length ? (y$1 = typeof _ === \"function\" ? _ : constant(+_), link) : y$1;\n",
" };\n",
"\n",
" link.context = function(_) {\n",
" return arguments.length ? ((context = _ == null ? null : _), link) : context;\n",
" };\n",
"\n",
" return link;\n",
"}\n",
"\n",
"function curveHorizontal(context, x0, y0, x1, y1) {\n",
" context.moveTo(x0, y0);\n",
" context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);\n",
"}\n",
"\n",
"function curveVertical(context, x0, y0, x1, y1) {\n",
" context.moveTo(x0, y0);\n",
" context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);\n",
"}\n",
"\n",
"function curveRadial$1(context, x0, y0, x1, y1) {\n",
" var p0 = pointRadial(x0, y0),\n",
" p1 = pointRadial(x0, y0 = (y0 + y1) / 2),\n",
" p2 = pointRadial(x1, y0),\n",
" p3 = pointRadial(x1, y1);\n",
" context.moveTo(p0[0], p0[1]);\n",
" context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);\n",
"}\n",
"\n",
"function linkHorizontal() {\n",
" return link(curveHorizontal);\n",
"}\n",
"\n",
"function linkVertical() {\n",
" return link(curveVertical);\n",
"}\n",
"\n",
"function linkRadial() {\n",
" var l = link(curveRadial$1);\n",
" l.angle = l.x, delete l.x;\n",
" l.radius = l.y, delete l.y;\n",
" return l;\n",
"}\n",
"\n",
"var circle = {\n",
" draw: function(context, size) {\n",
" var r = Math.sqrt(size / pi);\n",
" context.moveTo(r, 0);\n",
" context.arc(0, 0, r, 0, tau);\n",
" }\n",
"};\n",
"\n",
"var cross = {\n",
" draw: function(context, size) {\n",
" var r = Math.sqrt(size / 5) / 2;\n",
" context.moveTo(-3 * r, -r);\n",
" context.lineTo(-r, -r);\n",
" context.lineTo(-r, -3 * r);\n",
" context.lineTo(r, -3 * r);\n",
" context.lineTo(r, -r);\n",
" context.lineTo(3 * r, -r);\n",
" context.lineTo(3 * r, r);\n",
" context.lineTo(r, r);\n",
" context.lineTo(r, 3 * r);\n",
" context.lineTo(-r, 3 * r);\n",
" context.lineTo(-r, r);\n",
" context.lineTo(-3 * r, r);\n",
" context.closePath();\n",
" }\n",
"};\n",
"\n",
"var tan30 = Math.sqrt(1 / 3),\n",
" tan30_2 = tan30 * 2;\n",
"\n",
"var diamond = {\n",
" draw: function(context, size) {\n",
" var y = Math.sqrt(size / tan30_2),\n",
" x = y * tan30;\n",
" context.moveTo(0, -y);\n",
" context.lineTo(x, 0);\n",
" context.lineTo(0, y);\n",
" context.lineTo(-x, 0);\n",
" context.closePath();\n",
" }\n",
"};\n",
"\n",
"var ka = 0.89081309152928522810,\n",
" kr = Math.sin(pi / 10) / Math.sin(7 * pi / 10),\n",
" kx = Math.sin(tau / 10) * kr,\n",
" ky = -Math.cos(tau / 10) * kr;\n",
"\n",
"var star = {\n",
" draw: function(context, size) {\n",
" var r = Math.sqrt(size * ka),\n",
" x = kx * r,\n",
" y = ky * r;\n",
" context.moveTo(0, -r);\n",
" context.lineTo(x, y);\n",
" for (var i = 1; i < 5; ++i) {\n",
" var a = tau * i / 5,\n",
" c = Math.cos(a),\n",
" s = Math.sin(a);\n",
" context.lineTo(s * r, -c * r);\n",
" context.lineTo(c * x - s * y, s * x + c * y);\n",
" }\n",
" context.closePath();\n",
" }\n",
"};\n",
"\n",
"var square = {\n",
" draw: function(context, size) {\n",
" var w = Math.sqrt(size),\n",
" x = -w / 2;\n",
" context.rect(x, x, w, w);\n",
" }\n",
"};\n",
"\n",
"var sqrt3 = Math.sqrt(3);\n",
"\n",
"var triangle = {\n",
" draw: function(context, size) {\n",
" var y = -Math.sqrt(size / (sqrt3 * 3));\n",
" context.moveTo(0, y * 2);\n",
" context.lineTo(-sqrt3 * y, -y);\n",
" context.lineTo(sqrt3 * y, -y);\n",
" context.closePath();\n",
" }\n",
"};\n",
"\n",
"var c = -0.5,\n",
" s = Math.sqrt(3) / 2,\n",
" k = 1 / Math.sqrt(12),\n",
" a = (k / 2 + 1) * 3;\n",
"\n",
"var wye = {\n",
" draw: function(context, size) {\n",
" var r = Math.sqrt(size / a),\n",
" x0 = r / 2,\n",
" y0 = r * k,\n",
" x1 = x0,\n",
" y1 = r * k + r,\n",
" x2 = -x1,\n",
" y2 = y1;\n",
" context.moveTo(x0, y0);\n",
" context.lineTo(x1, y1);\n",
" context.lineTo(x2, y2);\n",
" context.lineTo(c * x0 - s * y0, s * x0 + c * y0);\n",
" context.lineTo(c * x1 - s * y1, s * x1 + c * y1);\n",
" context.lineTo(c * x2 - s * y2, s * x2 + c * y2);\n",
" context.lineTo(c * x0 + s * y0, c * y0 - s * x0);\n",
" context.lineTo(c * x1 + s * y1, c * y1 - s * x1);\n",
" context.lineTo(c * x2 + s * y2, c * y2 - s * x2);\n",
" context.closePath();\n",
" }\n",
"};\n",
"\n",
"var symbols = [\n",
" circle,\n",
" cross,\n",
" diamond,\n",
" square,\n",
" star,\n",
" triangle,\n",
" wye\n",
"];\n",
"\n",
"function symbol() {\n",
" var type = constant(circle),\n",
" size = constant(64),\n",
" context = null;\n",
"\n",
" function symbol() {\n",
" var buffer;\n",
" if (!context) context = buffer = d3Path.path();\n",
" type.apply(this, arguments).draw(context, +size.apply(this, arguments));\n",
" if (buffer) return context = null, buffer + \"\" || null;\n",
" }\n",
"\n",
" symbol.type = function(_) {\n",
" return arguments.length ? (type = typeof _ === \"function\" ? _ : constant(_), symbol) : type;\n",
" };\n",
"\n",
" symbol.size = function(_) {\n",
" return arguments.length ? (size = typeof _ === \"function\" ? _ : constant(+_), symbol) : size;\n",
" };\n",
"\n",
" symbol.context = function(_) {\n",
" return arguments.length ? (context = _ == null ? null : _, symbol) : context;\n",
" };\n",
"\n",
" return symbol;\n",
"}\n",
"\n",
"function noop() {}\n",
"\n",
"function point(that, x, y) {\n",
" that._context.bezierCurveTo(\n",
" (2 * that._x0 + that._x1) / 3,\n",
" (2 * that._y0 + that._y1) / 3,\n",
" (that._x0 + 2 * that._x1) / 3,\n",
" (that._y0 + 2 * that._y1) / 3,\n",
" (that._x0 + 4 * that._x1 + x) / 6,\n",
" (that._y0 + 4 * that._y1 + y) / 6\n",
" );\n",
"}\n",
"\n",
"function Basis(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"Basis.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x0 = this._x1 =\n",
" this._y0 = this._y1 = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" switch (this._point) {\n",
" case 3: point(this, this._x1, this._y1); // proceed\n",
" case 2: this._context.lineTo(this._x1, this._y1); break;\n",
" }\n",
" if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n",
" case 1: this._point = 2; break;\n",
" case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed\n",
" default: point(this, x, y); break;\n",
" }\n",
" this._x0 = this._x1, this._x1 = x;\n",
" this._y0 = this._y1, this._y1 = y;\n",
" }\n",
"};\n",
"\n",
"function basis(context) {\n",
" return new Basis(context);\n",
"}\n",
"\n",
"function BasisClosed(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"BasisClosed.prototype = {\n",
" areaStart: noop,\n",
" areaEnd: noop,\n",
" lineStart: function() {\n",
" this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =\n",
" this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" switch (this._point) {\n",
" case 1: {\n",
" this._context.moveTo(this._x2, this._y2);\n",
" this._context.closePath();\n",
" break;\n",
" }\n",
" case 2: {\n",
" this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);\n",
" this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);\n",
" this._context.closePath();\n",
" break;\n",
" }\n",
" case 3: {\n",
" this.point(this._x2, this._y2);\n",
" this.point(this._x3, this._y3);\n",
" this.point(this._x4, this._y4);\n",
" break;\n",
" }\n",
" }\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._x2 = x, this._y2 = y; break;\n",
" case 1: this._point = 2; this._x3 = x, this._y3 = y; break;\n",
" case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;\n",
" default: point(this, x, y); break;\n",
" }\n",
" this._x0 = this._x1, this._x1 = x;\n",
" this._y0 = this._y1, this._y1 = y;\n",
" }\n",
"};\n",
"\n",
"function basisClosed(context) {\n",
" return new BasisClosed(context);\n",
"}\n",
"\n",
"function BasisOpen(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"BasisOpen.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x0 = this._x1 =\n",
" this._y0 = this._y1 = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; break;\n",
" case 1: this._point = 2; break;\n",
" case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;\n",
" case 3: this._point = 4; // proceed\n",
" default: point(this, x, y); break;\n",
" }\n",
" this._x0 = this._x1, this._x1 = x;\n",
" this._y0 = this._y1, this._y1 = y;\n",
" }\n",
"};\n",
"\n",
"function basisOpen(context) {\n",
" return new BasisOpen(context);\n",
"}\n",
"\n",
"function Bundle(context, beta) {\n",
" this._basis = new Basis(context);\n",
" this._beta = beta;\n",
"}\n",
"\n",
"Bundle.prototype = {\n",
" lineStart: function() {\n",
" this._x = [];\n",
" this._y = [];\n",
" this._basis.lineStart();\n",
" },\n",
" lineEnd: function() {\n",
" var x = this._x,\n",
" y = this._y,\n",
" j = x.length - 1;\n",
"\n",
" if (j > 0) {\n",
" var x0 = x[0],\n",
" y0 = y[0],\n",
" dx = x[j] - x0,\n",
" dy = y[j] - y0,\n",
" i = -1,\n",
" t;\n",
"\n",
" while (++i <= j) {\n",
" t = i / j;\n",
" this._basis.point(\n",
" this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),\n",
" this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)\n",
" );\n",
" }\n",
" }\n",
"\n",
" this._x = this._y = null;\n",
" this._basis.lineEnd();\n",
" },\n",
" point: function(x, y) {\n",
" this._x.push(+x);\n",
" this._y.push(+y);\n",
" }\n",
"};\n",
"\n",
"var bundle = (function custom(beta) {\n",
"\n",
" function bundle(context) {\n",
" return beta === 1 ? new Basis(context) : new Bundle(context, beta);\n",
" }\n",
"\n",
" bundle.beta = function(beta) {\n",
" return custom(+beta);\n",
" };\n",
"\n",
" return bundle;\n",
"})(0.85);\n",
"\n",
"function point$1(that, x, y) {\n",
" that._context.bezierCurveTo(\n",
" that._x1 + that._k * (that._x2 - that._x0),\n",
" that._y1 + that._k * (that._y2 - that._y0),\n",
" that._x2 + that._k * (that._x1 - x),\n",
" that._y2 + that._k * (that._y1 - y),\n",
" that._x2,\n",
" that._y2\n",
" );\n",
"}\n",
"\n",
"function Cardinal(context, tension) {\n",
" this._context = context;\n",
" this._k = (1 - tension) / 6;\n",
"}\n",
"\n",
"Cardinal.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x0 = this._x1 = this._x2 =\n",
" this._y0 = this._y1 = this._y2 = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" switch (this._point) {\n",
" case 2: this._context.lineTo(this._x2, this._y2); break;\n",
" case 3: point$1(this, this._x1, this._y1); break;\n",
" }\n",
" if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n",
" case 1: this._point = 2; this._x1 = x, this._y1 = y; break;\n",
" case 2: this._point = 3; // proceed\n",
" default: point$1(this, x, y); break;\n",
" }\n",
" this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n",
" this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n",
" }\n",
"};\n",
"\n",
"var cardinal = (function custom(tension) {\n",
"\n",
" function cardinal(context) {\n",
" return new Cardinal(context, tension);\n",
" }\n",
"\n",
" cardinal.tension = function(tension) {\n",
" return custom(+tension);\n",
" };\n",
"\n",
" return cardinal;\n",
"})(0);\n",
"\n",
"function CardinalClosed(context, tension) {\n",
" this._context = context;\n",
" this._k = (1 - tension) / 6;\n",
"}\n",
"\n",
"CardinalClosed.prototype = {\n",
" areaStart: noop,\n",
" areaEnd: noop,\n",
" lineStart: function() {\n",
" this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n",
" this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" switch (this._point) {\n",
" case 1: {\n",
" this._context.moveTo(this._x3, this._y3);\n",
" this._context.closePath();\n",
" break;\n",
" }\n",
" case 2: {\n",
" this._context.lineTo(this._x3, this._y3);\n",
" this._context.closePath();\n",
" break;\n",
" }\n",
" case 3: {\n",
" this.point(this._x3, this._y3);\n",
" this.point(this._x4, this._y4);\n",
" this.point(this._x5, this._y5);\n",
" break;\n",
" }\n",
" }\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n",
" case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n",
" case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n",
" default: point$1(this, x, y); break;\n",
" }\n",
" this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n",
" this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n",
" }\n",
"};\n",
"\n",
"var cardinalClosed = (function custom(tension) {\n",
"\n",
" function cardinal(context) {\n",
" return new CardinalClosed(context, tension);\n",
" }\n",
"\n",
" cardinal.tension = function(tension) {\n",
" return custom(+tension);\n",
" };\n",
"\n",
" return cardinal;\n",
"})(0);\n",
"\n",
"function CardinalOpen(context, tension) {\n",
" this._context = context;\n",
" this._k = (1 - tension) / 6;\n",
"}\n",
"\n",
"CardinalOpen.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x0 = this._x1 = this._x2 =\n",
" this._y0 = this._y1 = this._y2 = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; break;\n",
" case 1: this._point = 2; break;\n",
" case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n",
" case 3: this._point = 4; // proceed\n",
" default: point$1(this, x, y); break;\n",
" }\n",
" this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n",
" this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n",
" }\n",
"};\n",
"\n",
"var cardinalOpen = (function custom(tension) {\n",
"\n",
" function cardinal(context) {\n",
" return new CardinalOpen(context, tension);\n",
" }\n",
"\n",
" cardinal.tension = function(tension) {\n",
" return custom(+tension);\n",
" };\n",
"\n",
" return cardinal;\n",
"})(0);\n",
"\n",
"function point$2(that, x, y) {\n",
" var x1 = that._x1,\n",
" y1 = that._y1,\n",
" x2 = that._x2,\n",
" y2 = that._y2;\n",
"\n",
" if (that._l01_a > epsilon) {\n",
" var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,\n",
" n = 3 * that._l01_a * (that._l01_a + that._l12_a);\n",
" x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;\n",
" y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;\n",
" }\n",
"\n",
" if (that._l23_a > epsilon) {\n",
" var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,\n",
" m = 3 * that._l23_a * (that._l23_a + that._l12_a);\n",
" x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;\n",
" y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;\n",
" }\n",
"\n",
" that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);\n",
"}\n",
"\n",
"function CatmullRom(context, alpha) {\n",
" this._context = context;\n",
" this._alpha = alpha;\n",
"}\n",
"\n",
"CatmullRom.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x0 = this._x1 = this._x2 =\n",
" this._y0 = this._y1 = this._y2 = NaN;\n",
" this._l01_a = this._l12_a = this._l23_a =\n",
" this._l01_2a = this._l12_2a = this._l23_2a =\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" switch (this._point) {\n",
" case 2: this._context.lineTo(this._x2, this._y2); break;\n",
" case 3: this.point(this._x2, this._y2); break;\n",
" }\n",
" if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
"\n",
" if (this._point) {\n",
" var x23 = this._x2 - x,\n",
" y23 = this._y2 - y;\n",
" this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n",
" }\n",
"\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n",
" case 1: this._point = 2; break;\n",
" case 2: this._point = 3; // proceed\n",
" default: point$2(this, x, y); break;\n",
" }\n",
"\n",
" this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n",
" this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n",
" this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n",
" this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n",
" }\n",
"};\n",
"\n",
"var catmullRom = (function custom(alpha) {\n",
"\n",
" function catmullRom(context) {\n",
" return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);\n",
" }\n",
"\n",
" catmullRom.alpha = function(alpha) {\n",
" return custom(+alpha);\n",
" };\n",
"\n",
" return catmullRom;\n",
"})(0.5);\n",
"\n",
"function CatmullRomClosed(context, alpha) {\n",
" this._context = context;\n",
" this._alpha = alpha;\n",
"}\n",
"\n",
"CatmullRomClosed.prototype = {\n",
" areaStart: noop,\n",
" areaEnd: noop,\n",
" lineStart: function() {\n",
" this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n",
" this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n",
" this._l01_a = this._l12_a = this._l23_a =\n",
" this._l01_2a = this._l12_2a = this._l23_2a =\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" switch (this._point) {\n",
" case 1: {\n",
" this._context.moveTo(this._x3, this._y3);\n",
" this._context.closePath();\n",
" break;\n",
" }\n",
" case 2: {\n",
" this._context.lineTo(this._x3, this._y3);\n",
" this._context.closePath();\n",
" break;\n",
" }\n",
" case 3: {\n",
" this.point(this._x3, this._y3);\n",
" this.point(this._x4, this._y4);\n",
" this.point(this._x5, this._y5);\n",
" break;\n",
" }\n",
" }\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
"\n",
" if (this._point) {\n",
" var x23 = this._x2 - x,\n",
" y23 = this._y2 - y;\n",
" this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n",
" }\n",
"\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n",
" case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n",
" case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n",
" default: point$2(this, x, y); break;\n",
" }\n",
"\n",
" this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n",
" this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n",
" this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n",
" this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n",
" }\n",
"};\n",
"\n",
"var catmullRomClosed = (function custom(alpha) {\n",
"\n",
" function catmullRom(context) {\n",
" return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);\n",
" }\n",
"\n",
" catmullRom.alpha = function(alpha) {\n",
" return custom(+alpha);\n",
" };\n",
"\n",
" return catmullRom;\n",
"})(0.5);\n",
"\n",
"function CatmullRomOpen(context, alpha) {\n",
" this._context = context;\n",
" this._alpha = alpha;\n",
"}\n",
"\n",
"CatmullRomOpen.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x0 = this._x1 = this._x2 =\n",
" this._y0 = this._y1 = this._y2 = NaN;\n",
" this._l01_a = this._l12_a = this._l23_a =\n",
" this._l01_2a = this._l12_2a = this._l23_2a =\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
"\n",
" if (this._point) {\n",
" var x23 = this._x2 - x,\n",
" y23 = this._y2 - y;\n",
" this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n",
" }\n",
"\n",
" switch (this._point) {\n",
" case 0: this._point = 1; break;\n",
" case 1: this._point = 2; break;\n",
" case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n",
" case 3: this._point = 4; // proceed\n",
" default: point$2(this, x, y); break;\n",
" }\n",
"\n",
" this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n",
" this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n",
" this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n",
" this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n",
" }\n",
"};\n",
"\n",
"var catmullRomOpen = (function custom(alpha) {\n",
"\n",
" function catmullRom(context) {\n",
" return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);\n",
" }\n",
"\n",
" catmullRom.alpha = function(alpha) {\n",
" return custom(+alpha);\n",
" };\n",
"\n",
" return catmullRom;\n",
"})(0.5);\n",
"\n",
"function LinearClosed(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"LinearClosed.prototype = {\n",
" areaStart: noop,\n",
" areaEnd: noop,\n",
" lineStart: function() {\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" if (this._point) this._context.closePath();\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" if (this._point) this._context.lineTo(x, y);\n",
" else this._point = 1, this._context.moveTo(x, y);\n",
" }\n",
"};\n",
"\n",
"function linearClosed(context) {\n",
" return new LinearClosed(context);\n",
"}\n",
"\n",
"function sign(x) {\n",
" return x < 0 ? -1 : 1;\n",
"}\n",
"\n",
"// Calculate the slopes of the tangents (Hermite-type interpolation) based on\n",
"// the following paper: Steffen, M. 1990. A Simple Method for Monotonic\n",
"// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.\n",
"// NOV(II), P. 443, 1990.\n",
"function slope3(that, x2, y2) {\n",
" var h0 = that._x1 - that._x0,\n",
" h1 = x2 - that._x1,\n",
" s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),\n",
" s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),\n",
" p = (s0 * h1 + s1 * h0) / (h0 + h1);\n",
" return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;\n",
"}\n",
"\n",
"// Calculate a one-sided slope.\n",
"function slope2(that, t) {\n",
" var h = that._x1 - that._x0;\n",
" return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;\n",
"}\n",
"\n",
"// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations\n",
"// \"you can express cubic Hermite interpolation in terms of cubic B'ezier curves\n",
"// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1\".\n",
"function point$3(that, t0, t1) {\n",
" var x0 = that._x0,\n",
" y0 = that._y0,\n",
" x1 = that._x1,\n",
" y1 = that._y1,\n",
" dx = (x1 - x0) / 3;\n",
" that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);\n",
"}\n",
"\n",
"function MonotoneX(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"MonotoneX.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x0 = this._x1 =\n",
" this._y0 = this._y1 =\n",
" this._t0 = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" switch (this._point) {\n",
" case 2: this._context.lineTo(this._x1, this._y1); break;\n",
" case 3: point$3(this, this._t0, slope2(this, this._t0)); break;\n",
" }\n",
" if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" var t1 = NaN;\n",
"\n",
" x = +x, y = +y;\n",
" if (x === this._x1 && y === this._y1) return; // Ignore coincident points.\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n",
" case 1: this._point = 2; break;\n",
" case 2: this._point = 3; point$3(this, slope2(this, t1 = slope3(this, x, y)), t1); break;\n",
" default: point$3(this, this._t0, t1 = slope3(this, x, y)); break;\n",
" }\n",
"\n",
" this._x0 = this._x1, this._x1 = x;\n",
" this._y0 = this._y1, this._y1 = y;\n",
" this._t0 = t1;\n",
" }\n",
"};\n",
"\n",
"function MonotoneY(context) {\n",
" this._context = new ReflectContext(context);\n",
"}\n",
"\n",
"(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {\n",
" MonotoneX.prototype.point.call(this, y, x);\n",
"};\n",
"\n",
"function ReflectContext(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"ReflectContext.prototype = {\n",
" moveTo: function(x, y) { this._context.moveTo(y, x); },\n",
" closePath: function() { this._context.closePath(); },\n",
" lineTo: function(x, y) { this._context.lineTo(y, x); },\n",
" bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }\n",
"};\n",
"\n",
"function monotoneX(context) {\n",
" return new MonotoneX(context);\n",
"}\n",
"\n",
"function monotoneY(context) {\n",
" return new MonotoneY(context);\n",
"}\n",
"\n",
"function Natural(context) {\n",
" this._context = context;\n",
"}\n",
"\n",
"Natural.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x = [];\n",
" this._y = [];\n",
" },\n",
" lineEnd: function() {\n",
" var x = this._x,\n",
" y = this._y,\n",
" n = x.length;\n",
"\n",
" if (n) {\n",
" this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);\n",
" if (n === 2) {\n",
" this._context.lineTo(x[1], y[1]);\n",
" } else {\n",
" var px = controlPoints(x),\n",
" py = controlPoints(y);\n",
" for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {\n",
" this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);\n",
" }\n",
" }\n",
" }\n",
"\n",
" if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();\n",
" this._line = 1 - this._line;\n",
" this._x = this._y = null;\n",
" },\n",
" point: function(x, y) {\n",
" this._x.push(+x);\n",
" this._y.push(+y);\n",
" }\n",
"};\n",
"\n",
"// See https://www.particleincell.com/2012/bezier-splines/ for derivation.\n",
"function controlPoints(x) {\n",
" var i,\n",
" n = x.length - 1,\n",
" m,\n",
" a = new Array(n),\n",
" b = new Array(n),\n",
" r = new Array(n);\n",
" a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];\n",
" for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];\n",
" a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];\n",
" for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];\n",
" a[n - 1] = r[n - 1] / b[n - 1];\n",
" for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];\n",
" b[n - 1] = (x[n] + a[n - 1]) / 2;\n",
" for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];\n",
" return [a, b];\n",
"}\n",
"\n",
"function natural(context) {\n",
" return new Natural(context);\n",
"}\n",
"\n",
"function Step(context, t) {\n",
" this._context = context;\n",
" this._t = t;\n",
"}\n",
"\n",
"Step.prototype = {\n",
" areaStart: function() {\n",
" this._line = 0;\n",
" },\n",
" areaEnd: function() {\n",
" this._line = NaN;\n",
" },\n",
" lineStart: function() {\n",
" this._x = this._y = NaN;\n",
" this._point = 0;\n",
" },\n",
" lineEnd: function() {\n",
" if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);\n",
" if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n",
" if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;\n",
" },\n",
" point: function(x, y) {\n",
" x = +x, y = +y;\n",
" switch (this._point) {\n",
" case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n",
" case 1: this._point = 2; // proceed\n",
" default: {\n",
" if (this._t <= 0) {\n",
" this._context.lineTo(this._x, y);\n",
" this._context.lineTo(x, y);\n",
" } else {\n",
" var x1 = this._x * (1 - this._t) + x * this._t;\n",
" this._context.lineTo(x1, this._y);\n",
" this._context.lineTo(x1, y);\n",
" }\n",
" break;\n",
" }\n",
" }\n",
" this._x = x, this._y = y;\n",
" }\n",
"};\n",
"\n",
"function step(context) {\n",
" return new Step(context, 0.5);\n",
"}\n",
"\n",
"function stepBefore(context) {\n",
" return new Step(context, 0);\n",
"}\n",
"\n",
"function stepAfter(context) {\n",
" return new Step(context, 1);\n",
"}\n",
"\n",
"function none(series, order) {\n",
" if (!((n = series.length) > 1)) return;\n",
" for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {\n",
" s0 = s1, s1 = series[order[i]];\n",
" for (j = 0; j < m; ++j) {\n",
" s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];\n",
" }\n",
" }\n",
"}\n",
"\n",
"function none$1(series) {\n",
" var n = series.length, o = new Array(n);\n",
" while (--n >= 0) o[n] = n;\n",
" return o;\n",
"}\n",
"\n",
"function stackValue(d, key) {\n",
" return d[key];\n",
"}\n",
"\n",
"function stack() {\n",
" var keys = constant([]),\n",
" order = none$1,\n",
" offset = none,\n",
" value = stackValue;\n",
"\n",
" function stack(data) {\n",
" var kz = keys.apply(this, arguments),\n",
" i,\n",
" m = data.length,\n",
" n = kz.length,\n",
" sz = new Array(n),\n",
" oz;\n",
"\n",
" for (i = 0; i < n; ++i) {\n",
" for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {\n",
" si[j] = sij = [0, +value(data[j], ki, j, data)];\n",
" sij.data = data[j];\n",
" }\n",
" si.key = ki;\n",
" }\n",
"\n",
" for (i = 0, oz = order(sz); i < n; ++i) {\n",
" sz[oz[i]].index = i;\n",
" }\n",
"\n",
" offset(sz, oz);\n",
" return sz;\n",
" }\n",
"\n",
" stack.keys = function(_) {\n",
" return arguments.length ? (keys = typeof _ === \"function\" ? _ : constant(slice.call(_)), stack) : keys;\n",
" };\n",
"\n",
" stack.value = function(_) {\n",
" return arguments.length ? (value = typeof _ === \"function\" ? _ : constant(+_), stack) : value;\n",
" };\n",
"\n",
" stack.order = function(_) {\n",
" return arguments.length ? (order = _ == null ? none$1 : typeof _ === \"function\" ? _ : constant(slice.call(_)), stack) : order;\n",
" };\n",
"\n",
" stack.offset = function(_) {\n",
" return arguments.length ? (offset = _ == null ? none : _, stack) : offset;\n",
" };\n",
"\n",
" return stack;\n",
"}\n",
"\n",
"function expand(series, order) {\n",
" if (!((n = series.length) > 0)) return;\n",
" for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {\n",
" for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;\n",
" if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;\n",
" }\n",
" none(series, order);\n",
"}\n",
"\n",
"function diverging(series, order) {\n",
" if (!((n = series.length) > 0)) return;\n",
" for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {\n",
" for (yp = yn = 0, i = 0; i < n; ++i) {\n",
" if ((dy = (d = series[order[i]][j])[1] - d[0]) > 0) {\n",
" d[0] = yp, d[1] = yp += dy;\n",
" } else if (dy < 0) {\n",
" d[1] = yn, d[0] = yn += dy;\n",
" } else {\n",
" d[0] = 0, d[1] = dy;\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"function silhouette(series, order) {\n",
" if (!((n = series.length) > 0)) return;\n",
" for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {\n",
" for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;\n",
" s0[j][1] += s0[j][0] = -y / 2;\n",
" }\n",
" none(series, order);\n",
"}\n",
"\n",
"function wiggle(series, order) {\n",
" if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;\n",
" for (var y = 0, j = 1, s0, m, n; j < m; ++j) {\n",
" for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {\n",
" var si = series[order[i]],\n",
" sij0 = si[j][1] || 0,\n",
" sij1 = si[j - 1][1] || 0,\n",
" s3 = (sij0 - sij1) / 2;\n",
" for (var k = 0; k < i; ++k) {\n",
" var sk = series[order[k]],\n",
" skj0 = sk[j][1] || 0,\n",
" skj1 = sk[j - 1][1] || 0;\n",
" s3 += skj0 - skj1;\n",
" }\n",
" s1 += sij0, s2 += s3 * sij0;\n",
" }\n",
" s0[j - 1][1] += s0[j - 1][0] = y;\n",
" if (s1) y -= s2 / s1;\n",
" }\n",
" s0[j - 1][1] += s0[j - 1][0] = y;\n",
" none(series, order);\n",
"}\n",
"\n",
"function appearance(series) {\n",
" var peaks = series.map(peak);\n",
" return none$1(series).sort(function(a, b) { return peaks[a] - peaks[b]; });\n",
"}\n",
"\n",
"function peak(series) {\n",
" var i = -1, j = 0, n = series.length, vi, vj = -Infinity;\n",
" while (++i < n) if ((vi = +series[i][1]) > vj) vj = vi, j = i;\n",
" return j;\n",
"}\n",
"\n",
"function ascending(series) {\n",
" var sums = series.map(sum);\n",
" return none$1(series).sort(function(a, b) { return sums[a] - sums[b]; });\n",
"}\n",
"\n",
"function sum(series) {\n",
" var s = 0, i = -1, n = series.length, v;\n",
" while (++i < n) if (v = +series[i][1]) s += v;\n",
" return s;\n",
"}\n",
"\n",
"function descending$1(series) {\n",
" return ascending(series).reverse();\n",
"}\n",
"\n",
"function insideOut(series) {\n",
" var n = series.length,\n",
" i,\n",
" j,\n",
" sums = series.map(sum),\n",
" order = appearance(series),\n",
" top = 0,\n",
" bottom = 0,\n",
" tops = [],\n",
" bottoms = [];\n",
"\n",
" for (i = 0; i < n; ++i) {\n",
" j = order[i];\n",
" if (top < bottom) {\n",
" top += sums[j];\n",
" tops.push(j);\n",
" } else {\n",
" bottom += sums[j];\n",
" bottoms.push(j);\n",
" }\n",
" }\n",
"\n",
" return bottoms.reverse().concat(tops);\n",
"}\n",
"\n",
"function reverse(series) {\n",
" return none$1(series).reverse();\n",
"}\n",
"\n",
"exports.arc = arc;\n",
"exports.area = area;\n",
"exports.areaRadial = areaRadial;\n",
"exports.curveBasis = basis;\n",
"exports.curveBasisClosed = basisClosed;\n",
"exports.curveBasisOpen = basisOpen;\n",
"exports.curveBundle = bundle;\n",
"exports.curveCardinal = cardinal;\n",
"exports.curveCardinalClosed = cardinalClosed;\n",
"exports.curveCardinalOpen = cardinalOpen;\n",
"exports.curveCatmullRom = catmullRom;\n",
"exports.curveCatmullRomClosed = catmullRomClosed;\n",
"exports.curveCatmullRomOpen = catmullRomOpen;\n",
"exports.curveLinear = curveLinear;\n",
"exports.curveLinearClosed = linearClosed;\n",
"exports.curveMonotoneX = monotoneX;\n",
"exports.curveMonotoneY = monotoneY;\n",
"exports.curveNatural = natural;\n",
"exports.curveStep = step;\n",
"exports.curveStepAfter = stepAfter;\n",
"exports.curveStepBefore = stepBefore;\n",
"exports.line = line;\n",
"exports.lineRadial = lineRadial$1;\n",
"exports.linkHorizontal = linkHorizontal;\n",
"exports.linkRadial = linkRadial;\n",
"exports.linkVertical = linkVertical;\n",
"exports.pie = pie;\n",
"exports.pointRadial = pointRadial;\n",
"exports.radialArea = areaRadial;\n",
"exports.radialLine = lineRadial$1;\n",
"exports.stack = stack;\n",
"exports.stackOffsetDiverging = diverging;\n",
"exports.stackOffsetExpand = expand;\n",
"exports.stackOffsetNone = none;\n",
"exports.stackOffsetSilhouette = silhouette;\n",
"exports.stackOffsetWiggle = wiggle;\n",
"exports.stackOrderAppearance = appearance;\n",
"exports.stackOrderAscending = ascending;\n",
"exports.stackOrderDescending = descending$1;\n",
"exports.stackOrderInsideOut = insideOut;\n",
"exports.stackOrderNone = none$1;\n",
"exports.stackOrderReverse = reverse;\n",
"exports.symbol = symbol;\n",
"exports.symbolCircle = circle;\n",
"exports.symbolCross = cross;\n",
"exports.symbolDiamond = diamond;\n",
"exports.symbolSquare = square;\n",
"exports.symbolStar = star;\n",
"exports.symbolTriangle = triangle;\n",
"exports.symbolWye = wye;\n",
"exports.symbols = symbols;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{\"d3-path\":9}],13:[function(require,module,exports){\n",
"// https://d3js.org/d3-time-format/ v2.2.2 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-time')) :\n",
"typeof define === 'function' && define.amd ? define(['exports', 'd3-time'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}, global.d3));\n",
"}(this, function (exports, d3Time) { 'use strict';\n",
"\n",
"function localDate(d) {\n",
" if (0 <= d.y && d.y < 100) {\n",
" var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);\n",
" date.setFullYear(d.y);\n",
" return date;\n",
" }\n",
" return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);\n",
"}\n",
"\n",
"function utcDate(d) {\n",
" if (0 <= d.y && d.y < 100) {\n",
" var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));\n",
" date.setUTCFullYear(d.y);\n",
" return date;\n",
" }\n",
" return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));\n",
"}\n",
"\n",
"function newDate(y, m, d) {\n",
" return {y: y, m: m, d: d, H: 0, M: 0, S: 0, L: 0};\n",
"}\n",
"\n",
"function formatLocale(locale) {\n",
" var locale_dateTime = locale.dateTime,\n",
" locale_date = locale.date,\n",
" locale_time = locale.time,\n",
" locale_periods = locale.periods,\n",
" locale_weekdays = locale.days,\n",
" locale_shortWeekdays = locale.shortDays,\n",
" locale_months = locale.months,\n",
" locale_shortMonths = locale.shortMonths;\n",
"\n",
" var periodRe = formatRe(locale_periods),\n",
" periodLookup = formatLookup(locale_periods),\n",
" weekdayRe = formatRe(locale_weekdays),\n",
" weekdayLookup = formatLookup(locale_weekdays),\n",
" shortWeekdayRe = formatRe(locale_shortWeekdays),\n",
" shortWeekdayLookup = formatLookup(locale_shortWeekdays),\n",
" monthRe = formatRe(locale_months),\n",
" monthLookup = formatLookup(locale_months),\n",
" shortMonthRe = formatRe(locale_shortMonths),\n",
" shortMonthLookup = formatLookup(locale_shortMonths);\n",
"\n",
" var formats = {\n",
" \"a\": formatShortWeekday,\n",
" \"A\": formatWeekday,\n",
" \"b\": formatShortMonth,\n",
" \"B\": formatMonth,\n",
" \"c\": null,\n",
" \"d\": formatDayOfMonth,\n",
" \"e\": formatDayOfMonth,\n",
" \"f\": formatMicroseconds,\n",
" \"H\": formatHour24,\n",
" \"I\": formatHour12,\n",
" \"j\": formatDayOfYear,\n",
" \"L\": formatMilliseconds,\n",
" \"m\": formatMonthNumber,\n",
" \"M\": formatMinutes,\n",
" \"p\": formatPeriod,\n",
" \"q\": formatQuarter,\n",
" \"Q\": formatUnixTimestamp,\n",
" \"s\": formatUnixTimestampSeconds,\n",
" \"S\": formatSeconds,\n",
" \"u\": formatWeekdayNumberMonday,\n",
" \"U\": formatWeekNumberSunday,\n",
" \"V\": formatWeekNumberISO,\n",
" \"w\": formatWeekdayNumberSunday,\n",
" \"W\": formatWeekNumberMonday,\n",
" \"x\": null,\n",
" \"X\": null,\n",
" \"y\": formatYear,\n",
" \"Y\": formatFullYear,\n",
" \"Z\": formatZone,\n",
" \"%\": formatLiteralPercent\n",
" };\n",
"\n",
" var utcFormats = {\n",
" \"a\": formatUTCShortWeekday,\n",
" \"A\": formatUTCWeekday,\n",
" \"b\": formatUTCShortMonth,\n",
" \"B\": formatUTCMonth,\n",
" \"c\": null,\n",
" \"d\": formatUTCDayOfMonth,\n",
" \"e\": formatUTCDayOfMonth,\n",
" \"f\": formatUTCMicroseconds,\n",
" \"H\": formatUTCHour24,\n",
" \"I\": formatUTCHour12,\n",
" \"j\": formatUTCDayOfYear,\n",
" \"L\": formatUTCMilliseconds,\n",
" \"m\": formatUTCMonthNumber,\n",
" \"M\": formatUTCMinutes,\n",
" \"p\": formatUTCPeriod,\n",
" \"q\": formatUTCQuarter,\n",
" \"Q\": formatUnixTimestamp,\n",
" \"s\": formatUnixTimestampSeconds,\n",
" \"S\": formatUTCSeconds,\n",
" \"u\": formatUTCWeekdayNumberMonday,\n",
" \"U\": formatUTCWeekNumberSunday,\n",
" \"V\": formatUTCWeekNumberISO,\n",
" \"w\": formatUTCWeekdayNumberSunday,\n",
" \"W\": formatUTCWeekNumberMonday,\n",
" \"x\": null,\n",
" \"X\": null,\n",
" \"y\": formatUTCYear,\n",
" \"Y\": formatUTCFullYear,\n",
" \"Z\": formatUTCZone,\n",
" \"%\": formatLiteralPercent\n",
" };\n",
"\n",
" var parses = {\n",
" \"a\": parseShortWeekday,\n",
" \"A\": parseWeekday,\n",
" \"b\": parseShortMonth,\n",
" \"B\": parseMonth,\n",
" \"c\": parseLocaleDateTime,\n",
" \"d\": parseDayOfMonth,\n",
" \"e\": parseDayOfMonth,\n",
" \"f\": parseMicroseconds,\n",
" \"H\": parseHour24,\n",
" \"I\": parseHour24,\n",
" \"j\": parseDayOfYear,\n",
" \"L\": parseMilliseconds,\n",
" \"m\": parseMonthNumber,\n",
" \"M\": parseMinutes,\n",
" \"p\": parsePeriod,\n",
" \"q\": parseQuarter,\n",
" \"Q\": parseUnixTimestamp,\n",
" \"s\": parseUnixTimestampSeconds,\n",
" \"S\": parseSeconds,\n",
" \"u\": parseWeekdayNumberMonday,\n",
" \"U\": parseWeekNumberSunday,\n",
" \"V\": parseWeekNumberISO,\n",
" \"w\": parseWeekdayNumberSunday,\n",
" \"W\": parseWeekNumberMonday,\n",
" \"x\": parseLocaleDate,\n",
" \"X\": parseLocaleTime,\n",
" \"y\": parseYear,\n",
" \"Y\": parseFullYear,\n",
" \"Z\": parseZone,\n",
" \"%\": parseLiteralPercent\n",
" };\n",
"\n",
" // These recursive directive definitions must be deferred.\n",
" formats.x = newFormat(locale_date, formats);\n",
" formats.X = newFormat(locale_time, formats);\n",
" formats.c = newFormat(locale_dateTime, formats);\n",
" utcFormats.x = newFormat(locale_date, utcFormats);\n",
" utcFormats.X = newFormat(locale_time, utcFormats);\n",
" utcFormats.c = newFormat(locale_dateTime, utcFormats);\n",
"\n",
" function newFormat(specifier, formats) {\n",
" return function(date) {\n",
" var string = [],\n",
" i = -1,\n",
" j = 0,\n",
" n = specifier.length,\n",
" c,\n",
" pad,\n",
" format;\n",
"\n",
" if (!(date instanceof Date)) date = new Date(+date);\n",
"\n",
" while (++i < n) {\n",
" if (specifier.charCodeAt(i) === 37) {\n",
" string.push(specifier.slice(j, i));\n",
" if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);\n",
" else pad = c === \"e\" ? \" \" : \"0\";\n",
" if (format = formats[c]) c = format(date, pad);\n",
" string.push(c);\n",
" j = i + 1;\n",
" }\n",
" }\n",
"\n",
" string.push(specifier.slice(j, i));\n",
" return string.join(\"\");\n",
" };\n",
" }\n",
"\n",
" function newParse(specifier, Z) {\n",
" return function(string) {\n",
" var d = newDate(1900, undefined, 1),\n",
" i = parseSpecifier(d, specifier, string += \"\", 0),\n",
" week, day;\n",
" if (i != string.length) return null;\n",
"\n",
" // If a UNIX timestamp is specified, return it.\n",
" if (\"Q\" in d) return new Date(d.Q);\n",
" if (\"s\" in d) return new Date(d.s * 1000 + (\"L\" in d ? d.L : 0));\n",
"\n",
" // If this is utcParse, never use the local timezone.\n",
" if (Z && !(\"Z\" in d)) d.Z = 0;\n",
"\n",
" // The am-pm flag is 0 for AM, and 1 for PM.\n",
" if (\"p\" in d) d.H = d.H % 12 + d.p * 12;\n",
"\n",
" // If the month was not specified, inherit from the quarter.\n",
" if (d.m === undefined) d.m = \"q\" in d ? d.q : 0;\n",
"\n",
" // Convert day-of-week and week-of-year to day-of-year.\n",
" if (\"V\" in d) {\n",
" if (d.V < 1 || d.V > 53) return null;\n",
" if (!(\"w\" in d)) d.w = 1;\n",
" if (\"Z\" in d) {\n",
" week = utcDate(newDate(d.y, 0, 1)), day = week.getUTCDay();\n",
" week = day > 4 || day === 0 ? d3Time.utcMonday.ceil(week) : d3Time.utcMonday(week);\n",
" week = d3Time.utcDay.offset(week, (d.V - 1) * 7);\n",
" d.y = week.getUTCFullYear();\n",
" d.m = week.getUTCMonth();\n",
" d.d = week.getUTCDate() + (d.w + 6) % 7;\n",
" } else {\n",
" week = localDate(newDate(d.y, 0, 1)), day = week.getDay();\n",
" week = day > 4 || day === 0 ? d3Time.timeMonday.ceil(week) : d3Time.timeMonday(week);\n",
" week = d3Time.timeDay.offset(week, (d.V - 1) * 7);\n",
" d.y = week.getFullYear();\n",
" d.m = week.getMonth();\n",
" d.d = week.getDate() + (d.w + 6) % 7;\n",
" }\n",
" } else if (\"W\" in d || \"U\" in d) {\n",
" if (!(\"w\" in d)) d.w = \"u\" in d ? d.u % 7 : \"W\" in d ? 1 : 0;\n",
" day = \"Z\" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay();\n",
" d.m = 0;\n",
" d.d = \"W\" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7;\n",
" }\n",
"\n",
" // If a time zone is specified, all fields are interpreted as UTC and then\n",
" // offset according to the specified time zone.\n",
" if (\"Z\" in d) {\n",
" d.H += d.Z / 100 | 0;\n",
" d.M += d.Z % 100;\n",
" return utcDate(d);\n",
" }\n",
"\n",
" // Otherwise, all fields are in local time.\n",
" return localDate(d);\n",
" };\n",
" }\n",
"\n",
" function parseSpecifier(d, specifier, string, j) {\n",
" var i = 0,\n",
" n = specifier.length,\n",
" m = string.length,\n",
" c,\n",
" parse;\n",
"\n",
" while (i < n) {\n",
" if (j >= m) return -1;\n",
" c = specifier.charCodeAt(i++);\n",
" if (c === 37) {\n",
" c = specifier.charAt(i++);\n",
" parse = parses[c in pads ? specifier.charAt(i++) : c];\n",
" if (!parse || ((j = parse(d, string, j)) < 0)) return -1;\n",
" } else if (c != string.charCodeAt(j++)) {\n",
" return -1;\n",
" }\n",
" }\n",
"\n",
" return j;\n",
" }\n",
"\n",
" function parsePeriod(d, string, i) {\n",
" var n = periodRe.exec(string.slice(i));\n",
" return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n",
" }\n",
"\n",
" function parseShortWeekday(d, string, i) {\n",
" var n = shortWeekdayRe.exec(string.slice(i));\n",
" return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n",
" }\n",
"\n",
" function parseWeekday(d, string, i) {\n",
" var n = weekdayRe.exec(string.slice(i));\n",
" return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n",
" }\n",
"\n",
" function parseShortMonth(d, string, i) {\n",
" var n = shortMonthRe.exec(string.slice(i));\n",
" return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n",
" }\n",
"\n",
" function parseMonth(d, string, i) {\n",
" var n = monthRe.exec(string.slice(i));\n",
" return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n",
" }\n",
"\n",
" function parseLocaleDateTime(d, string, i) {\n",
" return parseSpecifier(d, locale_dateTime, string, i);\n",
" }\n",
"\n",
" function parseLocaleDate(d, string, i) {\n",
" return parseSpecifier(d, locale_date, string, i);\n",
" }\n",
"\n",
" function parseLocaleTime(d, string, i) {\n",
" return parseSpecifier(d, locale_time, string, i);\n",
" }\n",
"\n",
" function formatShortWeekday(d) {\n",
" return locale_shortWeekdays[d.getDay()];\n",
" }\n",
"\n",
" function formatWeekday(d) {\n",
" return locale_weekdays[d.getDay()];\n",
" }\n",
"\n",
" function formatShortMonth(d) {\n",
" return locale_shortMonths[d.getMonth()];\n",
" }\n",
"\n",
" function formatMonth(d) {\n",
" return locale_months[d.getMonth()];\n",
" }\n",
"\n",
" function formatPeriod(d) {\n",
" return locale_periods[+(d.getHours() >= 12)];\n",
" }\n",
"\n",
" function formatQuarter(d) {\n",
" return 1 + ~~(d.getMonth() / 3);\n",
" }\n",
"\n",
" function formatUTCShortWeekday(d) {\n",
" return locale_shortWeekdays[d.getUTCDay()];\n",
" }\n",
"\n",
" function formatUTCWeekday(d) {\n",
" return locale_weekdays[d.getUTCDay()];\n",
" }\n",
"\n",
" function formatUTCShortMonth(d) {\n",
" return locale_shortMonths[d.getUTCMonth()];\n",
" }\n",
"\n",
" function formatUTCMonth(d) {\n",
" return locale_months[d.getUTCMonth()];\n",
" }\n",
"\n",
" function formatUTCPeriod(d) {\n",
" return locale_periods[+(d.getUTCHours() >= 12)];\n",
" }\n",
"\n",
" function formatUTCQuarter(d) {\n",
" return 1 + ~~(d.getUTCMonth() / 3);\n",
" }\n",
"\n",
" return {\n",
" format: function(specifier) {\n",
" var f = newFormat(specifier += \"\", formats);\n",
" f.toString = function() { return specifier; };\n",
" return f;\n",
" },\n",
" parse: function(specifier) {\n",
" var p = newParse(specifier += \"\", false);\n",
" p.toString = function() { return specifier; };\n",
" return p;\n",
" },\n",
" utcFormat: function(specifier) {\n",
" var f = newFormat(specifier += \"\", utcFormats);\n",
" f.toString = function() { return specifier; };\n",
" return f;\n",
" },\n",
" utcParse: function(specifier) {\n",
" var p = newParse(specifier += \"\", true);\n",
" p.toString = function() { return specifier; };\n",
" return p;\n",
" }\n",
" };\n",
"}\n",
"\n",
"var pads = {\"-\": \"\", \"_\": \" \", \"0\": \"0\"},\n",
" numberRe = /^\\s*\\d+/, // note: ignores next directive\n",
" percentRe = /^%/,\n",
" requoteRe = /[\\\\^$*+?|[\\]().{}]/g;\n",
"\n",
"function pad(value, fill, width) {\n",
" var sign = value < 0 ? \"-\" : \"\",\n",
" string = (sign ? -value : value) + \"\",\n",
" length = string.length;\n",
" return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);\n",
"}\n",
"\n",
"function requote(s) {\n",
" return s.replace(requoteRe, \"\\\\$&\");\n",
"}\n",
"\n",
"function formatRe(names) {\n",
" return new RegExp(\"^(?:\" + names.map(requote).join(\"|\") + \")\", \"i\");\n",
"}\n",
"\n",
"function formatLookup(names) {\n",
" var map = {}, i = -1, n = names.length;\n",
" while (++i < n) map[names[i].toLowerCase()] = i;\n",
" return map;\n",
"}\n",
"\n",
"function parseWeekdayNumberSunday(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 1));\n",
" return n ? (d.w = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseWeekdayNumberMonday(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 1));\n",
" return n ? (d.u = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseWeekNumberSunday(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.U = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseWeekNumberISO(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.V = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseWeekNumberMonday(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.W = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseFullYear(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 4));\n",
" return n ? (d.y = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseYear(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseZone(d, string, i) {\n",
" var n = /^(Z)|([+-]\\d\\d)(?::?(\\d\\d))?/.exec(string.slice(i, i + 6));\n",
" return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || \"00\")), i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseQuarter(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 1));\n",
" return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseMonthNumber(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.m = n[0] - 1, i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseDayOfMonth(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.d = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseDayOfYear(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 3));\n",
" return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseHour24(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.H = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseMinutes(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.M = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseSeconds(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 2));\n",
" return n ? (d.S = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseMilliseconds(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 3));\n",
" return n ? (d.L = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseMicroseconds(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i, i + 6));\n",
" return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseLiteralPercent(d, string, i) {\n",
" var n = percentRe.exec(string.slice(i, i + 1));\n",
" return n ? i + n[0].length : -1;\n",
"}\n",
"\n",
"function parseUnixTimestamp(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i));\n",
" return n ? (d.Q = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function parseUnixTimestampSeconds(d, string, i) {\n",
" var n = numberRe.exec(string.slice(i));\n",
" return n ? (d.s = +n[0], i + n[0].length) : -1;\n",
"}\n",
"\n",
"function formatDayOfMonth(d, p) {\n",
" return pad(d.getDate(), p, 2);\n",
"}\n",
"\n",
"function formatHour24(d, p) {\n",
" return pad(d.getHours(), p, 2);\n",
"}\n",
"\n",
"function formatHour12(d, p) {\n",
" return pad(d.getHours() % 12 || 12, p, 2);\n",
"}\n",
"\n",
"function formatDayOfYear(d, p) {\n",
" return pad(1 + d3Time.timeDay.count(d3Time.timeYear(d), d), p, 3);\n",
"}\n",
"\n",
"function formatMilliseconds(d, p) {\n",
" return pad(d.getMilliseconds(), p, 3);\n",
"}\n",
"\n",
"function formatMicroseconds(d, p) {\n",
" return formatMilliseconds(d, p) + \"000\";\n",
"}\n",
"\n",
"function formatMonthNumber(d, p) {\n",
" return pad(d.getMonth() + 1, p, 2);\n",
"}\n",
"\n",
"function formatMinutes(d, p) {\n",
" return pad(d.getMinutes(), p, 2);\n",
"}\n",
"\n",
"function formatSeconds(d, p) {\n",
" return pad(d.getSeconds(), p, 2);\n",
"}\n",
"\n",
"function formatWeekdayNumberMonday(d) {\n",
" var day = d.getDay();\n",
" return day === 0 ? 7 : day;\n",
"}\n",
"\n",
"function formatWeekNumberSunday(d, p) {\n",
" return pad(d3Time.timeSunday.count(d3Time.timeYear(d) - 1, d), p, 2);\n",
"}\n",
"\n",
"function formatWeekNumberISO(d, p) {\n",
" var day = d.getDay();\n",
" d = (day >= 4 || day === 0) ? d3Time.timeThursday(d) : d3Time.timeThursday.ceil(d);\n",
" return pad(d3Time.timeThursday.count(d3Time.timeYear(d), d) + (d3Time.timeYear(d).getDay() === 4), p, 2);\n",
"}\n",
"\n",
"function formatWeekdayNumberSunday(d) {\n",
" return d.getDay();\n",
"}\n",
"\n",
"function formatWeekNumberMonday(d, p) {\n",
" return pad(d3Time.timeMonday.count(d3Time.timeYear(d) - 1, d), p, 2);\n",
"}\n",
"\n",
"function formatYear(d, p) {\n",
" return pad(d.getFullYear() % 100, p, 2);\n",
"}\n",
"\n",
"function formatFullYear(d, p) {\n",
" return pad(d.getFullYear() % 10000, p, 4);\n",
"}\n",
"\n",
"function formatZone(d) {\n",
" var z = d.getTimezoneOffset();\n",
" return (z > 0 ? \"-\" : (z *= -1, \"+\"))\n",
" + pad(z / 60 | 0, \"0\", 2)\n",
" + pad(z % 60, \"0\", 2);\n",
"}\n",
"\n",
"function formatUTCDayOfMonth(d, p) {\n",
" return pad(d.getUTCDate(), p, 2);\n",
"}\n",
"\n",
"function formatUTCHour24(d, p) {\n",
" return pad(d.getUTCHours(), p, 2);\n",
"}\n",
"\n",
"function formatUTCHour12(d, p) {\n",
" return pad(d.getUTCHours() % 12 || 12, p, 2);\n",
"}\n",
"\n",
"function formatUTCDayOfYear(d, p) {\n",
" return pad(1 + d3Time.utcDay.count(d3Time.utcYear(d), d), p, 3);\n",
"}\n",
"\n",
"function formatUTCMilliseconds(d, p) {\n",
" return pad(d.getUTCMilliseconds(), p, 3);\n",
"}\n",
"\n",
"function formatUTCMicroseconds(d, p) {\n",
" return formatUTCMilliseconds(d, p) + \"000\";\n",
"}\n",
"\n",
"function formatUTCMonthNumber(d, p) {\n",
" return pad(d.getUTCMonth() + 1, p, 2);\n",
"}\n",
"\n",
"function formatUTCMinutes(d, p) {\n",
" return pad(d.getUTCMinutes(), p, 2);\n",
"}\n",
"\n",
"function formatUTCSeconds(d, p) {\n",
" return pad(d.getUTCSeconds(), p, 2);\n",
"}\n",
"\n",
"function formatUTCWeekdayNumberMonday(d) {\n",
" var dow = d.getUTCDay();\n",
" return dow === 0 ? 7 : dow;\n",
"}\n",
"\n",
"function formatUTCWeekNumberSunday(d, p) {\n",
" return pad(d3Time.utcSunday.count(d3Time.utcYear(d) - 1, d), p, 2);\n",
"}\n",
"\n",
"function formatUTCWeekNumberISO(d, p) {\n",
" var day = d.getUTCDay();\n",
" d = (day >= 4 || day === 0) ? d3Time.utcThursday(d) : d3Time.utcThursday.ceil(d);\n",
" return pad(d3Time.utcThursday.count(d3Time.utcYear(d), d) + (d3Time.utcYear(d).getUTCDay() === 4), p, 2);\n",
"}\n",
"\n",
"function formatUTCWeekdayNumberSunday(d) {\n",
" return d.getUTCDay();\n",
"}\n",
"\n",
"function formatUTCWeekNumberMonday(d, p) {\n",
" return pad(d3Time.utcMonday.count(d3Time.utcYear(d) - 1, d), p, 2);\n",
"}\n",
"\n",
"function formatUTCYear(d, p) {\n",
" return pad(d.getUTCFullYear() % 100, p, 2);\n",
"}\n",
"\n",
"function formatUTCFullYear(d, p) {\n",
" return pad(d.getUTCFullYear() % 10000, p, 4);\n",
"}\n",
"\n",
"function formatUTCZone() {\n",
" return \"+0000\";\n",
"}\n",
"\n",
"function formatLiteralPercent() {\n",
" return \"%\";\n",
"}\n",
"\n",
"function formatUnixTimestamp(d) {\n",
" return +d;\n",
"}\n",
"\n",
"function formatUnixTimestampSeconds(d) {\n",
" return Math.floor(+d / 1000);\n",
"}\n",
"\n",
"var locale;\n",
"\n",
"defaultLocale({\n",
" dateTime: \"%x, %X\",\n",
" date: \"%-m/%-d/%Y\",\n",
" time: \"%-I:%M:%S %p\",\n",
" periods: [\"AM\", \"PM\"],\n",
" days: [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\n",
" shortDays: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n",
" months: [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"],\n",
" shortMonths: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n",
"});\n",
"\n",
"function defaultLocale(definition) {\n",
" locale = formatLocale(definition);\n",
" exports.timeFormat = locale.format;\n",
" exports.timeParse = locale.parse;\n",
" exports.utcFormat = locale.utcFormat;\n",
" exports.utcParse = locale.utcParse;\n",
" return locale;\n",
"}\n",
"\n",
"var isoSpecifier = \"%Y-%m-%dT%H:%M:%S.%LZ\";\n",
"\n",
"function formatIsoNative(date) {\n",
" return date.toISOString();\n",
"}\n",
"\n",
"var formatIso = Date.prototype.toISOString\n",
" ? formatIsoNative\n",
" : exports.utcFormat(isoSpecifier);\n",
"\n",
"function parseIsoNative(string) {\n",
" var date = new Date(string);\n",
" return isNaN(date) ? null : date;\n",
"}\n",
"\n",
"var parseIso = +new Date(\"2000-01-01T00:00:00.000Z\")\n",
" ? parseIsoNative\n",
" : exports.utcParse(isoSpecifier);\n",
"\n",
"exports.isoFormat = formatIso;\n",
"exports.isoParse = parseIso;\n",
"exports.timeFormatDefaultLocale = defaultLocale;\n",
"exports.timeFormatLocale = formatLocale;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{\"d3-time\":14}],14:[function(require,module,exports){\n",
"// https://d3js.org/d3-time/ v1.1.0 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"var t0 = new Date,\n",
" t1 = new Date;\n",
"\n",
"function newInterval(floori, offseti, count, field) {\n",
"\n",
" function interval(date) {\n",
" return floori(date = arguments.length === 0 ? new Date : new Date(+date)), date;\n",
" }\n",
"\n",
" interval.floor = function(date) {\n",
" return floori(date = new Date(+date)), date;\n",
" };\n",
"\n",
" interval.ceil = function(date) {\n",
" return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;\n",
" };\n",
"\n",
" interval.round = function(date) {\n",
" var d0 = interval(date),\n",
" d1 = interval.ceil(date);\n",
" return date - d0 < d1 - date ? d0 : d1;\n",
" };\n",
"\n",
" interval.offset = function(date, step) {\n",
" return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;\n",
" };\n",
"\n",
" interval.range = function(start, stop, step) {\n",
" var range = [], previous;\n",
" start = interval.ceil(start);\n",
" step = step == null ? 1 : Math.floor(step);\n",
" if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date\n",
" do range.push(previous = new Date(+start)), offseti(start, step), floori(start);\n",
" while (previous < start && start < stop);\n",
" return range;\n",
" };\n",
"\n",
" interval.filter = function(test) {\n",
" return newInterval(function(date) {\n",
" if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);\n",
" }, function(date, step) {\n",
" if (date >= date) {\n",
" if (step < 0) while (++step <= 0) {\n",
" while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty\n",
" } else while (--step >= 0) {\n",
" while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty\n",
" }\n",
" }\n",
" });\n",
" };\n",
"\n",
" if (count) {\n",
" interval.count = function(start, end) {\n",
" t0.setTime(+start), t1.setTime(+end);\n",
" floori(t0), floori(t1);\n",
" return Math.floor(count(t0, t1));\n",
" };\n",
"\n",
" interval.every = function(step) {\n",
" step = Math.floor(step);\n",
" return !isFinite(step) || !(step > 0) ? null\n",
" : !(step > 1) ? interval\n",
" : interval.filter(field\n",
" ? function(d) { return field(d) % step === 0; }\n",
" : function(d) { return interval.count(0, d) % step === 0; });\n",
" };\n",
" }\n",
"\n",
" return interval;\n",
"}\n",
"\n",
"var millisecond = newInterval(function() {\n",
" // noop\n",
"}, function(date, step) {\n",
" date.setTime(+date + step);\n",
"}, function(start, end) {\n",
" return end - start;\n",
"});\n",
"\n",
"// An optimized implementation for this simple case.\n",
"millisecond.every = function(k) {\n",
" k = Math.floor(k);\n",
" if (!isFinite(k) || !(k > 0)) return null;\n",
" if (!(k > 1)) return millisecond;\n",
" return newInterval(function(date) {\n",
" date.setTime(Math.floor(date / k) * k);\n",
" }, function(date, step) {\n",
" date.setTime(+date + step * k);\n",
" }, function(start, end) {\n",
" return (end - start) / k;\n",
" });\n",
"};\n",
"var milliseconds = millisecond.range;\n",
"\n",
"var durationSecond = 1e3;\n",
"var durationMinute = 6e4;\n",
"var durationHour = 36e5;\n",
"var durationDay = 864e5;\n",
"var durationWeek = 6048e5;\n",
"\n",
"var second = newInterval(function(date) {\n",
" date.setTime(date - date.getMilliseconds());\n",
"}, function(date, step) {\n",
" date.setTime(+date + step * durationSecond);\n",
"}, function(start, end) {\n",
" return (end - start) / durationSecond;\n",
"}, function(date) {\n",
" return date.getUTCSeconds();\n",
"});\n",
"var seconds = second.range;\n",
"\n",
"var minute = newInterval(function(date) {\n",
" date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond);\n",
"}, function(date, step) {\n",
" date.setTime(+date + step * durationMinute);\n",
"}, function(start, end) {\n",
" return (end - start) / durationMinute;\n",
"}, function(date) {\n",
" return date.getMinutes();\n",
"});\n",
"var minutes = minute.range;\n",
"\n",
"var hour = newInterval(function(date) {\n",
" date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond - date.getMinutes() * durationMinute);\n",
"}, function(date, step) {\n",
" date.setTime(+date + step * durationHour);\n",
"}, function(start, end) {\n",
" return (end - start) / durationHour;\n",
"}, function(date) {\n",
" return date.getHours();\n",
"});\n",
"var hours = hour.range;\n",
"\n",
"var day = newInterval(function(date) {\n",
" date.setHours(0, 0, 0, 0);\n",
"}, function(date, step) {\n",
" date.setDate(date.getDate() + step);\n",
"}, function(start, end) {\n",
" return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;\n",
"}, function(date) {\n",
" return date.getDate() - 1;\n",
"});\n",
"var days = day.range;\n",
"\n",
"function weekday(i) {\n",
" return newInterval(function(date) {\n",
" date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);\n",
" date.setHours(0, 0, 0, 0);\n",
" }, function(date, step) {\n",
" date.setDate(date.getDate() + step * 7);\n",
" }, function(start, end) {\n",
" return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;\n",
" });\n",
"}\n",
"\n",
"var sunday = weekday(0);\n",
"var monday = weekday(1);\n",
"var tuesday = weekday(2);\n",
"var wednesday = weekday(3);\n",
"var thursday = weekday(4);\n",
"var friday = weekday(5);\n",
"var saturday = weekday(6);\n",
"\n",
"var sundays = sunday.range;\n",
"var mondays = monday.range;\n",
"var tuesdays = tuesday.range;\n",
"var wednesdays = wednesday.range;\n",
"var thursdays = thursday.range;\n",
"var fridays = friday.range;\n",
"var saturdays = saturday.range;\n",
"\n",
"var month = newInterval(function(date) {\n",
" date.setDate(1);\n",
" date.setHours(0, 0, 0, 0);\n",
"}, function(date, step) {\n",
" date.setMonth(date.getMonth() + step);\n",
"}, function(start, end) {\n",
" return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;\n",
"}, function(date) {\n",
" return date.getMonth();\n",
"});\n",
"var months = month.range;\n",
"\n",
"var year = newInterval(function(date) {\n",
" date.setMonth(0, 1);\n",
" date.setHours(0, 0, 0, 0);\n",
"}, function(date, step) {\n",
" date.setFullYear(date.getFullYear() + step);\n",
"}, function(start, end) {\n",
" return end.getFullYear() - start.getFullYear();\n",
"}, function(date) {\n",
" return date.getFullYear();\n",
"});\n",
"\n",
"// An optimized implementation for this simple case.\n",
"year.every = function(k) {\n",
" return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {\n",
" date.setFullYear(Math.floor(date.getFullYear() / k) * k);\n",
" date.setMonth(0, 1);\n",
" date.setHours(0, 0, 0, 0);\n",
" }, function(date, step) {\n",
" date.setFullYear(date.getFullYear() + step * k);\n",
" });\n",
"};\n",
"var years = year.range;\n",
"\n",
"var utcMinute = newInterval(function(date) {\n",
" date.setUTCSeconds(0, 0);\n",
"}, function(date, step) {\n",
" date.setTime(+date + step * durationMinute);\n",
"}, function(start, end) {\n",
" return (end - start) / durationMinute;\n",
"}, function(date) {\n",
" return date.getUTCMinutes();\n",
"});\n",
"var utcMinutes = utcMinute.range;\n",
"\n",
"var utcHour = newInterval(function(date) {\n",
" date.setUTCMinutes(0, 0, 0);\n",
"}, function(date, step) {\n",
" date.setTime(+date + step * durationHour);\n",
"}, function(start, end) {\n",
" return (end - start) / durationHour;\n",
"}, function(date) {\n",
" return date.getUTCHours();\n",
"});\n",
"var utcHours = utcHour.range;\n",
"\n",
"var utcDay = newInterval(function(date) {\n",
" date.setUTCHours(0, 0, 0, 0);\n",
"}, function(date, step) {\n",
" date.setUTCDate(date.getUTCDate() + step);\n",
"}, function(start, end) {\n",
" return (end - start) / durationDay;\n",
"}, function(date) {\n",
" return date.getUTCDate() - 1;\n",
"});\n",
"var utcDays = utcDay.range;\n",
"\n",
"function utcWeekday(i) {\n",
" return newInterval(function(date) {\n",
" date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);\n",
" date.setUTCHours(0, 0, 0, 0);\n",
" }, function(date, step) {\n",
" date.setUTCDate(date.getUTCDate() + step * 7);\n",
" }, function(start, end) {\n",
" return (end - start) / durationWeek;\n",
" });\n",
"}\n",
"\n",
"var utcSunday = utcWeekday(0);\n",
"var utcMonday = utcWeekday(1);\n",
"var utcTuesday = utcWeekday(2);\n",
"var utcWednesday = utcWeekday(3);\n",
"var utcThursday = utcWeekday(4);\n",
"var utcFriday = utcWeekday(5);\n",
"var utcSaturday = utcWeekday(6);\n",
"\n",
"var utcSundays = utcSunday.range;\n",
"var utcMondays = utcMonday.range;\n",
"var utcTuesdays = utcTuesday.range;\n",
"var utcWednesdays = utcWednesday.range;\n",
"var utcThursdays = utcThursday.range;\n",
"var utcFridays = utcFriday.range;\n",
"var utcSaturdays = utcSaturday.range;\n",
"\n",
"var utcMonth = newInterval(function(date) {\n",
" date.setUTCDate(1);\n",
" date.setUTCHours(0, 0, 0, 0);\n",
"}, function(date, step) {\n",
" date.setUTCMonth(date.getUTCMonth() + step);\n",
"}, function(start, end) {\n",
" return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;\n",
"}, function(date) {\n",
" return date.getUTCMonth();\n",
"});\n",
"var utcMonths = utcMonth.range;\n",
"\n",
"var utcYear = newInterval(function(date) {\n",
" date.setUTCMonth(0, 1);\n",
" date.setUTCHours(0, 0, 0, 0);\n",
"}, function(date, step) {\n",
" date.setUTCFullYear(date.getUTCFullYear() + step);\n",
"}, function(start, end) {\n",
" return end.getUTCFullYear() - start.getUTCFullYear();\n",
"}, function(date) {\n",
" return date.getUTCFullYear();\n",
"});\n",
"\n",
"// An optimized implementation for this simple case.\n",
"utcYear.every = function(k) {\n",
" return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {\n",
" date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);\n",
" date.setUTCMonth(0, 1);\n",
" date.setUTCHours(0, 0, 0, 0);\n",
" }, function(date, step) {\n",
" date.setUTCFullYear(date.getUTCFullYear() + step * k);\n",
" });\n",
"};\n",
"var utcYears = utcYear.range;\n",
"\n",
"exports.timeDay = day;\n",
"exports.timeDays = days;\n",
"exports.timeFriday = friday;\n",
"exports.timeFridays = fridays;\n",
"exports.timeHour = hour;\n",
"exports.timeHours = hours;\n",
"exports.timeInterval = newInterval;\n",
"exports.timeMillisecond = millisecond;\n",
"exports.timeMilliseconds = milliseconds;\n",
"exports.timeMinute = minute;\n",
"exports.timeMinutes = minutes;\n",
"exports.timeMonday = monday;\n",
"exports.timeMondays = mondays;\n",
"exports.timeMonth = month;\n",
"exports.timeMonths = months;\n",
"exports.timeSaturday = saturday;\n",
"exports.timeSaturdays = saturdays;\n",
"exports.timeSecond = second;\n",
"exports.timeSeconds = seconds;\n",
"exports.timeSunday = sunday;\n",
"exports.timeSundays = sundays;\n",
"exports.timeThursday = thursday;\n",
"exports.timeThursdays = thursdays;\n",
"exports.timeTuesday = tuesday;\n",
"exports.timeTuesdays = tuesdays;\n",
"exports.timeWednesday = wednesday;\n",
"exports.timeWednesdays = wednesdays;\n",
"exports.timeWeek = sunday;\n",
"exports.timeWeeks = sundays;\n",
"exports.timeYear = year;\n",
"exports.timeYears = years;\n",
"exports.utcDay = utcDay;\n",
"exports.utcDays = utcDays;\n",
"exports.utcFriday = utcFriday;\n",
"exports.utcFridays = utcFridays;\n",
"exports.utcHour = utcHour;\n",
"exports.utcHours = utcHours;\n",
"exports.utcMillisecond = millisecond;\n",
"exports.utcMilliseconds = milliseconds;\n",
"exports.utcMinute = utcMinute;\n",
"exports.utcMinutes = utcMinutes;\n",
"exports.utcMonday = utcMonday;\n",
"exports.utcMondays = utcMondays;\n",
"exports.utcMonth = utcMonth;\n",
"exports.utcMonths = utcMonths;\n",
"exports.utcSaturday = utcSaturday;\n",
"exports.utcSaturdays = utcSaturdays;\n",
"exports.utcSecond = second;\n",
"exports.utcSeconds = seconds;\n",
"exports.utcSunday = utcSunday;\n",
"exports.utcSundays = utcSundays;\n",
"exports.utcThursday = utcThursday;\n",
"exports.utcThursdays = utcThursdays;\n",
"exports.utcTuesday = utcTuesday;\n",
"exports.utcTuesdays = utcTuesdays;\n",
"exports.utcWednesday = utcWednesday;\n",
"exports.utcWednesdays = utcWednesdays;\n",
"exports.utcWeek = utcSunday;\n",
"exports.utcWeeks = utcSundays;\n",
"exports.utcYear = utcYear;\n",
"exports.utcYears = utcYears;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],15:[function(require,module,exports){\n",
"// https://d3js.org/d3-timer/ v1.0.10 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n",
"typeof define === 'function' && define.amd ? define(['exports'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}));\n",
"}(this, function (exports) { 'use strict';\n",
"\n",
"var frame = 0, // is an animation frame pending?\n",
" timeout = 0, // is a timeout pending?\n",
" interval = 0, // are any timers active?\n",
" pokeDelay = 1000, // how frequently we check for clock skew\n",
" taskHead,\n",
" taskTail,\n",
" clockLast = 0,\n",
" clockNow = 0,\n",
" clockSkew = 0,\n",
" clock = typeof performance === \"object\" && performance.now ? performance : Date,\n",
" setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n",
"\n",
"function now() {\n",
" return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n",
"}\n",
"\n",
"function clearNow() {\n",
" clockNow = 0;\n",
"}\n",
"\n",
"function Timer() {\n",
" this._call =\n",
" this._time =\n",
" this._next = null;\n",
"}\n",
"\n",
"Timer.prototype = timer.prototype = {\n",
" constructor: Timer,\n",
" restart: function(callback, delay, time) {\n",
" if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n",
" time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n",
" if (!this._next && taskTail !== this) {\n",
" if (taskTail) taskTail._next = this;\n",
" else taskHead = this;\n",
" taskTail = this;\n",
" }\n",
" this._call = callback;\n",
" this._time = time;\n",
" sleep();\n",
" },\n",
" stop: function() {\n",
" if (this._call) {\n",
" this._call = null;\n",
" this._time = Infinity;\n",
" sleep();\n",
" }\n",
" }\n",
"};\n",
"\n",
"function timer(callback, delay, time) {\n",
" var t = new Timer;\n",
" t.restart(callback, delay, time);\n",
" return t;\n",
"}\n",
"\n",
"function timerFlush() {\n",
" now(); // Get the current time, if not already set.\n",
" ++frame; // Pretend we've set an alarm, if we haven't already.\n",
" var t = taskHead, e;\n",
" while (t) {\n",
" if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n",
" t = t._next;\n",
" }\n",
" --frame;\n",
"}\n",
"\n",
"function wake() {\n",
" clockNow = (clockLast = clock.now()) + clockSkew;\n",
" frame = timeout = 0;\n",
" try {\n",
" timerFlush();\n",
" } finally {\n",
" frame = 0;\n",
" nap();\n",
" clockNow = 0;\n",
" }\n",
"}\n",
"\n",
"function poke() {\n",
" var now = clock.now(), delay = now - clockLast;\n",
" if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n",
"}\n",
"\n",
"function nap() {\n",
" var t0, t1 = taskHead, t2, time = Infinity;\n",
" while (t1) {\n",
" if (t1._call) {\n",
" if (time > t1._time) time = t1._time;\n",
" t0 = t1, t1 = t1._next;\n",
" } else {\n",
" t2 = t1._next, t1._next = null;\n",
" t1 = t0 ? t0._next = t2 : taskHead = t2;\n",
" }\n",
" }\n",
" taskTail = t0;\n",
" sleep(time);\n",
"}\n",
"\n",
"function sleep(time) {\n",
" if (frame) return; // Soonest alarm already set, or will be.\n",
" if (timeout) timeout = clearTimeout(timeout);\n",
" var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n",
" if (delay > 24) {\n",
" if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n",
" if (interval) interval = clearInterval(interval);\n",
" } else {\n",
" if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n",
" frame = 1, setFrame(wake);\n",
" }\n",
"}\n",
"\n",
"function timeout$1(callback, delay, time) {\n",
" var t = new Timer;\n",
" delay = delay == null ? 0 : +delay;\n",
" t.restart(function(elapsed) {\n",
" t.stop();\n",
" callback(elapsed + delay);\n",
" }, delay, time);\n",
" return t;\n",
"}\n",
"\n",
"function interval$1(callback, delay, time) {\n",
" var t = new Timer, total = delay;\n",
" if (delay == null) return t.restart(callback, delay, time), t;\n",
" delay = +delay, time = time == null ? now() : +time;\n",
" t.restart(function tick(elapsed) {\n",
" elapsed += total;\n",
" t.restart(tick, total += delay, time);\n",
" callback(elapsed);\n",
" }, delay, time);\n",
" return t;\n",
"}\n",
"\n",
"exports.interval = interval$1;\n",
"exports.now = now;\n",
"exports.timeout = timeout$1;\n",
"exports.timer = timer;\n",
"exports.timerFlush = timerFlush;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{}],16:[function(require,module,exports){\n",
"// https://d3js.org/d3-transition/ v1.3.2 Copyright 2019 Mike Bostock\n",
"(function (global, factory) {\n",
"typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-selection'), require('d3-dispatch'), require('d3-timer'), require('d3-interpolate'), require('d3-color'), require('d3-ease')) :\n",
"typeof define === 'function' && define.amd ? define(['exports', 'd3-selection', 'd3-dispatch', 'd3-timer', 'd3-interpolate', 'd3-color', 'd3-ease'], factory) :\n",
"(global = global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3, global.d3, global.d3, global.d3, global.d3));\n",
"}(this, function (exports, d3Selection, d3Dispatch, d3Timer, d3Interpolate, d3Color, d3Ease) { 'use strict';\n",
"\n",
"var emptyOn = d3Dispatch.dispatch(\"start\", \"end\", \"cancel\", \"interrupt\");\n",
"var emptyTween = [];\n",
"\n",
"var CREATED = 0;\n",
"var SCHEDULED = 1;\n",
"var STARTING = 2;\n",
"var STARTED = 3;\n",
"var RUNNING = 4;\n",
"var ENDING = 5;\n",
"var ENDED = 6;\n",
"\n",
"function schedule(node, name, id, index, group, timing) {\n",
" var schedules = node.__transition;\n",
" if (!schedules) node.__transition = {};\n",
" else if (id in schedules) return;\n",
" create(node, id, {\n",
" name: name,\n",
" index: index, // For context during callback.\n",
" group: group, // For context during callback.\n",
" on: emptyOn,\n",
" tween: emptyTween,\n",
" time: timing.time,\n",
" delay: timing.delay,\n",
" duration: timing.duration,\n",
" ease: timing.ease,\n",
" timer: null,\n",
" state: CREATED\n",
" });\n",
"}\n",
"\n",
"function init(node, id) {\n",
" var schedule = get(node, id);\n",
" if (schedule.state > CREATED) throw new Error(\"too late; already scheduled\");\n",
" return schedule;\n",
"}\n",
"\n",
"function set(node, id) {\n",
" var schedule = get(node, id);\n",
" if (schedule.state > STARTED) throw new Error(\"too late; already running\");\n",
" return schedule;\n",
"}\n",
"\n",
"function get(node, id) {\n",
" var schedule = node.__transition;\n",
" if (!schedule || !(schedule = schedule[id])) throw new Error(\"transition not found\");\n",
" return schedule;\n",
"}\n",
"\n",
"function create(node, id, self) {\n",
" var schedules = node.__transition,\n",
" tween;\n",
"\n",
" // Initialize the self timer when the transition is created.\n",
" // Note the actual delay is not known until the first callback!\n",
" schedules[id] = self;\n",
" self.timer = d3Timer.timer(schedule, 0, self.time);\n",
"\n",
" function schedule(elapsed) {\n",
" self.state = SCHEDULED;\n",
" self.timer.restart(start, self.delay, self.time);\n",
"\n",
" // If the elapsed delay is less than our first sleep, start immediately.\n",
" if (self.delay <= elapsed) start(elapsed - self.delay);\n",
" }\n",
"\n",
" function start(elapsed) {\n",
" var i, j, n, o;\n",
"\n",
" // If the state is not SCHEDULED, then we previously errored on start.\n",
" if (self.state !== SCHEDULED) return stop();\n",
"\n",
" for (i in schedules) {\n",
" o = schedules[i];\n",
" if (o.name !== self.name) continue;\n",
"\n",
" // While this element already has a starting transition during this frame,\n",
" // defer starting an interrupting transition until that transition has a\n",
" // chance to tick (and possibly end); see d3/d3-transition#54!\n",
" if (o.state === STARTED) return d3Timer.timeout(start);\n",
"\n",
" // Interrupt the active transition, if any.\n",
" if (o.state === RUNNING) {\n",
" o.state = ENDED;\n",
" o.timer.stop();\n",
" o.on.call(\"interrupt\", node, node.__data__, o.index, o.group);\n",
" delete schedules[i];\n",
" }\n",
"\n",
" // Cancel any pre-empted transitions.\n",
" else if (+i < id) {\n",
" o.state = ENDED;\n",
" o.timer.stop();\n",
" o.on.call(\"cancel\", node, node.__data__, o.index, o.group);\n",
" delete schedules[i];\n",
" }\n",
" }\n",
"\n",
" // Defer the first tick to end of the current frame; see d3/d3#1576.\n",
" // Note the transition may be canceled after start and before the first tick!\n",
" // Note this must be scheduled before the start event; see d3/d3-transition#16!\n",
" // Assuming this is successful, subsequent callbacks go straight to tick.\n",
" d3Timer.timeout(function() {\n",
" if (self.state === STARTED) {\n",
" self.state = RUNNING;\n",
" self.timer.restart(tick, self.delay, self.time);\n",
" tick(elapsed);\n",
" }\n",
" });\n",
"\n",
" // Dispatch the start event.\n",
" // Note this must be done before the tween are initialized.\n",
" self.state = STARTING;\n",
" self.on.call(\"start\", node, node.__data__, self.index, self.group);\n",
" if (self.state !== STARTING) return; // interrupted\n",
" self.state = STARTED;\n",
"\n",
" // Initialize the tween, deleting null tween.\n",
" tween = new Array(n = self.tween.length);\n",
" for (i = 0, j = -1; i < n; ++i) {\n",
" if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {\n",
" tween[++j] = o;\n",
" }\n",
" }\n",
" tween.length = j + 1;\n",
" }\n",
"\n",
" function tick(elapsed) {\n",
" var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),\n",
" i = -1,\n",
" n = tween.length;\n",
"\n",
" while (++i < n) {\n",
" tween[i].call(node, t);\n",
" }\n",
"\n",
" // Dispatch the end event.\n",
" if (self.state === ENDING) {\n",
" self.on.call(\"end\", node, node.__data__, self.index, self.group);\n",
" stop();\n",
" }\n",
" }\n",
"\n",
" function stop() {\n",
" self.state = ENDED;\n",
" self.timer.stop();\n",
" delete schedules[id];\n",
" for (var i in schedules) return; // eslint-disable-line no-unused-vars\n",
" delete node.__transition;\n",
" }\n",
"}\n",
"\n",
"function interrupt(node, name) {\n",
" var schedules = node.__transition,\n",
" schedule,\n",
" active,\n",
" empty = true,\n",
" i;\n",
"\n",
" if (!schedules) return;\n",
"\n",
" name = name == null ? null : name + \"\";\n",
"\n",
" for (i in schedules) {\n",
" if ((schedule = schedules[i]).name !== name) { empty = false; continue; }\n",
" active = schedule.state > STARTING && schedule.state < ENDING;\n",
" schedule.state = ENDED;\n",
" schedule.timer.stop();\n",
" schedule.on.call(active ? \"interrupt\" : \"cancel\", node, node.__data__, schedule.index, schedule.group);\n",
" delete schedules[i];\n",
" }\n",
"\n",
" if (empty) delete node.__transition;\n",
"}\n",
"\n",
"function selection_interrupt(name) {\n",
" return this.each(function() {\n",
" interrupt(this, name);\n",
" });\n",
"}\n",
"\n",
"function tweenRemove(id, name) {\n",
" var tween0, tween1;\n",
" return function() {\n",
" var schedule = set(this, id),\n",
" tween = schedule.tween;\n",
"\n",
" // If this node shared tween with the previous node,\n",
" // just assign the updated shared tween and we're done!\n",
" // Otherwise, copy-on-write.\n",
" if (tween !== tween0) {\n",
" tween1 = tween0 = tween;\n",
" for (var i = 0, n = tween1.length; i < n; ++i) {\n",
" if (tween1[i].name === name) {\n",
" tween1 = tween1.slice();\n",
" tween1.splice(i, 1);\n",
" break;\n",
" }\n",
" }\n",
" }\n",
"\n",
" schedule.tween = tween1;\n",
" };\n",
"}\n",
"\n",
"function tweenFunction(id, name, value) {\n",
" var tween0, tween1;\n",
" if (typeof value !== \"function\") throw new Error;\n",
" return function() {\n",
" var schedule = set(this, id),\n",
" tween = schedule.tween;\n",
"\n",
" // If this node shared tween with the previous node,\n",
" // just assign the updated shared tween and we're done!\n",
" // Otherwise, copy-on-write.\n",
" if (tween !== tween0) {\n",
" tween1 = (tween0 = tween).slice();\n",
" for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {\n",
" if (tween1[i].name === name) {\n",
" tween1[i] = t;\n",
" break;\n",
" }\n",
" }\n",
" if (i === n) tween1.push(t);\n",
" }\n",
"\n",
" schedule.tween = tween1;\n",
" };\n",
"}\n",
"\n",
"function transition_tween(name, value) {\n",
" var id = this._id;\n",
"\n",
" name += \"\";\n",
"\n",
" if (arguments.length < 2) {\n",
" var tween = get(this.node(), id).tween;\n",
" for (var i = 0, n = tween.length, t; i < n; ++i) {\n",
" if ((t = tween[i]).name === name) {\n",
" return t.value;\n",
" }\n",
" }\n",
" return null;\n",
" }\n",
"\n",
" return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));\n",
"}\n",
"\n",
"function tweenValue(transition, name, value) {\n",
" var id = transition._id;\n",
"\n",
" transition.each(function() {\n",
" var schedule = set(this, id);\n",
" (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);\n",
" });\n",
"\n",
" return function(node) {\n",
" return get(node, id).value[name];\n",
" };\n",
"}\n",
"\n",
"function interpolate(a, b) {\n",
" var c;\n",
" return (typeof b === \"number\" ? d3Interpolate.interpolateNumber\n",
" : b instanceof d3Color.color ? d3Interpolate.interpolateRgb\n",
" : (c = d3Color.color(b)) ? (b = c, d3Interpolate.interpolateRgb)\n",
" : d3Interpolate.interpolateString)(a, b);\n",
"}\n",
"\n",
"function attrRemove(name) {\n",
" return function() {\n",
" this.removeAttribute(name);\n",
" };\n",
"}\n",
"\n",
"function attrRemoveNS(fullname) {\n",
" return function() {\n",
" this.removeAttributeNS(fullname.space, fullname.local);\n",
" };\n",
"}\n",
"\n",
"function attrConstant(name, interpolate, value1) {\n",
" var string00,\n",
" string1 = value1 + \"\",\n",
" interpolate0;\n",
" return function() {\n",
" var string0 = this.getAttribute(name);\n",
" return string0 === string1 ? null\n",
" : string0 === string00 ? interpolate0\n",
" : interpolate0 = interpolate(string00 = string0, value1);\n",
" };\n",
"}\n",
"\n",
"function attrConstantNS(fullname, interpolate, value1) {\n",
" var string00,\n",
" string1 = value1 + \"\",\n",
" interpolate0;\n",
" return function() {\n",
" var string0 = this.getAttributeNS(fullname.space, fullname.local);\n",
" return string0 === string1 ? null\n",
" : string0 === string00 ? interpolate0\n",
" : interpolate0 = interpolate(string00 = string0, value1);\n",
" };\n",
"}\n",
"\n",
"function attrFunction(name, interpolate, value) {\n",
" var string00,\n",
" string10,\n",
" interpolate0;\n",
" return function() {\n",
" var string0, value1 = value(this), string1;\n",
" if (value1 == null) return void this.removeAttribute(name);\n",
" string0 = this.getAttribute(name);\n",
" string1 = value1 + \"\";\n",
" return string0 === string1 ? null\n",
" : string0 === string00 && string1 === string10 ? interpolate0\n",
" : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));\n",
" };\n",
"}\n",
"\n",
"function attrFunctionNS(fullname, interpolate, value) {\n",
" var string00,\n",
" string10,\n",
" interpolate0;\n",
" return function() {\n",
" var string0, value1 = value(this), string1;\n",
" if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);\n",
" string0 = this.getAttributeNS(fullname.space, fullname.local);\n",
" string1 = value1 + \"\";\n",
" return string0 === string1 ? null\n",
" : string0 === string00 && string1 === string10 ? interpolate0\n",
" : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));\n",
" };\n",
"}\n",
"\n",
"function transition_attr(name, value) {\n",
" var fullname = d3Selection.namespace(name), i = fullname === \"transform\" ? d3Interpolate.interpolateTransformSvg : interpolate;\n",
" return this.attrTween(name, typeof value === \"function\"\n",
" ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, \"attr.\" + name, value))\n",
" : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)\n",
" : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value));\n",
"}\n",
"\n",
"function attrInterpolate(name, i) {\n",
" return function(t) {\n",
" this.setAttribute(name, i.call(this, t));\n",
" };\n",
"}\n",
"\n",
"function attrInterpolateNS(fullname, i) {\n",
" return function(t) {\n",
" this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));\n",
" };\n",
"}\n",
"\n",
"function attrTweenNS(fullname, value) {\n",
" var t0, i0;\n",
" function tween() {\n",
" var i = value.apply(this, arguments);\n",
" if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);\n",
" return t0;\n",
" }\n",
" tween._value = value;\n",
" return tween;\n",
"}\n",
"\n",
"function attrTween(name, value) {\n",
" var t0, i0;\n",
" function tween() {\n",
" var i = value.apply(this, arguments);\n",
" if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);\n",
" return t0;\n",
" }\n",
" tween._value = value;\n",
" return tween;\n",
"}\n",
"\n",
"function transition_attrTween(name, value) {\n",
" var key = \"attr.\" + name;\n",
" if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n",
" if (value == null) return this.tween(key, null);\n",
" if (typeof value !== \"function\") throw new Error;\n",
" var fullname = d3Selection.namespace(name);\n",
" return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));\n",
"}\n",
"\n",
"function delayFunction(id, value) {\n",
" return function() {\n",
" init(this, id).delay = +value.apply(this, arguments);\n",
" };\n",
"}\n",
"\n",
"function delayConstant(id, value) {\n",
" return value = +value, function() {\n",
" init(this, id).delay = value;\n",
" };\n",
"}\n",
"\n",
"function transition_delay(value) {\n",
" var id = this._id;\n",
"\n",
" return arguments.length\n",
" ? this.each((typeof value === \"function\"\n",
" ? delayFunction\n",
" : delayConstant)(id, value))\n",
" : get(this.node(), id).delay;\n",
"}\n",
"\n",
"function durationFunction(id, value) {\n",
" return function() {\n",
" set(this, id).duration = +value.apply(this, arguments);\n",
" };\n",
"}\n",
"\n",
"function durationConstant(id, value) {\n",
" return value = +value, function() {\n",
" set(this, id).duration = value;\n",
" };\n",
"}\n",
"\n",
"function transition_duration(value) {\n",
" var id = this._id;\n",
"\n",
" return arguments.length\n",
" ? this.each((typeof value === \"function\"\n",
" ? durationFunction\n",
" : durationConstant)(id, value))\n",
" : get(this.node(), id).duration;\n",
"}\n",
"\n",
"function easeConstant(id, value) {\n",
" if (typeof value !== \"function\") throw new Error;\n",
" return function() {\n",
" set(this, id).ease = value;\n",
" };\n",
"}\n",
"\n",
"function transition_ease(value) {\n",
" var id = this._id;\n",
"\n",
" return arguments.length\n",
" ? this.each(easeConstant(id, value))\n",
" : get(this.node(), id).ease;\n",
"}\n",
"\n",
"function transition_filter(match) {\n",
" if (typeof match !== \"function\") match = d3Selection.matcher(match);\n",
"\n",
" for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n",
" if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n",
" subgroup.push(node);\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Transition(subgroups, this._parents, this._name, this._id);\n",
"}\n",
"\n",
"function transition_merge(transition) {\n",
" if (transition._id !== this._id) throw new Error;\n",
"\n",
" for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n",
" for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n",
" if (node = group0[i] || group1[i]) {\n",
" merge[i] = node;\n",
" }\n",
" }\n",
" }\n",
"\n",
" for (; j < m0; ++j) {\n",
" merges[j] = groups0[j];\n",
" }\n",
"\n",
" return new Transition(merges, this._parents, this._name, this._id);\n",
"}\n",
"\n",
"function start(name) {\n",
" return (name + \"\").trim().split(/^|\\s+/).every(function(t) {\n",
" var i = t.indexOf(\".\");\n",
" if (i >= 0) t = t.slice(0, i);\n",
" return !t || t === \"start\";\n",
" });\n",
"}\n",
"\n",
"function onFunction(id, name, listener) {\n",
" var on0, on1, sit = start(name) ? init : set;\n",
" return function() {\n",
" var schedule = sit(this, id),\n",
" on = schedule.on;\n",
"\n",
" // If this node shared a dispatch with the previous node,\n",
" // just assign the updated shared dispatch and we're done!\n",
" // Otherwise, copy-on-write.\n",
" if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);\n",
"\n",
" schedule.on = on1;\n",
" };\n",
"}\n",
"\n",
"function transition_on(name, listener) {\n",
" var id = this._id;\n",
"\n",
" return arguments.length < 2\n",
" ? get(this.node(), id).on.on(name)\n",
" : this.each(onFunction(id, name, listener));\n",
"}\n",
"\n",
"function removeFunction(id) {\n",
" return function() {\n",
" var parent = this.parentNode;\n",
" for (var i in this.__transition) if (+i !== id) return;\n",
" if (parent) parent.removeChild(this);\n",
" };\n",
"}\n",
"\n",
"function transition_remove() {\n",
" return this.on(\"end.remove\", removeFunction(this._id));\n",
"}\n",
"\n",
"function transition_select(select) {\n",
" var name = this._name,\n",
" id = this._id;\n",
"\n",
" if (typeof select !== \"function\") select = d3Selection.selector(select);\n",
"\n",
" for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n",
" if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n",
" if (\"__data__\" in node) subnode.__data__ = node.__data__;\n",
" subgroup[i] = subnode;\n",
" schedule(subgroup[i], name, id, i, subgroup, get(node, id));\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Transition(subgroups, this._parents, name, id);\n",
"}\n",
"\n",
"function transition_selectAll(select) {\n",
" var name = this._name,\n",
" id = this._id;\n",
"\n",
" if (typeof select !== \"function\") select = d3Selection.selectorAll(select);\n",
"\n",
" for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n",
" if (node = group[i]) {\n",
" for (var children = select.call(node, node.__data__, i, group), child, inherit = get(node, id), k = 0, l = children.length; k < l; ++k) {\n",
" if (child = children[k]) {\n",
" schedule(child, name, id, k, children, inherit);\n",
" }\n",
" }\n",
" subgroups.push(children);\n",
" parents.push(node);\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Transition(subgroups, parents, name, id);\n",
"}\n",
"\n",
"var Selection = d3Selection.selection.prototype.constructor;\n",
"\n",
"function transition_selection() {\n",
" return new Selection(this._groups, this._parents);\n",
"}\n",
"\n",
"function styleNull(name, interpolate) {\n",
" var string00,\n",
" string10,\n",
" interpolate0;\n",
" return function() {\n",
" var string0 = d3Selection.style(this, name),\n",
" string1 = (this.style.removeProperty(name), d3Selection.style(this, name));\n",
" return string0 === string1 ? null\n",
" : string0 === string00 && string1 === string10 ? interpolate0\n",
" : interpolate0 = interpolate(string00 = string0, string10 = string1);\n",
" };\n",
"}\n",
"\n",
"function styleRemove(name) {\n",
" return function() {\n",
" this.style.removeProperty(name);\n",
" };\n",
"}\n",
"\n",
"function styleConstant(name, interpolate, value1) {\n",
" var string00,\n",
" string1 = value1 + \"\",\n",
" interpolate0;\n",
" return function() {\n",
" var string0 = d3Selection.style(this, name);\n",
" return string0 === string1 ? null\n",
" : string0 === string00 ? interpolate0\n",
" : interpolate0 = interpolate(string00 = string0, value1);\n",
" };\n",
"}\n",
"\n",
"function styleFunction(name, interpolate, value) {\n",
" var string00,\n",
" string10,\n",
" interpolate0;\n",
" return function() {\n",
" var string0 = d3Selection.style(this, name),\n",
" value1 = value(this),\n",
" string1 = value1 + \"\";\n",
" if (value1 == null) string1 = value1 = (this.style.removeProperty(name), d3Selection.style(this, name));\n",
" return string0 === string1 ? null\n",
" : string0 === string00 && string1 === string10 ? interpolate0\n",
" : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));\n",
" };\n",
"}\n",
"\n",
"function styleMaybeRemove(id, name) {\n",
" var on0, on1, listener0, key = \"style.\" + name, event = \"end.\" + key, remove;\n",
" return function() {\n",
" var schedule = set(this, id),\n",
" on = schedule.on,\n",
" listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined;\n",
"\n",
" // If this node shared a dispatch with the previous node,\n",
" // just assign the updated shared dispatch and we're done!\n",
" // Otherwise, copy-on-write.\n",
" if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);\n",
"\n",
" schedule.on = on1;\n",
" };\n",
"}\n",
"\n",
"function transition_style(name, value, priority) {\n",
" var i = (name += \"\") === \"transform\" ? d3Interpolate.interpolateTransformCss : interpolate;\n",
" return value == null ? this\n",
" .styleTween(name, styleNull(name, i))\n",
" .on(\"end.style.\" + name, styleRemove(name))\n",
" : typeof value === \"function\" ? this\n",
" .styleTween(name, styleFunction(name, i, tweenValue(this, \"style.\" + name, value)))\n",
" .each(styleMaybeRemove(this._id, name))\n",
" : this\n",
" .styleTween(name, styleConstant(name, i, value), priority)\n",
" .on(\"end.style.\" + name, null);\n",
"}\n",
"\n",
"function styleInterpolate(name, i, priority) {\n",
" return function(t) {\n",
" this.style.setProperty(name, i.call(this, t), priority);\n",
" };\n",
"}\n",
"\n",
"function styleTween(name, value, priority) {\n",
" var t, i0;\n",
" function tween() {\n",
" var i = value.apply(this, arguments);\n",
" if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);\n",
" return t;\n",
" }\n",
" tween._value = value;\n",
" return tween;\n",
"}\n",
"\n",
"function transition_styleTween(name, value, priority) {\n",
" var key = \"style.\" + (name += \"\");\n",
" if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n",
" if (value == null) return this.tween(key, null);\n",
" if (typeof value !== \"function\") throw new Error;\n",
" return this.tween(key, styleTween(name, value, priority == null ? \"\" : priority));\n",
"}\n",
"\n",
"function textConstant(value) {\n",
" return function() {\n",
" this.textContent = value;\n",
" };\n",
"}\n",
"\n",
"function textFunction(value) {\n",
" return function() {\n",
" var value1 = value(this);\n",
" this.textContent = value1 == null ? \"\" : value1;\n",
" };\n",
"}\n",
"\n",
"function transition_text(value) {\n",
" return this.tween(\"text\", typeof value === \"function\"\n",
" ? textFunction(tweenValue(this, \"text\", value))\n",
" : textConstant(value == null ? \"\" : value + \"\"));\n",
"}\n",
"\n",
"function textInterpolate(i) {\n",
" return function(t) {\n",
" this.textContent = i.call(this, t);\n",
" };\n",
"}\n",
"\n",
"function textTween(value) {\n",
" var t0, i0;\n",
" function tween() {\n",
" var i = value.apply(this, arguments);\n",
" if (i !== i0) t0 = (i0 = i) && textInterpolate(i);\n",
" return t0;\n",
" }\n",
" tween._value = value;\n",
" return tween;\n",
"}\n",
"\n",
"function transition_textTween(value) {\n",
" var key = \"text\";\n",
" if (arguments.length < 1) return (key = this.tween(key)) && key._value;\n",
" if (value == null) return this.tween(key, null);\n",
" if (typeof value !== \"function\") throw new Error;\n",
" return this.tween(key, textTween(value));\n",
"}\n",
"\n",
"function transition_transition() {\n",
" var name = this._name,\n",
" id0 = this._id,\n",
" id1 = newId();\n",
"\n",
" for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n",
" if (node = group[i]) {\n",
" var inherit = get(node, id0);\n",
" schedule(node, name, id1, i, group, {\n",
" time: inherit.time + inherit.delay + inherit.duration,\n",
" delay: 0,\n",
" duration: inherit.duration,\n",
" ease: inherit.ease\n",
" });\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Transition(groups, this._parents, name, id1);\n",
"}\n",
"\n",
"function transition_end() {\n",
" var on0, on1, that = this, id = that._id, size = that.size();\n",
" return new Promise(function(resolve, reject) {\n",
" var cancel = {value: reject},\n",
" end = {value: function() { if (--size === 0) resolve(); }};\n",
"\n",
" that.each(function() {\n",
" var schedule = set(this, id),\n",
" on = schedule.on;\n",
"\n",
" // If this node shared a dispatch with the previous node,\n",
" // just assign the updated shared dispatch and we're done!\n",
" // Otherwise, copy-on-write.\n",
" if (on !== on0) {\n",
" on1 = (on0 = on).copy();\n",
" on1._.cancel.push(cancel);\n",
" on1._.interrupt.push(cancel);\n",
" on1._.end.push(end);\n",
" }\n",
"\n",
" schedule.on = on1;\n",
" });\n",
" });\n",
"}\n",
"\n",
"var id = 0;\n",
"\n",
"function Transition(groups, parents, name, id) {\n",
" this._groups = groups;\n",
" this._parents = parents;\n",
" this._name = name;\n",
" this._id = id;\n",
"}\n",
"\n",
"function transition(name) {\n",
" return d3Selection.selection().transition(name);\n",
"}\n",
"\n",
"function newId() {\n",
" return ++id;\n",
"}\n",
"\n",
"var selection_prototype = d3Selection.selection.prototype;\n",
"\n",
"Transition.prototype = transition.prototype = {\n",
" constructor: Transition,\n",
" select: transition_select,\n",
" selectAll: transition_selectAll,\n",
" filter: transition_filter,\n",
" merge: transition_merge,\n",
" selection: transition_selection,\n",
" transition: transition_transition,\n",
" call: selection_prototype.call,\n",
" nodes: selection_prototype.nodes,\n",
" node: selection_prototype.node,\n",
" size: selection_prototype.size,\n",
" empty: selection_prototype.empty,\n",
" each: selection_prototype.each,\n",
" on: transition_on,\n",
" attr: transition_attr,\n",
" attrTween: transition_attrTween,\n",
" style: transition_style,\n",
" styleTween: transition_styleTween,\n",
" text: transition_text,\n",
" textTween: transition_textTween,\n",
" remove: transition_remove,\n",
" tween: transition_tween,\n",
" delay: transition_delay,\n",
" duration: transition_duration,\n",
" ease: transition_ease,\n",
" end: transition_end\n",
"};\n",
"\n",
"var defaultTiming = {\n",
" time: null, // Set on use.\n",
" delay: 0,\n",
" duration: 250,\n",
" ease: d3Ease.easeCubicInOut\n",
"};\n",
"\n",
"function inherit(node, id) {\n",
" var timing;\n",
" while (!(timing = node.__transition) || !(timing = timing[id])) {\n",
" if (!(node = node.parentNode)) {\n",
" return defaultTiming.time = d3Timer.now(), defaultTiming;\n",
" }\n",
" }\n",
" return timing;\n",
"}\n",
"\n",
"function selection_transition(name) {\n",
" var id,\n",
" timing;\n",
"\n",
" if (name instanceof Transition) {\n",
" id = name._id, name = name._name;\n",
" } else {\n",
" id = newId(), (timing = defaultTiming).time = d3Timer.now(), name = name == null ? null : name + \"\";\n",
" }\n",
"\n",
" for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n",
" for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n",
" if (node = group[i]) {\n",
" schedule(node, name, id, i, group, timing || inherit(node, id));\n",
" }\n",
" }\n",
" }\n",
"\n",
" return new Transition(groups, this._parents, name, id);\n",
"}\n",
"\n",
"d3Selection.selection.prototype.interrupt = selection_interrupt;\n",
"d3Selection.selection.prototype.transition = selection_transition;\n",
"\n",
"var root = [null];\n",
"\n",
"function active(node, name) {\n",
" var schedules = node.__transition,\n",
" schedule,\n",
" i;\n",
"\n",
" if (schedules) {\n",
" name = name == null ? null : name + \"\";\n",
" for (i in schedules) {\n",
" if ((schedule = schedules[i]).state > SCHEDULED && schedule.name === name) {\n",
" return new Transition([[node]], root, name, +i);\n",
" }\n",
" }\n",
" }\n",
"\n",
" return null;\n",
"}\n",
"\n",
"exports.active = active;\n",
"exports.interrupt = interrupt;\n",
"exports.transition = transition;\n",
"\n",
"Object.defineProperty(exports, '__esModule', { value: true });\n",
"\n",
"}));\n",
"\n",
"},{\"d3-color\":4,\"d3-dispatch\":5,\"d3-ease\":6,\"d3-interpolate\":8,\"d3-selection\":11,\"d3-timer\":15}]},{},[1]);\n",
"</script><script>;(function () {\n",
" 'use strict';\n",
" const d3 = window.d3;\n",
"\n",
" const margin = {top: 10, right: 10, bottom: 10, left: 35};\n",
" const axisMargin = { top: 10, right: 15, bottom: 10, left: 15 };\n",
" const facetWidth = 30;\n",
" const legendHeight = 40;\n",
" const xAxisHeight = 30;\n",
" const xLabelHeight = 20;\n",
"\n",
" function computeLimit(original, data, lineKeys, fn) {\n",
" let min = Infinity;\n",
" let max = -Infinity;\n",
"\n",
" for (const lineKey of lineKeys) {\n",
" const [localMin, localMax] = d3.extent(data.get(lineKey).map(fn));\n",
" min = Math.min(min, localMin);\n",
" max = Math.max(max, localMax);\n",
" }\n",
"\n",
" return [\n",
" original[0] === null ? min : original[0],\n",
" original[1] === null ? max : original[1]\n",
" ];\n",
" }\n",
"\n",
" function highestMinorMod(majorTickCount, minorTickMax) {\n",
" return Math.floor((minorTickMax - 1) / (majorTickCount - 1));\n",
" }\n",
"\n",
" function createGridTicks(majorTicks, minorMod) {\n",
" const minorTicks = [];\n",
" for (let majorIndex = 0; majorIndex < majorTicks.length - 1; majorIndex++) {\n",
" minorTicks.push(majorTicks[majorIndex]);\n",
" const distance = majorTicks[majorIndex + 1] - majorTicks[majorIndex];\n",
" for (let i = 1; i <= minorMod - 1; i++) {\n",
" minorTicks.push(majorTicks[majorIndex] + (i / minorMod) * distance);\n",
" }\n",
" }\n",
" minorTicks.push(majorTicks[majorTicks.length - 1]);\n",
" return minorTicks;\n",
" }\n",
"\n",
" class SubGraph {\n",
" constructor({ container, id, index, height, width, drawXAxis, lineConfig, facetLabel, ylim, xlim }) {\n",
" this.container = container;\n",
"\n",
" this.graphWidth = width - facetWidth - margin.left - margin.right;\n",
" this.graphHeight = height - margin.top - margin.bottom;\n",
"\n",
" this.axisWidth = this.graphWidth - axisMargin.left - axisMargin.right;\n",
" this.axisHeight = this.graphHeight - axisMargin.top - axisMargin.bottom;\n",
"\n",
" this.xlim = xlim;\n",
" this.dynamicXlim = this.xlim.includes(null);\n",
" this.ylim = ylim;\n",
" this.dynamicYlim = this.ylim.includes(null);\n",
"\n",
" this.lineKeys = Object.keys(lineConfig);\n",
" this.lineConfig = lineConfig;\n",
"\n",
" // Create graph container\n",
" this.graph = this.container.append('g')\n",
" .attr('transform',\n",
" 'translate(' + margin.left + ',' + margin.top + ')');\n",
"\n",
" // Create facet\n",
" this.facet = this.container.append('g')\n",
" .classed('facet', true)\n",
" .attr('transform', `translate(${margin.left + this.graphWidth}, ${margin.top})`);\n",
" this.facet.append('rect')\n",
" .classed('facet-background', true)\n",
" .attr('width', facetWidth)\n",
" .attr('height', this.graphHeight);\n",
" const facetTextPath = this.facet.append('path')\n",
" .attr('d', `M10,0 V${this.graphHeight}`)\n",
" .attr('id', `learning-curve-${id}-${index}-facet-text`);\n",
" const facetText = this.facet.append('text')\n",
" .append('textPath')\n",
" .attr('startOffset', '50%')\n",
" .attr('href', `#learning-curve-${id}-${index}-facet-text`)\n",
" .attr('text-anchor', 'middle')\n",
" .text(facetLabel);\n",
" facetText.node()\n",
" .setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', `#learning-curve-${id}-${index}-facet-text`);\n",
"\n",
" // Create background\n",
" this.background = this.graph.append(\"rect\")\n",
" .attr(\"class\", \"background\")\n",
" .attr(\"height\", this.graphHeight)\n",
" .attr(\"width\", this.graphWidth);\n",
"\n",
" // define scales\n",
" this.xScale = d3.scaleLinear()\n",
" .range([0, this.axisWidth]);\n",
"\n",
" this.yScale = d3.scaleLinear()\n",
" .range([this.axisHeight, 0]);\n",
"\n",
" // compute tick marks\n",
" this._updateXscale(this.xlim);\n",
" this._updateYscale(this.ylim);\n",
"\n",
" // create x-grid\n",
" this.xGrid = d3.axisBottom(this.xScale)\n",
" .tickValues(this.xTicksGrid)\n",
" .tickSize(-this.graphHeight);\n",
" this.xGridElement = this.graph.append(\"g\")\n",
" .attr(\"class\", \"grid\")\n",
" .attr(\"transform\", `translate(${axisMargin.left},${this.graphHeight})`);\n",
"\n",
" // create y-grid\n",
" this.yGrid = d3.axisLeft(this.yScale)\n",
" .tickValues(this.yTicksGrid)\n",
" .tickSize(-this.graphWidth);\n",
" this.yGridElement = this.graph.append(\"g\")\n",
" .attr(\"class\", \"grid\")\n",
" .attr('transform', `translate(0,${axisMargin.top})`);\n",
"\n",
" // define x-axis\n",
" this.xAxis = d3.axisBottom(this.xScale)\n",
" .tickValues(this.xTicks);\n",
" this.xAxisElement = this.graph.append('g')\n",
" .attr(\"class\", \"axis\")\n",
" .classed('hide-axis', !drawXAxis)\n",
" .attr('transform', `translate(${axisMargin.left},${this.graphHeight})`);\n",
"\n",
" // define y-axis\n",
" this.yAxis = d3.axisLeft(this.yScale)\n",
" .tickValues(this.yTicks);\n",
" this.yAxisElement = this.graph.append('g')\n",
" .attr(\"class\", \"axis\")\n",
" .attr('transform', `translate(0,${axisMargin.top})`);\n",
"\n",
" // draw axis\n",
" if (!this.dynamicYlim) this._drawYaxis();\n",
" if (!this.dynamicXlim) this._drawXaxis();\n",
"\n",
" // Define drawer functions and line elements\n",
" this.lineDrawers = new Map();\n",
" this.lineElements = new Map();\n",
" const self = this;\n",
" for (const lineKey of this.lineKeys) {\n",
" // create drawer function\n",
" const lineDrawer = d3.line()\n",
" .x((d) => self.xScale(d.x))\n",
" .y((d) => this.yScale(d.y));\n",
" this.lineDrawers.set(lineKey, lineDrawer);\n",
"\n",
" // create line element\n",
" const lineElement = this.graph.append('path')\n",
" .attr('class', 'line')\n",
" .attr('transform', `translate(${axisMargin.left},${axisMargin.top})`)\n",
" .attr('stroke', lineConfig[lineKey].color);\n",
" this.lineElements.set(lineKey, lineElement);\n",
" }\n",
" }\n",
"\n",
" _updateXscale(xlim) {\n",
" this.xScale.domain(xlim).nice(6);\n",
" this.xTicks = this.xScale.ticks(6);\n",
" this.xTicksMod = highestMinorMod(this.xTicks.length, 19);\n",
" this.xTicksGrid = createGridTicks(this.xTicks, this.xTicksMod);\n",
" }\n",
"\n",
" _drawXaxis() {\n",
" // update x-grid\n",
" this.xGridElement.transition()\n",
" .call(this.xGrid.tickValues(this.xTicksGrid))\n",
" .call((transition) => transition\n",
" .selectAll('.tick')\n",
" .style('stroke-opacity', (v) => this.xTicks.includes(v) ? '1.0' : '0.5')\n",
" );\n",
"\n",
" // update x-axis\n",
" this.xAxisElement.transition().call(\n",
" this.xAxis.tickValues(this.xTicks)\n",
" );\n",
" }\n",
"\n",
" _updateYscale(ylim) {\n",
" this.yScale.domain(ylim).nice(3);\n",
" this.yTicks = this.yScale.ticks(3);\n",
" this.yTicksMod = highestMinorMod(this.yTicks.length, 9);\n",
" this.yTicksGrid = createGridTicks(this.yTicks, this.yTicksMod);\n",
" }\n",
"\n",
" _drawYaxis() {\n",
" // update x-grid\n",
" this.yGridElement.transition()\n",
" .call(this.yGrid.tickValues(this.yTicksGrid))\n",
" .call((transition) => transition\n",
" .selectAll('.tick')\n",
" .style('stroke-opacity', (v) => this.yTicks.includes(v) ? '1.0' : '0.5')\n",
" );\n",
" // update x-axis\n",
" this.yAxisElement.transition().call(\n",
" this.yAxis.tickValues(this.yTicks)\n",
" );\n",
" }\n",
"\n",
" setData (data) {\n",
" // Compute x-axis limit\n",
" if (this.dynamicXlim) {\n",
" const xlim = computeLimit(this.xlim, data, this.lineKeys, (d) => d.x);\n",
" if (xlim[0] !== this.xlim[0] || xlim[1] !== this.xlim[1]) {\n",
" this._updateXscale(xlim);\n",
" }\n",
" }\n",
"\n",
" // Update y-axis limit\n",
" if (this.dynamicYlim) {\n",
" const ylim = computeLimit(this.ylim, data, this.lineKeys, (d) => d.y);\n",
" if (ylim[0] !== this.ylim[0] || ylim[1] !== this.ylim[1]) {\n",
" this._updateYscale(ylim);\n",
" }\n",
" }\n",
"\n",
" // Update line data\n",
" for (let lineKey of this.lineKeys) {\n",
" this.lineElements.get(lineKey).data([\n",
" data.get(lineKey)\n",
" ]);\n",
" }\n",
" }\n",
"\n",
" draw() {\n",
" if (this.dynamicXlim) this._drawXaxis();\n",
" if (this.dynamicYlim) this._drawYaxis();\n",
"\n",
" // update lines\n",
" for (let lineKey of this.lineKeys) {\n",
" this.lineElements.get(lineKey)\n",
" .attr('d', this.lineDrawers.get(lineKey));\n",
" }\n",
" }\n",
" }\n",
"\n",
" class LearningCurvePlot {\n",
" constructor({ container, id, height, width, facetConfig, lineConfig, xAxisConfig }) {\n",
" this.facetKeys = Object.keys(facetConfig);\n",
"\n",
" const innerHeight = height - legendHeight - xLabelHeight - xAxisHeight;\n",
" const subGraphHeight = innerHeight / this.facetKeys.length;\n",
"\n",
" this._container = d3.select(container)\n",
" .classed('learning-curve', true)\n",
" .style('height', `${height}px`)\n",
" .style('width', `${width}px`)\n",
" .attr('height', height)\n",
" .attr('width', width)\n",
" .attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');\n",
"\n",
" // Create a SubGraph for each facet\n",
" this._facets = new Map();\n",
" for (let facetIndex = 0; facetIndex < this.facetKeys.length; facetIndex++) {\n",
" const facetKey = this.facetKeys[facetIndex];\n",
" this._facets.set(\n",
" facetKey,\n",
" new SubGraph({\n",
" container: this._container.append('g')\n",
" .attr('transform', `translate(0, ${facetIndex * subGraphHeight})`),\n",
" id: id,\n",
" index: facetIndex,\n",
" height: Math.round(subGraphHeight),\n",
" width: width,\n",
"\n",
" drawXAxis: facetIndex == this.facetKeys.length - 1,\n",
"\n",
" lineConfig: lineConfig,\n",
" facetLabel: facetConfig[facetKey].name,\n",
" ylim: facetConfig[facetKey].limit,\n",
" xlim: xAxisConfig.limit\n",
" })\n",
" );\n",
" }\n",
"\n",
" const afterSubGraphHeight = height - legendHeight - xLabelHeight;\n",
" const plotWidth = width - margin.left - margin.right;\n",
" const xAxisWidth = plotWidth - facetWidth;\n",
"\n",
" // Draw x-axis label\n",
" this._xLabel = this._container.append('text')\n",
" .attr('text-anchor', 'middle')\n",
" .attr('transform', `translate(${margin.left}, ${afterSubGraphHeight})`)\n",
" .attr('x', xAxisWidth / 2)\n",
" .text(xAxisConfig.name);\n",
"\n",
" // Draw legends\n",
" this._legend = this._container\n",
" .append('g')\n",
" .classed('legned', true)\n",
" .attr('transform', `translate(${margin.left}, ${afterSubGraphHeight + xLabelHeight})`);\n",
" this._legendOfsset = this._legend.append('g');\n",
"\n",
" let currentOffset = 0;\n",
" for (const {name, color} of Object.values(lineConfig)) {\n",
" // Draw rect with line inside [-]\n",
" this._legendOfsset.append('rect')\n",
" .attr('width', 25)\n",
" .attr('height', 25)\n",
" .attr('x', currentOffset);\n",
" this._legendOfsset.append('line')\n",
" .attr('x1', currentOffset + 2)\n",
" .attr('x2', currentOffset + 25 - 2)\n",
" .attr('y1', 25/2)\n",
" .attr('y2', 25/2)\n",
" .attr('stroke', color);\n",
" currentOffset += 30;\n",
"\n",
" // Draw text\n",
" const textNode = this._legendOfsset.append('text')\n",
" .attr('x', currentOffset)\n",
" .attr('y', 19)\n",
" .text(name);\n",
" const textWidth = textNode.node().getComputedTextLength();\n",
" currentOffset += textWidth + 20;\n",
" }\n",
" currentOffset -= 20;\n",
"\n",
" this._legendOfsset\n",
" .attr('transform', `translate(${(plotWidth - currentOffset) / 2}, 0)`);\n",
" }\n",
"\n",
" setData(data) {\n",
" for (let facetKey of this.facetKeys) {\n",
" this._facets.get(facetKey).setData(data.getFacetData(facetKey));\n",
" }\n",
" }\n",
"\n",
" draw() {\n",
" for (let facetKey of this.facetKeys) {\n",
" this._facets.get(facetKey).draw();\n",
" }\n",
" }\n",
" }\n",
"\n",
" // Class to accumulate and store all data\n",
" class LearningCurveData {\n",
" constructor(facetLabels, lineLabels) {\n",
" this.facetKeys = Object.keys(facetLabels);\n",
" this.lineKeys = Object.keys(lineLabels);\n",
"\n",
" this.data = new Map();\n",
" for (const facetKey of this.facetKeys) {\n",
" this.data.set(facetKey, new Map());\n",
" for (const lineKey of this.lineKeys) {\n",
" this.data.get(facetKey).set(lineKey, []);\n",
" }\n",
" }\n",
" }\n",
"\n",
" appendAll(rows) {\n",
" for (const facetKey of this.facetKeys) {\n",
" for (const lineKey of this.lineKeys) {\n",
" const storage = this.data.get(facetKey).get(lineKey);\n",
" for (const row of rows) {\n",
" storage.push({\n",
" x: row.x,\n",
" y: row.y[facetKey][lineKey]\n",
" });\n",
" }\n",
" }\n",
" }\n",
" }\n",
"\n",
" getFacetData(facetKey) {\n",
" return this.data.get(facetKey);\n",
" }\n",
" }\n",
"\n",
" window.setupLearningCurve = function (settings) {\n",
" const data = new LearningCurveData(settings.facetConfig, settings.lineConfig);\n",
" const graph = new LearningCurvePlot({\n",
" container: document.getElementById(settings.id),\n",
" ...settings\n",
" });\n",
"\n",
" let waitingForDrawing = false;\n",
" function drawer() {\n",
" waitingForDrawing = false;\n",
" graph.setData(data);\n",
" graph.draw();\n",
" }\n",
"\n",
" window.appendLearningCurve = function(rows) {\n",
" data.appendAll(rows);\n",
"\n",
" if (!waitingForDrawing) {\n",
" waitingForDrawing = true;\n",
" window.requestAnimationFrame(drawer);\n",
" }\n",
" };\n",
" };\n",
"})();\n",
"</script><svg id=\"772b7ed5-4fb1-461a-b6e2-13bfe8bd154c\" class=\"learning-curve\"></svg><script> window.setupLearningCurve({\"id\": \"772b7ed5-4fb1-461a-b6e2-13bfe8bd154c\", \"width\": 600, \"height\": 490, \"lineConfig\": {\"Train\": {\"name\": \"Train\", \"color\": \"#1f77b4\"}, \"Validation\": {\"name\": \"Validation\", \"color\": \"#ff7f0e\"}}, \"facetConfig\": {\"Cross entropy\": {\"name\": \"Cross entropy\", \"limit\": [null, null]}, \"Accuracy\": {\"name\": \"Accuracy\", \"limit\": [null, null]}}, \"xAxisConfig\": {\"name\": \"Iteration\", \"limit\": [0, 34000]}});</script>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": "window.appendLearningCurve([{\"x\": 124.0, \"y\": {\"Cross entropy\": {\"Train\": 1.8584183756742794, \"Validation\": 1.8472406692662053}, \"Accuracy\": {\"Train\": 0.23, \"Validation\": 0.27923627684964203}}}]);",
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"ename": "KeyboardInterrupt",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn [121], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m main()\n",
"Cell \u001b[1;32mIn [116], line 67\u001b[0m, in \u001b[0;36mmain\u001b[1;34m()\u001b[0m\n\u001b[0;32m 65\u001b[0m \u001b[39m# Train model.\u001b[39;00m\n\u001b[0;32m 66\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39mtraining...\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[1;32m---> 67\u001b[0m trained_model, stats \u001b[39m=\u001b[39m Train(model, NNForward, NNBackward, NNUpdate, eps,\n\u001b[0;32m 68\u001b[0m momentum, num_epochs, batch_size)\n\u001b[0;32m 70\u001b[0m plt\u001b[39m.\u001b[39mfigure(\u001b[39m0\u001b[39m)\n\u001b[0;32m 71\u001b[0m plt\u001b[39m.\u001b[39mplot(np\u001b[39m.\u001b[39marray(stats[\u001b[39m'\u001b[39m\u001b[39mtrain_ce\u001b[39m\u001b[39m'\u001b[39m])[:, \u001b[39m0\u001b[39m], np\u001b[39m.\u001b[39marray(stats[\u001b[39m'\u001b[39m\u001b[39mtrain_ce\u001b[39m\u001b[39m'\u001b[39m])[:, \u001b[39m1\u001b[39m], \u001b[39m'\u001b[39m\u001b[39mb\u001b[39m\u001b[39m'\u001b[39m, label\u001b[39m=\u001b[39m\u001b[39m'\u001b[39m\u001b[39mTrain\u001b[39m\u001b[39m'\u001b[39m)\n",
"Cell \u001b[1;32mIn [53], line 68\u001b[0m, in \u001b[0;36mTrain\u001b[1;34m(model, forward, backward, update, eps, momentum, num_epochs, batch_size)\u001b[0m\n\u001b[0;32m 65\u001b[0m error \u001b[39m=\u001b[39m (prediction \u001b[39m-\u001b[39m t) \u001b[39m/\u001b[39m x\u001b[39m.\u001b[39mshape[\u001b[39m0\u001b[39m]\n\u001b[0;32m 67\u001b[0m \u001b[39m# Backward prop.\u001b[39;00m\n\u001b[1;32m---> 68\u001b[0m grads \u001b[39m=\u001b[39m backward(model, error, var)\n\u001b[0;32m 70\u001b[0m \u001b[39m# Update weights.\u001b[39;00m\n\u001b[0;32m 71\u001b[0m update(model, eps, momentum, optimizer_state, grads)\n",
"Cell \u001b[1;32mIn [51], line 15\u001b[0m, in \u001b[0;36mNNBackward\u001b[1;34m(model, err, var)\u001b[0m\n\u001b[0;32m 13\u001b[0m dE_dh1r, dE_dW2, dE_db2 \u001b[39m=\u001b[39m AffineBackward(dE_dh2, var[\u001b[39m'\u001b[39m\u001b[39mh1r\u001b[39m\u001b[39m'\u001b[39m], model[\u001b[39m'\u001b[39m\u001b[39mW2\u001b[39m\u001b[39m'\u001b[39m])\n\u001b[0;32m 14\u001b[0m dE_dh1 \u001b[39m=\u001b[39m ReLUBackward(dE_dh1r, var[\u001b[39m'\u001b[39m\u001b[39mh1\u001b[39m\u001b[39m'\u001b[39m], var[\u001b[39m'\u001b[39m\u001b[39mh1r\u001b[39m\u001b[39m'\u001b[39m])\n\u001b[1;32m---> 15\u001b[0m _, dE_dW1, dE_db1 \u001b[39m=\u001b[39m AffineBackward(dE_dh1, var[\u001b[39m'\u001b[39;49m\u001b[39mx\u001b[39;49m\u001b[39m'\u001b[39;49m], model[\u001b[39m'\u001b[39;49m\u001b[39mW1\u001b[39;49m\u001b[39m'\u001b[39;49m])\n\u001b[0;32m 17\u001b[0m grads \u001b[39m=\u001b[39m {}\n\u001b[0;32m 18\u001b[0m grads[\u001b[39m'\u001b[39m\u001b[39mW1\u001b[39m\u001b[39m'\u001b[39m] \u001b[39m=\u001b[39m dE_dW1\n",
"Cell \u001b[1;32mIn [49], line 14\u001b[0m, in \u001b[0;36mAffineBackward\u001b[1;34m(grad_y, x, w)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mAffineBackward\u001b[39m(grad_y: np\u001b[39m.\u001b[39mndarray, x: np\u001b[39m.\u001b[39mndarray, w: np\u001b[39m.\u001b[39mndarray) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m Tuple[np\u001b[39m.\u001b[39mndarray, np\u001b[39m.\u001b[39mndarray, np\u001b[39m.\u001b[39mndarray]:\n\u001b[0;32m 2\u001b[0m \u001b[39m\"\"\"Computes gradients of affine transformation.\u001b[39;00m\n\u001b[0;32m 3\u001b[0m \n\u001b[0;32m 4\u001b[0m \u001b[39m Args:\u001b[39;00m\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[39m grad_b: Gradients wrt. the biases.\u001b[39;00m\n\u001b[0;32m 13\u001b[0m \u001b[39m \"\"\"\u001b[39;00m\n\u001b[1;32m---> 14\u001b[0m grad_x \u001b[39m=\u001b[39m grad_y\u001b[39m.\u001b[39;49mdot(w\u001b[39m.\u001b[39;49mT)\n\u001b[0;32m 15\u001b[0m grad_w \u001b[39m=\u001b[39m x\u001b[39m.\u001b[39mT\u001b[39m.\u001b[39mdot(grad_y)\n\u001b[0;32m 16\u001b[0m grad_b \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39msum(grad_y, axis\u001b[39m=\u001b[39m\u001b[39m0\u001b[39m)\n",
"\u001b[1;31mKeyboardInterrupt\u001b[0m: "
]
}
],
"source": [
"main()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"학습 오차(training error)와 일반화를 위한 검증 오차(validation error) 결과는 다음과 같습니다.\n",
"\n",
"```\n",
"CE: Train 0.25610 Validation 0.97890 Test 0.78023\n",
"Acc: Train 0.90486 Validation 0.73986 Test 0.77143\n",
"```\n",
"\n",
"그래프는 다음과 같습니다.\n",
"\n",
"![loss_graph](./defaultLossGraph.png)\n",
"![accuracy_graph](./defaultAccuracyGraph.png)\n",
"\n",
"학습 오차가 크게 감소하고 일반화 검증 오차는 점점 증가하는 것을 확인할 수 있습니다."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2. 최적화 (optimization): Learning rate, momentum, mini-batch size 세 가지 종류의 parameter 들을 아래와 같이 변화시키면서 다양한 조합들에 대해 신경망이 cross-entropy 관점에서 어떻게 수렴하는지 살펴본다. 가장 우수한 성능을 나타내는 hyperparameter 들의 조합이 어떤 것인지 제시하시오. (모든 경우의 수를 다 따지면 75 가지 신경망 모델을 테스트해야 하나 시간이 너무 많이 결릴 수 있으므로 이 중에서 일부분의 경우들만 테스트해도 된다. 그러나 어떤 근거로 해당 조합들만 테스트했는지 적당한 설명이 있어야 함.)\n",
" - Learning rate ( $\\epsilon$ ): 0.001 에서 1.0 사이의 5 가지 경우\n",
" - Momentum: 0.0 에서 0.9 사이의 3 가지 경우\n",
" - Mini-batch size: 1 에서 1000 까지의 5 가지 경우\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 실험 코드"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{1, 2, 7, 14, 241, 482, 1687, 3374}"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import itertools\n",
"# 3374 can be factorized into 2 * 7 * 241\n",
"factor = set([1, 2, 7, 241])\n",
"# make all the multiplication of combinations of factors\n",
"combinations = [set(np.prod(x) for x in itertools.combinations(factor, i)) for i in range(1, len(factor)+1)]\n",
"combinations = set.union(*combinations)\n",
"combinations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"나머지가 없는 Mini-batch size는 1, 2, 7, 14, 241, 482, 1687, 3374 입니다."
]
},
{
"cell_type": "code",
"execution_count": 165,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[3.9810717055349727,\n",
" 15.848931924611136,\n",
" 63.095734448019314,\n",
" 251.1886431509581,\n",
" 1000.0]"
]
},
"execution_count": 165,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[1000**(i/5) for i in range(1, 6)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1000을 배수단위로 5등분을 하면 3.98, 15.84, 63.09, 251.18, 1000 이 됩니다."
]
},
{
"cell_type": "code",
"execution_count": 203,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(65, 59, 0.09230769230769231),\n",
" (69, 62, 0.10144927536231885),\n",
" (64, 46, 0.28125),\n",
" (68, 42, 0.38235294117647056),\n",
" (63, 35, 0.4444444444444444),\n",
" (62, 26, 0.5806451612903226),\n",
" (67, 24, 0.6417910447761194),\n",
" (61, 19, 0.6885245901639344),\n",
" (56, 14, 0.75),\n",
" (60, 14, 0.7666666666666667)]"
]
},
"execution_count": 203,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# pick top 10\n",
"center, width = 63, 15\n",
"cand = [(i,3374 % i, (i - 3374 % i) / i) for i in range(center-(width//2), center+(width//2))]\n",
"cand = sorted(cand, key=lambda x: x[2])\n",
"cand[:10]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"그래서 2, 14, 65, 241, 844를 선택하였습니다."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"ename": "TypeError",
"evalue": "ExperimentMLP() missing 1 required positional argument: 'title'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn [20], line 12\u001b[0m\n\u001b[0;32m 1\u001b[0m conf \u001b[39m=\u001b[39m Config(\n\u001b[0;32m 2\u001b[0m num_inputs\u001b[39m=\u001b[39m\u001b[39m2304\u001b[39m,\n\u001b[0;32m 3\u001b[0m num_hiddens\u001b[39m=\u001b[39m[\u001b[39m16\u001b[39m, \u001b[39m32\u001b[39m],\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 10\u001b[0m patience\u001b[39m=\u001b[39m\u001b[39m100\u001b[39m,\n\u001b[0;32m 11\u001b[0m )\n\u001b[1;32m---> 12\u001b[0m _, stat \u001b[39m=\u001b[39m ExperimentMLP(conf, save_dir\u001b[39m=\u001b[39;49m\u001b[39m'\u001b[39;49m\u001b[39mresults/example_lr=0.5\u001b[39;49m\u001b[39m'\u001b[39;49m, show\u001b[39m=\u001b[39;49m\u001b[39mTrue\u001b[39;49;00m, pplot\u001b[39m=\u001b[39;49m\u001b[39mTrue\u001b[39;49;00m)\n\u001b[0;32m 13\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mTest accuracy: \u001b[39m\u001b[39m{\u001b[39;00mstat\u001b[39m.\u001b[39mtest_acc\u001b[39m:\u001b[39;00m\u001b[39m.4f\u001b[39m\u001b[39m}\u001b[39;00m\u001b[39m, Test cross entropy: \u001b[39m\u001b[39m{\u001b[39;00mstat\u001b[39m.\u001b[39mtest_ce\u001b[39m:\u001b[39;00m\u001b[39m.4f\u001b[39m\u001b[39m}\u001b[39;00m\u001b[39m\"\u001b[39m)\n\u001b[0;32m 14\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39mDone\u001b[39m\u001b[39m\"\u001b[39m)\n",
"\u001b[1;31mTypeError\u001b[0m: ExperimentMLP() missing 1 required positional argument: 'title'"
]
}
],
"source": [
"conf = Config(\n",
" num_inputs=2304,\n",
" num_hiddens=[16, 32],\n",
" num_outputs=7,\n",
" eps=0.5,\n",
" momentum=0.0,\n",
" num_epochs=1000,\n",
" batch_size=844,\n",
" early_stopping=True,\n",
" patience=100,\n",
")\n",
"_, stat = ExperimentMLP(conf, save_dir='results/example_lr=0.5', show=True, pplot=True)\n",
"print(f\"Test accuracy: {stat.test_acc:.4f}, Test cross entropy: {stat.test_ce:.4f}\")\n",
"print(\"Done\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"위 셀에서 lr을 변경해서 나올 수 있는 결과는 다음과 같습니다.\n",
"\n",
"![lr1.0plot](./lr1plot.png)\n",
"\n",
"> lr 1.0 plot\n",
" \n",
"![lr0.5plot](./lr0.5plot.png)\n",
"\n",
"> lr 0.5 plot \n",
"\n",
"다음 그래프를 보면 알 수 있듯 배치를 아무리 높여도 lr = 0.5 일때 최적화가 잘 될 수 없다고 판단 하였습니다. 그래서 제외하였습니다. 1.0도 마찬가지이므로 제외하였습니다."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
"conf = Config(\n",
" num_inputs=2304,\n",
" num_hiddens=[16, 32],\n",
" num_outputs=7,\n",
" eps=0.01,\n",
" momentum=0.0,\n",
" num_epochs=1000,\n",
" batch_size=100,\n",
" early_stopping=True,\n",
" patience=50,\n",
")\n",
"\n",
"# Grid search for hyperparameters.\n",
"lr_candidates = [0.1, 0.05, 0.01, 0.005, 0.001]\n",
"momentum_candidates = [0.0, 0.5, 0.9]\n",
"mini_batch_size_candidates = [2, 14, 65, 241, 844]\n",
"\n",
"# Make all combinations of hyperparameters.\n",
"import itertools\n",
"\n",
"experiments_list = [*itertools.product(lr_candidates, momentum_candidates, mini_batch_size_candidates)]"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"All experiments completed\n"
]
}
],
"source": [
"import time\n",
"\n",
"experiments = load_experiment_metafile('experiments.json', \n",
" init_task_if_not_exists=experiments_list)\n",
"\n",
"if len(experiments[\"remain_experiments\"]) == 0:\n",
" print(\"All experiments completed\")\n",
"\n",
"# Run experiments.\n",
"# tqdm nested progress bar is not working in my jupyter notebook\n",
"# so I just print the progress\n",
"while len(experiments['remain_experiments']) > 0:\n",
" # get next experiment\n",
" lr, momentum, mini_batch_size = experiments['remain_experiments'].pop(0)\n",
" # set experiment directory\n",
" save_dir = f\"results/lr={lr}_momentum={momentum}_batch_size={mini_batch_size}\"\n",
" # create experiment config\n",
" conf.eps = lr\n",
" conf.momentum = momentum\n",
" conf.batch_size = mini_batch_size\n",
" # print experiment parameters\n",
" print(f\"Experiment: lr={lr}, momentum={momentum}, batch_size={mini_batch_size}\")\n",
" \n",
" start = time.time()\n",
" # run experiment\n",
" ExperimentMLP(conf, save_dir=save_dir, show=False)\n",
" \n",
" end = time.time()\n",
" # add experiment to completed experiments\n",
" experiments['completed_experiment_results'].append({\n",
" \"lr\": lr,\n",
" \"momentum\": momentum,\n",
" \"mini_batch_size\": mini_batch_size,\n",
" \"save_dir\": save_dir,\n",
" \"time\": end - start\n",
" })\n",
" # print completed experiments , remaining experiments and time taken\n",
" print(\"\\n\".join([f\"Completed experiments: {len(experiments['completed_experiment_results'])}\",\n",
" f\"Remaining experiments: {len(experiments['remain_experiments'])}\",\n",
" f\"Time taken: {end - start:.2f} seconds\"]))\n",
" # save experiments\n",
" save_experiment_metafile('experiments.json', experiments)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"위의 셀들를 실행하면 실험을 진행할 수 있습니다."
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {},
"outputs": [],
"source": [
"from tqdm import tqdm\n",
"def get_lbm_experiment_results(experiments: Dict[str, Any], use_tqdm: bool = False):\n",
" \"\"\"Get experiment results from meta data\"\"\"\n",
" experiments_results = experiments['completed_experiment_results']\n",
" if use_tqdm:\n",
" experiments_results = tqdm(experiments_results)\n",
" results = []\n",
" for experiment in experiments_results:\n",
" # load experiment statistics\n",
" _, stat, _ = load_experiment(experiment['save_dir'], load_model=False)\n",
" i, best_valid_acc = stat.best_valid_acc()\n",
" # add experiment parameters and statistics to results\n",
" results.append({\n",
" \"lr\": experiment['lr'],\n",
" \"momentum\": experiment['momentum'],\n",
" \"mini_batch_size\": experiment['mini_batch_size'],\n",
" \"test_acc\": stat.test_acc,\n",
" \"test_ce\": stat.test_ce,\n",
" \"train_acc\": stat.train_acc[i][1],\n",
" \"train_ce\": stat.train_ce[i][1],\n",
" \"valid_acc\": best_valid_acc,\n",
" \"valid_ce\": stat.valid_ce[i][1],\n",
" \"time\": experiment['time']\n",
" })\n",
" return results"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 결과"
]
},
{
"cell_type": "code",
"execution_count": 97,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 75/75 [00:00<00:00, 612.94it/s]\n"
]
}
],
"source": [
"# load experiments\n",
"experiments = load_experiment_metafile('experiments.json')\n",
"# get experiment results\n",
"results = get_lbm_experiment_results(experiments, use_tqdm=True)"
]
},
{
"cell_type": "code",
"execution_count": 88,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"#### Momentum = 0.0\n",
"| |0.1|0.05|0.01|0.005|0.001|\n",
"|---|---|---|---|---|---|\n",
"|2|28.16|44.87|75.18|76.37|75.42|\n",
"|14|71.12|74.22|77.57|76.37|72.32|\n",
"|65|74.46|76.61|74.46|74.70|66.11|\n",
"|241|65.16|73.27|73.51|66.83|47.26|\n",
"|844|50.60|68.97|63.48|55.61|28.16|"
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"#### Momentum = 0.5\n",
"| |0.1|0.05|0.01|0.005|0.001|\n",
"|---|---|---|---|---|---|\n",
"|2|27.92|35.08|72.55|76.61|75.66|\n",
"|14|47.26|70.41|74.46|77.33|74.22|\n",
"|65|53.22|67.78|75.42|74.94|69.69|\n",
"|241|47.97|67.54|72.55|71.12|59.43|\n",
"|844|50.12|64.20|70.64|63.96|27.92|"
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"#### Momentum = 0.9\n",
"| |0.1|0.05|0.01|0.005|0.001|\n",
"|---|---|---|---|---|---|\n",
"|2|27.92|27.92|27.92|47.73|73.51|\n",
"|14|27.92|27.92|69.45|72.32|76.37|\n",
"|65|27.92|47.73|74.46|75.89|73.27|\n",
"|241|32.70|58.47|75.66|73.51|71.84|\n",
"|844|35.80|53.46|72.79|71.36|60.86|"
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from IPython.display import display, HTML, Markdown\n",
"\n",
"for m in momentum_candidates:\n",
" markdown_table_content = []\n",
" markdown_table_content.append(\"| |\" + \"|\".join(map(str, lr_candidates)) + \"|\")\n",
" markdown_table_content.append(\"|\" + \"|\".join([\"---\"] * (len(lr_candidates) + 1)) + \"|\")\n",
" for batch in mini_batch_size_candidates:\n",
" inner_content = []\n",
" markdown_table_content.append(\"|\" + str(batch) + \"|\")\n",
" for lr in lr_candidates:\n",
" # filter results\n",
" filtered_results = [result for result in results if result['lr'] == lr and result['momentum'] == m and result['mini_batch_size'] == batch]\n",
" if len(filtered_results) == 1:\n",
" result = filtered_results[0]\n",
" inner_content.append(f\"{result['valid_acc'] * 100:.2f}\")\n",
" markdown_table_content[-1] += \"|\".join(inner_content) + \"|\"\n",
" display(Markdown(f\"#### Momentum = {m}\\n\" + \"\\n\".join(markdown_table_content)))\n",
" # print(f\"#### Momentum = {m}\\n\"+\"\\n\".join(markdown_table_content))\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Momentum = 0.0\n",
"| |0.1|0.05|0.01|0.005|0.001|\n",
"|---|---|---|---|---|---|\n",
"|2|28.16|44.87|75.18|76.37|75.42|\n",
"|14|71.12|74.22|77.57|76.37|72.32|\n",
"|65|74.46|76.61|74.46|74.70|66.11|\n",
"|241|65.16|73.27|73.51|66.83|47.26|\n",
"|844|50.60|68.97|63.48|55.61|28.16|\n",
"\n",
"##### Momentum = 0.5\n",
"| |0.1|0.05|0.01|0.005|0.001|\n",
"|---|---|---|---|---|---|\n",
"|2|27.92|35.08|72.55|76.61|75.66|\n",
"|14|47.26|70.41|74.46|77.33|74.22|\n",
"|65|53.22|67.78|75.42|74.94|69.69|\n",
"|241|47.97|67.54|72.55|71.12|59.43|\n",
"|844|50.12|64.20|70.64|63.96|27.92|\n",
"\n",
"##### Momentum = 0.9\n",
"| |0.1|0.05|0.01|0.005|0.001|\n",
"|---|---|---|---|---|---|\n",
"|2|27.92|27.92|27.92|47.73|73.51|\n",
"|14|27.92|27.92|69.45|72.32|76.37|\n",
"|65|27.92|47.73|74.46|75.89|73.27|\n",
"|241|32.70|58.47|75.66|73.51|71.84|\n",
"|844|35.80|53.46|72.79|71.36|60.86|\n",
"\n",
"다음과 같은 결과가 나왔습니다."
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAbYAAAHWCAYAAAAb/awqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAADB60lEQVR4nOydd1xb1/n/P5LQQBISexrjjfdeeOGBjW0w03tm9tuRNt+66a/Nt20SJ6nTNs3oStOm2dN7YsB4b2zjBR6AsY0xNhsh0Jbu+f1BoMYsCV1dDc47r/uKdTj3nEfS1X3uec4zeIQQAgqFQqFQPAS+swWgUCgUCoVNqGKjUCgUikdBFRuFQqFQPAqq2CgUCoXiUVDFRqFQKBSPgio2CoVCoXgUVLFRKBQKxaOgio1CoVAoHgVVbBQKhULxKKhio1BcGB6Ph9dee6319WeffQYej4d79+51e26/fv3w1FNPOUw2CsVVoYqNYjNarRavvfYajh075mxROkSlUuEHP/gBgoKCIJPJMGfOHFy6dMnq82/evImFCxdCLpfD398f69atQ3V1tQMlplAobEIVG8VmtFotNm3a5JKKjWEYJCQk4JtvvsELL7yAP/3pT6iqqsLs2bNRXFzc7fkPHjzArFmzcPv2bWzevBkvvfQSMjIyMH/+fBiNRg7eQdesW7cOOp0OUVFRzhaFQnFZvJwtAIXCJtu3b8eZM2ewbds2LF26FACwfPlyDBkyBK+++iq++eabLs/fvHkzNBoN8vLy0LdvXwDA5MmTMX/+fHz22Wf4wQ9+4PD30BUCgQACgcCpMjgTvV4PkUgEPp8+k1M6h14dPeS1114Dj8dDUVER1q5dC6VSiaCgIPzud78DIQRlZWVITk6GQqFAaGgo3nnnnXZjVFVV4dlnn0VISAgkEgnGjBmDzz//vE2fe/fugcfj4c9//jP+8Y9/YMCAAZBKpViwYAHKyspACMEbb7yBPn36wNvbG8nJyairq2s3V2ZmJmbOnAmZTAYfHx8kJCTg+vXrbfo89dRTkMvlKC8vR0pKCuRyOYKCgvDSSy/BYrG0yhMUFAQA2LRpE3g8Xpt9oNmzZ2P27Nnt5n/qqafQr18/1t/Xk2zfvh0hISFIS0trbQsKCsLy5cuxZ88eGAyGLs/fsWMHEhMTW5UaAMTFxWHIkCHYunVrp+eZTCb4+/vj6aefbvc3tVoNiUSCl156CQBgNBrxyiuvYMKECVAqlZDJZJg5cyaOHj3a7fvraI+NEII333wTffr0gVQqxZw5c9p9t13x5z//GdOmTUNAQAC8vb0xYcIEbN++vcO+X331FSZPngypVAo/Pz/MmjULBw8ebNMnMzMTsbGx8PHxgUKhwKRJk9o8UHS29/fktXPs2DHweDx89913+O1vf4uIiAhIpVKo1WrU1dXhpZdewqhRoyCXy6FQKLBo0SJcvXq13bh6vR6vvfYahgwZAolEgrCwMKSlpaGkpASEEPTr1w/JyckdnqdUKvE///M/Vn6SFFeBKjY7WbFiBRiGwR/+8AdMmTIFb775Jt5//33Mnz8fERER+OMf/4hBgwbhpZdewokTJ1rP0+l0mD17Nr788kusWbMGb7/9NpRKJZ566in85S9/aTfP119/jQ8++AA//elP8Ytf/ALHjx/H8uXL8dvf/hZZWVn41a9+hR/84AfYt29f6w20hS+//BIJCQmQy+X44x//iN/97ne4ceMGZsyY0c4JwWKxID4+HgEBAfjzn/+M2NhYvPPOO/j3v/8NoFlJ/POf/wQApKam4ssvv8SXX37ZRpHYgj3vqyMuX76M8ePHt3uinzx5MrRaLYqKijo9t7y8HFVVVZg4cWK7v02ePBmXL1/u9FyhUIjU1FTs3r27ncly9+7dMBgMWLlyJYBmRfef//wHs2fPxh//+Ee89tprqK6uRnx8PK5cudLte3ySV155Bb/73e8wZswYvP322xgwYAAWLFgAjUZj1fl/+ctfMG7cOLz++uvYvHkzvLy8sGzZMmRkZLTpt2nTJqxbtw5CoRCvv/46Nm3ahMjISBw5cqS1z2effYaEhATU1dXh5Zdfxh/+8AeMHTsWWVlZNr+vFt544w1kZGTgpZdewubNmyESiXDnzh3s3r0biYmJePfdd/HLX/4S+fn5iI2NxcOHD1vPtVgsSExMxKZNmzBhwgS88847ePHFF9HQ0ICCggLweDysXbsWmZmZ7R6c9u3bB7VajbVr1/ZYdoqTIJQe8eqrrxIA5Ac/+EFrm9lsJn369CE8Ho/84Q9/aG2vr68n3t7eZMOGDa1t77//PgFAvvrqq9Y2o9FIYmJiiFwuJ2q1mhBCyN27dwkAEhQURFQqVWvfl19+mQAgY8aMISaTqbV91apVRCQSEb1eTwghpLGxkfj6+pLnn3++jfwVFRVEqVS2ad+wYQMBQF5//fU2fceNG0cmTJjQ+rq6upoAIK+++mq7zyU2NpbExsa2a9+wYQOJiopqfW3v++oMmUxGnnnmmXbtGRkZBADJysrq9NwLFy4QAOSLL75o97df/vKXBECX82dnZxMAZN++fW3aFy9eTAYMGND62mw2E4PB0KZPfX09CQkJaSf7k5/zp59+SgCQu3fvEkIIqaqqIiKRiCQkJBCGYVr7/d///R8B0Oaa6wytVtvmtdFoJCNHjiRz585tbSsuLiZ8Pp+kpqYSi8XSpn/LvCqVivj4+JApU6YQnU7XYR9CCImKiupQrievnaNHjxIAZMCAAe1k1Ov17eS4e/cuEYvFba7fTz75hAAg7777brv5WmQqLCwkAMg///nPNn9PSkoi/fr1ayM7xT2gKzY7ee6551r/LRAIMHHiRBBC8Oyzz7a2+/r6Ijo6Gnfu3GltO3DgAEJDQ7Fq1arWNqFQiJ/97GdoamrC8ePH28yzbNkyKJXK1tdTpkwBAKxduxZeXl5t2o1GI8rLywEAOTk5UKlUWLVqFWpqaloPgUCAKVOmdGj++uEPf9jm9cyZM9vIziY9fV+dodPpIBaL27VLJJLWv3d1LoAenz937lwEBgZiy5YtrW319fXIycnBihUrWtsEAgFEIhGAZmeXuro6mM1mTJw40SbvTQA4dOgQjEYjfvrTn4LH47W2/+///q/VY3h7e7eRt6GhATNnzmwjy+7du8EwDF555ZV2q+GWeXNyctDY2Ihf//rXrZ/Xk316woYNG9rICDR/Ry1yWCwW1NbWQi6XIzo6uo3cO3bsQGBgIH7605+2G7dFpiFDhmDKlCn4+uuvW/9WV1eHzMxMrFmzxi7ZKc6BOo/YyeN7MQCgVCohkUgQGBjYrr22trb1dWlpKQYPHtzuJjFs2LDWv3c3DwBERkZ22F5fXw8ArZ6Ac+fO7VB+hULR5rVEImndQ2vBz8+vdTy26en76gxvb+8O99H0en3r37s6F0CPz/fy8kJ6ejq++eYbGAwGiMVi7Ny5EyaTqY1iA4DPP/8c77zzDm7dugWTydTa3r9//y7eXXtarpPBgwe3aQ8KCoKfn59VY+zfvx9vvvkmrly50ua9P35DLykpAZ/Px/Dhwzsdp6SkBAAwcuRIq+W3ho4+E4Zh8Je//AUffPAB7t6927oHDAABAQFtZIqOjm7zkNQR69evxwsvvIDS0lJERUVh27ZtMJlMWLduHXtvhMIZdMVmJx15qHXmtUYIYXUea+ZiGAZA8z5bTk5Ou2PPnj1WjWctnT3dPn7jsWa+nn6GYWFhePToUbv2lrbw8PAuz32875Pn+/v7d7iae5yVK1eisbERmZmZAICtW7di6NChGDNmTGufr776Ck899RQGDhyIjz/+GFlZWcjJycHcuXNbvy+uOHnyJJKSkiCRSPDBBx/gwIEDyMnJwerVq+26XrvC1muko4eJzZs3Y+PGjZg1axa++uorZGdnIycnByNGjOjRZ7hy5UoIhcLWVdtXX32FiRMnIjo62uaxKM6HrticRFRUFK5duwaGYdqs2m7dutX6dzYYOHAgACA4OBhxcXGsjNmVacbPz69Ds+WTK1BHMXbsWJw8ebLd55qbmwupVIohQ4Z0em5ERASCgoJw8eLFdn8
"text/plain": [
"<Figure size 500x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAbYAAAHWCAYAAAAb/awqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAADQ9UlEQVR4nOydd1wc17n3f7O9sbv0JiSEUEO9d4F6oYOQULGKHeemOPG149zEbxL3yM51XJJcJ3bibsnqDZBAvXcJhIQqEiqoIOqyvc3M+wcGC9F2tu8yX3/mY+3sKc8us/PMOed3noegaZoGCwsLCwuLn8DxtAEsLCwsLCzOhHVsLCwsLCx+BevYWFhYWFj8CtaxsbCwsLD4FaxjY2FhYWHxK1jHxsLCwsLiV7COjYWFhYXFr2AdGwsLCwuLX8E6NhYWFhYWv4J1bCwsXgxBEHjjjTdaXn/99dcgCAJ37tzpsm5sbCxWrlzpMttYWLwV1rGxMEav1+ONN97AoUOHPG1Ku6hUKvz0pz9FaGgopFIppk2bhuLiYpvqrly5EgRBtDkGDBjgYqtZWFicBc/TBrD4Hnq9Hm+++SYAICkpybPGPAVFUUhOTkZpaSl++9vfIiQkBP/85z+RlJSE8+fPo2/fvl22IRQK8fnnn7c6p1AoXGUyI5555hnk5uZCKBR62hQWFq+FdWwsfsXmzZtx4sQJbNq0CQsWLAAALFy4EP369cPrr7+O77//vss2eDweli1b5mpT7YLL5YLL5XraDI9hNBohEAjA4bCTTSwdw14ddvLGG2+AIAjcuHEDy5Ytg0KhQGhoKP70pz+BpmlUVlYiPT0dcrkcERER+OCDD9q0UV1djeeeew7h4eEQiUQYNmwYvvnmm1Zl7ty5A4Ig8Ne//hWffPIJ4uLiIJFIMHv2bFRWVoKmabz99tvo0aMHxGIx0tPTUV9f36avwsJCTJkyBVKpFAEBAUhOTsbly5dblVm5ciVkMhkePHiAjIwMyGQyhIaG4pVXXgFJki32hIaGAgDefPPNlqm65nWgpKSkdkdxK1euRGxsrNM/19Ns3rwZ4eHhyMrKajkXGhqKhQsXYseOHTCZTF22AQAkSUKtVttUFgAsFguCgoKwatWqNu+p1WqIRCK88sorAACz2YzXXnsNo0aNgkKhgFQqxZQpU3Dw4MEu+2lvjY2mabzzzjvo0aMHJBIJpk2b1uZv2xl//etfMXHiRAQHB0MsFmPUqFHYvHlzu2XXrFmDsWPHQiKRIDAwEFOnTsWePXtalSksLERiYiICAgIgl8sxZsyYVg8UHa39PX3tHDp0CARBYP369fjjH/+I6OhoSCQSqNVq1NfX45VXXsGQIUMgk8kgl8sxb948lJaWtmnXaDTijTfeQL9+/SASiRAZGYmsrCzcunULNE0jNjYW6enp7dZTKBT4r//6Lxu/SRZvgXVsDrJo0SJQFIX33nsP48aNwzvvvIOPP/4Ys2bNQnR0NP7yl78gPj4er7zyCo4cOdJSz2AwICkpCd999x2WLl2K999/HwqFAitXrsTf/va3Nv2sXbsW//znP/GrX/0Kv/nNb3D48GEsXLgQf/zjH1FUVITf/e53+OlPf4r8/PyWG2gz3333HZKTkyGTyfCXv/wFf/rTn3DlyhVMnjy5jQiBJEnMmTMHwcHB+Otf/4rExER88MEH+Pe//w2gyUn861//AgBkZmbiu+++w3fffdfKkTDBkc/VHiUlJRg5cmSbJ/qxY8dCr9fjxo0bXbah1+shl8uhUCgQFBSEX/7yl9BqtZ3W4fP5yMzMxPbt22E2m1u9t337dphMJuTm5gJocnSff/45kpKS8Je//AVvvPEGampqMGfOHFy4cKFL+57mtddew5/+9CcMGzYM77//PuLi4jB79mzodDqb6v/tb3/DiBEj8NZbb2H16tXg8XjIycnBzp07W5V788038cwzz4DP5+Ott97Cm2++iZiYGBw4cKClzNdff43k5GTU19fj1VdfxXvvvYfhw4ejqKiI8edq5u2338bOnTvxyiuvYPXq1RAIBKioqMD27duRkpKCDz/8EL/97W9x6dIlJCYm4uHDhy11SZJESkoK3nzzTYwaNQoffPABXnzxRTQ2NqKsrAwEQWDZsmUoLCxs8+CUn58PtVrttaN3lk6gWezi9ddfpwHQP/3pT1vOWa1WukePHjRBEPR7773Xcr6hoYEWi8X0ihUrWs59/PHHNAB6zZo1LefMZjM9YcIEWiaT0Wq1mqZpmr59+zYNgA4NDaVVKlVL2VdffZUGQA8bNoy2WCwt5xcvXkwLBALaaDTSNE3TGo2GViqV9PPPP9/K/qqqKlqhULQ6v2LFChoA/dZbb7UqO2LECHrUqFEtr2tqamgA9Ouvv97me0lMTKQTExPbnF+xYgXdq1evlteOfq6OkEql9LPPPtvm/M6dO2kAdFFRUaf1f//739O/+93v6A0bNtDr1q1r+U4mTZrUyp722L17Nw2Azs/Pb3V+/vz5dFxcXMtrq9VKm0ymVmUaGhro8PDwNrY//T1/9dVXNAD69u3bNE3TdHV1NS0QCOjk5GSaoqiWcv/v//0/GkCra64j9Hp9q9dms5kePHgwPX369JZz5eXlNIfDoTMzM2mSJFuVb+5XpVLRAQEB9Lhx42iDwdBuGZqm6V69erVr19PXzsGDB2kAdFxcXBsbjUZjGztu375NC4XCVtfvl19+SQOgP/zwwzb9Ndt0/fp1GgD9r3/9q9X7aWlpdGxsbCvbWXwDdsTmID/5yU9a/s3lcjF69GjQNI3nnnuu5bxSqUT//v1RUVHRcm7Xrl2IiIjA4sWLW87x+Xz8+te/hlarxeHDh1v1k5OT00rAMG7cOADAsmXLwOPxWp03m8148OABAGDv3r1QqVRYvHgxamtrWw4ul4tx48a1O/31s5/9rNXrKVOmtLLdmdj7uTrCYDC0K6wQiUQt73fGu+++i/feew8LFy5Ebm4uvv76a/z5z3/G8ePHO5yea2b69OkICQnBhg0bWs41NDRg7969WLRoUcs5LpcLgUAAoEnsUl9fD6vVitGjR9us3mxm3759MJvN+NWvfgWCIFrO//d//7fNbYjF4lb2NjY2YsqUKa1s2b59OyiKwmuvvdZmNNzc7969e6HRaPD73/++5ft+uow9rFixopWNQJPAp9kOkiRRV1cHmUyG/v37t7J7y5YtCAkJwa9+9as27Tbb1K9fP4wbNw5r165tea++vh6FhYVYunSpQ7azeAbWsTlIz549W71WKBQQiUQICQlpc76hoaHl9d27d9G3b982N4mBAwe2vN9VPwAQExPT7vnmvsrLywE03XRDQ0NbHXv27EF1dXWr+iKRqGUNrZnAwMBWtjsTez9XR4jF4nbX0YxGY8v7THnppZfA4XCwb9++TsvxeDxkZ2e3WsvbunUrLBZLK8cGAN988w2GDh0KkUiE4OBghIaGYufOnWhsbGRkW/N18rTaMzQ0FIGBgTa1UVBQgPHjx0MkEiEoKKhluvlJW27dugUOh4OEhIQO27l16xYAYPDgwYw+Q1f07t27zTmKovDRRx+hb9++EAqFCAkJQWhoKC5evNjG7v79+7d6SGqP5cuX4/jx4y3f56ZNm2CxWPDMM8849bOwuAfWsTlIewq1jlRrNE07tR9b+qIoCkDTOtvevXvbHDt27LCpPVvp6Om2WXzyNPZ+ro6IjIzEo0eP2pxvPhcVFdVp/fYQi8UIDg62SbySm5sLjUaDwsJCAMDGjRsxYMAADBs2rKXMmjVrsHLlSvTp0wdffPEFioqKsHfvXkyfPr3l7+Uujh49irS0NIhEIvzzn//Erl27sHfvXixZssSh67UzmF4j7T2MrF69Gi+//DKmTp2KNWvWYPfu3di7dy8GDRpk13eYm5sLPp/fMmpbs2YNRo8ejf79+zNui8XzsHJ/D9GrVy9cvHgRFEW1GrVdu3at5X1n0KdPHwBAWFgYZs6c6ZQ2O5uaCQwMbHfa8ukRqKsYPnw4jh492uZ7PX36NCQSCfr168e4TY1Gg9ra2jYj2faYOnUqIiMjsWHDBkyePBk
"text/plain": [
"<Figure size 500x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAbYAAAHWCAYAAAAb/awqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAADQrUlEQVR4nOyddXxb57nHf2K0LLMdjuMwo+MwMzjMbdKu2+3WrWvX3a13W3FL13WF3d213bq2KzfMMYc5cRxqmNEoWSwdeu8frt04tqVz7CPJcL756POJj194ZB/rOc/7kIwQQiAhISEhIdFMkIdbAAkJCQkJCTGRFJuEhISERLNCUmwSEhISEs0KSbFJSEhISDQrJMUmISEhIdGskBSbhISEhESzQlJsEhISEhLNCkmxSUhISEg0KyTFJiEhISHRrJAUm4REI0Ymk+GVV16p+vo///kPZDIZbt68GXBuhw4dsHLlyqDJJiHRWJEUm4Rg3G43XnnlFezZsyfcotRKeXk5fvzjHyMuLg4GgwFjx47FyZMnec//v//7P3Tv3h0ajQatW7fG888/D5fLFUSJJSQkxERSbBKCcbvdePXVVxulYuM4DtOnT8fXX3+NZ555Bn/5y19QXFyMMWPG4MqVKwHn/+Y3v8HPf/5z9OrVC3/7298wb948/P3vf8fcuXNDIH1gVqxYAY/Hg/bt24dbFAmJRosy3AJISIjJ+vXrcejQIaxbtw7z588HACxcuBBdunTByy+/jK+//rrOuQ8ePMA777yDFStW4PPPP6+63qVLF/z85z/Htm3bMHPmzKC/B38oFAooFIqwyhBOvF4v1Go15HLpmVyibqS7o5688sorkMlkuHz5MpYvX47IyEjExcXhD3/4AwghuHPnDmbPng2TyYTExES8/fbbNdYoLi7Gk08+iYSEBGi1WvTt2xefffZZtTE3b96ETCbDX//6V/zjH/9AcnIy9Ho9Jk2ahDt37oAQgtdffx1t2rSBTqfD7NmzYbFYauyVkZGBkSNHwmAwICIiAtOnT8d3331XbczKlSthNBpx7949pKenw2g0Ii4uDi+88AJYlq2SJy4uDgDw6quvQiaTVfMDjRkzBmPGjKmx/8qVK9GhQwfR39ejrF+/HgkJCdUsrLi4OCxcuBBbtmyBz+erc+7hw4fBMAwWL15c7Xrl199++22dc2maRnR0NFatWlXje3a7HVqtFi+88AIAgKIovPTSSxg4cCAiIyNhMBgwcuRI7N69O+D7q83HRgjBH//4R7Rp0wZ6vR5jx46t8bv1x1//+lcMGzYMMTEx0Ol0GDhwINavX1/r2C+//BJDhgyBXq9HVFQURo0ahezs7GpjMjIyMHr0aERERMBkMmHw4MHVHijq8v09eu/s2bMHMpkM3377LX7/+9+jdevW0Ov1sNvtsFgseOGFF9C7d28YjUaYTCZMnToVp0+frrGu1+vFK6+8gi5dukCr1SIpKQlz587FtWvXQAhBhw4dMHv27FrnRUZG4ic/+QnPn6REY0FSbA1k0aJF4DgOf/7zn5Gamoo//vGPeO+99zBx4kS0bt0ab775JlJSUvDCCy9g3759VfM8Hg/GjBmDL774AsuWLcNbb72FyMhIrFy5En/7299q7PPVV1/h/fffx89//nP86le/wt69e7Fw4UL8/ve/R2ZmJn7zm9/gxz/+MbZt21b1AVrJF198genTp8NoNOLNN9/EH/7wB5w/fx4jRoyoEYTAsiwmT56MmJgY/PWvf8Xo0aPx9ttv41//+heACiXxwQcfAADmzJmDL774Al988UW9j+oa8r5qo6CgAAMGDKjxRD9kyBC43W5cvny5zrmVSk+n01W7rtfrAQD5+fl1zlWpVJgzZw42b94MiqKqfW/z5s3w+XxVCtJut+Pf//43xowZgzfffBOvvPIKSkpKMHnyZJw6dSrge3yUl156CX/4wx/Qt29fvPXWW0hOTsakSZN4+wX/9re/oX///njttdewevVqKJVKLFiwADt27Kg27tVXX8WKFSugUqnw2muv4dVXX0Xbtm2xa9euqjH/+c9/MH36dFgsFrz44ov485//jH79+iEzM1Pw+6rk9ddfx44dO/DCCy9g9erVUKvVuH79OjZv3owZM2bgnXfewa9//WucPXsWo0ePxv3796vmsiyLGTNm4NVXX8XAgQPx9ttv49lnn4XNZsO5c+cgk8mwfPlyZGRk1Hhw2rZtG+x2O5YvX15v2SXCBJGoFy+//DIBQH784x9XXWMYhrRp04bIZDLy5z//ueq61WolOp2OPP7441XX3nvvPQKAfPnll1XXKIoiaWlpxGg0ErvdTggh5MaNGwQAiYuLI+Xl5VVjX3zxRQKA9O3bl9A0XXV9yZIlRK1WE6/XSwghxOFwELPZTJ566qlq8hcWFpLIyMhq1x9//HECgLz22mvVxvbv358MHDiw6uuSkhICgLz88ss1fi6jR48mo0ePrnH98ccfJ+3bt6/6uqHvqy4MBgN54oknalzfsWMHAUAyMzPrnJufn08AkNdff73a9czMTAKAGI1Gv3tnZWURAGTbtm3Vrk+bNo0kJydXfc0wDPH5fNXGWK1WkpCQUEP2R3/On376KQFAbty4QQghpLi4mKjVajJ9+nTCcVzVuP/5n/8hAKrdc3XhdrurfU1RFOnVqxcZN25c1bUrV64QuVxO5syZQ1iWrTa+ct/y8nISERFBUlNTicfjqXUMIYS0b9++VrkevXd2795NAJDk5OQaMnq93hpy3Lhxg2g0mmr37yeffEIAkHfeeafGfpUyXbp0iQAgH3zwQbXvz5o1i3To0KGa7BJNA8liayA/+tGPqv6vUCgwaNAgEELw5JNPVl03m83o2rUrrl+/XnVt586dSExMxJIlS6quqVQq/OIXv4DT6cTevXur7bNgwQJERkZWfZ2amgoAWL58OZRKZbXrFEXh3r17AICcnByUl5djyZIlKC0trXopFAqkpqbWevz1X//1X9W+HjlyZDXZxaS+76suPB4PNBpNjetarbbq+3UxYMAApKam4s0338Snn36KmzdvIiMjAz/5yU+gUqn8zgWAcePGITY2FmvWrKm6ZrVakZOTg0WLFlVdUygUUKvVACqCXSwWCxiGwaBBgwRFbwJAbm4uKIrCz3/+c8hksqrrv/zlL3mv8bCFarVaYbPZMHLkyGqybN68GRzH4aWXXqphDVfum5OTA4fDgd/+9rdVP+9Hx9SHxx9/vIYVrdFoquRgWRZlZWUwGo3o2rVrNbk3bNiA2NhY/PznP6+xbqVMXbp0QWpqKr766quq71ksFmRkZGDZsmUNkl0iPEjBIw2kXbt21b6OjIyEVqtFbGxsjetlZWVVX9+6dQudO3eu8SHRvXv3qu8H2gcA2rZtW+t1q9UKAFWRgOPGjatVfpPJVO1rrVZb5UOrJCoqqmo9sanv+6oLnU5Xqx/N6/VWfd8fGzZswKJFi/DEE08AqFBCzz//PPbu3YtLly75natUKjFv3jx8/fXX8Pl80Gg02LhxI2iarqbYAOCzzz7D22+/jYsXL4Km6arrHTt29LvHo1TeJ507d652PS4uDlFRUbzW2L59O/74xz/i1KlT1X52D3+gX7t2DXK5HD169KhznWvXrgEAevXqxVt+PtT2M+E4Dn/729/w/vvv48aNG1U+YACIiYmpJlPXrl2rPSTVxmOPPYZnnnkGt27dQvv27bFu3TrQNI0VK1aI90YkQoak2BpIbRFqdUWtEUJE3YfPXhzHAajwsyUmJtYY9+gffEMj7mQyWa3v8+EPHj771fdnmJSUhAcPHtS4XnmtVatWfue3bt0aBw4cwJUrV1BYWIjOnTsjMTERrVq1QpcuXfzOBSoCTf75z38iIyMD6enpWLt2Lbp164a+fftWjfnyyy+xcuVKpKen49e//jXi4+OhUCjwxhtvVCmHULF//37MmjULo0aNwvvvv4+kpCSoVCp8+umnfiNIG0JdFhDLsrX+3mt7GFm9ejX+8Ic/4IknnsDrr7+O6OhoyOVy/PKXv6y654WwePFiPPfcc/jqq6/wP//zP/jyyy8xaNA
"text/plain": [
"<Figure size 500x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from typing import Callable\n",
"import matplotlib.tri as tri\n",
"\n",
"def plot_results(x_values: Union[List[float], np.ndarray],\n",
" y_values: Union[List[float], np.ndarray],\n",
" z_values: Union[List[float], np.ndarray],\n",
" xlabel: str, \n",
" ylabel: str,\n",
" title: str,\n",
" contour_levels: int = 14,\n",
" figsize: Tuple[int, int] = (10, 10)):\n",
" \"\"\"Plot experiment results\n",
" \n",
" Args:\n",
" results: list of experiment results\n",
" x_values: x list or array\n",
" y_values: y list or array\n",
" z_values: z list or array\n",
" title: plot title\n",
" xlabel: x axis label\n",
" ylabel: y axis label\n",
" contour_levels: number of contour levels\n",
" figsize: figure size\n",
" \"\"\"\n",
" \n",
" plt.figure(figsize=figsize)\n",
" plt.title(title)\n",
" plt.xlabel(xlabel)\n",
" plt.ylabel(ylabel)\n",
" plt.scatter(x_values, y_values, c=z_values, cmap='viridis')\n",
" plt.colorbar()\n",
"\n",
" # create triangulation\n",
" triang = tri.Triangulation(x_values, y_values)\n",
"\n",
" # interpolate data\n",
" interpolator = tri.LinearTriInterpolator(triang, z_values)\n",
" xi = np.linspace(min(x_values), max(x_values), 100)\n",
" yi = np.linspace(min(y_values), max(y_values), 100)\n",
" Xi, Yi = np.meshgrid(xi, yi)\n",
" zi = interpolator(Xi, Yi)\n",
"\n",
" # plot contour\n",
" plt.contour(xi, yi, zi, colors='k', levels=contour_levels, linewidths=0.5, alpha=0.5)\n",
" plt.contourf(xi, yi, zi, levels=contour_levels, cmap='viridis', alpha=0.5)\n",
" plt.show()\n",
"\n",
"for m in momentum_candidates:\n",
" x_values = np.log([r['lr'] for r in results if r['momentum'] == m])\n",
" y_values = np.log([r['mini_batch_size'] for r in results if r['momentum'] == m])\n",
" z_values = [r['valid_acc'] for r in results if r['momentum'] == m]\n",
"\n",
" plot_results(\n",
" x_values=x_values,\n",
" y_values=y_values,\n",
" z_values=z_values,\n",
" title=f'momentum {m} valid accuracy',\n",
" xlabel='Logged Learning rate',\n",
" ylabel='Logged mini batch size',\n",
" contour_levels=10,\n",
" figsize=(5, 5))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"위 셀을 실행하면 다음과 같은 결과가 나옵니다.\n",
"\n",
"![batch_lr_graph](./llr_lbs_m0_v_acc.png)\n",
"![batch_lr_graph](./llr_lbs_m5_v_acc.png)\n",
"![batch_lr_graph](./llr_lbs_m9_v_acc.png)\n",
"\n",
"이 그래프를 보면 알 수 있듯이 learning rate와 mini-batch size는 서로 비례 관계에 있습니다. learning rate가 커지면 mini-batch size도 커져야 좋은 결과를 얻을 수 있습니다. 그리고 momentum은 batch size를 크게 해도 성능이 떨어지지 않게 해주는 것을 알 수 있습니다. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"65, 0.01, 0.5의 조합이 가장 최적으로 보입니다."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3. 신경망 모델 구조 변경: Momentum 을 0.9로 고정시킨 상태에서 신경망의 hidden unit 들의 갯수를 2 에서 100 사이의 3 가지 다른 경우에 대해 성능을 비교한다. 필요한 경우 learning rate 와 학습 기간(epochs)은 신경망 구조에 따라 적당하게 변경할 수 있다. Hidden unit 의 갯수들이 학습에서의 수렴과 신경망의 일반화 성는에 미치는 영향에 대한 데이터(표나 그래프)를 제시하고 경향을 분석하시오."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 실험 코드"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"conf = Config(\n",
" num_inputs=2304,\n",
" num_hiddens=[16, 32],\n",
" num_outputs=7,\n",
" eps=0.01,\n",
" momentum=0.9,\n",
" num_epochs=1000,\n",
" batch_size=241,\n",
" early_stopping=True,\n",
" patience=50,\n",
")\n",
"\n",
"num_hidden_candidates = [2, 4, 8, 16, 32, 64, 100]\n",
"hidden_candidates = itertools.product(num_hidden_candidates, num_hidden_candidates)\n",
"\n",
"experiments_list = load_experiment_metafile('experiments_hidden.json',\n",
" init_task_if_not_exists=hidden_candidates)\n",
"\n",
"while len(experiments_list) > 0:\n",
" num_hiddens = experiments_list[\"remain_experiments\"].pop()\n",
"\n",
" print(f\"Running experiment with {num_hiddens} hidden units\")\n",
" conf.num_hiddens = num_hiddens\n",
"\n",
" save_dir = f\"results_hidden/{num_hiddens[0]}_{num_hiddens[1]}\"\n",
" _, stat, _ = ExperimentMLP(conf, title=f\"hidden {num_hiddens}\", show=False, \n",
" save_dir=save_dir)\n",
" \n",
" i, best_valid_acc = stat.best_valid_acc()\n",
" experiments_list[\"completed_experiment_results\"].append({\n",
" \"num_hiddens\": num_hiddens,\n",
" \"save_dir\": save_dir,\n",
" \"test_acc\": stat.test_acc,\n",
" \"test_ce\": stat.test_ce,\n",
" \"train_acc\": stat.train_acc[i][1],\n",
" \"train_ce\": stat.train_ce[i][1],\n",
" \"valid_acc\": best_valid_acc,\n",
" \"valid_ce\": stat.valid_ce[i][1],\n",
" \"time\": stat.time\n",
" })\n",
" save_experiment_metafile('experiments_hidden.json', experiments_list)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.10.2 ('hw3': venv)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.2"
},
"vscode": {
"interpreter": {
"hash": "82fd07bec16cb4479257adc108d4dc98de3f270fc95dcdba0cb0fb16f10a7c36"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}