diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 000000000..539c91022 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,36 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python application + +on: + push: + branches: [ develop ] + pull_request: + branches: [ develop ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with unittest + run: | + python -m unittest discover diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 90d2dd1aa..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "python.pythonPath": "C:\\Users\\bmidt\\AppData\\Local\\Programs\\Python\\Python37\\python.exe", - "restructuredtext.confPath": "${workspaceFolder}\\docs\\source" -} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..795fdb722 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Soft Matter Lab + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 49c085ece..23f3eb7e0 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Everybody learns in different ways! Depending on your preferences, and what you ### Fundamentals -First, we have a very general walkthrough of [basic](https://softmatterlab.github.io/DeepTrack-2.0/basics.html) and [advanced](https://softmatterlab.github.io/DeepTrack-2.0/advanced.html) topics. This is a 5-10 minute read, that well get you well on your way to understand the unique interactions available in DeepTrack. +First, we have a very general walkthrough of [basic](https://softmatterlab.github.io/DeepTrack-2.0/basics.html) and [advanced](https://softmatterlab.github.io/DeepTrack-2.0/advanced.html) topics. This is a 5-10 minute read, that will get you well on your way to understand the unique interactions available in DeepTrack. ### DeepTrack 2.0 in action @@ -47,7 +47,45 @@ Additionally, we have seven more case studies which are less documented, but giv ### Video Tutorials -[TBA] +DeepTrack 2.0 introduction tutorial video: https://youtu.be/hyfaxF8q6VE + + +DeepTrack 2.0 recognizing handwritten digits tutorial video: https://youtu.be/QD9JUXyLJpc + + +DeepTrack 2.0 single particle tracking tutorial video: https://youtu.be/6Cntik6AfBI + + +DeepTrack 2.0 single-particle characterization tutorial video: https://youtu.be/ia2H1QO1cHg + + +DeepTrack 2.0 multiple particle tracking tutorial video: https://youtu.be/wFV2VqzpeZs + + +DeepTrack 2.0 multiple particle tracking in 3D tutorial video: https://youtu.be/fzD1QIEIJ04 + + +DeepTrack 2.0 cell counting tutorial video: https://youtu.be/C6hu_IYoWtI + + +DeepTrack 2.0 GAN image generation tutorial video: https://youtu.be/8g44Yks7cis + ### In-depth dives @@ -83,7 +121,7 @@ The detailed documentation of DeepTrack 2.0 is available at the following link: If you use DeepTrack 2.0 in your project, please cite us here: - Benjamin Midtvedt, Saga Helgadottir, Aykut Argun, Jesús Pineda, Daniel Midtvedt, Giovanni Volpe. "Quantitative Digital Microscopy with Deep Learning." [arXiv:2010.08260](https://arxiv.org/abs/2010.08260) + Benjamin Midtvedt, Saga Helgadottir, Aykut Argun, Jesús Pineda, Daniel Midtvedt, Giovanni Volpe. "Quantitative Digital Microscopy with Deep Learning." [https://doi.org/10.1063/5.0034891](https://aip.scitation.org/doi/10.1063/5.0034891) See also: diff --git a/_src/source/augmentations.rst b/_src/source/augmentations.rst index e37c74a9d..6d007ab44 100644 --- a/_src/source/augmentations.rst +++ b/_src/source/augmentations.rst @@ -69,6 +69,13 @@ Pad :members: :exclude-members: get +PadToMultiplesOf +^^^^^^^^^^^^^^^^ + +.. autoclass:: deeptrack.augmentations.PadToMultiplesOf + :members: + :exclude-members: get + PreLoad ^^^^^^^ diff --git a/_src/source/conf.py b/_src/source/conf.py index 2abc60731..ac7a20ce8 100644 --- a/_src/source/conf.py +++ b/_src/source/conf.py @@ -31,7 +31,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["pydata_sphinx_theme", "sphinx.ext.autodoc", "numpydoc"] +extensions = ["sphinx.ext.autodoc", "numpydoc"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -57,3 +57,4 @@ html_static_path = ["_static"] master_doc = "index" +autodoc_typehints = "none" diff --git a/_src/source/deeptrack.rst b/_src/source/deeptrack.rst index f98b9cdd3..b804e53ed 100644 --- a/_src/source/deeptrack.rst +++ b/_src/source/deeptrack.rst @@ -19,4 +19,6 @@ Documentation properties scatterers sequences + test + types utils diff --git a/_src/source/features.rst b/_src/source/features.rst index d989b39f1..07b9d6aba 100644 --- a/_src/source/features.rst +++ b/_src/source/features.rst @@ -6,6 +6,34 @@ features Module classes <<<<<<<<<<<<<< +AsType +^^^^^^ + +.. autoclass:: deeptrack.features.AsType + :members: + :exclude-members: get + +Bind +^^^^ + +.. autoclass:: deeptrack.features.Bind + :members: + :exclude-members: get + +BindResolve +^^^^^^^^^^^ + +.. autoclass:: deeptrack.features.BindResolve + :members: + :exclude-members: get + +BindUpdate +^^^^^^^^^^ + +.. autoclass:: deeptrack.features.BindUpdate + :members: + :exclude-members: get + Branch ^^^^^^ @@ -104,6 +132,13 @@ SampleToMasks :members: :exclude-members: get +Slice +^^^^^ + +.. autoclass:: deeptrack.features.Slice + :members: + :exclude-members: get + StructuralFeature ^^^^^^^^^^^^^^^^^ diff --git a/_src/source/generators.rst b/_src/source/generators.rst index 935df8b84..2263a3623 100644 --- a/_src/source/generators.rst +++ b/_src/source/generators.rst @@ -6,6 +6,13 @@ generators Module classes <<<<<<<<<<<<<< +CappedContinuousGenerator +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: deeptrack.generators.CappedContinuousGenerator + :members: + :exclude-members: get + ContinuousGenerator ^^^^^^^^^^^^^^^^^^^ diff --git a/_src/source/test.rst b/_src/source/test.rst new file mode 100644 index 000000000..af1a1cd1d --- /dev/null +++ b/_src/source/test.rst @@ -0,0 +1,5 @@ +test +==== + +.. automodule:: deeptrack.test + diff --git a/_src/source/types.rst b/_src/source/types.rst new file mode 100644 index 000000000..9b7a6803d --- /dev/null +++ b/_src/source/types.rst @@ -0,0 +1,5 @@ +types +===== + +.. automodule:: deeptrack.types + diff --git a/deeptrack/__init__.py b/deeptrack/__init__.py index de40a84c7..c8bab984d 100644 --- a/deeptrack/__init__.py +++ b/deeptrack/__init__.py @@ -7,4 +7,5 @@ from .scatterers import * from .sequences import * -from . import image, losses, generators, models, utils, layers, backend \ No newline at end of file + +from . import image, losses, generators, models, utils, layers, backend, test \ No newline at end of file diff --git a/deeptrack/aberrations.py b/deeptrack/aberrations.py index 7a9ef09c8..3e4648ae2 100644 --- a/deeptrack/aberrations.py +++ b/deeptrack/aberrations.py @@ -32,9 +32,12 @@ Zernike polynomial with n=4, m=0. """ +from typing import List, Tuple + import numpy as np -from deeptrack.features import Feature -from deeptrack.utils import as_list +from .types import PropertyLike +from .features import Feature +from .utils import as_list class Aberration(Feature): @@ -53,7 +56,7 @@ def _process_and_get(self, image_list, **kwargs): for image in image_list: x = np.arange(image.shape[0]) - image.shape[0] / 2 y = np.arange(image.shape[1]) - image.shape[1] / 2 - X, Y = np.meshgrid(x, y) + X, Y = np.meshgrid(y, x) rho = np.sqrt(X ** 2 + Y ** 2) rho /= np.max(rho[image != 0]) theta = np.arctan2(Y, X) @@ -83,7 +86,12 @@ class GaussianApodization(Aberration): """ - def __init__(self, sigma=1, offset=(0, 0), **kwargs): + def __init__( + self, + sigma: PropertyLike[float] = 1, + offset: PropertyLike[Tuple[int, int]] = (0, 0), + **kwargs + ): super().__init__(sigma=sigma, offset=offset, **kwargs) def get(self, pupil, offset, sigma, rho, **kwargs): @@ -121,7 +129,13 @@ class Zernike(Aberration): The coefficient of the polynomial """ - def __init__(self, n, m, coefficient=1, **kwargs): + def __init__( + self, + n: PropertyLike[int or List[int]], + m: PropertyLike[int or List[int]], + coefficient: PropertyLike[float or List[float]] = 1, + **kwargs + ): super().__init__(n=n, m=m, coefficient=coefficient, **kwargs) def get(self, pupil, rho, theta, n, m, coefficient, **kwargs): @@ -186,7 +200,9 @@ class Piston(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=0, m=0, coefficient=coefficient, **kwargs) @@ -199,7 +215,9 @@ class VerticalTilt(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=1, m=-1, coefficient=coefficient, **kwargs) @@ -212,7 +230,9 @@ class HorizontalTilt(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=1, m=1, coefficient=coefficient, **kwargs) @@ -225,7 +245,9 @@ class ObliqueAstigmatism(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=2, m=-2, coefficient=coefficient, **kwargs) @@ -238,7 +260,9 @@ class Defocus(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=2, m=0, coefficient=coefficient, **kwargs) @@ -251,7 +275,9 @@ class Astigmatism(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=2, m=2, coefficient=coefficient, **kwargs) @@ -264,7 +290,9 @@ class ObliqueTrefoil(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=3, m=-3, coefficient=coefficient, **kwargs) @@ -277,7 +305,9 @@ class VerticalComa(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=3, m=-1, coefficient=coefficient, **kwargs) @@ -290,7 +320,9 @@ class HorizontalComa(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=3, m=1, coefficient=coefficient, **kwargs) @@ -303,7 +335,9 @@ class Trefoil(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=3, m=3, coefficient=coefficient, **kwargs) @@ -316,5 +350,7 @@ class SphericalAberration(Zernike): The coefficient of the polynomial """ - def __init__(self, *args, coefficient=1, **kwargs): + def __init__( + self, *args, coefficient: PropertyLike[float or List[float]] = 1, **kwargs + ): super().__init__(*args, n=4, m=0, coefficient=coefficient, **kwargs) diff --git a/deeptrack/augmentations.py b/deeptrack/augmentations.py index ad0124571..35169e944 100644 --- a/deeptrack/augmentations.py +++ b/deeptrack/augmentations.py @@ -19,12 +19,17 @@ Flips images diagonally. """ -from deeptrack.features import Feature -from deeptrack.image import Image +from .features import Feature +from .image import Image +from . import utils +from .types import ArrayLike, PropertyLike + import numpy as np -from typing import Callable +import scipy.ndimage as ndimage from scipy.ndimage.interpolation import map_coordinates from scipy.ndimage.filters import gaussian_filter + +from typing import Callable import warnings @@ -66,13 +71,13 @@ class Augmentation(Feature): def __init__( self, feature: Feature = None, - load_size: int = 1, - updates_per_reload: int = 1, + load_size: PropertyLike[int] = 1, + updates_per_reload: PropertyLike[int] = 1, update_properties: Callable or None = None, **kwargs ): - if load_size is not 1: + if load_size != 1: warnings.warn( "Using an augmentation with a load size other than one is no longer supported", DeprecationWarning, @@ -115,6 +120,7 @@ def _process_and_get(self, *args, update_properties=None, **kwargs): not hasattr(self, "cache") or kwargs["update_tally"] - self.last_update >= kwargs["updates_per_reload"] ): + if isinstance(self.feature, list): self.cache = [feature.resolve() for feature in self.feature] else: @@ -151,11 +157,15 @@ def _process_and_get(self, *args, update_properties=None, **kwargs): ] ) else: - new_list_of_lists.append( - Image(self.get(Image(image_list), **kwargs)).merge_properties_from( - image_list - ) - ) + # DANGEROUS + # if not isinstance(image_list, Image): + image_list = Image(image_list) + + output = self.get(image_list, **kwargs) + + if not isinstance(output, Image): + output = Image(output) + new_list_of_lists.append(output.merge_properties_from(image_list)) if update_properties: if not isinstance(new_list_of_lists, list): @@ -224,13 +234,9 @@ def update_properties(self, image, number_of_updates, **kwargs): if number_of_updates % 2: for prop in image.properties: if "position" in prop: - position = prop["position"] - new_position = ( - position[0], - image.shape[1] - position[1] - 1, - *position[2:], - ) - prop["position"] = new_position + position = np.array(prop["position"]) + position[..., 1] = image.shape[1] - position[..., 1] + prop["position"] = position class FlipUD(Augmentation): @@ -251,9 +257,9 @@ def update_properties(self, image, number_of_updates, **kwargs): if number_of_updates % 2: for prop in image.properties: if "position" in prop: - position = prop["position"] - new_position = (image.shape[0] - position[0] - 1, *position[1:]) - prop["position"] = new_position + position = np.array(prop["position"]) + position[..., 0] = image.shape[1] - position[..., 0] + prop["position"] = position class FlipDiagonal(Augmentation): @@ -274,16 +280,11 @@ def update_properties(self, image, number_of_updates, **kwargs): if number_of_updates % 2: for prop in image.properties: if "position" in prop: - position = prop["position"] - new_position = (position[1], position[0], *position[2:]) - prop["position"] = new_position - - -from deeptrack.utils import get_kwarg_names -import warnings - -import scipy.ndimage as ndimage -import deeptrack.utils as utils + position = np.array(prop["position"]) + t = np.array(position[..., 0]) + position[..., 0] = position[..., 1] + position[..., 1] = t + prop["position"] = position class Affine(Augmentation): @@ -336,14 +337,14 @@ class Affine(Augmentation): def __init__( self, - scale=1, - translate=None, - translate_px=0, - rotate=0, - shear=0, - order=1, - cval=0, - mode="reflect", + scale: PropertyLike[float or ArrayLike[float]] = 1, + translate: PropertyLike[float or ArrayLike[float] or None] = None, + translate_px: PropertyLike[float or ArrayLike[float]] = 0, + rotate: PropertyLike[float or ArrayLike[float]] = 0, + shear: PropertyLike[float or ArrayLike[float]] = 0, + order: PropertyLike[int] = 1, + cval: PropertyLike[float] = 0, + mode: PropertyLike[str] = "reflect", **kwargs ): if translate is None: @@ -386,7 +387,9 @@ def get(self, image, scale, translate, rotate, shear, **kwargs): assert ( image.ndim == 2 or image.ndim == 3 - ), "Affine only supports 2-dimensional or 3-dimension inputs." + ), "Affine only supports 2-dimensional or 3-dimension inputs, got {0}".format( + image.ndim + ) dx, dy = translate fx, fy = scale @@ -442,18 +445,21 @@ def get(self, image, scale, translate, rotate, shear, **kwargs): for prop in image.properties: if "position" in prop: position = np.array(prop["position"]) - prop["position"] = np.array( - ( - *( - ( - inverse_mapping - @ (position[:2] - center + np.array([dy, dx])) - + center - ) - ), - *position[3:], + + inverted = ( + np.dot( + inverse_mapping, + (position[..., :2] - center + np.array([dy, dx]))[ + ..., np.newaxis + ], ) - ) + .squeeze() + .transpose() + ) + center + + position[..., :2] = inverted + + prop["position"] = position return image @@ -519,12 +525,12 @@ class ElasticTransformation(Augmentation): def __init__( self, - alpha=20, - sigma=2, - ignore_last_dim=True, - order=3, - cval=0, - mode="constant", + alpha: PropertyLike[float] = 20, + sigma: PropertyLike[float] = 2, + ignore_last_dim: PropertyLike[bool] = True, + order: PropertyLike[int] = 3, + cval: PropertyLike[float] = 0, + mode: PropertyLike[str] = "constant", **kwargs ): super().__init__( @@ -551,7 +557,10 @@ def get(self, image, sigma, alpha, ignore_last_dim, **kwargs): for dim in shape: deltas.append( gaussian_filter( - (np.random.rand(*shape) * 2 - 1), sigma, mode="constant", cval=0 + (np.random.rand(*shape) * 2 - 1), + sigma, + mode="constant", + cval=0, ) * alpha ) @@ -595,9 +604,10 @@ class Crop(Augmentation): Number of pixels to remove or retain (depending in `crop_mode`) If a tuple or list, it is assumed to be per axis. Can also be a function that returns any of the other types. - crop_mode : str {"remove", "retain"} + crop_mode : str {"retain", "remove"} How the `crop` argument is interpreted. If "remove", then - crop + `crop` denotes the amount to crop from the edges. If "retain", + `crop` denotes the size of the output. corner : tuple of ints or Callable[Image]->tuple of ints or "random" Top left corner of the cropped region. Can be a tuple of ints, a function that returns a tuple of ints or the string random. @@ -607,7 +617,12 @@ class Crop(Augmentation): """ def __init__( - self, *args, crop=(64, 64), crop_mode="retain", corner="random", **kwargs + self, + *args, + crop: PropertyLike[int or ArrayLike[int]] = (64, 64), + crop_mode: PropertyLike[str] = "retain", + corner: PropertyLike[str] = "random", + **kwargs ): super().__init__(*args, crop=crop, crop_mode=crop_mode, corner=corner, **kwargs) @@ -619,6 +634,8 @@ def get(self, image, corner, crop, crop_mode, **kwargs): if isinstance(crop, int): crop = (crop,) * image.ndim + crop = [c if c is not None else image.shape[i] for i, c in enumerate(crop)] + # Get amount to crop from image if crop_mode == "retain": crop_amount = np.array(image.shape) - np.array(crop) @@ -631,12 +648,9 @@ def get(self, image, corner, crop, crop_mode, **kwargs): crop_amount = np.amax((np.array(crop_amount), [0] * image.ndim), axis=0) crop_amount = np.amin((np.array(image.shape) - 1, crop_amount), axis=0) # Get corner of crop - if corner == "random": + if isinstance(corner, str) and corner == "random": # Ensure seed is consistent - slice_start = np.random.randint( - [0] * crop_amount.size, - crop_amount + 1, - ) + slice_start = [np.random.randint(m + 1) for m in crop_amount] elif callable(corner): slice_start = corner(image) else: @@ -654,6 +668,7 @@ def get(self, image, corner, crop, crop_mode, **kwargs): for slice_start_i, slice_end_i in zip(slice_start, slice_end) ] ) + cropped_image = image[slices] # Update positions @@ -662,7 +677,7 @@ def get(self, image, corner, crop, crop_mode, **kwargs): if "position" in prop: position = np.array(prop["position"]) try: - position[0:2] -= np.array(slice_start)[0:2] + position[..., 0:2] -= np.array(slice_start)[0:2] prop["position"] = position except IndexError: pass @@ -682,7 +697,12 @@ class CropToMultiplesOf(Crop): """ - def __init__(self, multiple=1, corner="random", **kwargs): + def __init__( + self, + multiple: PropertyLike[int or ArrayLike[int] or None] = 1, + corner: PropertyLike[str] = "random", + **kwargs + ): kwargs.pop("crop", False) kwargs.pop("crop_mode", False) @@ -695,7 +715,7 @@ def image_to_crop(image): new_shape = list(shape) idx = 0 for dim, mul in zip(shape, multiple): - if mul is not None and mul is not -1: + if mul is not None and mul != -1: new_shape[idx] = int((dim // mul) * mul) idx += 1 @@ -723,21 +743,86 @@ class Pad(Augmentation): """ - def __init__(self, px=(0, 0, 0, 0), mode="constant", cval=0, **kwargs): + def __init__( + self, + px: PropertyLike[int or ArrayLike[int]] = (0, 0, 0, 0), + mode: PropertyLike[str] = "constant", + cval: PropertyLike[float] = 0, + **kwargs + ): super().__init__(px=px, mode=mode, cval=cval, **kwargs) def get(self, image, px, **kwargs): padding = [] - if isinstance(px, int): + if callable(px): + px = px(image) + elif isinstance(px, int): padding = [(px, px)] * image.ndom + for idx in range(0, len(px), 2): padding.append((px[idx], px[idx + 1])) while len(padding) < image.ndim: padding.append((0, 0)) - return utils.safe_call(np.pad, positional_args=(image, padding), **kwargs) + return ( + utils.safe_call(np.pad, positional_args=(image, padding), **kwargs), + padding, + ) + + def _process_and_get(self, images, **kwargs): + results = [self.get(image, **kwargs) for image in images] + for idx, result in enumerate(results): + if isinstance(result, tuple): + shape = result[0].shape + padding = result[1] + de_pad = tuple( + slice(p[0], shape[dim] - p[1]) for dim, p in enumerate(padding) + ) + results[idx] = ( + Image(result[0]).merge_properties_from(images[idx]), + {"undo_padding": de_pad}, + ) + else: + Image(results[idx]).merge_properties_from(images[idx]) + return results + + +class PadToMultiplesOf(Pad): + """Pad images until their height/width is a multiple of a value. + + Parameters + ---------- + multiple : int or tuple of (int or None) + Images will be padded until their width is a multiple of + this value. If a tuple, it is assumed to be a multiple per axis. + A value of None or -1 indicates to skip that axis. + + """ + + def __init__(self, multiple: PropertyLike[int or None] = 1, **kwargs): + def amount_to_pad(image): + shape = image.shape + multiple = self.multiple.current_value + + if not isinstance(multiple, (list, tuple, np.ndarray)): + multiple = (multiple,) * image.ndim + new_shape = [0] * (image.ndim * 2) + idx = 0 + for dim, mul in zip(shape, multiple): + if mul is not None and mul != -1: + to_add = -dim % mul + to_add_first = to_add // 2 + to_add_after = to_add - to_add_first + new_shape[idx * 2] = to_add_first + new_shape[idx * 2 + 1] = to_add_after + + idx += 1 + + return new_shape + + super().__init__(multiple=multiple, px=lambda: amount_to_pad, **kwargs) -# TODO: add resizing by rescaling \ No newline at end of file +# TODO: add resizing by rescaling diff --git a/deeptrack/backend/mie.py b/deeptrack/backend/mie.py index c3107645e..03f9064fa 100644 --- a/deeptrack/backend/mie.py +++ b/deeptrack/backend/mie.py @@ -4,7 +4,7 @@ import numpy as np from typing import Tuple -from deeptrack.backend import ( +from . import ( ricbesh, ricbesy, ricbesj, diff --git a/deeptrack/features.py b/deeptrack/features.py index 03e6df5ec..00c7ad6b5 100644 --- a/deeptrack/features.py +++ b/deeptrack/features.py @@ -9,28 +9,27 @@ StructuralFeature Abstract extension of feature for interactions between features. Branch - Implementation of `StructuralFeature` that resolves two features + Implementation of `StructuralFeature` that resolves two features sequentially. Probability - Implementation of `StructuralFeature` that randomly resolves a feature + Implementation of `StructuralFeature` that randomly resolves a feature with a certain probability. Duplicate - Implementation of `StructuralFeature` that sequentially resolves an + Implementation of `StructuralFeature` that sequentially resolves an integer number of deep-copies of a feature. """ import copy -from abc import ABC, abstractmethod -from typing import List +from typing import Any, Callable, Iterable, Iterator, List, Tuple +import warnings import numpy as np -import time import threading -from deeptrack.image import Image -from deeptrack.properties import Property, PropertyDict -from deeptrack.utils import isiterable, hasmethod, get_kwarg_names, kwarg_has_default +from .image import Image +from .properties import Property, PropertyDict +from .types import PropertyLike, ArrayLike MERGE_STRATEGY_OVERRIDE = 0 @@ -200,8 +199,12 @@ def resolve(self, image_list: Image or List[Image] = None, **global_kwargs): property_verbosity = global_kwargs.get("property_memorability", 1) feature_input["name"] = type(self).__name__ if self.__property_memorability__ <= property_verbosity: - for image in new_list: - image.append(feature_input) + for index, image in enumerate(new_list): + if isinstance(image, tuple): + image[0].append({**feature_input, **image[1]}) + new_list[index] = image[0] + else: + image.append(feature_input) # Merge input and new_list if self.__list_merge_strategy__ == MERGE_STRATEGY_OVERRIDE: @@ -305,11 +308,11 @@ def plot( display(HTML(anim.to_jshtml())) return anim - except NameError as e: + except NameError: # Not in an notebook plt.show() - except RuntimeError as e: + except RuntimeError: # In notebook, but animation failed import ipywidgets as widgets @@ -333,18 +336,29 @@ def _process_and_get(self, image_list, **feature_input) -> List[Image]: if self.__distributed__: # Call get on each image in list, and merge properties from corresponding image - return [ - Image(self.get(image, **feature_input)).merge_properties_from(image) - for image in image_list - ] + + results = [] + + for image in image_list: + output = self.get(image, **feature_input) + if not isinstance(output, Image): + output = Image(output) + + output.merge_properties_from(image) + results.append(output) + + return results + else: # Call get on entire list. new_list = self.get(image_list, **feature_input) if not isinstance(new_list, list): - new_list = [Image(new_list)] + new_list = [new_list] - new_list = [Image(image) for image in new_list] + for idx, image in enumerate(new_list): + if not isinstance(image, Image): + new_list[idx] = Image(image) return new_list def _format_input(self, image_list, **kwargs) -> List[Image]: @@ -382,14 +396,32 @@ def __getattr__(self, key): else: raise AttributeError - def __add__(self, other: "Feature") -> "Feature": - # Overrides add operator + def __rshift__(self, other:"Feature") -> "Feature": + # Overrides >> operator if isinstance(other, list) and all(isinstance(f) for f in other): other = Combine(features=other) if isinstance(other, Feature): return Branch(self, other) + def __xor__(self, other) -> "Feature": + # Duplicate the feature to resolve more items + if isinstance(other, list) and all(isinstance(f) for f in other): + other = Combine(features=other) + + return Duplicate(self, other) + + + def __add__(self, other: "Feature") -> "Feature": + import warnings + warnings.warn( + "Chaining features with the + operator is deprecated, use the >> operator instead", + DeprecationWarning + ) + return self.__rshift__(other) + + + def __radd__(self, other) -> "Feature": # Add when left hand is not a feature # If left hand is falesly, return self @@ -405,6 +437,7 @@ def __radd__(self, other) -> "Feature": return NotImplemented def __mul__(self, other: float) -> "Feature": + warnings.warn("Using * to probabilistically resolve a feature is deprecated. Use `Probability` instead.", DeprecationWarning) # Introduces a probablity of a feature to be resolved. if isinstance(other, list) and all(isinstance(f) for f in other): other = Combine(features=other) @@ -414,12 +447,18 @@ def __mul__(self, other: float) -> "Feature": __rmul__ = __mul__ def __pow__(self, other) -> "Feature": + warnings.warn("Using ** to duplicate a feature is deprecated. Use ^ instead.", DeprecationWarning) # Duplicate the feature to resolve more items - if isinstance(other, list) and all(isinstance(f) for f in other): - other = Combine(features=other) + return self.__xor__(other) - return Duplicate(self, other) + def __getitem__(self, slices) -> "Feature": + # Allows direct slicing of the data. + if not isinstance(slices, tuple): + slices = (slices, ) + slices = list(slices) + + return self + Slice(slices) class StructuralFeature(Feature): """Provides the structure of a feature-set @@ -461,7 +500,9 @@ class Probability(StructuralFeature): Probability to resolve """ - def __init__(self, feature: Feature, probability: float, *args, **kwargs): + def __init__( + self, feature: Feature, probability: PropertyLike[float], *args, **kwargs + ): super().__init__( *args, feature=feature, @@ -498,7 +539,9 @@ class Duplicate(StructuralFeature): The number of duplicates to create """ - def __init__(self, feature: Feature, num_duplicates: int, *args, **kwargs): + def __init__( + self, feature: Feature, num_duplicates: PropertyLike[int], *args, **kwargs + ): self.feature = feature super().__init__( @@ -560,13 +603,126 @@ class Combine(StructuralFeature): __distribute__ = False - def __init__(self, features=[], **kwargs): + def __init__(self, features:List[Feature], **kwargs): super().__init__(features=features, **kwargs) def get(self, image_list, features, **kwargs): return [feature.resolve(image_list, **kwargs) for feature in features] +class Slice(Feature): + ''' Array indexing for each Image in list. + + Note, this feature is rarely needed to be used directly. Instead, + you can do normal array indexing on a feature directly. For example:: + + feature = dt.DummyFeature() + sliced_feature = feature[ + lambda: 0 : lambda: 1, + 1:2, + lambda: slice(None, None, -2) + ] + sliced_feature.resolve(np.arange(27).reshape((3, 3, 3))) + + In the example above, `lambda` is used to demonstrate different ways + to interact with the slices. In this case, the `lambda` keyword is + redundant. + + Using `Slice` directly can be required in some cases, however. For example if + dependencies between properties are required. In this case, one can replicate + the previous example as follows:: + + feature = dt.DummyFeature() + sliced_feature = feature + dt.Slice( + slices=lambda dim1, dim2: (dim1, dim2), + dim1=slice(lambda: 0, lambda: 1, 1), + dim2=slice(1, 2, None), + dim3=lambda: slice(None, None, -2) + ) + sliced_feature.resolve(np.arange(27).reshape((3, 3, 3))) + + Parameters + ---------- + slices : iterable of int, slice or ellipsis + The indexing of each dimension in order. + ''' + + def __init__(self, + slices:PropertyLike[ + Iterable[ + PropertyLike[int] or + PropertyLike[slice] or + PropertyLike[Ellipsis] + ] + ], **kwargs): + super().__init__(slices=slices, **kwargs) + + def get(self, image, slices, **kwargs): + + try: + slices = tuple(slices) + except ValueError: + pass + + return image[slices] + + + +class Bind(StructuralFeature): + """Binds a feature with property arguments. + + When the feature is resolved, the kwarg arguments are passed + to the child feature. + + Parameters + ---------- + feature : Feature + The child feature + **kwargs + Properties to send to child + + """ + + __distributed__ = False + + def __init__(self, feature: Feature, **kwargs): + super().__init__(feature=feature, **kwargs) + + def get(self, image, feature, **kwargs): + return feature.resolve(image, **kwargs) + + +BindResolve = Bind + + +class BindUpdate(StructuralFeature): + """Binds a feature with certain arguments. + + When the feature is updated, the child feature + + Parameters + ---------- + feature : Feature + The child feature + **kwargs + Properties to send to child + + """ + + __distributed__ = False + + def __init__(self, feature: Feature, **kwargs): + self.feature = feature + super().__init__(**kwargs) + + def _update(self, **kwargs): + super()._update(**kwargs) + self.feature._update(**{**kwargs, **self.properties}) + + def get(self, image, **kwargs): + return self.feature.resolve(image, **kwargs) + + class ConditionalSetProperty(StructuralFeature): """Conditionally overrides the properties of child features @@ -583,7 +739,7 @@ class ConditionalSetProperty(StructuralFeature): __distributed__ = False - def __init__(self, feature: Feature, condition="is_label", **kwargs): + def __init__(self, feature: Feature, condition=PropertyLike[str], **kwargs): super().__init__(feature=feature, condition=condition, **kwargs) def get(self, image, feature, condition, **kwargs): @@ -625,7 +781,7 @@ def __init__( self, on_false: Feature = None, on_true: Feature = None, - condition="is_label", + condition: PropertyLike[str] = "is_label", **kwargs ): @@ -661,7 +817,7 @@ class Lambda(Feature): Function that takes the current image as first input """ - def __init__(self, function, **kwargs): + def __init__(self, function: Callable[..., Callable[[Image], Image]], **kwargs): super().__init__(function=function, **kwargs) def get(self, image, function, **kwargs): @@ -684,7 +840,11 @@ class Merge(Feature): __distributed__ = False - def __init__(self, function, **kwargs): + def __init__( + self, + function: Callable[..., Callable[[List[Image]], Image or List[Image]]], + **kwargs + ): super().__init__(function=function, **kwargs) def get(self, list_of_images, function, **kwargs): @@ -709,7 +869,9 @@ class Dataset(Feature): __distributed__ = False - def __init__(self, data, **kwargs): + def __init__( + self, data: Iterator or PropertyLike[float or ArrayLike[float]], **kwargs + ): super().__init__(data=data, **kwargs) def get(self, *ignore, data, **kwargs): @@ -742,7 +904,7 @@ class Label(Feature): __distributed__ = False - def __init__(self, output_shape=None, **kwargs): + def __init__(self, output_shape: PropertyLike[int] = None, **kwargs): super().__init__(output_shape=output_shape, **kwargs) def get(self, image, output_shape=None, hash_key=None, **kwargs): @@ -789,12 +951,12 @@ class LoadImage(Feature): def __init__( self, - path, - load_options=None, - as_list=False, - ndim=None, - to_grayscale=False, - get_one_random=False, + path: PropertyLike[str or List[str]], + load_options: PropertyLike[dict] = None, + as_list: PropertyLike[bool] = False, + ndim: PropertyLike[int] = None, + to_grayscale: PropertyLike[bool] = False, + get_one_random: PropertyLike[bool] = False, **kwargs ): super().__init__( @@ -818,30 +980,32 @@ def get( get_one_random, **kwargs ): + if not isinstance(path, List): + path = [path] if load_options is None: load_options = {} try: - image = np.load(path, **load_options) + image = [np.load(file, **load_options) for file in path] except (IOError, ValueError): try: from skimage import io - image = io.imread(path) + image = [io.imread(file) for file in path] except (IOError, ImportError, AttributeError): try: import PIL.Image - omage = np.array(PIL.Image.open(path, **load_options)) + image = [PIL.Image.open(file, **load_options) for file in path] except (IOError, ImportError): import cv2 - image = np.array(cv2.imread(path, **load_options)) + image = [cv2.imread(file, **load_options) for file in path] if not image: raise IOError( "No filereader available for file {0}".format(path) ) - image = np.squeeze(image) + image = np.stack(image, axis=-1) if to_grayscale: try: @@ -887,31 +1051,31 @@ class SampleToMasks(Feature): Parameters ---------- transformation_function : function - Function that takes an image as input, and outputs another image with `number_of_masks` - layers. + Function that takes an image as input, and outputs another image with `number_of_masks` + layers. number_of_masks : int - The number of masks to create. + The number of masks to create. output_region : (int, int, int, int) - Size and relative position of the mask. Should generally be the same as - `optics.output_region`. + Size and relative position of the mask. Should generally be the same as + `optics.output_region`. merge_method : str or function or list - How to merge the individual masks to a single image. If a list, the merge_metod - is per mask. Can be - * "add": Adds the masks together. - * "overwrite": later masks overwrite earlier masks. - * "or": 1 if either any mask is non-zero at that pixel - * function: a function that accepts two images. The first is the current - value of the output image where a new mask will be places, and - the second is the mask to merge with the output image. + How to merge the individual masks to a single image. If a list, the merge_metod + is per mask. Can be + * "add": Adds the masks together. + * "overwrite": later masks overwrite earlier masks. + * "or": 1 if either any mask is non-zero at that pixel + * function: a function that accepts two images. The first is the current + value of the output image where a new mask will be places, and + the second is the mask to merge with the output image. """ def __init__( self, - transformation_function, - number_of_masks=1, - output_region=None, - merge_method="add", + transformation_function: Callable[..., Callable[[Image], Image]], + number_of_masks: PropertyLike[int] = 1, + output_region: PropertyLike[ArrayLike[int]] = None, + merge_method: PropertyLike[str] = "add", **kwargs ): super().__init__( @@ -974,7 +1138,8 @@ def _process_and_get(self, images, **kwargs): p0 = p0.astype(np.int) output_slice = output[ - p0[0] : p0[0] + labelarg.shape[0], p0[1] : p0[1] + labelarg.shape[1] + p0[0] : p0[0] + labelarg.shape[0], + p0[1] : p0[1] + labelarg.shape[1], ] for label_index in range(kwargs["number_of_masks"]): @@ -1024,7 +1189,8 @@ def _process_and_get(self, images, **kwargs): p0[1] : p0[1] + labelarg.shape[1], label_index, ] = merge( - output_slice[..., label_index], labelarg[..., label_index] + output_slice[..., label_index], + labelarg[..., label_index], ) output = Image(output) for label in list_of_labels: @@ -1038,7 +1204,7 @@ def _get_position(image, mode="corner", return_z=False): if mode == "corner": shift = (np.array(image.shape) - 1) / 2 else: - shift = np.zeros((num_outputs)) + shift = np.zeros((3 if return_z else 2)) positions = image.get_property("position", False, []) @@ -1054,7 +1220,11 @@ def _get_position(image, mode="corner", return_z=False): if return_z: outp = ( np.array( - [position[0], position[1], image.get_property("z", default=0)] + [ + position[0], + position[1], + image.get_property("z", default=0), + ] ) - shift ) @@ -1063,3 +1233,25 @@ def _get_position(image, mode="corner", return_z=False): positions_out.append(position - shift[0:2]) return positions_out + + +class AsType(Feature): + """Converts the data type of images + + Accepts same types as numpy arrays. Common types include + + `float64, int32, uint16, int16, uint8, int8` + + Parameters + ---------- + dtype : str + dtype string. Same as numpy dtype. + """ + + def __init__(self, dtype: PropertyLike[Any] = "float64", **kwargs): + super().__init__(dtype=dtype, **kwargs) + + def get(self, image, dtype, **kwargs): + return image.astype(dtype) + + diff --git a/deeptrack/generators.py b/deeptrack/generators.py index 8c8d4412d..9d50079cf 100644 --- a/deeptrack/generators.py +++ b/deeptrack/generators.py @@ -12,8 +12,8 @@ from typing import List import tensorflow.keras as keras -from deeptrack.features import Feature -from deeptrack.image import Image +from .features import Feature +from .image import Image import threading import random import time @@ -107,10 +107,12 @@ def generate( if sub_batch.ndim > ndim: dims_to_remove = sub_batch.ndim - ndim sub_batch = np.reshape( - sub_batch, (-1, *sub_batch.shape[dims_to_remove + 1 :]) + sub_batch, + (-1, *sub_batch.shape[dims_to_remove + 1 :]), ) sub_labels = np.reshape( - sub_labels, (-1, *sub_labels.shape[dims_to_remove + 1 :]) + sub_labels, + (-1, *sub_labels.shape[dims_to_remove + 1 :]), ) elif sub_batch.ndim < ndim: @@ -199,6 +201,7 @@ def __init__( update_kwargs={}, verbose=1, ndim=4, + max_sample_exposure=np.inf, ): if min_data_size is None: @@ -302,11 +305,11 @@ def __getitem__(self, idx): return outputs def __len__(self): - l = int((len(self.current_data) // self._batch_size)) + steps = int((len(self.current_data) // self._batch_size)) assert ( - l > 0 + steps > 0 ), "There needs to be at least batch_size number of datapoints. Try increasing min_data_size." - return l + return steps def _continuous_get_training_data(self): index = 0 @@ -317,24 +320,34 @@ def _continuous_get_training_data(self): new_image = self._get(self.feature, self.feature_kwargs) - if self.label_function: - new_label = Image(self.label_function(new_image)) + # if self.label_function: + new_label = self.label_function(new_image) if self.batch_function: - new_image = Image(self.batch_function(new_image)) + new_image = self.batch_function(new_image) + new_image = np.array(new_image) if new_image.ndim < self.ndim: new_image = [new_image] new_label = [new_label] for new_image_i, new_label_i in zip(new_image, new_label): + datapoint = self.construct_datapoint(new_image_i, new_label_i) if len(self.data) >= self.max_data_size: - self.data[index % self.max_data_size] = (new_image_i, new_label_i) + self.data[index % self.max_data_size] = datapoint else: - self.data.append((new_image_i, new_label_i)) + self.data.append(datapoint) index += 1 + self.cleanup() + + def construct_datapoint(self, image, label): + return (image, label) + + def cleanup(self): + pass + def _get(self, features: Feature or List[Feature], feature_kwargs) -> Image: # Updates and resolves a feature or list of features. if isinstance(features, list): @@ -352,3 +365,78 @@ def _get(self, features: Feature or List[Feature], feature_kwargs) -> Image: else: features.update() return features.resolve(**feature_kwargs) + + +class CappedContinuousGenerator(ContinuousGenerator): + + """Generator that asynchronously expands the dataset. + + Generator that aims to speed up the training of networks by striking a + balance between the generalization gained by generating new images + and the speed gained from reusing images. The generator will continuously + create new training data during training, until `max_data_size` is reached, + at which point the oldest data point is replaced. + + Unlike the `ContinuousGenerator`, this generator will purge any data that + has been seen more than `max_sample_exposure` times. + + The generator is expected to be used with the python "with" statement, which + ensures that the generator worker is consumed correctly. + + Parameters + ---------- + feature : Feature + The feature to resolve images from. + label_function : Callable[Image or list of Image] -> array_like + Function that returns the label corresponding to a feature output. + batch_function : Callable[Image or list of Image] -> array_like, optional + Function that returns the training data corresponding a feature output. + min_data_size : int + Minimum size of the training data before training starts + max_data_set : int + Maximum size of the training data before old data is replaced. + max_sample_exposure : int + Any sample that has been seen for more than `max_sample_exposure` will be removed from the dataset + batch_size : int or Callable[int, int] -> int + Number of images per batch. A function is expected to accept the current epoch + and the size of the training data as input. + shuffle_batch : bool + If True, the batches are shuffled before outputting. + feature_kwargs : dict or list of dicts + Set of options to pass to the feature when resolving + ndim : int + Number of dimensions of each batch (including the batch dimension). + max_sample_exposure : int + Any sample that has been seen for more than `max_sample_exposure` will be removed from the dataset + """ + + def __init__(self, *args, max_sample_exposure=np.inf, **kwargs): + self.max_sample_exposure = max_sample_exposure + super().__init__(*args, **kwargs) + + def __getitem__(self, idx): + + # TODO: Use parent method + batch_size = self._batch_size + subset = self.current_data[idx * batch_size : (idx + 1) * batch_size] + for a in subset: + a[-1] += 1 + outputs = [np.array(a) for a in list(zip(*subset))] + outputs = (outputs[0], *outputs[1:-1]) + return outputs + + def construct_datapoint(self, image, label): + return [image, label, 0] + + def cleanup(self): + self.data = [ + sample for sample in self.data if sample[-1] < self.max_sample_exposure + ] + + def on_epoch_end(self): + + while len(self.data) < self.min_data_size: + print("Awaiting dataset to reach minimum size...", end="\r") + time.sleep(0.1) + + return super().on_epoch_end() \ No newline at end of file diff --git a/deeptrack/image.py b/deeptrack/image.py index be20fba44..72984ca3a 100644 --- a/deeptrack/image.py +++ b/deeptrack/image.py @@ -176,7 +176,7 @@ def __array_wrap__(self, image_after_function, context=None): for arg in input_args: - if not arg is self and isinstance(arg, Image): + if arg is not self and isinstance(arg, Image): self.merge_properties_from(arg) return image_with_restored_properties diff --git a/deeptrack/layers.py b/deeptrack/layers.py index 5e68ec756..0dbeff3f5 100644 --- a/deeptrack/layers.py +++ b/deeptrack/layers.py @@ -1,17 +1,16 @@ """ Standardized layers implemented in keras. """ -import tensorflow -from tensorflow.keras import layers, activations -from tensorflow.keras.initializers import RandomNormal + +from tensorflow.keras import layers try: from tensorflow_addons.layers import InstanceNormalization -except: +except Exception: import warnings - InstanceNormalization = layers.Layer() + InstanceNormalization = layers.Layer warnings.warn( "DeepTrack not installed with tensorflow addons. Instance normalization will not work. Consider upgrading to tensorflow >= 2.0.", ImportWarning, @@ -29,7 +28,8 @@ def as_block(x): + ", ".join(_string_to_block.keys()) ) if isinstance(x, layers.Layer) or not callable(x): - raise TypeError("Layer block should be a function that returns a keras Layer.") + raise TypeError( + "Layer block should be a function that returns a keras Layer.") else: return x @@ -37,10 +37,12 @@ def as_block(x): def _as_activation(x): if x is None: return layers.Layer() - if isinstance(x, layers.Layer): + elif isinstance(x, str): + return layers.Activation(x) + elif isinstance(x, layers.Layer): return x else: - return layers.Activation(x) + return layers.Layer(x) def _single_layer_call(x, layer, instance_norm, activation): @@ -225,6 +227,7 @@ def StaticUpsampleBlock( kernel_size=(1, 1), strides=1, padding="same", + with_conv=True, **kwargs ): """A single no-trainable 2d deconvolutional layer. @@ -247,21 +250,24 @@ def StaticUpsampleBlock( def Layer(filters, **kwargs_inner): kwargs_inner.update(kwargs) - layer = layers.UpSampling2D( - size=size, interpolation=interpolation, **kwargs_inner - ) + layer = layers.UpSampling2D(size=size, interpolation=interpolation) + conv = layers.Conv2D( filters, kernel_size=kernel_size, strides=strides, padding=padding, + **kwargs_inner ) def call(x): y = layer(x) - return _single_layer_call( - y, conv, _instance_norm(instance_norm, filters), activation - ) + if with_conv: + return _single_layer_call( + y, conv, _instance_norm(instance_norm, filters), activation + ) + else: + return layer(x) return call @@ -359,4 +365,4 @@ def Layer(filters, **kwargs_inner): "deconvolutional": DeconvolutionalBlock(), "none": Identity(), "identity": Identity(), -} \ No newline at end of file +} diff --git a/deeptrack/losses.py b/deeptrack/losses.py index 2a6117af0..ced7f5ddd 100644 --- a/deeptrack/losses.py +++ b/deeptrack/losses.py @@ -2,7 +2,7 @@ Functions --------- -flatten +flatten Flattends the inputs before calling the loss function. sigmoid Adds a signmoid transformation to the prediction before calling the loss function. @@ -117,4 +117,4 @@ def unet_crossentropy(T, P): # Wrap standard keras loss function with flatten. for keras_loss_function in _COMPATIBLE_LOSS_FUNCTIONS: deeptrack_loss_function = flatten(keras_loss_function) - globals()[deeptrack_loss_function.__name__] = deeptrack_loss_function \ No newline at end of file + globals()[deeptrack_loss_function.__name__] = deeptrack_loss_function diff --git a/deeptrack/math.py b/deeptrack/math.py index dfcf2fd5f..70e3ec8bb 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -8,9 +8,15 @@ Min-max image normalization. """ -from deeptrack.features import Feature -from deeptrack.image import Image +from typing import Callable, List +from .features import Feature +from .image import Image +from . import utils +from .types import PropertyLike import numpy as np +import skimage +import skimage.measure +import scipy.ndimage as ndimage class Add(Feature): @@ -22,7 +28,7 @@ class Add(Feature): The value to add """ - def __init__(self, value=0, **kwargs): + def __init__(self, value: PropertyLike[float] = 0, **kwargs): super().__init__(value=value, **kwargs) def get(self, image, value, **kwargs): @@ -38,7 +44,7 @@ class Subtract(Feature): The value to subtract """ - def __init__(self, value=0, **kwargs): + def __init__(self, value: PropertyLike[float] = 0, **kwargs): super().__init__(value=value, **kwargs) def get(self, image, value, **kwargs): @@ -54,7 +60,7 @@ class Multiply(Feature): The value to multiply with. """ - def __init__(self, value=0, **kwargs): + def __init__(self, value: PropertyLike[float] = 0, **kwargs): super().__init__(value=value, **kwargs) def get(self, image, value, **kwargs): @@ -70,7 +76,7 @@ class Divide(Feature): The value to divide with. """ - def __init__(self, value=0, **kwargs): + def __init__(self, value: PropertyLike[float] = 0, **kwargs): super().__init__(value=value, **kwargs) def get(self, image, value, **kwargs): @@ -86,7 +92,7 @@ class Power(Feature): The power to raise with. """ - def __init__(self, value=0, **kwargs): + def __init__(self, value: PropertyLike[float] = 0, **kwargs): super().__init__(value=value, **kwargs) def get(self, image, value, **kwargs): @@ -108,7 +114,12 @@ class Average(Feature): __distributed__ = False - def __init__(self, features=None, axis=0, **kwargs): + def __init__( + self, + features=PropertyLike[List[Feature] or None], + axis: PropertyLike[int] = 0, + **kwargs + ): super().__init__(axis=axis, features=features, **kwargs) def get(self, images, axis, features, **kwargs): @@ -133,7 +144,12 @@ class Clip(Feature): Clip the input to be smaller than this value. """ - def __init__(self, min=-np.inf, max=+np.inf, **kwargs): + def __init__( + self, + min: PropertyLike[float] = -np.inf, + max: PropertyLike[float] = +np.inf, + **kwargs + ): super().__init__(min=min, max=max, **kwargs) def get(self, image, min=None, max=None, **kwargs): @@ -154,7 +170,9 @@ class NormalizeMinMax(Feature): The maximum of the transformation. """ - def __init__(self, min=0, max=1, **kwargs): + def __init__( + self, min: PropertyLike[float] = 0, max: PropertyLike[float] = 1, **kwargs + ): super().__init__(min=min, max=max, **kwargs) def get(self, image, min, max, **kwargs): @@ -164,16 +182,12 @@ def get(self, image, min, max, **kwargs): return image -import deeptrack.utils as utils -import skimage - -import scipy.ndimage as ndimage - - class Blur(Feature): - def __init__(self, filter_function, mode="reflect", **kwargs): + def __init__( + self, filter_function: Callable, mode: PropertyLike[str] = "reflect", **kwargs + ): self.filter = filter_function - super().__init__(borderType=borderType, **kwargs) + super().__init__(borderType=mode, **kwargs) def get(self, image, **kwargs): kwargs.pop("input", False) @@ -191,7 +205,7 @@ class AverageBlur(Blur): Kernel size to use. """ - def __init__(self, ksize=3, **kwargs): + def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): super().__init__(None, ksize=ksize, **kwargs) def get(self, input, ksize, **kwargs): @@ -203,7 +217,7 @@ def get(self, input, ksize, **kwargs): weights = np.ones(ksize) / np.prod(ksize) - return safe_call(ndimage, input=input, weights=weights, **kwargs) + return utils.safe_call(ndimage, input=input, weights=weights, **kwargs) class GaussianBlur(Blur): @@ -216,7 +230,7 @@ class GaussianBlur(Blur): """ - def __init__(self, sigma=2, **kwargs): + def __init__(self, sigma: PropertyLike[float] = 2, **kwargs): super().__init__(ndimage.gaussian_filter, sigma=sigma, **kwargs) @@ -230,13 +244,11 @@ class MedianBlur(Blur): """ - def __init__(self, ksize=3, **kwargs): - super().__init__(ndimage.median_filter, k=k, **kwargs) + def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): + super().__init__(ndimage.median_filter, k=ksize, **kwargs) -## POOLING - -import skimage.measure +# POOLING class Pool(Feature): @@ -257,7 +269,9 @@ class Pool(Feature): Additional parameters sent to the pooling function. """ - def __init__(self, pooling_function, ksize=3, **kwargs): + def __init__( + self, pooling_function: Callable, ksize: PropertyLike[int] = 3, **kwargs + ): self.pooling = pooling_function super().__init__(ksize=ksize, **kwargs) @@ -287,11 +301,11 @@ class AveragePooling(Pool): Additional parameters sent to the pooling function. """ - def __init__(self, ksize=3, **kwargs): + def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): super().__init__(np.mean, ksize=ksize, **kwargs) -### OPENCV2 blur +# OPENCV2 blur try: import cv2 @@ -318,7 +332,9 @@ def __new__(cls, *args, **kwargs): return super(BlurCV2, cls).__new__(*args, **kwargs) - def __init__(self, filter_function, mode="refelct", **kwargs): + def __init__( + self, filter_function: Callable, mode: PropertyLike[str] = "refelct", **kwargs + ): self.filter = filter_function borderType = _map_mode_to_cv2_borderType[mode] super().__init__(borderType=borderType, **kwargs) @@ -356,11 +372,17 @@ class BilateralBlur(Blur): """ - def __init__(self, d=3, sigma_color=50, sigma_space=50, **kwargs): + def __init__( + self, + d: PropertyLike[int] = 3, + sigma_color: PropertyLike[float] = 50, + sigma_space: PropertyLike[float] = 50, + **kwargs + ): super().__init__( cv2.bilateralFilter, d=d, sigma_color=sigma_color, sigma_space=sigma_space, **kwargs - ) \ No newline at end of file + ) diff --git a/deeptrack/models.py b/deeptrack/models.py index 03f2dcee0..5c7df96a9 100644 --- a/deeptrack/models.py +++ b/deeptrack/models.py @@ -2,7 +2,7 @@ Classes ------- -ModelFeature +ModelFeature Base model feature class. Convolutional, convolutional Creates and compiles a convolutional neural network. @@ -12,12 +12,13 @@ Creates and compiles a recurrent neural network. """ -from deeptrack.losses import nd_mean_absolute_error -from deeptrack.features import Feature -from deeptrack.layers import as_block -from tensorflow.keras import models, layers, optimizers +import tensorflow +from .losses import nd_mean_absolute_error +from .features import Feature +from .layers import as_block +from tensorflow.keras import models, layers, backend as K +import tensorflow as tf import numpy as np -import warnings def _compile( @@ -143,7 +144,7 @@ def FullyConnected( dense_block = as_block(dense_block) - ### INITIALIZE DEEP LEARNING NETWORK + # INITIALIZE DEEP LEARNING NETWORK input_layer = layers.Input(shape=input_shape) layer = input_layer @@ -155,7 +156,7 @@ def FullyConnected( range(len(dense_layers_dimensions)), dense_layers_dimensions ): - if dense_layer_number is 0 and not flatten_input: + if dense_layer_number == 0 and not flatten_input: layer = dense_block(dense_layer_dimension, input_shape=input_shape)(layer) else: layer = dense_block(dense_layer_dimension)(layer) @@ -184,6 +185,7 @@ def Convolutional( output_activation=None, output_kernel_size=3, loss=nd_mean_absolute_error, + input_layer=None, convolution_block="convolutional", pooling_block="pooling", dense_block="dense", @@ -232,6 +234,9 @@ def Convolutional( layer = inputs + if input_layer: + layer = input_layer(layer) + ### CONVOLUTIONAL BASIS for conv_layer_dimension in conv_layers_dimensions: @@ -284,6 +289,7 @@ def UNet( output_kernel_size=3, output_activation=None, loss=nd_mean_absolute_error, + input_layer=None, encoder_convolution_block="convolutional", base_convolution_block="convolutional", decoder_convolution_block="convolutional", @@ -340,6 +346,9 @@ def UNet( layer = unet_input + if input_layer: + layer = input_layer(layer) + # Downsampling path for conv_layer_dimension in conv_layers_dimensions: for _ in range(steps_per_pooling): @@ -423,10 +432,10 @@ def RNN( Deep learning network. """ - ### INITIALIZE DEEP LEARNING NETWORK + # INITIALIZE DEEP LEARNING NETWORK network = models.Sequential() - ### CONVOLUTIONAL BASIS + # CONVOLUTIONAL BASIS for conv_layer_number, conv_layer_dimension in zip( range(len(conv_layers_dimensions)), conv_layers_dimensions ): @@ -506,7 +515,7 @@ def RNN( rnn = RNN -class cgan(Model): +class cgan(tf.keras.Model): def __init__( self, generator=None, @@ -517,8 +526,10 @@ def __init__( assemble_loss=None, assemble_optimizer=None, assemble_loss_weights=None, + metrics=[], **kwargs ): + super().__init__() # Build and compile the discriminator self.discriminator = discriminator @@ -545,79 +556,73 @@ def __init__( # The assembled model (stacked generator and discriminator) # Trains the generator to fool the discriminator - self.assemble = models.Model(self.model_input, [validity, img]) + self.assemble = tf.keras.models.Model(self.model_input, [validity, img]) + + self.num_losses = len(assemble_loss) + self.assemble.compile( loss=assemble_loss, optimizer=assemble_optimizer, loss_weights=assemble_loss_weights, + metrics=metrics, + ) + self._metrics = [tf.metrics.get(m) for m in metrics] + + def train_step(self, data): + + # Compute data and labels + batch_x, batch_y = data + gen_imgs = self.generator(batch_x) + + # Train the discriminator + with tf.GradientTape() as tape: + # Train in two steps + disc_pred_1 = self.discriminator([batch_y, batch_x]) + disc_pred_2 = self.discriminator([gen_imgs, batch_x]) + shape = tf.shape(disc_pred_1) + valid, fake = tf.ones(shape), tf.zeros(shape) + d_loss = ( + self.discriminator.compiled_loss(disc_pred_1, valid) + + self.discriminator.compiled_loss(disc_pred_2, fake) + ) / 2 + + # Compute gradient and apply gradient + grads = tape.gradient(d_loss, self.discriminator.trainable_weights) + self.discriminator.optimizer.apply_gradients( + zip(grads, self.discriminator.trainable_weights) ) - super().__init__(self.generator, **kwargs) + # Train the assembly - def fit(self, data_generator, epochs, steps_per_epoch=None, **kwargs): - for key in kwargs.keys(): - warnings.warn( - "{0} not implemented for cgan. Does not affect the execution.".format( - key - ) - ) - for epoch in range(epochs): - if not steps_per_epoch: - try: - steps = len(data_generator) - except: - steps = 1 - else: - steps = steps_per_epoch - - d_loss = 0 - g_loss = 0 - - for step in range(steps): - # update data - try: - data, labels = next(data_generator) - except: - data, labels = data_generator[step] - - # Grab disriminator labels - shape = (data.shape[0], *self.discriminator.output.shape[1:]) - valid, fake = np.ones(shape), np.zeros(shape) - - # --------------------- - # Train Discriminator - # --------------------- - - # Generate a batch of new images - gen_imgs = self.generator(data) - - # Train the discriminator - d_loss_real = self.discriminator.train_on_batch([labels, data], valid) - d_loss_fake = self.discriminator.train_on_batch([gen_imgs, data], fake) - d_loss += 0.5 * np.add(d_loss_real, d_loss_fake) - - # --------------------- - # Train Generator - # --------------------- - - # Train the generator (to have the discriminator label samples as valid) - g_loss += np.array(self.assemble.train_on_batch(data, [valid, labels])) - - # Plot the progress - - try: - data_generator.on_epoch_end() - except: - pass - - print( - "%d [D loss: %f, acc.: %.2f%%] [G loss: %f, %f, %f]" - % ( - epoch, - d_loss[0] / steps, - 100 * d_loss[1] / steps, - g_loss[0] / steps, - g_loss[1] / steps, - g_loss[2] / steps, - ) + with tf.GradientTape() as tape: + assemble_output = self.assemble(batch_x) + + generated_image_copies = [assemble_output[1]] * (self.num_losses - 1) + + batch_y_copies = [batch_y] * (self.num_losses - 1) + + g_loss = self.assemble.compiled_loss( + [assemble_output[0], *generated_image_copies], + [valid, *batch_y_copies], ) + + # Compute gradient and apply gradient + grads = tape.gradient(g_loss, self.assemble.trainable_weights) + self.assemble.optimizer.apply_gradients( + zip(grads, self.assemble.trainable_weights) + ) + + # Update the metrics + self.compiled_metrics.update_state(assemble_output[1], batch_y) + + # Define output + loss = { + "d_loss": d_loss, + "g_loss": g_loss, + **{m.name: m.result() for m in self.metrics}, + } + + return loss + + def call(self, *args, **kwargs): + return self.generator.call(*args, **kwargs) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index d53541a27..b47884985 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -13,8 +13,9 @@ """ import numpy as np -from deeptrack.features import Feature -from deeptrack.image import Image +from .features import Feature +from .image import Image +from .types import PropertyLike class Noise(Feature): @@ -29,7 +30,7 @@ class Background(Noise): The value to add to the image """ - def __init__(self, offset, **kwargs): + def __init__(self, offset: PropertyLike[float], **kwargs): super().__init__(offset=offset, **kwargs) def get(self, image, offset, **kwargs): @@ -51,7 +52,9 @@ class Gaussian(Noise): The root of the variance of the distribution. """ - def __init__(self, mu=0, sigma=1, **kwargs): + def __init__( + self, mu: PropertyLike[float] = 0, sigma: PropertyLike[float] = 1, **kwargs + ): super().__init__(mu=mu, sigma=sigma, **kwargs) def get(self, image, mu, sigma, **kwargs): @@ -73,7 +76,13 @@ class Poisson(Noise): signal of the image. """ - def __init__(self, *args, snr=100, background=0, **kwargs): + def __init__( + self, + *args, + snr: PropertyLike[float] = 100, + background: PropertyLike[float] = 0, + **kwargs + ): super().__init__(*args, snr=snr, background=background, **kwargs) def get(self, image, snr, background, **kwargs): diff --git a/deeptrack/optics.py b/deeptrack/optics.py index f4e970533..249fd24c9 100644 --- a/deeptrack/optics.py +++ b/deeptrack/optics.py @@ -16,10 +16,10 @@ """ import numpy as np -from deeptrack.features import Feature, StructuralFeature -from deeptrack.image import Image, pad_image_to_fft +from .features import Feature, StructuralFeature +from .image import Image, pad_image_to_fft +from .types import ArrayLike, PropertyLike -from scipy.interpolate import RectBivariateSpline from scipy.ndimage import convolve @@ -78,7 +78,13 @@ def get(self, image, sample, objective, **kwargs): if upscale > 1: mean_imaged_sample = np.reshape( imaged_sample, - (shape[0] // upscale, upscale, shape[1] // upscale, upscale, shape[2]), + ( + shape[0] // upscale, + upscale, + shape[1] // upscale, + upscale, + shape[2], + ), ).mean(axis=(3, 1)) imaged_sample = Image(mean_imaged_sample).merge_properties_from( @@ -97,7 +103,10 @@ def get(self, image, sample, objective, **kwargs): def _update(self, **kwargs): self.properties["sample"].update( - **{**kwargs, **self.objective.update(**kwargs).current_value.properties} + **{ + **kwargs, + **self.objective.update(**kwargs).current_value.properties, + } ) super()._update(**kwargs) @@ -138,14 +147,14 @@ class Optics(Feature): def __init__( self, - NA=0.7, - wavelength=0.66e-6, - magnification=10, - resolution=(1e-6, 1e-6, 1e-6), - refractive_index_medium=1.33, - upscale=1, - padding=(10, 10, 10, 10), - output_region=(0, 0, 128, 128), + NA: PropertyLike[float] = 0.7, + wavelength: PropertyLike[float] = 0.66e-6, + magnification: PropertyLike[float] = 10, + resolution: PropertyLike[float or ArrayLike[float]] = (1e-6, 1e-6, 1e-6), + refractive_index_medium: PropertyLike[float] = 1.33, + upscale: PropertyLike[float] = 1, + padding: PropertyLike[ArrayLike[int]] = (10, 10, 10, 10), + output_region: PropertyLike[ArrayLike[int]] = (0, 0, 128, 128), pupil: Feature = None, **kwargs ): @@ -282,11 +291,15 @@ def _pad_volume( new_limits[i, :] = ( np.min([new_limits[i, 0], upscaled_output_region[i] - padding[1]]), np.max( - [new_limits[i, 1], upscaled_output_region[i + 2] + padding[i + 2]] + [ + new_limits[i, 1], + upscaled_output_region[i + 2] + padding[i + 2], + ] ), ) new_volume = np.zeros( - np.diff(new_limits, axis=1)[:, 0].astype(np.int32), dtype=np.complex + np.diff(new_limits, axis=1)[:, 0].astype(np.int32), + dtype=np.complex, ) old_region = (limits - new_limits).astype(np.int32) @@ -370,7 +383,9 @@ def get(self, illuminated_volume, limits, **kwargs): ) padded_volume = padded_volume[ - output_region[0] : output_region[2], output_region[1] : output_region[3], : + output_region[0] : output_region[2], + output_region[1] : output_region[3], + :, ] z_limits = limits[2, :] @@ -380,7 +395,10 @@ def get(self, illuminated_volume, limits, **kwargs): # Get planes in volume where not all values are 0. z_iterator = np.linspace( - z_limits[0], z_limits[1], num=padded_volume.shape[2], endpoint=False + z_limits[0], + z_limits[1], + num=padded_volume.shape[2], + endpoint=False, ) zero_plane = np.all(padded_volume == 0, axis=(0, 1), keepdims=False) z_values = z_iterator[~zero_plane] @@ -496,7 +514,9 @@ def get(self, illuminated_volume, limits, fields, **kwargs): ) padded_volume = padded_volume[ - output_region[0] : output_region[2], output_region[1] : output_region[3], : + output_region[0] : output_region[2], + output_region[1] : output_region[3], + :, ] z_limits = limits[2, :] @@ -504,7 +524,10 @@ def get(self, illuminated_volume, limits, fields, **kwargs): index_iterator = range(padded_volume.shape[2]) z_iterator = np.linspace( - z_limits[0], z_limits[1], num=padded_volume.shape[2], endpoint=False + z_limits[0], + z_limits[1], + num=padded_volume.shape[2], + endpoint=False, ) zero_plane = np.all(padded_volume == 0, axis=(0, 1), keepdims=False) @@ -629,7 +652,14 @@ class IlluminationGradient(Feature): """ - def __init__(self, gradient=(0, 0), constant=0, vmin=0, vmax=np.inf, **kwargs): + def __init__( + self, + gradient: PropertyLike[ArrayLike[float]] = (0, 0), + constant: PropertyLike[float] = 0, + vmin: PropertyLike[float] = 0, + vmax: PropertyLike[float] = np.inf, + **kwargs + ): super().__init__( gradient=gradient, constant=constant, vmin=vmin, vmax=vmax, **kwargs ) @@ -766,7 +796,12 @@ def _create_volume( continue padded_scatterer = Image( - np.pad(scatterer, [(2, 2), (2, 2), (2, 2)], "constant", constant_values=0) + np.pad( + scatterer, + [(2, 2), (2, 2), (2, 2)], + "constant", + constant_values=0, + ) ) padded_scatterer.properties = scatterer.properties scatterer = padded_scatterer @@ -815,7 +850,8 @@ def _create_volume( if not (np.array(new_limits) == np.array(limits)).all(): new_volume = np.zeros( - np.diff(new_limits, axis=1)[:, 0].astype(np.int32), dtype=np.complex + np.diff(new_limits, axis=1)[:, 0].astype(np.int32), + dtype=np.complex, ) old_region = (limits - new_limits).astype(np.int32) limits = limits.astype(np.int32) diff --git a/deeptrack/properties.py b/deeptrack/properties.py index 300308c90..b4edd94c9 100644 --- a/deeptrack/properties.py +++ b/deeptrack/properties.py @@ -18,9 +18,19 @@ """ import numpy as np -from deeptrack.utils import isiterable, hasmethod, get_kwarg_names, kwarg_has_default -import deeptrack +from .utils import ( + isiterable, + get_kwarg_names, + kwarg_has_default, +) + +import typing + + +from . import features + import copy +import collections class Property: @@ -72,11 +82,11 @@ def current_value(self): @current_value.setter def current_value(self, updated_current_value): self._current_value = updated_current_value - if id(self) not in deeptrack.UPDATE_MEMO["memoization"]: + if id(self) not in features.UPDATE_MEMO["memoization"]: # Some values work, some don't. self, updated_current_value and self._current_value work # Best guess is an error in the gc reference counter causing it to dereference # But then again, I don't think it should be the same reference anyway - deeptrack.UPDATE_MEMO["memoization"][id(self)] = updated_current_value + features.UPDATE_MEMO["memoization"][id(self)] = updated_current_value @current_value.getter def current_value(self): @@ -109,15 +119,15 @@ def update(self, **kwargs) -> "Property": # a = 1+1 if ( - deeptrack.UPDATE_LOCK.locked() - and my_id in deeptrack.UPDATE_MEMO["memoization"] + features.UPDATE_LOCK.locked() + and my_id in features.UPDATE_MEMO["memoization"] ): return self if self.parent: kwargs.update(self.parent) - kwargs.update(deeptrack.UPDATE_MEMO["user_arguments"]) + kwargs.update(features.UPDATE_MEMO["user_arguments"]) self.current_value = self.sample(self.sampling_rule, **kwargs) return self @@ -155,7 +165,7 @@ def sample(self, sampling_rule, **kwargs): """ - if isinstance(sampling_rule, deeptrack.Feature): + if isinstance(sampling_rule, features.Feature): # I am worried passing kwargs may lead to name clash sampling_rule._update(**kwargs) return sampling_rule @@ -185,6 +195,12 @@ def sample(self, sampling_rule, **kwargs): return next(sampling_rule) except StopIteration: return self.current_value + elif isinstance(sampling_rule, slice): + return slice( + self.sample(sampling_rule.start, **kwargs), + self.sample(sampling_rule.stop, **kwargs), + self.sample(sampling_rule.step, **kwargs) + ) elif callable(sampling_rule): # If it's a function, extract the arguments it accepts. @@ -229,7 +245,7 @@ def sample(self, sampling_rule, **kwargs): return sampling_rule def __deepcopy__(self, memo): - is_in = id(self) in deeptrack.UPDATE_MEMO["memoization"] + is_in = id(self) in features.UPDATE_MEMO["memoization"] if is_in: return self else: @@ -309,12 +325,12 @@ def update(self, sequence_length=0, **kwargs): """ my_id = id(self) if ( - deeptrack.UPDATE_LOCK.locked() - and my_id in deeptrack.UPDATE_MEMO["memoization"] + features.UPDATE_LOCK.locked() + and my_id in features.UPDATE_MEMO["memoization"] ): return self - kwargs.update(deeptrack.UPDATE_MEMO["user_arguments"]) + kwargs.update(features.UPDATE_MEMO["user_arguments"]) new_current_value = [] @@ -338,13 +354,10 @@ def update(self, sequence_length=0, **kwargs): new_current_value.append(next_value) self.current_value = new_current_value - deeptrack.UPDATE_MEMO["memoization"][my_id] = new_current_value + features.UPDATE_MEMO["memoization"][my_id] = new_current_value return self -import collections - - class PropertyDict(collections.OrderedDict): """Dictionary with Property elements @@ -382,7 +395,7 @@ def current_value_dict(self, **kwargs) -> dict: # of the current timestep if isinstance(property, SequentialProperty): sequence_step = kwargs.get("sequence_step", None) - if not sequence_step is None: + if sequence_step is not None: property_value = property_value[sequence_step] current_value_dict[key] = property_value @@ -402,11 +415,11 @@ def update(self, **kwargs) -> "PropertyDict": """ property_arguments = collections.OrderedDict(self) property_arguments.update(kwargs) - property_arguments.update(deeptrack.UPDATE_MEMO["user_arguments"]) + property_arguments.update(features.UPDATE_MEMO["user_arguments"]) for key, prop in self.items(): if isinstance(property_arguments[key], Property): prop.update(**property_arguments) - elif id(prop) not in deeptrack.UPDATE_MEMO["memoization"]: + elif id(prop) not in features.UPDATE_MEMO["memoization"]: prop.current_value = property_arguments[key] return self @@ -447,4 +460,4 @@ def sample(self, **kwargs) -> dict: for key, property in self.items(): sample_dict[key] = property.sample(**kwargs) - return sample_dict \ No newline at end of file + return sample_dict diff --git a/deeptrack/scatterers.py b/deeptrack/scatterers.py index de9063b9a..0afed8649 100644 --- a/deeptrack/scatterers.py +++ b/deeptrack/scatterers.py @@ -1,6 +1,6 @@ """Implementations of Feature the model scattering objects. -Provides some basic implementations of scattering objects +Provides some basic implementations of scattering objects that are frequently used. Classes @@ -18,14 +18,15 @@ """ -from threading import Lock +from typing import Callable, Tuple import numpy as np -# from scipy.special import jv as jn, spherical_jn as jv, h1vp, eval_legendre as leg, jvp -import deeptrack.backend as D -from deeptrack.features import Feature, MERGE_STRATEGY_APPEND -from deeptrack.image import Image -import deeptrack.image +from . import backend as D +from .features import Feature, MERGE_STRATEGY_APPEND +from .image import Image +from . import image +from .types import PropertyLike, ArrayLike +import warnings class Scatterer(Feature): @@ -60,8 +61,8 @@ class Scatterer(Feature): Other Parameters ---------------- upsample_axes : tuple of ints - Sets the axes along which the calculation is upsampled (default is None, - which implies all axes are upsampled). + Sets the axes along which the calculation is upsampled (default is + None, which implies all axes are upsampled). crop_zeros : bool Whether to remove slices in which all elements are zero. """ @@ -71,11 +72,11 @@ class Scatterer(Feature): def __init__( self, - position=(32, 32), - z=0.0, - value=1.0, - position_unit="pixel", - upsample=1, + position: PropertyLike[ArrayLike[float]] = (32, 32), + z: PropertyLike[float] = 0.0, + value: PropertyLike[float] = 1.0, + position_unit: PropertyLike[str] = "pixel", + upsample: PropertyLike[int] = 1, **kwargs ): self._processed_properties = False @@ -112,10 +113,11 @@ def _process_and_get( # Post processes the created object to handle upsampling, # as well as cropping empty slices. if not self._processed_properties: - import warnings warnings.warn( - "Overridden _process_properties method does not call super. This is likely to result in errors if used with Optics.upscale != 1." + "Overridden _process_properties method does not call super. " + + "This is likely to result in errors if used with " + + "Optics.upscale != 1." ) # Calculates upsampled voxel_size @@ -134,7 +136,10 @@ def _process_and_get( if new_image.size == 0: warnings.warn( - "Scatterer created that is smaller than a pixel. This may yield inconsistent results. Consider using upsample on the scatterer, or upscale on the optics.", + "Scatterer created that is smaller than a pixel. " + + "This may yield inconsistent results." + + " Consider using upsample on the scatterer," + + " or upscale on the optics.", Warning, ) @@ -225,7 +230,12 @@ class Ellipse(Scatterer): Upsamples the calculations of the pixel occupancy fraction. """ - def __init__(self, radius=1e-6, rotation=0, **kwargs): + def __init__( + self, + radius: PropertyLike[float] = 1e-6, + rotation: PropertyLike[float] = 0, + **kwargs + ): super().__init__( radius=radius, rotation=rotation, upsample_axes=(0, 1), **kwargs ) @@ -293,7 +303,7 @@ class Sphere(Scatterer): Upsamples the calculations of the pixel occupancy fraction. """ - def __init__(self, radius=1e-6, **kwargs): + def __init__(self, radius: PropertyLike[float] = 1e-6, **kwargs): super().__init__(radius=radius, **kwargs) def get(self, image, radius, voxel_size, **kwargs): @@ -336,7 +346,12 @@ class Ellipsoid(Scatterer): Upsamples the calculations of the pixel occupancy fraction. """ - def __init__(self, radius=1e-6, rotation=0, **kwargs): + def __init__( + self, + radius: PropertyLike[float] = 1e-6, + rotation: PropertyLike[float] = 0, + **kwargs + ): super().__init__(radius=radius, rotation=rotation, **kwargs) def _process_properties(self, propertydict): @@ -346,7 +361,8 @@ def _process_properties(self, propertydict): length 3. If the radius is a single value, the particle is made a sphere - If the radius are two values, the smallest value is appended as the third value + If the radius are two values, the smallest value is appended as the + third value The rotation vector is padded with zeros until it is of length 3 """ @@ -426,12 +442,13 @@ def get(self, image, radius, rotation, voxel_size, **kwargs): class MieScatterer(Scatterer): """Base implementation of a Mie particle. - New Mie-theory scatterers can be implemented by extending this class, and passing - a function that calculates the coefficients of the harmonics up to order `L`. To be - precise, the feature expects a wrapper function that takes the current values of the - properties, as well as a inner function that takes an integer as the only parameter, - and calculates the coefficients up to that integer. The return format is expected to - be a tuple with two values, corresponding to `an` and `bn`. See + New Mie-theory scatterers can be implemented by extending this class, and + passing a function that calculates the coefficients of the harmonics up to + order `L`. To beprecise, the feature expects a wrapper function that takes + the current values of the properties, as well as a inner function that + takes an integer as the only parameter, and calculates the coefficients up + to that integer. The return format is expected to be a tuple with two + values, corresponding to `an` and `bn`. See `deeptrack.backend.mie_coefficients` for an example. Parameters @@ -439,11 +456,13 @@ class MieScatterer(Scatterer): coefficients : Callable[int] -> Tuple[ndarray, ndarray] Function that returns the harmonics coefficients. offset_z : "auto" or float - Distance from the particle in the z direction the field is evaluated. If "auto", - this is calculated from the pixel size and `collection_angle` + Distance from the particle in the z direction the field is evaluated. + If "auto", this is calculated from the pixel size and + `collection_angle` collection_angle : "auto" or float The maximum collection angle in radians. If "auto", this - is calculated from the objective NA (which is true if the objective is the limiting + is calculated from the objective NA (which is true if the objective is + the limiting aperature). polarization_angle : float Angle of the polarization of the incoming light relative to the x-axis. @@ -461,11 +480,11 @@ class MieScatterer(Scatterer): def __init__( self, - coefficients, - offset_z="auto", - polarization_angle=0, - collection_angle="auto", - L="auto", + coefficients: Callable[..., Callable[[int], Tuple[ArrayLike, ArrayLike]]], + offset_z: PropertyLike[str] = "auto", + polarization_angle: PropertyLike[float] = 0, + collection_angle: PropertyLike[str] = "auto", + L: PropertyLike[str] = "auto", **kwargs ): kwargs.pop("is_field", None) @@ -493,9 +512,10 @@ def _process_properties(self, properties): except (ValueError, TypeError): pass if properties["collection_angle"] == "auto": - properties["collection_angle"] = np.sqrt( - 1 - properties["NA"] ** 2 / properties["refractive_index_medium"] ** 2 + properties["collection_angle"] = np.arcsin( + properties["NA"] / properties["refractive_index_medium"] ) + if properties["offset_z"] == "auto": properties["offset_z"] = ( 32 @@ -507,7 +527,7 @@ def _process_properties(self, properties): def get( self, - image, + inp, position, upscaled_output_region, voxel_size, @@ -535,11 +555,11 @@ def get( - upscaled_output_region[1] + padding[1] ) - arr = deeptrack.image.pad_image_to_fft(np.zeros((xSize, ySize))) + arr = image.pad_image_to_fft(np.zeros((xSize, ySize))) # Evluation grid - x = np.arange(-padding[0], arr.shape[0] - padding[0]) - (position[1]) * upscale - y = np.arange(-padding[1], arr.shape[1] - padding[1]) - (position[0]) * upscale + x = np.arange(-padding[0], arr.shape[0] - padding[0]) - (position[0]) * upscale + y = np.arange(-padding[1], arr.shape[1] - padding[1]) - (position[1]) * upscale X, Y = np.meshgrid(x * voxel_size[0], y * voxel_size[1], indexing="ij") R2 = np.sqrt(X ** 2 + Y ** 2) @@ -561,11 +581,11 @@ def get( PI, TAU = D.mie_harmonics(ct, L) # Normalization factor - E = [(2 * l + 1) / (l * (l + 1)) for l in range(1, L + 1)] + E = [(2 * i + 1) / (i * (i + 1)) for i in range(1, L + 1)] # Scattering terms - S1 = sum([E[l] * A[l] * TAU[l] + E[l] * B[l] * PI[l] for l in range(0, L)]) - S2 = sum([E[l] * B[l] * TAU[l] + E[l] * A[l] * PI[l] for l in range(0, L)]) + S1 = sum([E[i] * A[i] * TAU[i] + E[i] * B[i] * PI[i] for i in range(0, L)]) + S2 = sum([E[i] * B[i] * TAU[i] + E[i] * A[i] * PI[i] for i in range(0, L)]) field = ( (ct > ct_max) @@ -581,11 +601,13 @@ def get( class MieSphere(MieScatterer): """Scattered field by a sphere - Should be calculated on at least a 64 by 64 grid. Use padding in the optics if necessary + Should be calculated on at least a 64 by 64 grid. Use padding in the + optics if necessary. - Calculates the scattered field by a spherical particle in a homogenous medium, - as predicted by Mie theory. Note that the induced phase shift is calculated - in comparison to the `refractive_index_medium` property of the optical device. + Calculates the scattered field by a spherical particle in a homogenous + medium, as predicted by Mie theory. Note that the induced phase shift is + calculated in comparison to the `refractive_index_medium` property of the + optical device. Parameters ---------- @@ -604,24 +626,21 @@ class MieSphere(MieScatterer): The position in the direction normal to the camera plane. Used if `position` is of length 2. offset_z : "auto" or float - Distance from the particle in the z direction the field is evaluated. If "auto", - this is calculated from the pixel size and `collection_angle` + Distance from the particle in the z direction the field is evaluated. + If "auto", this is calculated from the pixel size and + `collection_angle` collection_angle : "auto" or float The maximum collection angle in radians. If "auto", this - is calculated from the objective NA (which is true if the objective is the limiting - aperature). + is calculated from the objective NA (which is true if the objective + is the limiting aperature). polarization_angle : float Angle of the polarization of the incoming light relative to the x-axis. """ def __init__( self, - radius=1e-6, - refractive_index=1.45, - offset_z="auto", - polarization_angle=0, - collection_angle="auto", - L="auto", + radius: PropertyLike[float] = 1e-6, + refractive_index: PropertyLike[float] = 1.45, **kwargs ): def coeffs(radius, refractive_index, refractive_index_medium, wavelength): @@ -638,10 +657,6 @@ def inner(L): coefficients=coeffs, radius=radius, refractive_index=refractive_index, - L=L, - offset_z=offset_z, - polarization_angle=polarization_angle, - collection_angle=collection_angle, **kwargs ) @@ -649,19 +664,21 @@ def inner(L): class MieStratifiedSphere(MieScatterer): """Scattered field by a stratified sphere - A stratified sphere is a sphere with several concentric shells of uniform refractive index. + A stratified sphere is a sphere with several concentric shells of uniform + refractive index. - Should be calculated on at least a 64 by 64 grid. Use padding in the optics if necessary + Should be calculated on at least a 64 by 64 grid. Use padding in the + optics if necessary - Calculates the scattered field by in a homogenous medium, as predicted by Mie theory. - Note that the induced phase shift is calculated in comparison to the - `refractive_index_medium` property of the optical device. + Calculates the scattered field by in a homogenous medium, as predicted by + Mie theory. Note that the induced phase shift is calculated in comparison + to the `refractive_index_medium` property of the optical device. Parameters ---------- - radius : float + radius : list of float The radius of each cell in increasing order. - refractive_index : float + refractive_index : list of float Refractive index of each cell in the same order as `radius` L : int or str The number of terms used to evaluate the mie theory. If `"auto"`, @@ -674,24 +691,21 @@ class MieStratifiedSphere(MieScatterer): The position in the direction normal to the camera plane. Used if `position` is of length 2. offset_z : "auto" or float - Distance from the particle in the z direction the field is evaluated. If "auto", - this is calculated from the pixel size and `collection_angle` + Distance from the particle in the z direction the field is evaluated. + If "auto", this is calculated from the pixel size and + `collection_angle` collection_angle : "auto" or float The maximum collection angle in radians. If "auto", this - is calculated from the objective NA (which is true if the objective is the limiting - aperature). + is calculated from the objective NA (which is true if the objective + is the limiting aperature). polarization_angle : float Angle of the polarization of the incoming light relative to the x-axis. """ def __init__( self, - radius=1e-6, - refractive_index=1.45, - offset_z="auto", - polarization_angle=0, - collection_angle="auto", - L="auto", + radius: PropertyLike[ArrayLike[float]] = [1e-6], + refractive_index: PropertyLike[ArrayLike[float]] = [1.45], **kwargs ): def coeffs(radius, refractive_index, refractive_index_medium, wavelength): @@ -712,9 +726,5 @@ def inner(L): coefficients=coeffs, radius=radius, refractive_index=refractive_index, - L=L, - offset_z=offset_z, - polarization_angle=polarization_angle, - collection_angle=collection_angle, **kwargs ) diff --git a/deeptrack/sequences.py b/deeptrack/sequences.py index 8858d2c26..6dd9b6077 100644 --- a/deeptrack/sequences.py +++ b/deeptrack/sequences.py @@ -11,8 +11,9 @@ Converts a feature to be resolved as a sequence. """ -from deeptrack.features import Feature -from deeptrack.properties import SequentialProperty +from .features import Feature +from .properties import SequentialProperty +from .types import PropertyLike class Sequence(Feature): @@ -37,12 +38,14 @@ class Sequence(Feature): __distributed__ = False - def __init__(self, feature: Feature, sequence_length: int = 1, **kwargs): + def __init__( + self, feature: Feature, sequence_length: PropertyLike[int] = 1, **kwargs + ): self.feature = feature super().__init__(sequence_length=sequence_length, **kwargs) # Require update - self.update() + # self.update() def get(self, input_list, sequence_length=None, **kwargs): return (input_list or []) + [ @@ -52,14 +55,14 @@ def get(self, input_list, sequence_length=None, **kwargs): for sequence_step in range(sequence_length) ] - def update(self, **kwargs): - super().update(**kwargs) + def _update(self, **kwargs): + super()._update(**kwargs) sequence_length = self.properties["sequence_length"].current_value if "sequence_length" not in kwargs: kwargs["sequence_length"] = sequence_length - self.feature.update(**kwargs) + self.feature._update(**kwargs) return self @@ -67,11 +70,13 @@ def update(self, **kwargs): def Sequential(feature: Feature, **kwargs): """Converts a feature to be resolved as a sequence. - Should be called on individual features, not combinations of features. All keyword - arguments will be trated as sequential properties and will be passed to the parent feature. + Should be called on individual features, not combinations of features. All + keyword arguments will be trated as sequential properties and will be + passed to the parent feature. - If a property from the keyword argument already exists on the feature, the existing property - will be used to initilize the passed property (that is, it will be used for the first timestep). + If a property from the keyword argument already exists on the feature, the + existing property will be used to initilize the passed property (that is, + it will be used for the first timestep). Parameters ---------- diff --git a/deeptrack/test/__init__.py b/deeptrack/test/__init__.py new file mode 100644 index 000000000..de101117c --- /dev/null +++ b/deeptrack/test/__init__.py @@ -0,0 +1 @@ +import sys diff --git a/deeptrack/test/test.py b/deeptrack/test/test.py new file mode 100644 index 000000000..f6b621e3d --- /dev/null +++ b/deeptrack/test/test.py @@ -0,0 +1,8 @@ +# import unittest + +# loader = unittest.TestLoader() +# start_dir = "./deeptrack/test" +# suite = loader.discover(start_dir) + +# runner = unittest.TextTestRunner() +# runner.run(suite) diff --git a/test/test_aberrations.py b/deeptrack/test/test_aberrations.py old mode 100755 new mode 100644 similarity index 95% rename from test/test_aberrations.py rename to deeptrack/test/test_aberrations.py index eaee22426..0510c6e81 --- a/test/test_aberrations.py +++ b/deeptrack/test/test_aberrations.py @@ -1,14 +1,14 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.aberrations as aberrations +from .. import aberrations -from deeptrack.scatterers import PointParticle -from deeptrack.optics import Fluorescence -from deeptrack.image import Image +from ..scatterers import PointParticle +from ..optics import Fluorescence +from ..image import Image class TestAberrations(unittest.TestCase): @@ -37,7 +37,7 @@ def testPiston(self): resolution=1e-6, magnification=10, wavelength=530e-9, - output_region=(0, 0, 64, 64), + output_region=(0, 0, 64, 48), padding=(64, 64, 64, 64), aberration=aberrations.Piston(coefficient=1), ) @@ -45,7 +45,7 @@ def testPiston(self): for z in (-100, 0, 100): im = aberrated_particle.resolve(z=z) self.assertIsInstance(im, Image) - self.assertEqual(im.shape, (64, 64, 1)) + self.assertEqual(im.shape, (64, 48, 1)) def testVerticalTilt(self): aberrated_optics = Fluorescence( diff --git a/test/test_augmentations.py b/deeptrack/test/test_augmentations.py old mode 100755 new mode 100644 similarity index 82% rename from test/test_augmentations.py rename to deeptrack/test/test_augmentations.py index 63f66b9d5..982606fb4 --- a/test/test_augmentations.py +++ b/deeptrack/test/test_augmentations.py @@ -1,12 +1,12 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack as dt -import deeptrack.augmentations as augmentations -from deeptrack.features import Feature +from .. import augmentations, optics, scatterers + +from ..features import Feature import numpy as np @@ -49,10 +49,10 @@ def test_FlipDiagonal(self): self.assertTrue(np.all(output_2 == np.array([[[1], [0]], [[2], [0]]]))) def test_Affine(self): - optics = dt.Fluorescence(magnification=10) - particle = dt.PointParticle( + opt = optics.Fluorescence(magnification=10) + particle = scatterers.PointParticle( position=lambda image_size: np.random.rand(2) * image_size[-2:], - image_size=optics.output_region, + image_size=opt.output_region, ) augmentation = augmentations.Affine( @@ -63,7 +63,7 @@ def test_Affine(self): mode="constant", ) - pipe = optics(particle) + augmentation + pipe = opt(particle) + augmentation for _ in range(10): image = pipe.update().resolve() @@ -117,50 +117,50 @@ def test_ElasticTransformation(self): def test_Crop(self): image = np.ones((10, 10, 10)) - cropper = dt.Crop(crop=(3, 2, 1), crop_mode="remove") + cropper = augmentations.Crop(crop=(3, 2, 1), crop_mode="remove") out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (7, 8, 9)) - cropper = dt.Crop(crop=(3, 2, 1), crop_mode="retain") + cropper = augmentations.Crop(crop=(3, 2, 1), crop_mode="retain") out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (3, 2, 1)) - cropper = dt.Crop(crop=2, crop_mode="remove") + cropper = augmentations.Crop(crop=2, crop_mode="remove") out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (8, 8, 8)) - cropper = dt.Crop(crop=2, crop_mode="retain") + cropper = augmentations.Crop(crop=2, crop_mode="retain") out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (2, 2, 2)) - cropper = dt.Crop(crop=12, crop_mode="remove") + cropper = augmentations.Crop(crop=12, crop_mode="remove") out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (1, 1, 1)) - cropper = dt.Crop(crop=0, crop_mode="retain") + cropper = augmentations.Crop(crop=0, crop_mode="retain") out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (1, 1, 1)) def test_CropToMultiple(self): image = np.ones((11, 11, 11)) - cropper = dt.CropToMultiplesOf(multiple=2) + cropper = augmentations.CropToMultiplesOf(multiple=2) out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (10, 10, 10)) - cropper = dt.CropToMultiplesOf(multiple=-1) + cropper = augmentations.CropToMultiplesOf(multiple=-1) out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (11, 11, 11)) - cropper = dt.CropToMultiplesOf(multiple=(2, 3, 5)) + cropper = augmentations.CropToMultiplesOf(multiple=(2, 3, 5)) out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (10, 9, 10)) - cropper = dt.CropToMultiplesOf(multiple=(2, -1, 7)) + cropper = augmentations.CropToMultiplesOf(multiple=(2, -1, 7)) out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (10, 11, 7)) - cropper = dt.CropToMultiplesOf(multiple=(2, 3, None)) + cropper = augmentations.CropToMultiplesOf(multiple=(2, 3, None)) out = cropper.update().resolve(image) self.assertSequenceEqual(out.shape, (10, 9, 11)) diff --git a/test/test_features.py b/deeptrack/test/test_features.py old mode 100755 new mode 100644 similarity index 82% rename from test/test_features.py rename to deeptrack/test/test_features.py index 145e53e20..fd738dd70 --- a/test/test_features.py +++ b/deeptrack/test/test_features.py @@ -1,13 +1,13 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.features as features +from .. import features, Image import numpy as np -from deeptrack.image import Image + class TestFeatures(unittest.TestCase): @@ -280,6 +280,87 @@ def test_LambdaDependenceOtherFeature(self): C.update(key="c") self.assertEqual(C.prop.current_value, 12) + def test_SliceConstant(self): + + input = np.arange(9).reshape((3, 3)) + + A = features.DummyFeature() + + A0 = A[0] + A1 = A[1] + A22 = A[2, 2] + A12 = A[1, lambda: -1] + + a0 = A0.resolve(input) + a1 = A1.resolve(input) + a22 = A22.resolve(input) + a12 = A12.resolve(input) + + + self.assertEqual(a0.tolist(), input[0].tolist()) + self.assertEqual(a1.tolist(), input[1].tolist()) + self.assertEqual(a22, input[2, 2]) + self.assertEqual(a12, input[1, -1]) + + def test_SliceColon(self): + + input = np.arange(16).reshape((4, 4)) + + A = features.DummyFeature() + + A0 = A[0, :1] + A1 = A[ + 1, + lambda: 0: + lambda: 4: + lambda: 2 + ] + A2 = A[lambda: slice(0, 4, 1), 2] + A3 = A[lambda: 0 : + lambda: 2, + :] + + a0 = A0.resolve(input) + a1 = A1.resolve(input) + a2 = A2.resolve(input) + a3 = A3.resolve(input) + + + self.assertEqual(a0.tolist(), input[0, :1].tolist()) + self.assertEqual(a1.tolist(), input[1, 0:4:2].tolist()) + self.assertEqual(a2.tolist(), input[:, 2].tolist()) + self.assertEqual(a3.tolist(), input[0:2, :].tolist()) + + def test_SliceEllipse(self): + + input = np.arange(16).reshape((4, 4)) + + A = features.DummyFeature() + + A0 = A[..., :1] + A1 = A[ + ..., + lambda: 0: + lambda: 4: + lambda: 2 + ] + A2 = A[lambda: slice(0, 4, 1), ...] + A3 = A[lambda: 0 : + lambda: 2, + lambda: ...] + + a0 = A0.resolve(input) + a1 = A1.resolve(input) + a2 = A2.resolve(input) + a3 = A3.resolve(input) + + + self.assertEqual(a0.tolist(), input[..., :1].tolist()) + self.assertEqual(a1.tolist(), input[..., 0:4:2].tolist()) + self.assertEqual(a2.tolist(), input[:, ...].tolist()) + self.assertEqual(a3.tolist(), input[0:2, ...].tolist()) + + if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/deeptrack/test/test_generators.py b/deeptrack/test/test_generators.py new file mode 100644 index 000000000..f0f4136a6 --- /dev/null +++ b/deeptrack/test/test_generators.py @@ -0,0 +1,115 @@ +import sys + +# sys.path.append(".") # Adds the module to path + +import unittest + +from .. import generators +from ..optics import Fluorescence +from ..scatterers import PointParticle +import numpy as np + + +class TestGenerators(unittest.TestCase): + def test_Generator(self): + optics = Fluorescence( + NA=0.7, + wavelength=680e-9, + resolution=1e-6, + magnification=10, + output_region=(0, 0, 128, 128), + ) + scatterer = PointParticle( + intensity=100, + position_unit="pixel", + position=lambda: np.random.rand(2) * 128, + ) + imaged_scatterer = optics(scatterer) + + def get_particle_position(result): + for property in result.properties: + if "position" in property: + return property["position"] + + generator = generators.Generator() + particle_generator = generator.generate( + imaged_scatterer, get_particle_position, batch_size=4 + ) + particles, positions = next(particle_generator) + for particle, position in zip(particles, positions): + self.assertEqual(particle.shape, (128, 128, 1)) + self.assertTrue(np.all(position >= 0)) + self.assertTrue(np.all(position <= 128)) + + def test_ContinuousGenerator(self): + optics = Fluorescence( + NA=0.7, + wavelength=680e-9, + resolution=1e-6, + magnification=10, + output_region=(0, 0, 128, 128), + ) + scatterer = PointParticle( + intensity=100, + position_unit="pixel", + position=lambda: np.random.rand(2) * 128, + ) + imaged_scatterer = optics(scatterer) + + def get_particle_position(result): + for property in result.properties: + if "position" in property: + return property["position"] + + generator = generators.ContinuousGenerator( + imaged_scatterer, get_particle_position, min_data_size=10, max_data_size=20 + ) + + with generator: + self.assertGreater(len(generator.data), 10) + self.assertLess(len(generator.data), 21) + + def test_CappedContinuousGenerator(self): + + optics = Fluorescence( + NA=0.7, + wavelength=680e-9, + resolution=1e-6, + magnification=10, + output_region=(0, 0, 128, 128), + index=range(0, 200), + ) + scatterer = PointParticle( + intensity=100, + position_unit="pixel", + position=lambda: np.random.rand(2) * 128, + ) + imaged_scatterer = optics(scatterer) + + def get_particle_position(result): + for property in result.properties: + if "position" in property: + return property["position"] + + generator = generators.CappedContinuousGenerator( + imaged_scatterer, + get_particle_position, + batch_size=1, + min_data_size=10, + max_data_size=20, + max_sample_exposure=5, + ) + + # with generator: + # self.assertGreater(len(generator.data), 10) + # self.assertLess(len(generator.data), 21) + # for _ in range(10): + # generator.on_epoch_end() + # for idx in range(len(generator)): + # a = generator[idx] + + # [self.assertLess(d[-1], 8) for d in generator.data] + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/test/test_image.py b/deeptrack/test/test_image.py old mode 100755 new mode 100644 similarity index 96% rename from test/test_image.py rename to deeptrack/test/test_image.py index 123ae4ab7..7479e462a --- a/test/test_image.py +++ b/deeptrack/test/test_image.py @@ -1,12 +1,12 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.image as image +from .. import image -from deeptrack.features import Feature +from ..features import Feature import numpy as np diff --git a/test/test_layers.py b/deeptrack/test/test_layers.py similarity index 93% rename from test/test_layers.py rename to deeptrack/test/test_layers.py index c116d9e0c..efaea697a 100644 --- a/test/test_layers.py +++ b/deeptrack/test/test_layers.py @@ -1,13 +1,13 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.layers as layers +from .. import layers import tensorflow.keras.layers as k_layers import tensorflow.keras.models as k_models -from tensorflow_addons.layers import InstanceNormalization +from ..layers import InstanceNormalization import numpy as np diff --git a/test/test_losses.py b/deeptrack/test/test_losses.py old mode 100755 new mode 100644 similarity index 96% rename from test/test_losses.py rename to deeptrack/test/test_losses.py index ae47d6c4a..385ae0f10 --- a/test/test_losses.py +++ b/deeptrack/test/test_losses.py @@ -1,13 +1,13 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.losses as losses +from .. import losses import numpy as np -from keras import backend as K +from tensorflow.keras import backend as K class TestLosses(unittest.TestCase): @@ -16,7 +16,7 @@ class TestLosses(unittest.TestCase): falsely = K.constant(np.zeros((2, 2, 1))) def test_flatten(self): - from keras.losses import mse + from tensorflow.keras.losses import mse loss_function = losses.flatten(mse) loss = K.eval(loss_function(self.truthly, self.truthly)) diff --git a/test/test_math.py b/deeptrack/test/test_math.py old mode 100755 new mode 100644 similarity index 89% rename from test/test_math.py rename to deeptrack/test/test_math.py index ec6a156b9..45c3dc6cb --- a/test/test_math.py +++ b/deeptrack/test/test_math.py @@ -1,10 +1,10 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.math as math +from .. import math import numpy as np diff --git a/test/test_models.py b/deeptrack/test/test_models.py old mode 100755 new mode 100644 similarity index 90% rename from test/test_models.py rename to deeptrack/test/test_models.py index 33c5a613c..0511f6c74 --- a/test/test_models.py +++ b/deeptrack/test/test_models.py @@ -1,10 +1,10 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.models as models +from .. import models import numpy as np @@ -25,7 +25,7 @@ def test_FullyConnected(self): def test_Convolutions_with_single_input(self): model = models.Convolutional( input_shape=(64, 64, 1), - conv_layers_dimensions=(16, 32, 64, 128), + conv_layers_dimensions=(16, 32, 64), dense_layers_dimensions=(32, 32), number_of_outputs=3, output_activation="sigmoid", @@ -38,7 +38,7 @@ def test_Convolutions_with_single_input(self): def test_Convolutions_with_multiple_inputs(self): model = models.Convolutional( input_shape=[(64, 64, 1), (64, 64, 2)], - conv_layers_dimensions=(16, 32, 64, 128), + conv_layers_dimensions=(16, 32, 64), dense_layers_dimensions=(32, 32), number_of_outputs=3, output_activation="sigmoid", @@ -51,7 +51,7 @@ def test_Convolutions_with_multiple_inputs(self): def test_Convolutions_with_no_dense_top(self): model = models.Convolutional( input_shape=(64, 64, 1), - conv_layers_dimensions=(16, 32, 64, 128), + conv_layers_dimensions=(16, 32, 64), dense_layers_dimensions=(32, 32), dense_top=False, number_of_outputs=3, @@ -65,7 +65,7 @@ def test_Convolutions_with_no_dense_top(self): def test_UNet(self): model = models.UNet( input_shape=(64, 64, 1), - conv_layers_dimensions=(16, 32, 64, 128), + conv_layers_dimensions=(16, 32, 64), base_conv_layers_dimensions=(256, 256), output_conv_layers_dimensions=(32, 32), steps_per_pooling=1, diff --git a/test/test_noises.py b/deeptrack/test/test_noises.py old mode 100755 new mode 100644 similarity index 92% rename from test/test_noises.py rename to deeptrack/test/test_noises.py index 057922794..7a83dbe4a --- a/test/test_noises.py +++ b/deeptrack/test/test_noises.py @@ -1,12 +1,12 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.noises as noises +from .. import noises -from deeptrack.image import Image +from ..image import Image import numpy as np diff --git a/test/test_optics.py b/deeptrack/test/test_optics.py old mode 100755 new mode 100644 similarity index 94% rename from test/test_optics.py rename to deeptrack/test/test_optics.py index 458a951c6..05d3ced9d --- a/test/test_optics.py +++ b/deeptrack/test/test_optics.py @@ -1,13 +1,13 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.optics as optics +from .. import optics -from deeptrack.scatterers import PointParticle -from deeptrack.image import Image +from ..scatterers import PointParticle +from ..image import Image class TestOptics(unittest.TestCase): diff --git a/test/test_properties.py b/deeptrack/test/test_properties.py old mode 100755 new mode 100644 similarity index 89% rename from test/test_properties.py rename to deeptrack/test/test_properties.py index 9bc9b7321..ac0293021 --- a/test/test_properties.py +++ b/deeptrack/test/test_properties.py @@ -1,11 +1,10 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.properties as properties -import deeptrack as dt +from .. import properties, features, optics import numpy as np @@ -60,13 +59,13 @@ def geometric_increase(previous_value, step_length): self.assertEqual(P2.current_value, [3, 6, 10, 15, 21]) def test_UpdateFromParent(self): - optics = dt.Fluorescence() - sample = dt.DummyFeature(voxel_size=optics.voxel_size) - together = optics(sample) + opt = optics.Fluorescence() + sample = features.DummyFeature(voxel_size=opt.voxel_size) + together = opt(sample) together.update() self.assertEqual( - tuple(optics.voxel_size.current_value), - tuple(sample.voxel_size.current_value), + tuple(opt.voxel_size.current_value), + tuple(opt.voxel_size.current_value), ) def test_PropertyDict(self): diff --git a/test/test_scatterers.py b/deeptrack/test/test_scatterers.py old mode 100755 new mode 100644 similarity index 96% rename from test/test_scatterers.py rename to deeptrack/test/test_scatterers.py index 7861a003a..552c63b90 --- a/test/test_scatterers.py +++ b/deeptrack/test/test_scatterers.py @@ -5,11 +5,11 @@ import unittest -import deeptrack.scatterers as scatterers +from .. import scatterers import numpy as np -from deeptrack.optics import Fluorescence, Brightfield -from deeptrack.image import Image +from ..optics import Fluorescence, Brightfield +from ..image import Image class TestScatterers(unittest.TestCase): diff --git a/test/test_sequences.py b/deeptrack/test/test_sequences.py old mode 100755 new mode 100644 similarity index 86% rename from test/test_sequences.py rename to deeptrack/test/test_sequences.py index bb625f856..2859f1aab --- a/test/test_sequences.py +++ b/deeptrack/test/test_sequences.py @@ -1,13 +1,13 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.sequences as sequences +from .. import sequences -from deeptrack.optics import Fluorescence -from deeptrack.scatterers import Ellipse +from ..optics import Fluorescence +from ..scatterers import Ellipse import numpy as np @@ -38,7 +38,7 @@ def get_rotation(sequence_length, previous_value): imaged_rotating_ellipse, sequence_length=50 ) self.assertIsInstance(imaged_rotating_ellipse_sequence, sequences.Sequence) - + if __name__ == "__main__": unittest.main() \ No newline at end of file diff --git a/test/test_utils.py b/deeptrack/test/test_utils.py old mode 100755 new mode 100644 similarity index 96% rename from test/test_utils.py rename to deeptrack/test/test_utils.py index 94a745870..84f4b98ce --- a/test/test_utils.py +++ b/deeptrack/test/test_utils.py @@ -1,10 +1,10 @@ import sys -sys.path.append(".") # Adds the module to path +# sys.path.append(".") # Adds the module to path import unittest -import deeptrack.utils as utils +from .. import utils class TestUtils(unittest.TestCase): diff --git a/deeptrack/types.py b/deeptrack/types.py new file mode 100644 index 000000000..b7b8e305f --- /dev/null +++ b/deeptrack/types.py @@ -0,0 +1,11 @@ +""" Type declarations for internal use +""" + +import typing +import numpy as np + +# Property type declaration +T = typing.TypeVar("T") +PropertyLike = typing.Union[T, typing.Callable[..., T]] + +ArrayLike = typing.Union[typing.Tuple[T], typing.List[T], np.ndarray] \ No newline at end of file diff --git a/deeptrack/utils.py b/deeptrack/utils.py index c35d3f077..9ced9f335 100644 --- a/deeptrack/utils.py +++ b/deeptrack/utils.py @@ -11,7 +11,7 @@ isiterable(obj: any) Return True if the object is iterable. Else, return False. as_list(obj: any) - If the input is iterable, convert it to list. + If the input is iterable, convert it to list. Otherwise, wrap the input in a list. get_kwarg_names(function: Callable) Return the names of the keyword arguments the function accepts. @@ -142,8 +142,8 @@ def kwarg_has_default(function: Callable, argument: str) -> bool: def safe_call(function, positional_args=[], **kwargs): """Calls a function, using keyword arguments from a dictionary of arguments. - If the function does not accept one of the argument provided, it will not be passed. Does not support - non-keyword arguments. + If the function does not accept one of the argument provided, it will not + be passed. Does not support non-keyword arguments. Parameters ---------- diff --git a/docs/.buildinfo b/docs/.buildinfo index a0bb66822..3b244a6be 100644 --- a/docs/.buildinfo +++ b/docs/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 7995104a544b788a8e5f9b4c9019979f +config: 72bdd1a6dea0ed523a6bb5f48cd3e5d8 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_sources/augmentations.rst.txt b/docs/_sources/augmentations.rst.txt index e37c74a9d..6d007ab44 100644 --- a/docs/_sources/augmentations.rst.txt +++ b/docs/_sources/augmentations.rst.txt @@ -69,6 +69,13 @@ Pad :members: :exclude-members: get +PadToMultiplesOf +^^^^^^^^^^^^^^^^ + +.. autoclass:: deeptrack.augmentations.PadToMultiplesOf + :members: + :exclude-members: get + PreLoad ^^^^^^^ diff --git a/docs/_sources/deeptrack.rst.txt b/docs/_sources/deeptrack.rst.txt index f98b9cdd3..b804e53ed 100644 --- a/docs/_sources/deeptrack.rst.txt +++ b/docs/_sources/deeptrack.rst.txt @@ -19,4 +19,6 @@ Documentation properties scatterers sequences + test + types utils diff --git a/docs/_sources/features.rst.txt b/docs/_sources/features.rst.txt index d989b39f1..07b9d6aba 100644 --- a/docs/_sources/features.rst.txt +++ b/docs/_sources/features.rst.txt @@ -6,6 +6,34 @@ features Module classes <<<<<<<<<<<<<< +AsType +^^^^^^ + +.. autoclass:: deeptrack.features.AsType + :members: + :exclude-members: get + +Bind +^^^^ + +.. autoclass:: deeptrack.features.Bind + :members: + :exclude-members: get + +BindResolve +^^^^^^^^^^^ + +.. autoclass:: deeptrack.features.BindResolve + :members: + :exclude-members: get + +BindUpdate +^^^^^^^^^^ + +.. autoclass:: deeptrack.features.BindUpdate + :members: + :exclude-members: get + Branch ^^^^^^ @@ -104,6 +132,13 @@ SampleToMasks :members: :exclude-members: get +Slice +^^^^^ + +.. autoclass:: deeptrack.features.Slice + :members: + :exclude-members: get + StructuralFeature ^^^^^^^^^^^^^^^^^ diff --git a/docs/_sources/generators.rst.txt b/docs/_sources/generators.rst.txt index 935df8b84..2263a3623 100644 --- a/docs/_sources/generators.rst.txt +++ b/docs/_sources/generators.rst.txt @@ -6,6 +6,13 @@ generators Module classes <<<<<<<<<<<<<< +CappedContinuousGenerator +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: deeptrack.generators.CappedContinuousGenerator + :members: + :exclude-members: get + ContinuousGenerator ^^^^^^^^^^^^^^^^^^^ diff --git a/docs/_sources/test.rst.txt b/docs/_sources/test.rst.txt new file mode 100644 index 000000000..af1a1cd1d --- /dev/null +++ b/docs/_sources/test.rst.txt @@ -0,0 +1,5 @@ +test +==== + +.. automodule:: deeptrack.test + diff --git a/docs/_sources/types.rst.txt b/docs/_sources/types.rst.txt new file mode 100644 index 000000000..9b7a6803d --- /dev/null +++ b/docs/_sources/types.rst.txt @@ -0,0 +1,5 @@ +types +===== + +.. automodule:: deeptrack.types + diff --git a/docs/_static/basic.css b/docs/_static/basic.css index 616111c17..24f8a6387 100644 --- a/docs/_static/basic.css +++ b/docs/_static/basic.css @@ -4,7 +4,7 @@ * * Sphinx stylesheet -- basic theme. * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -15,12 +15,6 @@ div.clearer { clear: both; } -div.section::after { - display: block; - content: ''; - clear: left; -} - /* -- relbar ---------------------------------------------------------------- */ div.related { @@ -322,27 +316,21 @@ img.align-default, .figure.align-default { div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; - padding: 7px; + padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; - clear: right; - overflow-x: auto; } p.sidebar-title { font-weight: bold; } -div.admonition, div.topic, blockquote { - clear: left; -} - /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; - padding: 7px; + padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } @@ -364,6 +352,10 @@ div.admonition dt { font-weight: bold; } +div.admonition dl { + margin-bottom: 0; +} + p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; @@ -374,28 +366,9 @@ div.body p.centered { margin-top: 25px; } -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - /* -- tables ---------------------------------------------------------------- */ table.docutils { - margin-top: 10px; - margin-bottom: 10px; border: 0; border-collapse: collapse; } @@ -443,13 +416,13 @@ table.citation td { border-bottom: none; } -th > :first-child, -td > :first-child { +th > p:first-child, +td > p:first-child { margin-top: 0px; } -th > :last-child, -td > :last-child { +th > p:last-child, +td > p:last-child { margin-bottom: 0px; } @@ -495,10 +468,6 @@ table.field-list td, table.field-list th { /* -- hlist styles ---------------------------------------------------------- */ -table.hlist { - margin: 1em 0; -} - table.hlist td { vertical-align: top; } @@ -526,37 +495,17 @@ ol.upperroman { list-style: upper-roman; } -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { +li > p:first-child { margin-top: 0px; } -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { +li > p:last-child { margin-bottom: 0px; } -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - dl.footnote > dt, dl.citation > dt { float: left; - margin-right: 0.5em; } dl.footnote > dd, @@ -597,7 +546,7 @@ dl { margin-bottom: 15px; } -dd > :first-child { +dd > p:first-child { margin-top: 0px; } @@ -611,11 +560,6 @@ dd { margin-left: 30px; } -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - dt:target, span.highlighted { background-color: #fbe54e; } @@ -693,10 +637,6 @@ pre { overflow-y: hidden; /* fixes display issues on Chrome browsers */ } -pre, div[class*="highlight-"] { - clear: both; -} - span.pre { -moz-hyphens: none; -ms-hyphens: none; @@ -704,57 +644,22 @@ span.pre { hyphens: none; } -div[class*="highlight-"] { - margin: 1em 0; -} - td.linenos pre { + padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; + margin-left: 0.5em; } table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; + padding: 0 0.5em 0 0.5em; } div.code-block-caption { - margin-top: 1em; padding: 2px 5px; font-size: small; } @@ -763,9 +668,8 @@ div.code-block-caption code { background-color: transparent; } -table.highlighttable td.linenos, -div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; } div.code-block-caption span.caption-number { @@ -777,7 +681,11 @@ div.code-block-caption span.caption-text { } div.literal-block-wrapper { - margin: 1em 0; + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; } code.descname { @@ -828,7 +736,8 @@ span.eqno { } span.eqno a.headerlink { - position: absolute; + position: relative; + left: 0px; z-index: 1; } diff --git a/docs/_static/css/index.f6b7ca918bee2f46fd9abac01cfb07d5.css b/docs/_static/css/index.f6b7ca918bee2f46fd9abac01cfb07d5.css new file mode 100644 index 000000000..57710af73 --- /dev/null +++ b/docs/_static/css/index.f6b7ca918bee2f46fd9abac01cfb07d5.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:540px;--breakpoint-md:720px;--breakpoint-lg:960px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1rem;line-height:1.5;color:#212529;text-align:left}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;background-color:transparent}a:hover{color:#0056b3}a:not([href]),a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{border-style:none}img,svg{vertical-align:middle}svg{overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem}.display-1,.display-2{font-weight:300;line-height:1.2}.display-2{font-size:5.5rem}.display-3{font-size:4.5rem}.display-3,.display-4{font-weight:300;line-height:1.2}.display-4{font-size:3.5rem}hr{margin-top:1rem;margin-bottom:1rem;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer:before{content:"\2014\00A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:540px){.container{max-width:540px}}@media (min-width:720px){.container{max-width:720px}}@media (min-width:960px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1400px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:540px){.container,.container-sm{max-width:540px}}@media (min-width:720px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:960px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1400px}}.row{display:flex;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-1>*{flex:0 0 100%;max-width:100%}.row-cols-2>*{flex:0 0 50%;max-width:50%}.row-cols-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-4>*{flex:0 0 25%;max-width:25%}.row-cols-5>*{flex:0 0 20%;max-width:20%}.row-cols-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-auto{flex:0 0 auto;width:auto;max-width:100%}.col-1{flex:0 0 8.33333%;max-width:8.33333%}.col-2{flex:0 0 16.66667%;max-width:16.66667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.33333%;max-width:33.33333%}.col-5{flex:0 0 41.66667%;max-width:41.66667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.33333%;max-width:58.33333%}.col-8{flex:0 0 66.66667%;max-width:66.66667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.33333%;max-width:83.33333%}.col-11{flex:0 0 91.66667%;max-width:91.66667%}.col-12{flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333%}.offset-2{margin-left:16.66667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333%}.offset-5{margin-left:41.66667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333%}.offset-8{margin-left:66.66667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333%}.offset-11{margin-left:91.66667%}@media (min-width:540px){.col-sm{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-sm-1>*{flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-sm-4>*{flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-sm-auto{flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media (min-width:720px){.col-md{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-md-1>*{flex:0 0 100%;max-width:100%}.row-cols-md-2>*{flex:0 0 50%;max-width:50%}.row-cols-md-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-md-4>*{flex:0 0 25%;max-width:25%}.row-cols-md-5>*{flex:0 0 20%;max-width:20%}.row-cols-md-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-md-auto{flex:0 0 auto;width:auto;max-width:100%}.col-md-1{flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media (min-width:960px){.col-lg{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-lg-1>*{flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-lg-4>*{flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-lg-auto{flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media (min-width:1200px){.col-xl{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-xl-1>*{flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-xl-4>*{flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-xl-auto{flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:hsla(0,0%,100%,.075)}@media (max-width:539.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:719.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:959.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size],textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label:before,.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label:before,.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media (min-width:540px){.form-inline label{justify-content:center}.form-inline .form-group,.form-inline label{display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{flex:0 0 auto;flex-flow:row wrap}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:flex;align-items:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary.focus,.btn-primary:focus,.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus,.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success.focus,.btn-success:focus,.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info.focus,.btn-info:focus,.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning.focus,.btn-warning:focus,.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger.focus,.btn-danger:focus,.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light.focus,.btn-light:focus,.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark.focus,.btn-dark:focus,.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:540px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:720px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:960px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after,.dropright .dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after{margin-left:0}.dropleft .dropdown-toggle-split:before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label:before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label:before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label:before,.custom-control-input[disabled]~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label:before{pointer-events:none;background-color:#fff;border:1px solid #adb5bd}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label:before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label:before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label:after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label:after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label:after{background-color:#fff;transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:calc(1.5em + .75rem + 2px)}.custom-file-input{z-index:2;margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]:after{content:attr(data-browse)}.custom-file-label{left:0;z-index:1;height:calc(1.5em + .75rem + 2px);font-weight:400;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower,.custom-range::-ms-fill-upper{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label:before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label:before,.custom-file-label,.custom-select{transition:none}}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{flex:1 1 auto;text-align:center}.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media (max-width:539.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:540px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:719.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:720px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:959.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:960px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(0,0,0,0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(255,255,255,0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-body{flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-bottom:-.75rem;border-bottom:0}.card-header-pills,.card-header-tabs{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:540px){.card-deck{display:flex;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:540px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:540px){.card-columns{column-count:3;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb,.breadcrumb-item{display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item:before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:540px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{height:1rem;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress,.progress-bar{display:flex;overflow:hidden}.progress-bar{flex-direction:column;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.media{display:flex;align-items:flex-start}.media-body{flex:1}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:540px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:720px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:960px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:flex;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translateY(-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered:before{display:block;height:calc(100vh - 1rem);height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{flex-direction:column;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable:before{content:none}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;padding:1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:540px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered:before{height:calc(100vh - 3.5rem);height:min-content}.modal-sm{max-width:300px}}@media (min-width:960px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow:before,.bs-popover-top>.arrow:before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow:after,.bs-popover-top>.arrow:after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow:before,.bs-popover-right>.arrow:before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow:after,.bs-popover-right>.arrow:after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow:before,.bs-popover-bottom>.arrow:before{top:0;border-width:0 .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow:after,.bs-popover-bottom>.arrow:after{top:1px;border-width:0 .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow:before,.bs-popover-left>.arrow:before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow:after,.bs-popover-left>.arrow:after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner:after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:flex;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@keyframes spinner-border{to{transform:rotate(1turn)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid;border-right:.25em solid transparent;border-radius:50%;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-right,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}@media (min-width:540px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}}@media (min-width:720px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}}@media (min-width:960px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.85714%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-fill{flex:1 1 auto!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}@media (min-width:540px){.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}}@media (min-width:720px){.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}}@media (min-width:960px){.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:540px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:720px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:960px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{user-select:all!important}.user-select-auto{user-select:auto!important}.user-select-none{user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports (position:sticky){.sticky-top{position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:540px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:720px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:960px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link:after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:transparent}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:540px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:720px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:960px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:hsla(0,0%,100%,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:960px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}html{font-size:var(--pst-font-size-base);scroll-padding-top:calc(var(--pst-header-height) + 12px)}body{padding-top:calc(var(--pst-header-height) + 20px);background-color:#fff;font-family:var(--pst-font-family-base);font-weight:400;line-height:1.65;color:rgba(var(--pst-color-text-base),1)}p{margin-bottom:1.15rem;font-size:1em;color:rgba(var(--pst-color-paragraph),1)}p.rubric{border-bottom:1px solid #c9c9c9}a{color:rgba(var(--pst-color-link),1);text-decoration:none}a:hover{color:rgba(var(--pst-color-link-hover),1);text-decoration:underline}a.headerlink{color:rgba(var(--pst-color-headerlink),1);font-size:.8em;padding:0 4px;text-decoration:none}a.headerlink:hover{background-color:rgba(var(--pst-color-headerlink),1);color:rgba(var(--pst-color-headerlink-hover),1)}.heading-style,h1,h2,h3,h4,h5,h6{margin:2.75rem 0 1.05rem;font-family:var(--pst-font-family-heading);font-weight:400;line-height:1.15}h1{margin-top:0;font-size:var(--pst-font-size-h1);color:rgba(var(--pst-color-h1),1)}h2{font-size:var(--pst-font-size-h2);color:rgba(var(--pst-color-h2),1)}h3{font-size:var(--pst-font-size-h3);color:rgba(var(--pst-color-h3),1)}h4{font-size:var(--pst-font-size-h4);color:rgba(var(--pst-color-h4),1)}h5{font-size:var(--pst-font-size-h5);color:rgba(var(--pst-color-h5),1)}h6{font-size:var(--pst-font-size-h6);color:rgba(var(--pst-color-h6),1)}.text_small,small{font-size:var(--pst-font-size-milli)}hr{border:0;border-top:1px solid #e5e5e5}code,kbd,pre,samp{font-family:var(--pst-font-family-monospace)}pre{margin:1.5em 0;padding:10px;background-color:rgba(var(--pst-color-preformatted-background),1);color:rgba(var(--pst-color-preformatted-text),1);line-height:1.2em;border:1px solid #c9c9c9;box-shadow:1px 1px 1px #d8d8d8}.navbar{position:fixed;min-height:var(--pst-header-height);width:100%;padding:0}.navbar .container-xl{height:100%}.navbar-brand{position:relative;height:var(--pst-header-height);width:auto;padding:.5rem 0}.navbar-brand img{max-width:100%;height:100%;width:auto}.navbar-light{background:#fff!important;box-shadow:0 .125rem .25rem 0 rgba(0,0,0,.11)}.navbar-light .navbar-nav li a.nav-link{padding:0 .5rem;color:rgba(var(--pst-color-navbar-link),1)}.navbar-light .navbar-nav li a.nav-link:hover{color:rgba(var(--pst-color-navbar-link-hover),1)}.navbar-light .navbar-nav>.active>.nav-link{font-weight:600;color:rgba(var(--pst-color-navbar-link-active),1)}.navbar-header a{padding:0 15px}.admonition{margin:1.5625em auto;padding:0 .6rem .8rem!important;overflow:hidden;page-break-inside:avoid;border-left:.2rem solid;border-left-color:rgba(var(--pst-color-admonition-default),1);border-bottom-color:rgba(var(--pst-color-admonition-default),1);border-right-color:rgba(var(--pst-color-admonition-default),1);border-top-color:rgba(var(--pst-color-admonition-default),1);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .05rem rgba(0,0,0,.1);transition:color .25s,background-color .25s,border-color .25s}.admonition :last-child{margin-bottom:0}.admonition p.admonition-title~*{padding:0 1.4rem}.admonition>ol,.admonition>ul{margin-left:1em}.admonition .admonition-title{position:relative;margin:0 -.6rem!important;padding:.4rem .6rem .4rem 2rem;font-weight:700;background-color:rgba(var(--pst-color-admonition-default),.1)}.admonition .admonition-title:before{position:absolute;left:.6rem;width:1rem;height:1rem;color:rgba(var(--pst-color-admonition-default),1);font-family:Font Awesome\ 5 Free;font-weight:900;content:var(--pst-icon-admonition-default)}.admonition .admonition-title+*{margin-top:.4em}.admonition.attention{border-color:rgba(var(--pst-color-admonition-attention),1)}.admonition.attention .admonition-title{background-color:rgba(var(--pst-color-admonition-attention),.1)}.admonition.attention .admonition-title:before{color:rgba(var(--pst-color-admonition-attention),1);content:var(--pst-icon-admonition-attention)}.admonition.caution{border-color:rgba(var(--pst-color-admonition-caution),1)}.admonition.caution .admonition-title{background-color:rgba(var(--pst-color-admonition-caution),.1)}.admonition.caution .admonition-title:before{color:rgba(var(--pst-color-admonition-caution),1);content:var(--pst-icon-admonition-caution)}.admonition.warning{border-color:rgba(var(--pst-color-admonition-warning),1)}.admonition.warning .admonition-title{background-color:rgba(var(--pst-color-admonition-warning),.1)}.admonition.warning .admonition-title:before{color:rgba(var(--pst-color-admonition-warning),1);content:var(--pst-icon-admonition-warning)}.admonition.danger{border-color:rgba(var(--pst-color-admonition-danger),1)}.admonition.danger .admonition-title{background-color:rgba(var(--pst-color-admonition-danger),.1)}.admonition.danger .admonition-title:before{color:rgba(var(--pst-color-admonition-danger),1);content:var(--pst-icon-admonition-danger)}.admonition.error{border-color:rgba(var(--pst-color-admonition-error),1)}.admonition.error .admonition-title{background-color:rgba(var(--pst-color-admonition-error),.1)}.admonition.error .admonition-title:before{color:rgba(var(--pst-color-admonition-error),1);content:var(--pst-icon-admonition-error)}.admonition.hint{border-color:rgba(var(--pst-color-admonition-hint),1)}.admonition.hint .admonition-title{background-color:rgba(var(--pst-color-admonition-hint),.1)}.admonition.hint .admonition-title:before{color:rgba(var(--pst-color-admonition-hint),1);content:var(--pst-icon-admonition-hint)}.admonition.tip{border-color:rgba(var(--pst-color-admonition-tip),1)}.admonition.tip .admonition-title{background-color:rgba(var(--pst-color-admonition-tip),.1)}.admonition.tip .admonition-title:before{color:rgba(var(--pst-color-admonition-tip),1);content:var(--pst-icon-admonition-tip)}.admonition.important{border-color:rgba(var(--pst-color-admonition-important),1)}.admonition.important .admonition-title{background-color:rgba(var(--pst-color-admonition-important),.1)}.admonition.important .admonition-title:before{color:rgba(var(--pst-color-admonition-important),1);content:var(--pst-icon-admonition-important)}.admonition.note{border-color:rgba(var(--pst-color-admonition-note),1)}.admonition.note .admonition-title{background-color:rgba(var(--pst-color-admonition-note),.1)}.admonition.note .admonition-title:before{color:rgba(var(--pst-color-admonition-note),1);content:var(--pst-icon-admonition-note)}div.deprecated{margin-bottom:10px;margin-top:10px;padding:7px;background-color:#f3e5e5;border:1px solid #eed3d7;border-radius:.5rem}div.deprecated p{color:#b94a48;display:inline}.topic{background-color:#eee}.seealso dd{margin-top:0;margin-bottom:0}.viewcode-back{font-family:var(--pst-font-family-base)}.viewcode-block:target{background-color:#f4debf;border-top:1px solid #ac9;border-bottom:1px solid #ac9}span.guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}table.field-list{border-collapse:separate;border-spacing:10px;margin-left:1px}table.field-list th.field-name{padding:1px 8px 1px 5px;white-space:nowrap;background-color:#eee}table.field-list td.field-body p{font-style:italic}table.field-list td.field-body p>strong{font-style:normal}table.field-list td.field-body blockquote{border-left:none;margin:0 0 .3em;padding-left:30px}.table.autosummary td:first-child{white-space:nowrap}.footer{width:100%;border-top:1px solid #ccc;padding-top:10px}.bd-search{position:relative;padding:1rem 15px;margin-right:-15px;margin-left:-15px}.bd-search .icon{position:absolute;color:#a4a6a7;left:25px;top:25px}.bd-search input{border-radius:0;border:0;border-bottom:1px solid #e5e5e5;padding-left:35px}.bd-toc{-ms-flex-order:2;order:2;height:calc(100vh - 2rem);overflow-y:auto}@supports (position:-webkit-sticky) or (position:sticky){.bd-toc{position:-webkit-sticky;position:sticky;top:calc(var(--pst-header-height) + 20px);height:calc(100vh - 5rem);overflow-y:auto}}.bd-toc .onthispage{color:#a4a6a7}.section-nav{padding-left:0;border-left:1px solid #eee;border-bottom:none}.section-nav ul{padding-left:1rem}.toc-entry,.toc-entry a{display:block}.toc-entry a{padding:.125rem 1.5rem;color:rgba(var(--pst-color-toc-link),1)}@media (min-width:1200px){.toc-entry a{padding-right:0}}.toc-entry a:hover{color:rgba(var(--pst-color-toc-link-hover),1);text-decoration:none}.bd-sidebar{padding-top:1em}@media (min-width:720px){.bd-sidebar{border-right:1px solid rgba(0,0,0,.1)}@supports (position:-webkit-sticky) or (position:sticky){.bd-sidebar{position:-webkit-sticky;position:sticky;top:calc(var(--pst-header-height) + 20px);z-index:1000;height:calc(100vh - 4rem)}}}.bd-sidebar.no-sidebar{border-right:0}.bd-links{padding-top:1rem;padding-bottom:1rem;margin-right:-15px;margin-left:-15px}@media (min-width:720px){.bd-links{display:block!important}@supports (position:-webkit-sticky) or (position:sticky){.bd-links{max-height:calc(100vh - 9rem);overflow-y:auto}}}.bd-sidenav{display:none}.bd-content{padding-top:20px}.bd-content .section{max-width:100%}.bd-content .section table{display:block;overflow:auto}.bd-toc-link{display:block;padding:.25rem 1.5rem;font-weight:600;color:rgba(0,0,0,.65)}.bd-toc-link:hover{color:rgba(0,0,0,.85);text-decoration:none}.bd-toc-item.active{margin-bottom:1rem}.bd-toc-item.active:not(:first-child){margin-top:1rem}.bd-toc-item.active>.bd-toc-link{color:rgba(0,0,0,.85)}.bd-toc-item.active>.bd-toc-link:hover{background-color:transparent}.bd-toc-item.active>.bd-sidenav{display:block}.bd-sidebar .nav ul{list-style:none;padding:0 1.5rem}.bd-sidebar .nav li>a{display:block;padding:.25rem 1.5rem;font-size:.9em;color:rgba(var(--pst-color-sidebar-link),1)}.bd-sidebar .nav li>a:hover{color:rgba(var(--pst-color-sidebar-link-hover),1);text-decoration:none;background-color:transparent}.bd-sidebar .nav .active:hover>a,.bd-sidebar .nav .active>a{font-weight:600;color:rgba(var(--pst-color-sidebar-link-active),1)}.toc-h2{font-size:.85rem}.toc-h3{font-size:.75rem}.toc-h4{font-size:.65rem}.toc-entry>.nav-link.active{font-weight:600;color:#130654;color:rgba(var(--pst-color-toc-link-active),1);background-color:transparent;border-left:2px solid rgba(var(--pst-color-toc-link-active),1)}.nav-link:hover{border-style:none}#navbar-main-elements li.nav-item i{font-size:.7rem;padding-left:2px;vertical-align:middle}.bd-toc .nav .nav{display:none}.bd-toc .nav .nav.visible,.bd-toc .nav>.active>ul{display:block}.prev-next-bottom{margin:20px 0}.prev-next-bottom a.left-prev,.prev-next-bottom a.right-next{padding:10px;border:1px solid rgba(0,0,0,.2);max-width:45%;overflow-x:hidden;color:rgba(0,0,0,.65)}.prev-next-bottom a.left-prev{float:left}.prev-next-bottom a.left-prev:before{content:"<< "}.prev-next-bottom a.right-next{float:right}.prev-next-bottom a.right-next:after{content:" >>"}.alert{padding-bottom:0}.alert-info a{color:#e83e8c}#navbar-icon-links i.fa,#navbar-icon-links i.fab,#navbar-icon-links i.far,#navbar-icon-links i.fas{vertical-align:middle;font-style:normal;font-size:1.5rem;line-height:1.25}#navbar-icon-links i.fa-github-square:before{color:#333}#navbar-icon-links i.fa-twitter-square:before{color:#55acee}#navbar-icon-links i.fa-gitlab:before{color:#548}.tocsection{border-left:1px solid #eee;padding:.3rem 1.5rem}.tocsection i{padding-right:.5rem}.editthispage{padding-top:2rem}.editthispage a{color:#130754}.xr-wrap[hidden]{display:block!important} \ No newline at end of file diff --git a/docs/_static/css/theme.css b/docs/_static/css/theme.css new file mode 100644 index 000000000..f90b09150 --- /dev/null +++ b/docs/_static/css/theme.css @@ -0,0 +1,111 @@ +:root { + /***************************************************************************** + * Theme config + **/ + --pst-header-height: 60px; + + /***************************************************************************** + * Font size + **/ + --pst-font-size-base: 15px; /* base font size - applied at body / html level */ + + /* heading font sizes */ + --pst-font-size-h1: 36px; + --pst-font-size-h2: 32px; + --pst-font-size-h3: 26px; + --pst-font-size-h4: 21px; + --pst-font-size-h5: 18px; + --pst-font-size-h6: 16px; + + /* smaller then heading font sizes*/ + --pst-font-size-milli: 12px; + + /***************************************************************************** + * Font family + **/ + /* These are adapted from https://systemfontstack.com/ */ + --pst-font-family-base-system: -apple-system, BlinkMacSystemFont, Segoe UI, "Helvetica Neue", + Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + --pst-font-family-monospace-system: "SFMono-Regular", Menlo, Consolas, Monaco, + Liberation Mono, Lucida Console, monospace; + + --pst-font-family-base: var(--pst-font-family-base-system); + --pst-font-family-heading: var(--pst-font-family-base); + --pst-font-family-monospace: var(--pst-font-family-monospace-system); + + /***************************************************************************** + * Color + * + * Colors are defined in rgb string way, "red, green, blue" + **/ + --pst-color-primary: 19, 6, 84; + --pst-color-success: 40, 167, 69; + --pst-color-info: 0, 123, 255; /*23, 162, 184;*/ + --pst-color-warning: 255, 193, 7; + --pst-color-danger: 220, 53, 69; + --pst-color-text-base: 51, 51, 51; + + --pst-color-h1: var(--pst-color-primary); + --pst-color-h2: var(--pst-color-primary); + --pst-color-h3: var(--pst-color-text-base); + --pst-color-h4: var(--pst-color-text-base); + --pst-color-h5: var(--pst-color-text-base); + --pst-color-h6: var(--pst-color-text-base); + --pst-color-paragraph: var(--pst-color-text-base); + --pst-color-link: 0, 91, 129; + --pst-color-link-hover: 227, 46, 0; + --pst-color-headerlink: 198, 15, 15; + --pst-color-headerlink-hover: 255, 255, 255; + --pst-color-preformatted-text: 34, 34, 34; + --pst-color-preformatted-background: 250, 250, 250; + + --pst-color-active-navigation: 19, 6, 84; + --pst-color-navbar-link: 77, 77, 77; + --pst-color-navbar-link-hover: var(--pst-color-active-navigation); + --pst-color-navbar-link-active: var(--pst-color-active-navigation); + --pst-color-sidebar-link: 77, 77, 77; + --pst-color-sidebar-link-hover: var(--pst-color-active-navigation); + --pst-color-sidebar-link-active: var(--pst-color-active-navigation); + --pst-color-toc-link: 119, 117, 122; + --pst-color-toc-link-hover: var(--pst-color-active-navigation); + --pst-color-toc-link-active: var(--pst-color-active-navigation); + + /***************************************************************************** + * Icon + **/ + + /* font awesome icons*/ + --pst-icon-check-circle: '\f058'; + --pst-icon-info-circle: '\f05a'; + --pst-icon-exclamation-triangle: '\f071'; + --pst-icon-exclamation-circle: '\f06a'; + --pst-icon-times-circle: '\f057'; + --pst-icon-lightbulb: '\f0eb'; + + /***************************************************************************** + * Admonitions + **/ + + --pst-color-admonition-default: var(--pst-color-info); + --pst-color-admonition-note: var(--pst-color-info); + --pst-color-admonition-attention: var(--pst-color-warning); + --pst-color-admonition-caution: var(--pst-color-warning); + --pst-color-admonition-warning: var(--pst-color-warning); + --pst-color-admonition-danger: var(--pst-color-danger); + --pst-color-admonition-error: var(--pst-color-danger); + --pst-color-admonition-hint: var(--pst-color-success); + --pst-color-admonition-tip: var(--pst-color-success); + --pst-color-admonition-important: var(--pst-color-success); + + --pst-icon-admonition-default: var(--pst-icon-info-circle); + --pst-icon-admonition-note: var(--pst-icon-info-circle); + --pst-icon-admonition-attention: var(--pst-icon-exclamation-circle); + --pst-icon-admonition-caution: var(--pst-icon-exclamation-triangle); + --pst-icon-admonition-warning: var(--pst-icon-exclamation-triangle); + --pst-icon-admonition-danger: var(--pst-icon-exclamation-triangle); + --pst-icon-admonition-error: var(--pst-icon-times-circle); + --pst-icon-admonition-hint: var(--pst-icon-lightbulb); + --pst-icon-admonition-tip: var(--pst-icon-lightbulb); + --pst-icon-admonition-important: var(--pst-icon-exclamation-circle); + +} diff --git a/docs/_static/doctools.js b/docs/_static/doctools.js index daccd209d..b33f87fcb 100644 --- a/docs/_static/doctools.js +++ b/docs/_static/doctools.js @@ -4,7 +4,7 @@ * * Sphinx JavaScript utilities for all documentation. * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @@ -283,11 +283,10 @@ var Documentation = { }, initOnKeyListeners: function() { - $(document).keydown(function(event) { + $(document).keyup(function(event) { var activeElementType = document.activeElement.tagName; // don't navigate when in search box or textarea - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' - && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) { + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { switch (event.keyCode) { case 37: // left var prevHref = $('link[rel="prev"]').prop('href'); diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js index 45d13f3f6..4fbdd0e85 100644 --- a/docs/_static/documentation_options.js +++ b/docs/_static/documentation_options.js @@ -3,9 +3,7 @@ var DOCUMENTATION_OPTIONS = { VERSION: '2.0', LANGUAGE: 'None', COLLAPSE_INDEX: false, - BUILDER: 'html', FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', HAS_SOURCE: true, SOURCELINK_SUFFIX: '.txt', NAVIGATION_WITH_KEYS: true diff --git a/docs/_static/jquery-3.4.1.js b/docs/_static/jquery-3.4.1.js new file mode 100644 index 000000000..773ad95c5 --- /dev/null +++ b/docs/_static/jquery-3.4.1.js @@ -0,0 +1,10598 @@ +/*! + * jQuery JavaScript Library v3.4.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2019-05-01T21:04Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.4.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a global context + globalEval: function( code, options ) { + DOMEval( code, { nonce: options && options.nonce } ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.4 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2019-04-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) && + + // Support: IE 8 only + // Exclude object elements + (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && rdescend.test( selector ) ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = (elem.ownerDocument || elem).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( typeof elem.contentDocument !== "undefined" ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "", "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "" ], + col: [ 2, "", "" ], + tr: [ 2, "", "" ], + td: [ 3, "", "" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "x"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = / + {% endmacro %} \ No newline at end of file diff --git a/docs/aberrations.html b/docs/aberrations.html index 6be38c603..815ed5afb 100644 --- a/docs/aberrations.html +++ b/docs/aberrations.html @@ -1,13 +1,13 @@ - + - aberrations — DeepTrack 2.0 documentation - + + - - + - - - - - + + + + + @@ -44,39 +40,44 @@ - - + + + + + DeepTrack + + + - - - - Getting started - - - - Tutorials and notebooks - - - - Documentation - - + + + Getting started + + + + + + Tutorials and notebooks + + + + + + Documentation + + + - - - - - - + @@ -86,129 +87,110 @@ - - - + + + - - - - - - - - - - - - - - - - backend - - - - - - aberrations - - - - - - augmentations - - - - - - features - - - - - - generators - - - - - - image - - - - - - layers - - - - - - losses - - - - - - math - - - - - - models - - - - - - noises - - - - - - optics - - - - - - properties - - - - - - scatterers - - - - - - sequences - - - - - - utils - - - - - - - + + + + backend + + + + + aberrations + + + + + augmentations + + + + + features + + + + + generators + + + + + image + + + + + layers + + + + + losses + + + + + math + + + + + models + + + + + noises + + + + + optics + + + + + properties + + + + + scatterers + + + + + sequences + + + + + test + + + + + types + + + + + utils + + + + - + + @@ -219,75 +201,91 @@ - - - - Classes - - - - Module classes - - - Aberration - - - - Astigmatism - - - - Defocus - - - - GaussianApodization - - - - HorizontalComa - - - - HorizontalTilt - - - - ObliqueAstigmatism - - - - ObliqueTrefoil - - - - Piston - - - - SphericalAberration - - - - Trefoil - - - - VerticalComa - - - - VerticalTilt - - - - Zernike - - - - - - + + + + Classes + + + + + Module classes + + + + + Aberration + + + + + Astigmatism + + + + + Defocus + + + + + GaussianApodization + + + + + HorizontalComa + + + + + HorizontalTilt + + + + + ObliqueAstigmatism + + + + + ObliqueTrefoil + + + + + Piston + + + + + SphericalAberration + + + + + Trefoil + + + + + VerticalComa + + + + + VerticalTilt + + + + + Zernike + + + + + + @@ -296,6 +294,9 @@ + + + @@ -340,9 +341,9 @@ Classes¶ Aberration¶ - + -class deeptrack.aberrations.Aberration(*args: dict, **kwargs)¶ +class deeptrack.aberrations.Aberration(*args, **kwargs)¶ Base abstract class. Ensures that the method .get() receives rho and theta as optional arguments, describing the polar coordinates of each pixel in the image @@ -373,9 +374,9 @@ Aberration Astigmatism¶ - + -class deeptrack.aberrations.Astigmatism(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.Astigmatism(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=2, m=2. Parameters @@ -411,9 +412,9 @@ Astigmatism Defocus¶ - + -class deeptrack.aberrations.Defocus(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.Defocus(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=2, m=0. Parameters @@ -449,9 +450,9 @@ Defocus GaussianApodization¶ - + -class deeptrack.aberrations.GaussianApodization(sigma=1, offset=0, 0, **kwargs)¶ +class deeptrack.aberrations.GaussianApodization(sigma=1, offset=(0, 0), **kwargs)¶ Introduces pupil apodization. Decreases the amplitude of the pupil at high frequencies according to a Gaussian distribution. @@ -492,9 +493,9 @@ GaussianApodization HorizontalComa¶ - + -class deeptrack.aberrations.HorizontalComa(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.HorizontalComa(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=3, m=1. Parameters @@ -530,9 +531,9 @@ HorizontalComa HorizontalTilt¶ - + -class deeptrack.aberrations.HorizontalTilt(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.HorizontalTilt(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=1, m=1. Parameters @@ -568,9 +569,9 @@ HorizontalTilt ObliqueAstigmatism¶ - + -class deeptrack.aberrations.ObliqueAstigmatism(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.ObliqueAstigmatism(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=2, m=-2. Parameters @@ -606,9 +607,9 @@ ObliqueAstigmatism ObliqueTrefoil¶ - + -class deeptrack.aberrations.ObliqueTrefoil(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.ObliqueTrefoil(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=3, m=-3. Parameters @@ -644,9 +645,9 @@ ObliqueTrefoil Piston¶ - + -class deeptrack.aberrations.Piston(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.Piston(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=0, m=0. Parameters @@ -682,9 +683,9 @@ Piston SphericalAberration¶ - + -class deeptrack.aberrations.SphericalAberration(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.SphericalAberration(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=4, m=0. Parameters @@ -720,9 +721,9 @@ SphericalAberration Trefoil¶ - + -class deeptrack.aberrations.Trefoil(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.Trefoil(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=3, m=3. Parameters @@ -758,9 +759,9 @@ Trefoil VerticalComa¶ - + -class deeptrack.aberrations.VerticalComa(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.VerticalComa(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=3, m=-1. Parameters @@ -796,9 +797,9 @@ VerticalComa VerticalTilt¶ - + -class deeptrack.aberrations.VerticalTilt(*args, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.VerticalTilt(*args, coefficient=1, **kwargs)¶ Zernike polynomial with n=1, m=-1. Parameters @@ -834,9 +835,9 @@ VerticalTilt Zernike¶ - + -class deeptrack.aberrations.Zernike(n, m, coefficient=1, **kwargs)¶ +class deeptrack.aberrations.Zernike(n, m, coefficient=1, **kwargs)¶ Introduces a Zernike phase aberration. Calculates the Zernike polynomial defined by the numbers n and m at each pixel in the pupil, multiplies it by coefficient, and adds the @@ -899,14 +900,14 @@ Zernike + diff --git a/docs/advanced.html b/docs/advanced.html index eb617cb39..1f2e0136e 100644 --- a/docs/advanced.html +++ b/docs/advanced.html @@ -1,13 +1,13 @@ - + - Advanced Topics — DeepTrack 2.0 documentation - + + - - + - - - - - + + + + + @@ -44,39 +40,44 @@ - - + + + + + DeepTrack + + + - - - - Getting started - - - - Tutorials and notebooks - - - - Documentation - - + + + Getting started + + + + + + Tutorials and notebooks + + + + + + Documentation + + + - - - - - - + @@ -86,45 +87,30 @@ - - - + + + - - - - - - - - - - - - - - Basics - - - - - - Advanced Topics - - - - - - - - - + + + + Basics + + + + + Advanced Topics + + + + - + + @@ -135,35 +121,41 @@ - - - - Dummy arguments - - - - Dependent arguments - - - - Dependencies between features - - - - Overriding properties - - - Overriding with update - - - - Overriding with resolve - - - - - - + + + + Dummy arguments + + + + + Dependent arguments + + + + + Dependencies between features + + + + + Overriding properties + + + + + Overriding with update + + + + + Overriding with resolve + + + + + + @@ -172,6 +164,9 @@ + + + @@ -328,14 +323,14 @@ Overriding with resolve - + diff --git a/docs/augmentations.html b/docs/augmentations.html index 879a1f7eb..729637961 100644 --- a/docs/augmentations.html +++ b/docs/augmentations.html @@ -1,13 +1,13 @@ - + - augmentations — DeepTrack 2.0 documentation - + + - - + - - - - - + + + + + @@ -44,39 +40,44 @@ - - + + + + DeepTrack + + + + - - - - Getting started - - - - Tutorials and notebooks - - - - Documentation - - + + + Getting started + + + + + + Tutorials and notebooks + + + + + + Documentation + + + - - - - - - + @@ -86,129 +87,110 @@ - - - + + + - - - - - - - - - - - - - - - - backend - - - - - - aberrations - - - - - - augmentations - - - - - - features - - - - - - generators - - - - - - image - - - - - - layers - - - - - - losses - - - - - - math - - - - - - models - - - - - - noises - - - - - - optics - - - - - - properties - - - - - - scatterers - - - - - - sequences - - - - - - utils - - - - - - - + + + + backend + + + + + aberrations + + + + + augmentations + + + + + features + + + + + generators + + + + + image + + + + + layers + + + + + losses + + + + + math + + + + + models + + + + + noises + + + + + optics + + + + + properties + + + + + scatterers + + + + + sequences + + + + + test + + + + + types + + + + + utils + + + + - + + @@ -219,59 +201,76 @@ - - - - Classes - - - - Module classes - - - Affine - - - - Augmentation - - - - Crop - - - - CropToMultiplesOf - - - - ElasticTransformation - - - - FlipDiagonal - - - - FlipLR - - - - FlipUD - - - - Pad - - - - PreLoad - - - - - - + + + + Classes + + + + + Module classes + + + + + Affine + + + + + Augmentation + + + + + Crop + + + + + CropToMultiplesOf + + + + + ElasticTransformation + + + + + FlipDiagonal + + + + + FlipLR + + + + + FlipUD + + + + + Pad + + + + + PadToMultiplesOf + + + + + PreLoad + + + + + + @@ -280,6 +279,9 @@ + + + @@ -310,9 +312,9 @@ Classes¶ Affine¶ - + -class deeptrack.augmentations.Affine(scale=1, translate=None, translate_px=0, rotate=0, shear=0, order=1, cval=0, mode='reflect', **kwargs)¶ +class deeptrack.augmentations.Affine(scale=1, translate=None, translate_px=0, rotate=0, shear=0, order=1, cval=0, mode='reflect', **kwargs)¶ Augmenter to apply affine transformations to images. Affine transformations involve: @@ -394,9 +396,9 @@ Affine Augmentation¶ - + -class deeptrack.augmentations.Augmentation(feature: deeptrack.features.Feature = None, load_size: int = 1, updates_per_reload: int = 1, update_properties: Callable = None, **kwargs)¶ +class deeptrack.augmentations.Augmentation(feature=None, load_size=1, updates_per_reload=1, update_properties=None, **kwargs)¶ Base abstract augmentation class. Augmentations are features that can resolve more than one image without calling .resolve() on the parent feature. Specifically, they create @@ -465,9 +467,9 @@ Augmentation Crop¶ - + -class deeptrack.augmentations.Crop(*args, crop=64, 64, crop_mode='retain', corner='random', **kwargs)¶ +class deeptrack.augmentations.Crop(*args, crop=(64, 64), crop_mode='retain', corner='random', **kwargs)¶ Crops a regions of an image. Parameters @@ -478,8 +480,9 @@ Crop¶ If a tuple or list, it is assumed to be per axis. Can also be a function that returns any of the other types. -crop_modestr {“remove”, “retain”}How the crop argument is interpreted. If “remove”, then -crop +crop_modestr {“retain”, “remove”}How the crop argument is interpreted. If “remove”, then +crop denotes the amount to crop from the edges. If “retain”, +crop denotes the size of the output. cornertuple of ints or Callable[Image]->tuple of ints or “random”Top left corner of the cropped region. Can be a tuple of ints, a function that returns a tuple of ints or the string random. @@ -526,9 +529,9 @@ Crop¶ CropToMultiplesOf¶ - + -class deeptrack.augmentations.CropToMultiplesOf(multiple=1, corner='random', **kwargs)¶ +class deeptrack.augmentations.CropToMultiplesOf(multiple=1, corner='random', **kwargs)¶ Crop images down until their height/width is a multiple of a value. Parameters @@ -577,9 +580,9 @@ CropToMultiplesOf ElasticTransformation¶ - + -class deeptrack.augmentations.ElasticTransformation(alpha=20, sigma=2, ignore_last_dim=True, order=3, cval=0, mode='constant', **kwargs)¶ +class deeptrack.augmentations.ElasticTransformation(alpha=20, sigma=2, ignore_last_dim=True, order=3, cval=0, mode='constant', **kwargs)¶ Transform images by moving pixels locally around using displacement fields. The augmenter creates a random distortion field using alpha and sigma, which define the strength and smoothness of the field respectively. These are used to transform the @@ -664,9 +667,9 @@ ElasticTransformation FlipDiagonal¶ - + -class deeptrack.augmentations.FlipDiagonal(*args, **kwargs)¶ +class deeptrack.augmentations.FlipDiagonal(*args, **kwargs)¶ Flips images along the main diagonal. Updates all properties called “position” by swapping the first and second index. Methods @@ -706,9 +709,9 @@ FlipDiagonal FlipLR¶ - + -class deeptrack.augmentations.FlipLR(*args, **kwargs)¶ +class deeptrack.augmentations.FlipLR(*args, **kwargs)¶ Flips images left-right. Updates all properties called “position” to flip the second index. Methods @@ -748,9 +751,9 @@ FlipLR FlipUD¶ - + -class deeptrack.augmentations.FlipUD(*args, **kwargs)¶ +class deeptrack.augmentations.FlipUD(*args, **kwargs)¶ Flips images up-down. Updates all properties called “position” by flipping the first index. Methods @@ -790,9 +793,9 @@ FlipUD Pad¶ - + -class deeptrack.augmentations.Pad(px=0, 0, 0, 0, mode='constant', cval=0, **kwargs)¶ +class deeptrack.augmentations.Pad(px=(0, 0, 0, 0), mode='constant', cval=0, **kwargs)¶ Pads the image. Arguments match this of numpy.pad, save for pad_width, which is called px, and is defined as (left, right, up, down, before_axis_3, after_axis_3, …) @@ -838,12 +841,63 @@ Pad¶ + + +PadToMultiplesOf¶ + + +class deeptrack.augmentations.PadToMultiplesOf(multiple=1, **kwargs)¶ +Pad images until their height/width is a multiple of a value. + +Parameters + +multipleint or tuple of (int or None)Images will be padded until their width is a multiple of +this value. If a tuple, it is assumed to be a multiple per axis. +A value of None or -1 indicates to skip that axis. + + + + +Methods + + + + + + +plot([input_image, resolve_kwargs, interval]) +Visualizes the output of the feature. + +resolve([image_list]) +Creates the image. + +sample(**kwargs) +Returns the feature + +update(**kwargs) +Updates the state of all properties. + + + + + + + + + +update_properties + + + + + + PreLoad¶ - + -class deeptrack.augmentations.PreLoad(feature: deeptrack.features.Feature = None, load_size: int = 1, updates_per_reload: int = 1, update_properties: Callable = None, **kwargs)¶ +class deeptrack.augmentations.PreLoad(feature=None, load_size=1, updates_per_reload=1, update_properties=None, **kwargs)¶ Simple storage with no augmentation. Parameters @@ -920,14 +974,14 @@ PreLoad + diff --git a/docs/backend.html b/docs/backend.html index f78368820..f24785de3 100644 --- a/docs/backend.html +++ b/docs/backend.html @@ -1,13 +1,13 @@ - + - backend — DeepTrack 2.0 documentation - + + - - + - - - - - + + + + + @@ -44,39 +40,44 @@ - - + + + + + DeepTrack + + + - - - - Getting started - - - - Tutorials and notebooks - - - - Documentation - - + + + Getting started + + + + + + Tutorials and notebooks + + + + + + Documentation + + + - - - - - - + @@ -86,129 +87,110 @@ - - - + + + - - - - - - - - - - - - - - - - backend - - - - - - aberrations - - - - - - augmentations - - - - - - features - - - - - - generators - - - - - - image - - - - - - layers - - - - - - losses - - - - - - math - - - - - - models - - - - - - noises - - - - - - optics - - - - - - properties - - - - - - scatterers - - - - - - sequences - - - - - - utils - - - - - - - + + + + backend + + + + + aberrations + + + + + augmentations + + + + + features + + + + + generators + + + + + image + + + + + layers + + + + + losses + + + + + math + + + + + models + + + + + noises + + + + + optics + + + + + properties + + + + + scatterers + + + + + sequences + + + + + test + + + + + types + + + + + utils + + + + - + + @@ -219,85 +201,102 @@ + + + + mie + + + + + Module functions + - - - mie - - - Module functions - - - mie_coefficients - - - - mie_harmonics - - - - stratified_mie_coefficients - - - - - - - - - - polynomials - - - Module functions - - - besselj - - - - bessely - - - - dbesselj - - - - dbessely - - - - dricbesh - - - - dricbesj - - - - dricbesy - - - - ricbesh - - - - ricbesj - - - - ricbesy - - - - - - - - + + + mie_coefficients + + + + + mie_harmonics + + + + + stratified_mie_coefficients + + + + + + + + + polynomials + + + + + Module functions + + + + + besselj + + + + + bessely + + + + + dbesselj + + + + + dbessely + + + + + dricbesh + + + + + dricbesj + + + + + dricbesy + + + + + ricbesh + + + + + ricbesj + + + + + ricbesy + + + + + + + @@ -306,6 +305,9 @@ + + + @@ -318,9 +320,9 @@ mie¶ Module functions¶ mie_coefficients¶ - + -deeptrack.backend.mie_coefficients(m: float, a: float, L: int) → Tuple[numpy.ndarray]¶ +deeptrack.backend.mie_coefficients(m, a, L)¶ Calculate the coefficients of the spherical harmonics. Calculates the terms up to (and including) order L using riccati-bessel polynomials. @@ -346,9 +348,9 @@ mie_coefficients mie_harmonics¶ - + -deeptrack.backend.mie_harmonics(x: numpy.ndarray, L: int) → Tuple[numpy.ndarray]¶ +deeptrack.backend.mie_harmonics(x, L)¶ Calculates the spherical harmonics of the mie field. The harmonics are calculated up to order L using the iterative method. @@ -373,9 +375,9 @@ mie_harmonics stratified_mie_coefficients¶ - + -deeptrack.backend.stratified_mie_coefficients(m, a, L)¶ +deeptrack.backend.stratified_mie_coefficients(m, a, L)¶ Calculate the coefficients of the stratified spherical harmonics. Calculates the terms up to (and including) order L using riccati-bessel polynomials. @@ -409,9 +411,9 @@ polynomials¶ besselj¶ - + -deeptrack.backend.besselj(l, x)¶ +deeptrack.backend.besselj(l, x)¶ The Bessel polynomial of the first kind. Parameters @@ -434,9 +436,9 @@ besselj bessely¶ - + -deeptrack.backend.bessely(l, x)¶ +deeptrack.backend.bessely(l, x)¶ The Bessel polynomial of the second kind. Parameters @@ -459,9 +461,9 @@ bessely dbesselj¶ - + -deeptrack.backend.dbesselj(l, x)¶ +deeptrack.backend.dbesselj(l, x)¶ The first derivative of the Bessel polynomial of the first kind. Parameters @@ -484,9 +486,9 @@ dbesselj dbessely¶ - + -deeptrack.backend.dbessely(l, x)¶ +deeptrack.backend.dbessely(l, x)¶ The first derivative of the Bessel polynomial of the second kind. Parameters @@ -509,9 +511,9 @@ dbessely dricbesh¶ - + -deeptrack.backend.dricbesh(l, x)¶ +deeptrack.backend.dricbesh(l, x)¶ The first derivative of the Riccati-Bessel polynomial of the third kind. Parameters @@ -534,9 +536,9 @@ dricbesh dricbesj¶ - + -deeptrack.backend.dricbesj(l, x)¶ +deeptrack.backend.dricbesj(l, x)¶ The first derivative of the Riccati-Bessel polynomial of the first kind. Parameters @@ -559,9 +561,9 @@ dricbesj dricbesy¶ - + -deeptrack.backend.dricbesy(l, x)¶ +deeptrack.backend.dricbesy(l, x)¶ The first derivative of the Riccati-Bessel polynomial of the second kind. Parameters @@ -584,9 +586,9 @@ dricbesy ricbesh¶ - + -deeptrack.backend.ricbesh(l, x)¶ +deeptrack.backend.ricbesh(l, x)¶ The Riccati-Bessel polynomial of the third kind. Parameters @@ -609,9 +611,9 @@ ricbesh ricbesj¶ - + -deeptrack.backend.ricbesj(l, x)¶ +deeptrack.backend.ricbesj(l, x)¶ The Riccati-Bessel polynomial of the first kind. Parameters @@ -634,9 +636,9 @@ ricbesj ricbesy¶ - + -deeptrack.backend.ricbesy(l, x)¶ +deeptrack.backend.ricbesy(l, x)¶ The Riccati-Bessel polynomial of the second kind. Parameters @@ -679,14 +681,14 @@ ricbesy + diff --git a/docs/basics.html b/docs/basics.html index 99a238992..a72794e6b 100644 --- a/docs/basics.html +++ b/docs/basics.html @@ -1,13 +1,13 @@ - + - Basics — DeepTrack 2.0 documentation - + + - - + - - - - - + + + + + @@ -44,39 +40,44 @@ - - + + + + + DeepTrack + + + - - - - Getting started - - - - Tutorials and notebooks - - - - Documentation - - + + + Getting started + + + + + + Tutorials and notebooks + + + + + + Documentation + + + - - - - - - + @@ -86,45 +87,30 @@ - - - + + + - - - - - - - - - - - - - - Basics - - - - - - Advanced Topics - - - - - - - - - + + + + Basics + + + + + Advanced Topics + + + + - + + @@ -135,29 +121,34 @@ - - - - Features - - - - Chaining features - - - - Executing a feature multiple times - - - - Using functions as arguments - - - - Done! - - - + + + + Features + + + + + Chaining features + + + + + Executing a feature multiple times + + + + + Using functions as arguments + + + + + Done! + + + + @@ -166,6 +157,9 @@ + + + @@ -265,14 +259,14 @@ Done! - + diff --git a/docs/deeptrack.html b/docs/deeptrack.html index 6e809a683..63020fbda 100644 --- a/docs/deeptrack.html +++ b/docs/deeptrack.html @@ -1,13 +1,13 @@ - + - Documentation — DeepTrack 2.0 documentation - + + - - + - - - - - + + + + + @@ -44,40 +40,45 @@ - - + + + + DeepTrack + + + + - - - - Getting started - - - - Tutorials and notebooks - - - - Documentation - - - - + + + Getting started + + + + + Tutorials and notebooks + + - + + + Documentation + + - - + + + @@ -86,129 +87,110 @@ - - - + + + - - - - - - - - - - - - - - - - backend - - - - - - aberrations - - - - - - augmentations - - - - - - features - - - - - - generators - - - - - - image - - - - - - layers - - - - - - losses - - - - - - math - - - - - - models - - - - - - noises - - - - - - optics - - - - - - properties - - - - - - scatterers - - - - - - sequences - - - - - - utils - - - - - - - + + + + backend + + + + + aberrations + + + + + augmentations + + + + + features + + + + + generators + + + + + image + + + + + layers + + + + + losses + + + + + math + + + + + models + + + + + noises + + + + + optics + + + + + properties + + + + + scatterers + + + + + sequences + + + + + test + + + + + types + + + + + utils + + + + - + + @@ -216,9 +198,7 @@ - - @@ -227,6 +207,9 @@ + + + @@ -250,6 +233,8 @@ Documentationproperties scatterers sequences +test +types utils @@ -273,14 +258,14 @@ Documentation + diff --git a/docs/features.html b/docs/features.html index 0e03ecae4..8b4e327e3 100644 --- a/docs/features.html +++ b/docs/features.html @@ -1,13 +1,13 @@ - + - features — DeepTrack 2.0 documentation - + + - - + - - - - - + + + + + @@ -44,40 +40,45 @@ - - + + + + + DeepTrack + + + - - - - Getting started - - - - Tutorials and notebooks - - - - Documentation - - - - + + + Getting started + + + + + Tutorials and notebooks + + - + + + Documentation + + - - + + + @@ -86,129 +87,110 @@ - - - + + + - - - - - - - - - - - - - - - - backend - - - - - - aberrations - - - - - - augmentations - - - - - - features - - - - - - generators - - - - - - image - - - - - - layers - - - - - - losses - - - - - - math - - - - - - models - - - - - - noises - - - - - - optics - - - - - - properties - - - - - - scatterers - - - - - - sequences - - - - - - utils - - - - - - - + + + + backend + + + + + aberrations + + + + + augmentations + + + + + features + + + + + generators + + + + + image + + + + + layers + + + + + losses + + + + + math + + + + + models + + + + + noises + + + + + optics + + + + + properties + + + + + scatterers + + + + + sequences + + + + + test + + + + + types + + + + + utils + + + + - + + @@ -219,79 +201,121 @@ - - - - Classes - - - - Module classes - - - Branch - - - - Combine - - - - ConditionalSetFeature - - - - ConditionalSetProperty - - - - Dataset - - - - DummyFeature - - - - Duplicate - - - - Feature - - - - Label - - - - Lambda - - - - LoadImage - - - - Merge - - - - Probability - - - - SampleToMasks - - - - StructuralFeature - - - - - - + + + + Classes + + + + + Module classes + + + + + AsType + + + + + Bind + + + + + BindResolve + + + + + BindUpdate + + + + + Branch + + + + + Combine + + + + + ConditionalSetFeature + + + + + ConditionalSetProperty + + + + + Dataset + + + + + DummyFeature + + + + + Duplicate + + + + + Feature + + + + + Label + + + + + Lambda + + + + + LoadImage + + + + + Merge + + + + + Probability + + + + + SampleToMasks + + + + + Slice + + + + + StructuralFeature + + + + + + @@ -300,6 +324,9 @@ + + + @@ -315,24 +342,156 @@ Classes Module classes¶ + +AsType¶ + + +class deeptrack.features.AsType(dtype='float64', **kwargs)¶ +Converts the data type of images +Accepts same types as numpy arrays. Common types include +float64, int32, uint16, int16, uint8, int8 + +Parameters + +dtypestrdtype string. Same as numpy dtype. + + + + +Methods + + + + + + +plot([input_image, resolve_kwargs, interval]) +Visualizes the output of the feature. + +resolve([image_list]) +Creates the image. + +sample(**kwargs) +Returns the feature + +update(**kwargs) +Updates the state of all properties. + + + + + + + +Bind¶ + + +class deeptrack.features.Bind(feature, **kwargs)¶ +Binds a feature with property arguments. +When the feature is resolved, the kwarg arguments are passed +to the child feature. + +Parameters + +featureFeatureThe child feature + +**kwargsProperties to send to child + + + + +Methods + + + + + + +plot([input_image, resolve_kwargs, interval]) +Visualizes the output of the feature. + +resolve([image_list]) +Creates the image. + +sample(**kwargs) +Returns the feature + +update(**kwargs) +Updates the state of all properties. + + + + + + + +BindResolve¶ + + +deeptrack.features.BindResolve¶ +alias of deeptrack.features.Bind + + + + +BindUpdate¶ + + +class deeptrack.features.BindUpdate(feature, **kwargs)¶ +Binds a feature with certain arguments. +When the feature is updated, the child feature + +Parameters + +featureFeatureThe child feature + +**kwargsProperties to send to child + + + + +Methods + + + + + + +plot([input_image, resolve_kwargs, interval]) +Visualizes the output of the feature. + +resolve([image_list]) +Creates the image. + +sample(**kwargs) +Returns the feature + +update(**kwargs) +Updates the state of all properties. + + + + + + Branch¶ - + -class deeptrack.features.Branch(feature_1: deeptrack.features.Feature, feature_2: deeptrack.features.Feature, *args, **kwargs)¶ +class deeptrack.features.Branch(feature_1, feature_2, *args, **kwargs)¶ Resolves two features sequentially. Passes the output of the first to the input of the second. Parameters @@ -365,9 +524,9 @@ Branch Combine¶ - + -class deeptrack.features.Combine(features=[], **kwargs)¶ +class deeptrack.features.Combine(features, **kwargs)¶ Combines multiple features into a single feature. Resolves each feature in features and returns them as a list of features. @@ -404,9 +563,9 @@ Combine ConditionalSetFeature¶ - + -class deeptrack.features.ConditionalSetFeature(on_false: deeptrack.features.Feature = None, on_true: deeptrack.features.Feature = None, condition='is_label', **kwargs)¶ +class deeptrack.features.ConditionalSetFeature(on_false=None, on_true=None, condition='is_label', **kwargs)¶ Conditionally resolves one of two features Set condition to the value to listen to. Example, if condition is “is_label”, then conditiona can be toggled @@ -452,9 +611,9 @@ ConditionalSetFeature ConditionalSetProperty¶ - + -class deeptrack.features.ConditionalSetProperty(
DeepTrack
deeptrack.aberrations.
Aberration
Base abstract class.
Ensures that the method .get() receives rho and theta as optional arguments, describing the polar coordinates of each pixel in the image @@ -373,9 +374,9 @@
Astigmatism
Zernike polynomial with n=2, m=2.
Defocus
Zernike polynomial with n=2, m=0.
GaussianApodization
Introduces pupil apodization.
Decreases the amplitude of the pupil at high frequencies according to a Gaussian distribution.
HorizontalComa
Zernike polynomial with n=3, m=1.
HorizontalTilt
Zernike polynomial with n=1, m=1.
ObliqueAstigmatism
Zernike polynomial with n=2, m=-2.
ObliqueTrefoil
Zernike polynomial with n=3, m=-3.
Piston
Zernike polynomial with n=0, m=0.
SphericalAberration
Zernike polynomial with n=4, m=0.
Trefoil
Zernike polynomial with n=3, m=3.
VerticalComa
Zernike polynomial with n=3, m=-1.
VerticalTilt
Zernike polynomial with n=1, m=-1.
Zernike
Introduces a Zernike phase aberration.
Calculates the Zernike polynomial defined by the numbers n and m at each pixel in the pupil, multiplies it by coefficient, and adds the @@ -899,14 +900,14 @@
deeptrack.augmentations.
Affine
Augmenter to apply affine transformations to images.
Affine transformations involve:
@@ -394,9 +396,9 @@ Affine Augmentation¶ - + -class deeptrack.augmentations.Augmentation(feature: deeptrack.features.Feature = None, load_size: int = 1, updates_per_reload: int = 1, update_properties: Callable = None, **kwargs)¶ +class deeptrack.augmentations.Augmentation(feature=None, load_size=1, updates_per_reload=1, update_properties=None, **kwargs)¶ Base abstract augmentation class. Augmentations are features that can resolve more than one image without calling .resolve() on the parent feature. Specifically, they create @@ -465,9 +467,9 @@ Augmentation Crop¶ - + -class deeptrack.augmentations.Crop(*args, crop=64, 64, crop_mode='retain', corner='random', **kwargs)¶ +class deeptrack.augmentations.Crop(*args, crop=(64, 64), crop_mode='retain', corner='random', **kwargs)¶ Crops a regions of an image. Parameters @@ -478,8 +480,9 @@ Crop¶ If a tuple or list, it is assumed to be per axis. Can also be a function that returns any of the other types. -crop_modestr {“remove”, “retain”}How the crop argument is interpreted. If “remove”, then -crop +crop_modestr {“retain”, “remove”}How the crop argument is interpreted. If “remove”, then +crop denotes the amount to crop from the edges. If “retain”, +crop denotes the size of the output. cornertuple of ints or Callable[Image]->tuple of ints or “random”Top left corner of the cropped region. Can be a tuple of ints, a function that returns a tuple of ints or the string random. @@ -526,9 +529,9 @@ Crop¶
Augmentation
Base abstract augmentation class.
Augmentations are features that can resolve more than one image without calling .resolve() on the parent feature. Specifically, they create @@ -465,9 +467,9 @@
Crop
Crops a regions of an image.
How the crop argument is interpreted. If “remove”, then -crop
How the crop argument is interpreted. If “remove”, then +crop denotes the amount to crop from the edges. If “retain”, +crop denotes the size of the output.
Top left corner of the cropped region. Can be a tuple of ints, a function that returns a tuple of ints or the string random. @@ -526,9 +529,9 @@
CropToMultiplesOf
Crop images down until their height/width is a multiple of a value.
ElasticTransformation
Transform images by moving pixels locally around using displacement fields.
The augmenter creates a random distortion field using alpha and sigma, which define the strength and smoothness of the field respectively. These are used to transform the @@ -664,9 +667,9 @@
FlipDiagonal
Flips images along the main diagonal.
Updates all properties called “position” by swapping the first and second index.
Methods
FlipLR
Flips images left-right.
Updates all properties called “position” to flip the second index.
FlipUD
Flips images up-down.
Updates all properties called “position” by flipping the first index.
Pad
Pads the image.
Arguments match this of numpy.pad, save for pad_width, which is called px, and is defined as (left, right, up, down, before_axis_3, after_axis_3, …)
PadToMultiplesOf
Pad images until their height/width is a multiple of a value.
Images will be padded until their width is a multiple of +this value. If a tuple, it is assumed to be a multiple per axis. +A value of None or -1 indicates to skip that axis.
plot([input_image, resolve_kwargs, interval])
plot
Visualizes the output of the feature.
resolve([image_list])
resolve
Creates the image.
sample(**kwargs)
sample
Returns the feature
update(**kwargs)
update
Updates the state of all properties.
update_properties
PreLoad
Simple storage with no augmentation.
deeptrack.backend.
mie_coefficients
Calculate the coefficients of the spherical harmonics.
Calculates the terms up to (and including) order L using riccati-bessel polynomials.
mie_harmonics
Calculates the spherical harmonics of the mie field.
The harmonics are calculated up to order L using the iterative method.
stratified_mie_coefficients
Calculate the coefficients of the stratified spherical harmonics.
besselj
The Bessel polynomial of the first kind.
bessely
The Bessel polynomial of the second kind.
dbesselj
The first derivative of the Bessel polynomial of the first kind.
dbessely
The first derivative of the Bessel polynomial of the second kind.
dricbesh
The first derivative of the Riccati-Bessel polynomial of the third kind.
dricbesj
The first derivative of the Riccati-Bessel polynomial of the first kind.
dricbesy
The first derivative of the Riccati-Bessel polynomial of the second kind.
ricbesh
The Riccati-Bessel polynomial of the third kind.
ricbesj
The Riccati-Bessel polynomial of the first kind.
ricbesy
The Riccati-Bessel polynomial of the second kind.
deeptrack.features.
AsType
Converts the data type of images
Accepts same types as numpy arrays. Common types include
float64, int32, uint16, int16, uint8, int8
dtype string. Same as numpy dtype.
Bind
Binds a feature with property arguments.
When the feature is resolved, the kwarg arguments are passed +to the child feature.
The child feature
Properties to send to child
BindResolve
alias of deeptrack.features.Bind
deeptrack.features.Bind
BindUpdate
Binds a feature with certain arguments.
When the feature is updated, the child feature
Branch
Resolves two features sequentially. Passes the output of the first to the input of the second. Parameters @@ -365,9 +524,9 @@
Combine
Combines multiple features into a single feature.
Resolves each feature in features and returns them as a list of features.
ConditionalSetFeature
Conditionally resolves one of two features
Set condition to the value to listen to. Example, if condition is “is_label”, then conditiona can be toggled @@ -452,9 +611,9 @@
ConditionalSetProperty