diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c41d45e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +name: Test CLI App Packaging + +on: [push] + +jobs: + test-packaging: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install hatch + run: pip install hatch + + - name: Build project + run: hatch build + + - name: Install uv + run: pip install uv + + - name: Create uv venv + run: | + uv venv + . .venv/bin/activate + + - name: Install package using uv + run: | + uv pip install dist/*.tar.gz + + - name: Run dont fret serve + run: | + timeout 10s dont-fret serve || exit_code=$? + if [ $exit_code -eq 124 ]; then + echo "ran for 10 seconds without error" + exit 0 + else + echo "failed or exited too quickly" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..3efb9f8 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,9 @@ +name: Ruff +on: [push, pull_request] + +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 \ No newline at end of file diff --git a/.github/workflows/pin_requirements.yml b/.github/workflows/pin_requirements.yml new file mode 100644 index 0000000..9394002 --- /dev/null +++ b/.github/workflows/pin_requirements.yml @@ -0,0 +1,33 @@ +name: Generate Requirements Files + +on: + workflow_dispatch: + +jobs: + generate-requirements: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + python-version: ["3.9", "3.10"] + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install pip-tools + run: pip install pip-tools + + - name: Generate requirements file + run: pip-compile --extra web --output-file requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt pyproject.toml + + - name: Upload requirements file + uses: actions/upload-artifact@v3 + with: + name: requirements + path: requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt \ No newline at end of file diff --git a/.github/workflows/pypi_main.yml b/.github/workflows/pypi_main.yml new file mode 100644 index 0000000..8fe54fe --- /dev/null +++ b/.github/workflows/pypi_main.yml @@ -0,0 +1,25 @@ +name: PyPi distribute main release +on: [release] + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up Python 3.9 + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Install Hatch + run: pip install hatch + - name: Build + run: hatch build + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/pypi_test.yml b/.github/workflows/pypi_test.yml new file mode 100644 index 0000000..24ad77f --- /dev/null +++ b/.github/workflows/pypi_test.yml @@ -0,0 +1,26 @@ +name: PyPi distribute test push +on: [push] + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up Python 3.9 + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Install Hatch + run: pip install hatch + - name: Build + run: hatch build + - name: Publish distribution 📦 to Test PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 0000000..afba137 --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,46 @@ +name: Testing +on: [push] + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ "ubuntu-latest", "macos-latest" , "windows-latest"] + python-version: ["3.9", "3.10" ] + defaults: + run: + shell: bash + runs-on: ${{ matrix.os }} + steps: + - name: Check out repository + uses: actions/checkout@v3 + + - name: Download test file windows + if: runner.os == 'Windows' + run: | + C:\\msys64\\usr\\bin\\wget.exe "https://filedn.eu/loRXwzWCNnU4XoFPGbllt1y/datafile_1.ptu" -O tests/test_data/input/ds1/datafile_1.ptu + - name: Download test file other platforms + if: runner.os != 'Windows' + run: | + wget "https://filedn.eu/loRXwzWCNnU4XoFPGbllt1y/datafile_1.ptu" -O tests/test_data/input/ds1/datafile_1.ptu + + - name: Set up python ${{ matrix.python-version }} + id: setup-python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: pip + cache-dependency-path: requirements/requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt + + - name: Install pinned requirements + run: | + python -m pip install --upgrade pip + pip install -r requirements/requirements-${{ matrix.os }}-${{ matrix.python-version }}.txt --prefer-binary + + - name: Install test requirements + run: pip install .[test] + + - name: Run tests + run: | + pytest tests/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82d1674 --- /dev/null +++ b/.gitignore @@ -0,0 +1,125 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +.venv*/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# PyCharm IDE +.idea/ + +# Generated conda requirements file +_req-*.txt + + +# project specific directories +_archive/ +dev/ +server_user_id.txt +_version.py + +# local testing datasets +tests/test_data/input/ds1 +tests/test_data/input/ds2 +tests/test_data/input/ds3 +tests/test_data/input/ds4 +tests/test_data/input/ds5 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9a65ec0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "python.analysis.typeCheckingMode": "basic", + "python.analysis.diagnosticSeverityOverrides": { + "reportPrivateImportUsage":"none" + }, + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/CITE.txt b/CITE.txt new file mode 100644 index 0000000..2e309ff --- /dev/null +++ b/CITE.txt @@ -0,0 +1,17 @@ + +https://pypi.org/project/fastkde/ +FastKDE: + O’Brien, T. A., Kashinath, K., Cavanaugh, N. R., Collins, W. D. & O’Brien, J. P. A fast and objective multidimensional kernel density estimation method: fastKDE. Comput. Stat. Data Anal. 101, 148–160 (2016). + + O’Brien, T. A., Collins, W. D., Rauscher, S. A. & Ringler, T. D. Reducing the computational cost of the ECF using a nuFFT: A fast and objective probability density estimation method. Comput. Stat. Data Anal. 79, 222–234 (2014). + +phconvert: +https://github.com/Photon-HDF5/phconvert +citation? + +photon-hdf5: +https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4805879/ + +burst search: +"Data registration and selective single-molecule analysis using multi-parameter fluorescence detection" +DOI: 10.1016/S0168-1656(00)00412-0 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dfbefe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Jochem Smit + +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 new file mode 100644 index 0000000..f79b7c7 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +Don't FRET +============ + + +Don't FRET! is a python package featuring a web application for performing burst search on confocal smFRET data. + + +## Process photon files + +``` +dont-fret process filename.ptu +``` + +Will process the file. It will perform burst search as specified in the configuration file. Output (default) are burst photons and bursts as .pq files. + +## Run the web application + +To launch with the default configuration file: + +``` +dont-fret serve +``` + +To create a local default configuration file: + +``` +dont-fret config +``` + +Then you can edit the created config file. To launch the web application with a specific config file: + +``` +dont-fret serve --config config.yaml +``` + +## Configuration + +Configuration for channels, photon streams and (default) burst search settings is done from the config .yaml file. + +First, define your channels: + +```yaml +channels: # refactor channels in code to channel_identifiers + laser_D: + target: nanotimes + value: [ 0, 1000 ] + laser_A: + target: nanotimes + value: [ 1000, 2000 ] # intervals are inclusive, exclusive + det_D: + target: detectors + value: 1 + det_A: + target: detectors + value: 0 +``` + +Currently supported targets are `nanotimes`, `detectors` and `timestamps`. These are as read from the file and not converted to seconds. Modulo is supported (untested) for us-ALEX: +```yaml +channels: # refactor channels in code to channel_identifiers + laser_D: + target: timestamps + value: [ 0, 100 ] + modulo: 200 +``` + +This will assign photons with a timestamp modulo 200 in the range from 0 up to 100 to "laser_D". + +Next, define your photon streams. Photons streams are combinations of channels ("AND"): + +```yaml +streams: + DD: [laser_D, det_D] + DA: [laser_D, det_A] + AA: [laser_A, det_A] + AD: [laser_A, det_D] +``` + +> [!IMPORTANT] +> The notation used here is excitation then emission, thus the FRET stream is 'DA' while in literature the FRET photon stream is often written as `A|D` (Acceptor emission during donor excitation). + + +> [!IMPORTANT] +> At the moment apparent FRET and stoichiometry are calculated from the defined photon streams and it is required the following streams are defined: 'AA', 'DD', 'DA' (=FRET). This is expected to be changed in future updates. + +## Development + +Download a test file: + +```sh +wget https://kuleuven-my.sharepoint.com/:u:/g/personal/jochem_smit_kuleuven_be/Efy7ur779ARNiBlP05Ki7NMBabKX3auswj30xmpRLaIfPg?e=E6wWoZ&download=1 +``` + + +If autoreload (refresh web application upon code changes) doesnt work, run from: +solara run dont_fret\tmp.py -- --config default_testing.yaml diff --git a/default_testing.yaml b/default_testing.yaml new file mode 100644 index 0000000..84fc04b --- /dev/null +++ b/default_testing.yaml @@ -0,0 +1,50 @@ +channels: # refactor channels in code to channel_identifiers + laser_D: + target: nanotimes + value: [ 0, 1000 ] + laser_A: + target: nanotimes + value: [ 1000, 2000 ] # intervals are inclusive, exclusive + det_D: + target: detectors + value: 1 + det_A: + target: detectors + value: 0 + +streams: + DD: [laser_D, det_D] + DA: [laser_D, det_A] + AA: [laser_A, det_A] + AD: [laser_A, det_D] + +# TODO this should move to web config +burst_search: + DCBS: # name of the burst search + - streams: [DD, DA] # photons streams to use + L: 50 + M: 35 + T: 500.e-6 + - streams: [AA] + L: 50 + M: 35 + T: 500.e-6 + APBS: + - streams: [DD, DA, AA] + L: 50 + M: 100 + T: 500.e-6 + test234: + - streams: [DD, DA, AA] + L: 500 + M: 100 + T: 500.e-6 + +# settings related to dont-fret's web interface +web: + password: null + default_dir: tests\test_data\input\ds2 # default directory show in the file browser + protect_filebrowser: false # true to prevent navigation above default_dir + burst_filters: # default filters to apply to burst search filters + - name: n_photons + min: 150 diff --git a/dependabot.yml b/dependabot.yml new file mode 100644 index 0000000..5c4ef04 --- /dev/null +++ b/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + requirements: + - file: "requirements/requirements-*.txt" \ No newline at end of file diff --git a/dont_fret/__init__.py b/dont_fret/__init__.py new file mode 100644 index 0000000..183075b --- /dev/null +++ b/dont_fret/__init__.py @@ -0,0 +1,5 @@ +from dont_fret.fileIO import * +from dont_fret.models import * + + +from dont_fret.__version__ import __version__ # noqa: F401 diff --git a/dont_fret/__main__.py b/dont_fret/__main__.py new file mode 100644 index 0000000..39c81f7 --- /dev/null +++ b/dont_fret/__main__.py @@ -0,0 +1,124 @@ +import os +from pathlib import Path +from typing import List, Literal, Optional + +import click +import yaml +from solara.__main__ import run + +from dont_fret.config import CONFIG_HOME, cfg +from dont_fret.process import batch_search_and_save, search_and_save + +ROOT = Path(__file__).parent +APP_PATH = ROOT / "web" / "main.py" + + +@click.group() +def cli(): + """Don't FRET! CLI for analyzing confocal solution smFRET data.""" + pass + + +@cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True)) +@click.option("--config", default=None, help="Configuration file to use") +@click.argument("solara_args", nargs=-1, type=click.UNPROCESSED) +def serve(config: Optional[str] = None, solara_args=None): + """Run the don't fret web application.""" + if config is not None: + data = yaml.safe_load(Path(config).read_text()) + cfg.update(data) + + solara_args = solara_args or tuple() + args = [str(APP_PATH), *solara_args] + + run(args) + + +@cli.command() +@click.option( + "--global", "is_global", is_flag=True, help="Create config file in user's home directory" +) +def config(is_global: bool): + """Create a local or global default configuration file.""" + src = ROOT / "config" / "default.yaml" + if is_global: + (CONFIG_HOME / "dont_fret").mkdir(exist_ok=True, parents=True) + output = CONFIG_HOME / "dont_fret" / "dont_fret.yaml" + else: + output = Path.cwd() / "dont_fret.yaml" + + if output.exists(): + click.echo(f"Configuration file already exists at '{str(output)}'") + return + + else: + output.write_text(src.read_text()) + + click.echo(f"Configuration file created at '{str(output)}'") + + +SUPPORTED_SUFFIXES = { + ".ptu", +} + + +@cli.command() +@click.argument("input_path", type=click.Path(exists=True)) +@click.option("--burst-colors", default=None, multiple=True, help="Burst colors to process") +@click.option( + "--write-photons/--no-write-photons", default=False, help="Whether to write photon data" +) +@click.option( + "--output-type", type=click.Choice([".pq", ".csv"]), default=".pq", help="Output file type" +) +@click.option("--max-workers", type=int, default=None, help="Maximum number of worker threads") +def process( + input_path: str, + burst_colors: Optional[list[str]], + write_photons: bool, + output_type: Literal[".pq", ".csv"], + max_workers: Optional[int], +): + """Process photon file(s) and perform burst search.""" + + pth = Path(input_path) + + if pth.is_file(): + files = [pth] + elif pth.is_dir(): + files = [f for f in pth.iterdir() if f.suffix in SUPPORTED_SUFFIXES] + else: + raise click.BadParameter("Input path must be a file or directory") + + if not files: + click.echo("No supported files found.") + return + + click.echo(f"Found {len(files)} file(s) to process.") + + # Convert burst_colors to the expected format + burst_colors_param = list(burst_colors) if burst_colors else None + + if len(files) == 1: + click.echo(f"Processing file: {files[0]}") + search_and_save( + files[0], + burst_colors=burst_colors_param, + write_photons=write_photons, + output_type=output_type, + ) + else: + click.echo("Processing files in batch mode.") + batch_search_and_save( + files, + burst_colors=burst_colors_param, + write_photons=write_photons, + output_type=output_type, + max_workers=max_workers, + ) + + click.echo("Processing completed.") + + +if __name__ == "__main__": + cli() diff --git a/dont_fret/__version__.py b/dont_fret/__version__.py new file mode 100644 index 0000000..2e35abf --- /dev/null +++ b/dont_fret/__version__.py @@ -0,0 +1,17 @@ +# Adapted from: https://github.com/maresb/hatch-vcs-footgun-example +# Define the variable '__version__': +try: + # If we are in an editable install, the _versioneer file exist and we can use it to find the version + from dont_fret._versioneer import get_versions + + # This will fail with LookupError if the package is not installed in + # editable mode or if Git is not installed. + __version__ = get_versions()["version"] +except ImportError: + # If the project build with hatch, there should be a _version.py file + try: + from dont_fret._version import __version__ # noqa: F401 # type: ignore + except ModuleNotFoundError: + # The user is probably trying to run this without having installed + # the package, so complain. + raise RuntimeError("dont_fret is not correctly installed. Please install it with pip.") diff --git a/dont_fret/_versioneer.py b/dont_fret/_versioneer.py new file mode 100644 index 0000000..e9319bc --- /dev/null +++ b/dont_fret/_versioneer.py @@ -0,0 +1,713 @@ +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. +# Generated by versioneer-0.29 +# https://github.com/python-versioneer/python-versioneer + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys +from typing import Any, Callable, Dict, List, Optional, Tuple +import functools + + +def get_keywords() -> Dict[str, str]: + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "$Format:%d$" + git_full = "$Format:%H$" + git_date = "$Format:%ci$" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + VCS: str + style: str + tag_prefix: str + parentdir_prefix: str + versionfile_source: str + verbose: bool + + +def get_config() -> VersioneerConfig: + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "pep440" + cfg.tag_prefix = "v" + cfg.parentdir_prefix = "" + cfg.versionfile_source = "__version__.py" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY: Dict[str, str] = {} +HANDLERS: Dict[str, Dict[str, Callable]] = {} + + +def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator + """Create decorator to mark a method as the handler of a VCS.""" + + def decorate(f: Callable) -> Callable: + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + + return decorate + + +def run_command( + commands: List[str], + args: List[str], + cwd: Optional[str] = None, + verbose: bool = False, + hide_stderr: bool = False, + env: Optional[Dict[str, str]] = None, +) -> Tuple[Optional[str], Optional[int]]: + """Call the given command(s).""" + assert isinstance(commands, list) + process = None + + popen_kwargs: Dict[str, Any] = {} + if sys.platform == "win32": + # This hides the console window if pythonw.exe is used + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + popen_kwargs["startupinfo"] = startupinfo + + for command in commands: + try: + dispcmd = str([command] + args) + # remember shell=False, so use git.cmd on windows, not just git + process = subprocess.Popen( + [command] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + **popen_kwargs, + ) + break + except OSError as e: + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = process.communicate()[0].strip().decode() + if process.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, process.returncode + return stdout, process.returncode + + +def versions_from_parentdir( + parentdir_prefix: str, + root: str, + verbose: bool, +) -> Dict[str, Any]: + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for _ in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs: str) -> Dict[str, str]: + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords: Dict[str, str] = {} + try: + with open(versionfile_abs, "r") as fobj: + for line in fobj: + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + except OSError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords( + keywords: Dict[str, str], + tag_prefix: str, + verbose: bool, +) -> Dict[str, Any]: + """Get version information from git keywords.""" + if "refnames" not in keywords: + raise NotThisMethod("Short version file found") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = {r.strip() for r in refnames.strip("()").split(",")} + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = {r for r in refs if re.search(r"\d", r)} + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix) :] + # Filter out refs that exactly match prefix or that don't start + # with a number once the prefix is stripped (mostly a concern + # when prefix is '') + if not re.match(r"\d", r): + continue + if verbose: + print("picking %s" % r) + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs( + tag_prefix: str, root: str, verbose: bool, runner: Callable = run_command +) -> Dict[str, Any]: + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + # GIT_DIR can interfere with correct operation of Versioneer. + # It may be intended to be passed to the Versioneer-versioned project, + # but that should not change where we get our version from. + env = os.environ.copy() + env.pop("GIT_DIR", None) + runner = functools.partial(runner, env=env) + + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = runner( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + f"{tag_prefix}[[:digit:]]*", + ], + cwd=root, + ) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces: Dict[str, Any] = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) + # --abbrev-ref was added in git-1.6.3 + if rc != 0 or branch_name is None: + raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") + branch_name = branch_name.strip() + + if branch_name == "HEAD": + # If we aren't exactly on a branch, pick a branch which represents + # the current commit. If all else fails, we are on a branchless + # commit. + branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) + # --contains was added in git-1.5.4 + if rc != 0 or branches is None: + raise NotThisMethod("'git branch --contains' returned error") + branches = branches.split("\n") + + # Remove the first line if we're running detached + if "(" in branches[0]: + branches.pop(0) + + # Strip off the leading "* " from the list of branches. + branches = [branch[2:] for branch in branches] + if "master" in branches: + branch_name = "master" + elif not branches: + branch_name = None + else: + # Pick the first branch that is returned. Good or bad. + branch_name = branches[0] + + pieces["branch"] = branch_name + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[: git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) + if not mo: + # unparsable. Maybe git-describe is misbehaving? + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%s' doesn't start with prefix '%s'" + print(fmt % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (full_tag, tag_prefix) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix) :] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) + pieces["distance"] = len(out.split()) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces: Dict[str, Any]) -> str: + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces: Dict[str, Any]) -> str: + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_branch(pieces: Dict[str, Any]) -> str: + """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . + + The ".dev0" means not master branch. Note that .dev0 sorts backwards + (a feature branch will appear "older" than the master branch). + + Exceptions: + 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0" + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def pep440_split_post(ver: str) -> Tuple[str, Optional[int]]: + """Split pep440 version string at the post-release segment. + + Returns the release segments before the post-release and the + post-release version number (or -1 if no post-release segment is present). + """ + vc = str.split(ver, ".post") + return vc[0], int(vc[1] or 0) if len(vc) == 2 else None + + +def render_pep440_pre(pieces: Dict[str, Any]) -> str: + """TAG[.postN.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + if pieces["distance"]: + # update the post release segment + tag_version, post_version = pep440_split_post(pieces["closest-tag"]) + rendered = tag_version + if post_version is not None: + rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) + else: + rendered += ".post0.dev%d" % (pieces["distance"]) + else: + # no commits, use the tag as the version + rendered = pieces["closest-tag"] + else: + # exception #1 + rendered = "0.post0.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces: Dict[str, Any]) -> str: + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_post_branch(pieces: Dict[str, Any]) -> str: + """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . + + The ".dev0" means not master branch. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_old(pieces: Dict[str, Any]) -> str: + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces: Dict[str, Any]) -> str: + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces: Dict[str, Any]) -> str: + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-branch": + rendered = render_pep440_branch(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-post-branch": + rendered = render_pep440_post_branch(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } + + +def get_versions() -> Dict[str, Any]: + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for _ in cfg.versionfile_source.split("/"): + root = os.path.dirname(root) + except NameError: + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None, + } + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } diff --git a/dont_fret/burst_search.py b/dont_fret/burst_search.py new file mode 100644 index 0000000..835b920 --- /dev/null +++ b/dont_fret/burst_search.py @@ -0,0 +1,144 @@ +from typing import List, Tuple, Union + +import numpy as np +from numba import int32, jit, types + + +@jit( + (types.Array(types.int64, 1, "C", readonly=True), int32, int32, int32), + nopython=True, + cache=True, + nogil=True, +) +def bs_eggeling(times: np.ndarray, L: int, M: int, tr: int) -> List[Tuple[int, int]]: + """ + Burst search algorithm from Eggeling et al. 2001. + + "Data registration and selective single-molecule analysis using multi-parameter fluorescence detection" + DOI: 10.1016/S0168-1656(00)00412-0 + + Args: + times: Array of photon arrival times (uint64) + L: Number of succesive 'burst' photons to be considered a burst + M: With at least this many neighbours + tr: Within this time period from the burst (total time window is 2*tr) + + Returns: + List of tuples of start and end indices of bursts. + + """ + + in_burst = False + bursts = [] + i_start = 0 + i_lower = 0 # index of lower limit neighbouring photon within time tr + i_upper = 0 # index of upper limit neighbouring photon within time tr + + for i, time in enumerate(times): + # Adjust upper limit + while times[i_upper] < time + tr: + i_upper += 1 + if i_upper == len(times): + i_upper -= 1 + break + + # Adjust lower limit + while times[i_lower] < time - tr: + i_lower += 1 + + n_neighbours = i_upper - i_lower + + if n_neighbours > M: + if not in_burst: + in_burst = True + i_start = i + elif in_burst: # Burst has ended + in_burst = False + if (i - 1 - i_start) > L: # Check enough photons in the burst + bursts.append((i_start, i - 1)) + + return bursts + + +def return_intersections( + intervals1: List[Tuple[int, int]], intervals2: List[Tuple[int, int]] +) -> List[Tuple[int, int]]: + """ + Return the intersections of two lists of intervals. + + The intervals need to be ordered. + + From: + https://codereview.stackexchange.com/questions/178427/given-2-disjoint-sets-of-intervals-find-the-intersections + + + Args: + intervals1: First set of intervals as list of tuples. + intervals2: First set of intervals as list of tuples. + + Returns: + List of overlaps of intervals1 and intervals2 as list of tuples. + """ + + i1 = 0 + i2 = 0 + + output = [] + + interval1 = intervals1[i1] + interval2 = intervals2[i2] + + while True: + start = max(interval1[0], interval2[0]) + end = min(interval1[1], interval2[1]) + + if start < end: + output.append((start, end)) + if end == interval1[1]: + i1 += 1 + if i1 >= len(intervals1): + break + interval1 = intervals1[i1] + else: + i2 += 1 + if i2 >= len(intervals2): + break + interval2 = intervals2[i2] + + while interval1[0] >= interval2[1]: + i2 += 1 + if i2 >= len(intervals2): + break + interval2 = intervals2[i2] + while interval2[0] >= interval1[1]: + i1 += 1 + if i1 >= len(intervals1): + break + interval1 = intervals1[i1] + + if i1 >= len(intervals1): + break + if i2 >= len(intervals2): + break + + return output + + +def get_intersection( + interval_1: Tuple[int, int], interval_2: Tuple[int, int] +) -> Union[Tuple[int, int], None]: + """Returns the intersection (overlap) of two intervals. + + Args: + interval_1: First interval as tuple. + interval_2: Second interval as tuple. + + Returns: + Intersection of the two intervals as tuple or `None` if no intersection. + + """ + start = max(interval_1[0], interval_2[0]) + end = min(interval_1[1], interval_2[1]) + if start < end: + return start, end + return None diff --git a/dont_fret/config/__init__.py b/dont_fret/config/__init__.py new file mode 100644 index 0000000..93a8f16 --- /dev/null +++ b/dont_fret/config/__init__.py @@ -0,0 +1,3 @@ +from .config import CONFIG_HOME, cfg + +__all__ = ["cfg", "CONFIG_HOME"] diff --git a/dont_fret/config/config.py b/dont_fret/config/config.py new file mode 100644 index 0000000..3a1d49b --- /dev/null +++ b/dont_fret/config/config.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +import os +from dataclasses import asdict, dataclass, field +from pathlib import Path +from typing import Optional, Union + +import polars as pl +import yaml +from dacite import Config, from_dict +from dacite.data import Data + +from dont_fret.utils import clean_types + +CONFIG_HOME = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) + + +@dataclass +class Channel: + # name: str + target: str + value: Union[int, float, list[int], list[float]] + modulo: Union[float, int, None] = None + + +@dataclass +class BurstFilterItem: + name: str + min: Optional[float] = field(default=None) + max: Optional[float] = field(default=None) + + def as_expr(self) -> list[pl.Expr]: + expr = [] + if self.min is not None: + expr.append(pl.col(self.name) >= self.min) + if self.max is not None: + expr.append(pl.col(self.name) <= self.max) + return expr + + +@dataclass +class Web: + """settings related to web application""" + + default_dir: Path + protect_filebrowser: bool = True + burst_filters: list[BurstFilterItem] = field(default_factory=list) + password: Optional[str] = None + + +@dataclass +class BurstColor: + streams: list[str] = field(default_factory=lambda: ["DD", "DA", "AA"]) + L: int = 50 + M: int = 100 + T: float = 500e-6 + + +@dataclass +class DontFRETConfig: + channels: dict[str, Channel] + streams: dict[str, list[str]] + burst_search: dict[str, list[BurstColor]] + web: Web + + @classmethod + def from_dict(cls, data: Data): + config = Config(type_hooks={Path: lambda v: Path(v).expanduser()}) + return from_dict(cls, data, config) + + @classmethod + def from_yaml(cls, fpath: Path): + data = yaml.safe_load(fpath.read_text()) + return cls.from_dict(data) + + def to_yaml(self, fpath: Path) -> None: + s = yaml.dump(clean_types(asdict(self)), sort_keys=False) + fpath.write_text(s) + + def update(self, data: Data): + new_data = {**self.__dict__, **data} + + # we use `from_dict` to cast to the correct types + new_cfg = DontFRETConfig.from_dict(new_data) + vars(self).update(vars(new_cfg)) + + +cfg_file_paths = [ + CONFIG_HOME / "dont_fret" / "dont_fret.yaml", + Path(__file__).parent / "default.yaml", +] + +# take the first one which exists +cfg_fpath = next((p for p in cfg_file_paths if p.exists()), None) +assert cfg_fpath +cfg = DontFRETConfig.from_yaml(cfg_fpath) diff --git a/dont_fret/config/default.yaml b/dont_fret/config/default.yaml new file mode 100644 index 0000000..f3dc249 --- /dev/null +++ b/dont_fret/config/default.yaml @@ -0,0 +1,45 @@ +channels: # refactor channels in code to channel_identifiers + laser_D: + target: nanotimes + value: [ 0, 1000 ] + laser_A: + target: nanotimes + value: [ 1000, 2000 ] # intervals are inclusive, exclusive + det_D: + target: detectors + value: 1 + det_A: + target: detectors + value: 0 + +streams: + DD: [laser_D, det_D] + DA: [laser_D, det_A] + AA: [laser_A, det_A] + AD: [laser_A, det_D] + +burst_search: + DCBS: # name of the burst search + - streams: [DD, DA] # photons streams to use + L: 50 + M: 35 + T: 500.e-6 + - streams: [AA] + L: 50 + M: 35 + T: 500.e-6 + APBS: + - streams: [DD, DA, AA] + L: 50 + M: 100 + T: 500.e-6 + +# settings related to dont-fret's web interface +web: + password: null # set to null to disable password protection + default_dir: "~" # default directory show in the file browser + protect_filebrowser: true # true to prevent navigation above default_dir + burst_filters: # default filters to apply to burst search filters + - name: n_photons + min: 150 + diff --git a/dont_fret/expr.py b/dont_fret/expr.py new file mode 100644 index 0000000..123dc07 --- /dev/null +++ b/dont_fret/expr.py @@ -0,0 +1,70 @@ +import ast +from functools import reduce +from operator import and_, or_ +from typing import Dict + +import polars as pl +import yaml + +from dont_fret.config.config import Channel + + +def from_channel(channel: Channel) -> pl.Expr: + expr = pl.col(channel.target) + if channel.modulo is not None: + expr = expr.mod(channel.modulo) + if isinstance(channel.value, (int, float)): + expr = expr == pl.lit(channel.value) + elif len(channel.value) == 2: + expr = expr.is_between(channel.value[0], channel.value[1], closed="left") + else: + raise ValueError( + "Channel specification field 'value' must be either one value or a range of two" + ) + return expr + + +def reduce_and(exprs: list[pl.Expr]) -> pl.Expr: + return reduce(and_, exprs) + + +def reduce_or(exprs: list[pl.Expr]) -> pl.Expr: + return reduce(or_, exprs) + + +def parse_yaml_expressions(yaml_content: str) -> Dict[str, pl.Expr]: + yaml_data = yaml.safe_load(yaml_content) + return {key: parse_expression(value).alias(key) for key, value in yaml_data.items()} + + +def parse_expression(expr: str) -> pl.Expr: + tree = ast.parse(expr, mode="eval") + return evaluate_node(tree.body) + + +def evaluate_node(node): + if isinstance(node, ast.Name): + return pl.col(node.id) + elif isinstance(node, ast.Constant): + return pl.lit(node.n) + elif isinstance(node, ast.BinOp): + left = evaluate_node(node.left) + right = evaluate_node(node.right) + return apply_operator(node.op, left, right) + else: + raise ValueError(f"Unsupported node type: {type(node).__name__}") + + +def apply_operator(op, left, right): + if isinstance(op, ast.Add): + return left + right + elif isinstance(op, ast.Sub): + return left - right + elif isinstance(op, ast.Mult): + return left * right + elif isinstance(op, ast.Div): + return left / right + elif isinstance(op, ast.Pow): + return left.pow(right) + else: + raise ValueError(f"Unsupported operator: {type(op).__name__}") diff --git a/dont_fret/fileIO.py b/dont_fret/fileIO.py new file mode 100644 index 0000000..68a8744 --- /dev/null +++ b/dont_fret/fileIO.py @@ -0,0 +1,182 @@ +import os +import struct +import time +from pathlib import Path + +import numpy as np +import polars as pl +from phconvert.pqreader import ( + _convert_multi_tags, + _ptu_rec_type_r, + _ptu_tag_type_r, + _ptu_TDateTime_to_time_t, + load_ptu, +) + +from dont_fret.config.config import DontFRETConfig, cfg +from dont_fret.expr import from_channel, reduce_and +from dont_fret.models import Bursts + + +def export_bursts(bursts: Bursts): + """ + export to .csv format as per + https://github.com/Fluorescence-Tools/exchange-formats/tree/master/burst/pam + + :param bursts: + :return: + """ + # FRET Efficiency,Stoichiometry,Proximity Ratio,Stoichiometry (raw),Lifetime D [ns],Lifetime A [ns],Anisotropy D,Anisotropy A,|TDX-TAA| Filter,ALEX 2CDE Filter,|TDD-TDA| Filter,FRET 2CDE Filter,Duration [ms],Mean Macrotime [s],Number of Photons,Count rate [kHz],Count rate (DD) [kHz],Count rate (DA) [kHz],Count rate (AA) [kHz],Count rate (DD par) [kHz],Count rate (DD perp) [kHz],Count rate (DA par) [kHz],Count rate (DA perp) [kHz],Count rate (AA par) [kHz],Count rate (AA perp) [kHz],Number of Photons (DD),Number of Photons (DA),Number of Photons (AA),Number of Photons (DD par),Number of Photons (DD perp),Number of Photons (DA par),Number of Photons (DA perp),Number of Photons (AA par),Number of Photons (AA perp),log(FD/FA),M1-M2,Number of Photons (DX),Count rate (DX) [kHz],Distance (from intensity) [A],FRET efficiency (from lifetime),Distance (from lifetime) [A],FRET efficiency (sens. Acc. Em.) + + return None + + +class PhotonFile(object): + """Base class for file objects of photon data""" + + def __init__(self, file_path: os.PathLike): + self.file_path = Path(file_path) + + if isinstance(self.file_path, Path) and not self.file_path.exists(): + raise ValueError("Supplied file path does not exist") + + @property + def filename(self) -> str: + try: + return self.file_path.name + except AttributeError: + return "" + + def load_file(self, cfg: DontFRETConfig = cfg) -> tuple[pl.DataFrame, dict]: + """ + returns data, metadata + """ + timestamps, detectors, nanotimes, metadata = load_ptu(self.file_path) + lazy_data = pl.LazyFrame( + {"timestamps": timestamps, "detectors": detectors, "nanotimes": nanotimes} + ) + + channel_expr = {k: from_channel(v) for k, v in cfg.channels.items()} + stream_expr = { + k: reduce_and([channel_expr[ch] for ch in v]) for k, v in cfg.streams.items() + } + + dtype = pl.Enum(stream_expr.keys()) + queries = [ + lazy_data.filter(v).with_columns(pl.lit(k).alias("stream").cast(dtype)) + for k, v in stream_expr.items() + ] + concat_query = pl.concat(queries).sort("timestamps") + data = concat_query.collect() + + return data, metadata + + +def _read_tag(f): + tag_struct = struct.unpack("32s i I q", f.read(48)) + + # and save it into a dict + tagname = tag_struct[0].rstrip(b"\0").decode() + keys = ("idx", "type", "value") + tag = {k: v for k, v in zip(keys, tag_struct[1:])} + # tag["offset"] = f.tell() # Uncomment if you want offset information + # Recover the name of the type (a string) + tag["type"] = _ptu_tag_type_r[tag["type"]] + + # Some tag types need conversion + if tag["type"] == "tyFloat8": + tag["value"] = np.int64(tag["value"]).view("float64") + elif tag["type"] == "tyBool8": + tag["value"] = bool(tag["value"]) + elif tag["type"] == "tyTDateTime": + TDateTime = np.uint64(tag["value"]).view("float64") + t = time.gmtime(_ptu_TDateTime_to_time_t(TDateTime)) + tag["value"] = time.strftime("%Y-%m-%d %H:%M:%S", t) + + # Some tag types have additional data + if tag["type"] == "tyAnsiString": + byte_string = f.read(tag["value"]).rstrip(b"\0") + try: + tag["data"] = byte_string.decode() # try decoding from UTF-8 + except UnicodeDecodeError: + # Not UTF-8, trying 'latin1' + # See https://github.com/Photon-HDF5/phconvert/issues/35 + tag["data"] = byte_string.decode("latin1") + elif tag["type"] == "tyFloat8Array": + buffer = f.read(tag["value"]) + tag["data"] = np.frombuffer(buffer, dtype="float", count=tag["value"] / 8) + elif tag["type"] == "tyWideString": + # WideString use type WCHAR in the original C++ demo code. + # WCHAR size is not fixed by C++ standard, but on windows + # is 2 bytes and the default encoding is UTF-16. + # I'm assuming this is what the PTU requires. + tag["data"] = f.read(tag["value"] * 2).decode("utf16") + elif tag["type"] == "tyBinaryBlob": + tag["data"] = f.read(tag["value"]) + return tagname, tag + + +def _read_ptu_tags(f) -> dict: + tags = {} + + while True: + tagname, tag = _read_tag(f) + # In case a `tagname` appears multiple times, we make a list + # to hold all the tags with the same name + if tagname in tags: + if not isinstance(tags[tagname], list): + tags[tagname] = [tags[tagname]] + tags[tagname].append(tag) + else: + tags[tagname] = tag + + if tagname == "Header_End": + break + + return tags + + +def _check_ptu_magic(f) -> tuple[bytes, bytes]: + magic = f.read(8).rstrip(b"\0") + version = f.read(8).rstrip(b"\0") + if magic != b"PQTTTR": + raise IOError("This file is not a valid PTU file. " "Magic: '%s'." % magic) + + return magic, version + + +def convert_ptu_tags(tags: dict) -> dict: + """Convert PTU tags to metadata dict""" + + acquisition_duration = tags["MeasDesc_AcquisitionTime"]["value"] * 1e-3 + ctime_t = time.strptime(tags["File_CreatingTime"]["value"], "%Y-%m-%d %H:%M:%S") + creation_time = time.strftime("%Y-%m-%d %H:%M:%S", ctime_t) + record_type = _ptu_rec_type_r[tags["TTResultFormat_TTTRRecType"]["value"]] + hw_type = tags["HW_Type"] + if isinstance(hw_type, list): + hw_type = hw_type[0] + metadata = { + "timestamps_unit": tags["MeasDesc_GlobalResolution"]["value"], # both T3 and T2 + "acquisition_duration": acquisition_duration, + "software": tags["CreatorSW_Name"]["data"], + "software_version": tags["CreatorSW_Version"]["data"], + "creation_time": creation_time, + "hardware_name": hw_type["data"], + "record_type": record_type, + "tags": _convert_multi_tags(tags), + } + if record_type.endswith("T3"): + metadata["nanotimes_unit"] = tags["MeasDesc_Resolution"]["value"] + metadata["laser_repetition_rate"] = tags["TTResult_SyncRate"]["value"] + + return metadata + + +def read_ptu_metadata(filename: os.PathLike) -> dict: + with open(filename, "rb") as f_obj: + _check_ptu_magic(f_obj) + tags = _read_ptu_tags(f_obj) + + metadata = convert_ptu_tags(tags) + + return metadata diff --git a/dont_fret/formatting.py b/dont_fret/formatting.py new file mode 100644 index 0000000..4210e4b --- /dev/null +++ b/dont_fret/formatting.py @@ -0,0 +1,12 @@ +TRACE_COLORS = { + "DD": "Green", + "DA": "Yellow", + "AA": "Red", +} +TRACE_SIGNS = { + "DD": 1, + "DA": 1, + "AA": -1, +} +HIST1D_KWARGS = dict(bins="fd", color="#4d4d4d") +CONTOURF_KWARGS = dict(levels=15, lw=0, cmap="viridis") diff --git a/dont_fret/models.py b/dont_fret/models.py new file mode 100644 index 0000000..7a78841 --- /dev/null +++ b/dont_fret/models.py @@ -0,0 +1,436 @@ +from __future__ import annotations + +import json +from functools import cached_property, reduce +from pathlib import Path +from typing import TYPE_CHECKING, Optional, Union + +import numpy as np +import polars as pl + +from dont_fret.burst_search import bs_eggeling, return_intersections +from dont_fret.config.config import BurstColor, DontFRETConfig, cfg +from dont_fret.support import get_binned +from dont_fret.utils import clean_types + +if TYPE_CHECKING: + from dont_fret.fileIO import PhotonFile + + +class PhotonData: + """Base object for timestamp data + + + does not have identified channels + and access the structured array with properties + + timestamps: ndarray int timestamps (global resolution) + detectors: ndarray int + nanotimes, optional: ndarray int + + metadata: dict; whatever contents + """ + + def __init__( + self, data: pl.DataFrame, metadata: Optional[dict] = None, cfg: DontFRETConfig = cfg + ): + self.data = data + self.metadata = metadata or {} + self.cfg = cfg + + def __str__(self): + s = str(self.__class__) + " object\n" + s += f"Number of photons: {len(self)}\n" + return s + + def __hash__(self) -> int: + # Hashes only the metadata + # If you butcher the metadata you might get a collision + + # TODO: combine with config and version + + s = json.dumps(clean_types(self.metadata), sort_keys=True) + return hash(s) + + @property + def timestamps(self) -> pl.Series: + """Array of integer typestamps""" + return self.data["timestamps"] + + @property + def detectors(self) -> pl.Series: + return self.data["detectors"] + + @property + def nanotimes(self) -> Optional[pl.Series]: + try: + return self.data["nanotimes"] + except KeyError: + return None + + @property + def cps(self) -> float: + """Average count rate (counts per second / Hz)""" + return len(self) / float(self.photon_times.max()) # type: ignore + + def __getitem__(self, key): + # TODO sort when slicing backwards? + return self.__class__(self.data[key], metadata=self.metadata, cfg=self.cfg) + + def __len__(self) -> int: + return len(self.data) + + @classmethod + def from_file(cls, f_obj: PhotonFile, metadata: Optional[dict] = None): + data, f_metadata = f_obj.load_file() + full_metadata = {**f_metadata, **(metadata or {})} + if "timestamps_unit" not in full_metadata.keys(): + raise ValueError("Missing field 'timestamps_unit' in metadata") + if f_obj.filename: + full_metadata["filename"] = f_obj.filename + + return cls(data, metadata=full_metadata) + + @classmethod + def load(cls, directory: Path) -> PhotonData: + data = pl.read_parquet(directory / "data.pq") + with open(directory / "metadata.json", "r") as f: + metadata = json.load(f) + + cfg = DontFRETConfig.from_yaml(directory / "config.yaml") + return PhotonData(data, metadata, cfg) + + def save(self, directory: Path) -> None: + directory.mkdir(parents=True, exist_ok=True) + self.data.write_parquet(directory / "data.pq") + with open(directory / "metadata.json", "w") as f: + json.dump(self.metadata, f) + self.cfg.to_yaml(directory / "config.yaml") + + @property + def monotonic(self) -> bool: + """Is `True` is the timestamps are monotonically increasing""" + return self.timestamps.diff().ge(0).all(ignore_nulls=True) + + @property + def timestamps_unit(self) -> Optional[float]: + """Multiplication factor to covert timestamps integers to seconds""" + try: + return self.metadata["timestamps_unit"] + except KeyError: + return None + + @property + def nanotimes_unit(self) -> Optional[float]: + """Multiplication factor to covert nanotimes integers to seconds""" + try: + return self.metadata["nanotimes_unit"] + except KeyError: + return None + + @property + def tau_mean(self) -> float: + """Mean of the nanotimes (in seconds). Sometimes referred to as 'fast lifetime' as it is fast to compute""" + if self.nanotimes is not None and self.nanotimes_unit is not None: + return self.nanotimes.mean() * self.nanotimes_unit + else: + return np.nan + + @property + def photon_times(self) -> pl.Series: + """Photon arrival times in seconds (without nanotime)""" + return self.timestamps * self.timestamps_unit + + @property + def n_photons(self) -> int: + """Number of photons""" + return len(self) + + @property + def tmax(self) -> float: + """Last timepoint in seconds""" + return self.timestamps[-1] * self.timestamps_unit + + @property + def tmin(self) -> float: + """First timepoint in seconds""" + return self.timestamps[0] * self.timestamps_unit + + @property + def description(self) -> str: + """User-defined description of the file""" + s = "" + s += f"Datetime: {self.metadata['tags']['File_CreatingTime']['value']}\n" + s += f"Duration: {self.metadata['acquisition_duration']}\n" + s += f"Power diode: {self.metadata['tags']['UsrPowerDiode']['value']:.2f}\n" + + if self.comment: + s += "Comment:\n" + s += self.comment + + return s + + @property + def comment(self) -> str: + """User-defined comment""" + try: + return self.metadata["tags"]["File_Comment"]["data"] + except KeyError: + return "" + + @property + def measurement_type(self) -> str: + """ + Photon-hdf5 qualifier + + Returns: + String of the measurement type + + """ + return self.metadata["measurement_type"] + + def to_file(self): + """write to photon-hdf5 file""" + ... + + def burst_search(self, colors: Union[str, list[BurstColor]]) -> Bursts: + """ + Search for bursts in the photon data. + + The burst search supports N-'color' burst search. For example, the following `colors` + can be used for a typical dual color burst search: + + >>> colors = [ + ... BurstColor(streams=['DD', 'DA'], L=35, M=50, T=0.0005), + ... BurstColor(streams=['AA'], L=30, M=50, T=0.0005) + ... ] + + Burst seach will be applied to each stream separately, after which overlapping intervals + between both streams are determined which yields the returned set of bursts. + + Args: + search_spec: Dictionary specifying the burst search parameters for each stream. + + Returns: + BurstSet object containing the identified bursts. + + """ + + if isinstance(colors, str): + burst_colors = cfg.burst_search[colors] + elif isinstance(colors, list): + burst_colors = colors + else: + raise ValueError("colors must be a string or list of BurstColor objects") + + if self.timestamps_unit is None: + raise ValueError("timestamps_unit must be set before searching for bursts") + + # Create a list of timestamp tuples marking start and end of the bursts + # Final output is a list of lists of tuples with one sublist of tuples per burst photon stream + times_list = [] + for c in burst_colors: + timestamps = self.data.filter(pl.col("stream").is_in(c.streams))[ + "timestamps" + ].to_numpy() + T_int = np.round(c.T / (2 * self.timestamps_unit)).astype("int32") + + # Indices of timestamps of burst time start and stop + indices = bs_eggeling(timestamps, c.L, c.M, T_int) + # Convert indices to times + # TODO: return timestamps directly + times = [(timestamps[imin], timestamps[imax]) for imin, imax in indices] + times_list.append(times) + + # Check if any of the times _items is empty, if so, bursts is empty + if any(len(t) == 0 for t in times_list): + burst_photons = pl.DataFrame({k: [] for k in self.data.columns + ["burst_index"]}) + else: + # Take the intersection of the time intervals found by the multi-color burst search + final_times = reduce(return_intersections, times_list) + + if len(final_times) == 0: # No overlap found + burst_photons = pl.DataFrame({k: [] for k in self.data.columns + ["burst_index"]}) + else: + tmin, tmax = np.array(final_times).T + + # Convert back to indices + imin = np.searchsorted(self.timestamps, tmin) + imax = np.searchsorted(self.timestamps, tmax) + # take all photons (up to and including? edges need to be checked!) + b_num = int(2 ** np.ceil(np.log2((np.log2(len(imin)))))) + dtype = getattr(pl, f"UInt{b_num}", pl.Int32) + bursts = [ + self.data[i1 : i2 + 1].with_columns(pl.lit(bi).alias("burst_index").cast(dtype)) + for bi, (i1, i2) in enumerate(zip(imin, imax)) + ] + burst_photons = pl.concat(bursts) + + bs = Bursts(burst_photons, metadata=self.metadata) + + return bs + + +class BinnedPhotonData: + """ + Binned photon data. + """ + + def __init__( + self, + photon_data: PhotonData, + binning_time: float = 1e-3, + bounds: tuple[Optional[float], Optional[float]] = (None, None), + ): + # bounds interval is inclusive, exclusive + + self.photon_data = photon_data + self.__binning_time = binning_time + + N = 10000 # max number of datapoints + tmin = bounds[0] or 0 + tmax = bounds[1] or min(photon_data.tmax + np.spacing(1.0), N * binning_time) + self.__bounds = (tmin, tmax) + + @property + def binning_time(self) -> float: + """Binning time in seconds""" + return self.__binning_time + + @binning_time.setter + def binning_time(self, value: float): + self.__binning_time = value + self.invalidate_cache() + + @property + def bounds(self) -> tuple[float, float]: + return self.__bounds + + @bounds.setter + def bounds(self, value: tuple[float, float]): + self.__bounds = value + self.invalidate_cache() + + def invalidate_cache(self): + if hasattr(self, "time"): + del self.time + if hasattr(self, "photons"): + del self.photons + + @cached_property + def photons(self) -> np.ndarray: # integer array + """Number of photons per time bin""" + self.time, self.photons = self._get_binned() + return self.photons + + @cached_property + def time(self) -> np.ndarray: # float array + """Time axis (in seconds). Time points are centers of bins""" + self.time, self.photons = self._get_binned() + return self.time + + @property + def photon_times(self) -> np.ndarray: + """Photon arrival times in this trace, subject to `bounds`""" + b_arr = np.array(self.bounds) / self.photon_data.timestamps_unit + bounds_int = np.array([np.floor(b_arr[0]), np.ceil(b_arr[1])]).astype("uint64") + i_min, i_max = np.searchsorted(self.photon_data.timestamps, bounds_int) + + return self.photon_data.photon_times[i_min:i_max] + + def _get_binned(self) -> tuple[np.ndarray, np.ndarray]: + """Gets bin centers (time, seconds) and photons per bin""" + + return get_binned(self.photon_times.to_numpy(), self.binning_time, self.bounds) + + def __len__(self) -> int: + """Length of the binned result""" + return len(self.time) + + +class Bursts(object): + """ + Class which holds a set of bursts. + + attrs: + bursts np.array with Burst Objects + """ + + # todo add metadata support + # bursts: numpy.typing.ArrayLike[Bursts] ? + def __init__( + self, photon_data: pl.DataFrame, metadata: Optional[dict] = None, cfg: DontFRETConfig = cfg + ): + self.photon_data = photon_data + self.metadata: dict = metadata or {} + self.cfg = cfg + + agg = [(pl.col("stream") == stream).sum().alias(f"n_{stream}") for stream in cfg.streams] + agg.extend( + [ + pl.when(pl.col("stream") == k).then(pl.col("nanotimes")).mean().alias(f"tau_{k}") + for k in cfg.streams + ] + ) + + agg.append(pl.col("timestamps").mean().alias("timestamps_mean")) + agg.append(pl.col("timestamps").min().alias("timestamps_min")) + agg.append(pl.col("timestamps").max().alias("timestamps_max")) + + # TODO as yaml in config + columns = [ + (pl.col("n_DA") / (pl.col("n_DD") + pl.col("n_DA"))).alias("E_app"), + ( + (pl.col("n_DA") + pl.col("n_DD")) + / (pl.col("n_DD") + pl.col("n_DA") + pl.col("n_AA")) + ).alias("S_app"), + photon_data["burst_index"].unique_counts().alias("n_photons"), + ] + + t_unit = self.metadata.get("timestamps_unit", None) + if t_unit is not None: + columns.extend( + [ + (pl.col("timestamps_mean") * t_unit).alias("time_mean"), + ((pl.col("timestamps_max") - pl.col("timestamps_min")) * t_unit).alias( + "time_length" + ), + ] + ) + + self.burst_data = ( + self.photon_data.group_by("burst_index", maintain_order=True) + .agg(agg) + .with_columns(columns) + ) + + @classmethod + def load(cls, directory: Path) -> Bursts: + data = pl.read_parquet(directory / "data.pq") + with open(directory / "metadata.json", "r") as f: + metadata = json.load(f) + + cfg = DontFRETConfig.from_yaml(directory / "config.yaml") + return Bursts(data, metadata, cfg) + + def save(self, directory: Path) -> None: + directory.mkdir(parents=True, exist_ok=True) + self.photon_data.write_parquet(directory / "data.pq") + with open(directory / "metadata.json", "w") as f: + json.dump(self.metadata, f) + self.cfg.to_yaml(directory / "config.yaml") + + def __len__(self) -> int: + """Number of bursts""" + return len(self.burst_data) + + def __iter__(self): + return self.photon_data.group_by("burst_index") + + @property + def timestamps_unit(self) -> Optional[float]: + """Multiplication factor to covert timestamps integers to seconds""" + try: + return self.metadata["timestamps_unit"] + except KeyError: + return None diff --git a/dont_fret/process.py b/dont_fret/process.py new file mode 100644 index 0000000..2ae17e0 --- /dev/null +++ b/dont_fret/process.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path +from typing import Literal, Optional + +import polars as pl +from tqdm.auto import tqdm + +from dont_fret.config import cfg +from dont_fret.fileIO import PhotonFile +from dont_fret.models import PhotonData + + +def search_and_save( + file: Path, + burst_colors: str | list[str] | None = None, + write_photons: bool = True, + output_type: Literal[".pq", ".csv"] = ".pq", +) -> None: + """ + Performs burst search on the supplied file and saves burst search output to disk. + """ + photons = PhotonData.from_file(PhotonFile(file)) + if burst_colors is None: + colors = cfg.burst_search.keys() + elif isinstance(burst_colors, str): + colors = [burst_colors] + elif isinstance(burst_colors, list): + colors = burst_colors + output_dir = file.parent + for color in colors: + bursts = photons.burst_search(color) + if write_photons: + write_dataframe( + bursts.photon_data, output_dir / f"{file.stem}_{color}_photon_data{output_type}" + ) + write_dataframe( + bursts.burst_data, output_dir / f"{file.stem}_{color}_burst_data{output_type}" + ) + + +def write_dataframe(df: pl.DataFrame, path: Path) -> None: + """Write a dataframe to disk. Writier used depends on path suffix.""" + if path.suffix == ".pq": + df.write_parquet(path) + elif path.suffix == ".csv": + df.write_csv(path) + else: + raise ValueError(f"Unsupported output type: {path.suffix}") + + +def batch_search_and_save( + files: list[Path], + burst_colors: str | list[str] | None = None, + write_photons: bool = True, + output_type: Literal[".pq", ".csv"] = ".pq", + max_workers: Optional[int] = None, +) -> None: + """ + Search all photon file items in batch threaded. + """ + + futures = [] + with ThreadPoolExecutor(max_workers=max_workers) as executor: + for f in files: + fut = executor.submit(search_and_save, f, burst_colors, write_photons, output_type) + futures.append(fut) + + for f in tqdm(as_completed(futures), total=len(futures)): + f.result() diff --git a/dont_fret/support.py b/dont_fret/support.py new file mode 100644 index 0000000..88dde49 --- /dev/null +++ b/dont_fret/support.py @@ -0,0 +1,31 @@ +"""data handling supporting functions""" + +import numpy as np + + +def get_binned( + times: np.ndarray, binning_time: float, bounds: tuple[float, float] +) -> tuple[np.ndarray, np.ndarray]: + """ + Returns asynchronous data represented as events at discrete `times` into a + time-binned format. + + Args: + times: Input event times. + binning_time: Width of the requested bins. + bounds: Requested time interval of binned data. Format is (`lower_bound`, `upper_bound`). + Value of `lower_bound` must be smaller than or equal to the min value of `times`, + mutatis mutandis `upper_bound`. + + + Returns: + A tuple (`time`, `binned`), where `time` is the bin centers and `binned` the number + of events in this bin. + """ + time = np.arange(bounds[0], bounds[-1], binning_time) + binning_time / 2 + t_min = int(bounds[0] / binning_time) + + t = (times / binning_time).astype(int) # binned times as integer mutiples of binning_time + binned = np.bincount(t - t_min, minlength=time.size) + + return time, binned diff --git a/dont_fret/tmp.py b/dont_fret/tmp.py new file mode 100644 index 0000000..1458dbe --- /dev/null +++ b/dont_fret/tmp.py @@ -0,0 +1,51 @@ +from pathlib import Path + +import solara +import solara.lab +import yaml + +import dont_fret.web.state as state +from dont_fret.config.config import cfg +from dont_fret.web.dev import load_burst_items, load_file_items +from dont_fret.web.home import HomePage +from dont_fret.web.main import Page as MainPage +from dont_fret.web.models import FRETNode + +data = yaml.safe_load(Path("default_testing.yaml").read_text()) +cfg.update(data) + + +# TODO default burst searches from config +DCBS_TEST = {"DD + DA": {"L": 50, "M": 35, "T": 0.0005}, "AA": {"L": 50, "M": 35, "T": 0.0005}} +APBS_TEST = {"DD + DA + AA": {"L": 50, "M": 35, "T": 0.0005}} + +pth = "ds2" +photon_file_items = load_file_items(pth) +# burst_items = load_burst_items(pth, suffix=".csv") + +fret_nodes = [ + FRETNode( + name="FRET NOT", + photons=photon_file_items, + # bursts=burst_items, + ), +] + +# %% + + +@solara.component +def Page(): + def preload(): + # state.FRET_NODES.set([]) + if len(state.fret_nodes.value) == 0: + state.fret_nodes.extend(fret_nodes) + + solara.use_effect(preload, dependencies=[]) + nodes = state.fret_nodes.value + + if len(nodes) != 0: + with solara.Column(style={"height": "100%"}): + MainPage() + else: + solara.Text("Loading fret nodes...") diff --git a/dont_fret/utils.py b/dont_fret/utils.py new file mode 100644 index 0000000..132e8f8 --- /dev/null +++ b/dont_fret/utils.py @@ -0,0 +1,55 @@ +from __future__ import annotations +from collections import OrderedDict +from pathlib import Path + +import numpy as np +from typing import Any + + +def clean_types(d: Any) -> Any: + """cleans up nested dict/list/tuple/other `d` for exporting as yaml/json + + Converts library specific types to python native types, including numpy dtypes, + OrderedDict, numpy arrays + + # https://stackoverflow.com/questions/59605943/python-convert-types-in-deeply-nested-dictionary-or-array + + """ + if isinstance(d, np.floating): + return float(d) + + if isinstance(d, np.integer): + return int(d) + + if isinstance(d, np.ndarray): + return d.tolist() + + if isinstance(d, list): + return [clean_types(item) for item in d] + + if isinstance(d, tuple): + return tuple(clean_types(item) for item in d) + + if isinstance(d, OrderedDict): + return clean_types(dict(d)) + + if isinstance(d, dict): + return {k: clean_types(v) for k, v in d.items()} + + if isinstance(d, Path): + return str(d) + + else: + return d + + +class Singleton(type): + _instances: dict[type, Singleton] = {} + + def __call__(cls, *args: Any, **kwargs: Any) -> Any: + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + def instance(cls: Any, *args: Any, **kwargs: Any) -> Any: + return cls(*args, **kwargs) diff --git a/dont_fret/web/__init__.py b/dont_fret/web/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dont_fret/web/bursts/__init__.py b/dont_fret/web/bursts/__init__.py new file mode 100644 index 0000000..288c1e8 --- /dev/null +++ b/dont_fret/web/bursts/__init__.py @@ -0,0 +1,3 @@ +from dont_fret.web.bursts.page import BurstPage + +__all__ = ["BurstPage"] diff --git a/dont_fret/web/bursts/components.py b/dont_fret/web/bursts/components.py new file mode 100644 index 0000000..0eac72c --- /dev/null +++ b/dont_fret/web/bursts/components.py @@ -0,0 +1,648 @@ +from __future__ import annotations + +import re +from typing import Callable, Literal, Optional, cast + +import numpy as np +import plotly.express as px +import plotly.graph_objects as go +import polars as pl +import solara +import solara.lab +from plotly.subplots import make_subplots +from solara.alias import rv +from solara.toestand import Ref + +from dont_fret.web.bursts.methods import create_histogram +from dont_fret.web.components import RangeInputField +from dont_fret.web.methods import chain_filters +from dont_fret.web.models import BinnedImage, BurstFilterItem, BurstItem, BurstPlotSettings +from dont_fret.web.reactive import ReactiveFRETNodes +from dont_fret.web.utils import not_none + +N_STEP = 1000 # number slider steps +CMAP = px.colors.sequential.Viridis +WRATIO = 3.5 + + +@solara.component +def FigurePlotlyShapes( + fig: go.Figure, + shapes: dict, + dependencies=None, +): + from plotly.graph_objs._figurewidget import FigureWidget + + fig_element = FigureWidget.element() # type: ignore + + def update_data(): + fig_widget: FigureWidget = solara.get_widget(fig_element) # type: ignore + fig_widget.layout = fig.layout + + length = len(fig_widget.data) # type: ignore + fig_widget.add_traces(fig.data) + data = list(fig_widget.data) + fig_widget.data = data[length:] + + def update_shapes(): + if shapes: + fig_widget: FigureWidget = solara.get_widget(fig_element) # type: ignore + fig_widget.update_shapes(**shapes) + + solara.use_effect(update_data, dependencies or fig) + solara.use_effect(update_shapes, shapes) + + return fig_element + + +@solara.component +def EditFilterDialog( + filter_item: solara.Reactive[BurstFilterItem], # should be a reactive + data: np.ndarray, + on_close: Callable[[], None], +): + def bin_data(): + data_f = data[~np.isnan(data)] + counts, binspace = np.histogram(data_f, bins="fd") + xbins = {"start": binspace[0], "end": binspace[-1], "size": binspace[1] - binspace[0]} + arange = 2 * binspace[0] - 0.05 * binspace[-1], 1.05 * binspace[-1] - binspace[0] + + return data_f, xbins, arange + + data_f, xbins, arange = solara.use_memo(bin_data, []) + + xr_default = ( + not_none(filter_item.value.min, arange[0]), + not_none(filter_item.value.max, arange[1]), + ) + + xrange, set_xrange = solara.use_state(xr_default) + shapes, set_shapes = solara.use_state({}) + + def make_figure(): + return create_histogram(data_f, xbins, arange, xrange) + + fig = solara.use_memo(make_figure, []) + + show_slider, set_show_slider = solara.use_state(True) + + def update_xmin(value): + set_xrange((value, xrange[1])) + d = {"patch": dict(x0=arange[0], x1=value), "selector": 0} + set_shapes(d) + + def update_xmax(value): + set_xrange((xrange[0], value)) + d = {"patch": dict(x0=value, x1=arange[1]), "selector": 1} + set_shapes(d) + + def on_slider(value: tuple[float, float]): + if value[0] != xrange[0]: + d = {"patch": dict(x0=arange[0], x1=value[0]), "selector": 0} + elif value[1] != xrange[1]: + d = {"patch": dict(x0=value[1], x1=arange[1]), "selector": 1} + else: + return + set_shapes(d) + set_xrange(value) + + with solara.Card(f"Filter: {filter_item.value.name}"): + FigurePlotlyShapes(fig, shapes=shapes) + step = (arange[1] - arange[0]) / N_STEP + with solara.Row(): + with solara.Tooltip( + "Disable slider to prevent threshold value rounding." # type: ignore + ): + rv.Switch(v_model=show_slider, on_v_model=set_show_slider) + if show_slider: + solara.SliderRangeFloat( + label="", + min=arange[0], + max=arange[1], + value=xrange, + step=step, + on_value=on_slider, + ) + with solara.Row(): + RangeInputField( + label="Min", + value=xrange[0], + vtype=float, + on_value=update_xmin, + vmin=arange[0], + ) + RangeInputField( + label="Max", + value=xrange[1], + vtype=float, + on_value=update_xmax, + vmax=arange[1], + ) + + def on_save(): + new_filter = BurstFilterItem( + filter_item.value.name, + min=xrange[0], + max=xrange[1], + ) + filter_item.set(new_filter) + on_close() + + with solara.CardActions(): + rv.Spacer() + solara.Button("Save", icon_name="mdi-content-save", on_click=on_save) + solara.Button("Cancel", icon_name="mdi-window-close", on_click=on_close) + + +@solara.component +def FilterListItem( + filter_item: solara.Reactive[BurstFilterItem], data: np.ndarray, on_delete: Callable[[], None] +): + edit, set_edit = solara.use_state(False) + with rv.ListItem(): + rv.ListItemAvatar(children=[rv.Icon(children=["mdi-filter"])]) + rv.ListItemTitle(children=[filter_item.value.name]) + + # TODO multi line + def fmt(v): + if v is None: + return "None" + else: + return f"{v:.5g}" + + rv.ListItemSubtitle( + children=[f"{fmt(filter_item.value.min)} - {fmt(filter_item.value.max)}"] + ) + + solara.IconButton( + color="secondary", + small=True, + rounded=True, + icon_name="mdi-delete", + on_click=on_delete, + ) + + solara.IconButton( + color="secondary", + small=True, + rounded=True, + icon_name="mdi-pencil", + on_click=lambda: set_edit(True), + ) + + with rv.Dialog(v_model=edit, max_width=750, on_v_model=set_edit): + if edit: + EditFilterDialog( + filter_item, + data, + on_close=lambda: set_edit(False), + ) + + +DTYPES = { + "E_app": float, + "S_app": float, + "n_photons": int, + "time_length": float, + "time_mean": float, + "time_min": float, + "time_max": float, + "n_DD": int, + "n_DA": int, + "n_AA": int, + "n_AD": int, + "tau_DD": float, + "tau_DA": float, + "tau_AA": float, + "tau_AD": float, +} + + +@solara.component +def BurstFilters(filters: solara.Reactive[list[BurstFilterItem]], dataframe: pl.DataFrame): + f_names = [f.name for f in filters.value] + attrs = [k for k in DTYPES if k not in f_names] + new_filter_name, set_new_filter_name = solara.use_state(attrs[0]) + + with solara.Card(title="Global filters"): + with rv.List(dense=False): + for idx, flt in enumerate(filters.value): + + def on_delete(idx=idx): + new_filters = filters.value.copy() + del new_filters[idx] + filters.set(new_filters) + + arr = dataframe[flt.name].to_numpy() + FilterListItem(Ref(filters.fields[idx]), arr, on_delete) + + def add_filter(): + item = BurstFilterItem(name=new_filter_name) + new_filters = filters.value.copy() + new_filters.append(item) + filters.set(new_filters) + + solara.Select( + label="Filter attribute", + value=new_filter_name, + values=attrs, + on_value=set_new_filter_name, + ) + solara.Button("Add filter", on_click=lambda: add_filter(), block=True) + + +def generate_figure( + df: pl.DataFrame, + plot_settings: BurstPlotSettings, + binned_image: BinnedImage, + dark: bool = False, +) -> go.Figure: + fig = make_subplots( + rows=2, + cols=2, + shared_yaxes="rows", # type: ignore + shared_xaxes="columns", # type: ignore + horizontal_spacing=0.02, + vertical_spacing=0.02, + column_widths=[WRATIO, 1], + row_heights=[1, WRATIO], + ) + + if sum([plot_settings.z_min is None, plot_settings.z_max is None]) == 1: + raise ValueError("z_min and z_max must be both None or both not None") + + hist2d = go.Heatmap( + x=binned_image.x, + y=binned_image.y, + z=binned_image.img_data.T, + zmin=plot_settings.z_min, + zmax=plot_settings.z_max, + colorscale=CMAP, + colorbar={"title": "Counts"}, + ) + + fig.add_trace(hist2d, row=2, col=1) + fig.update_xaxes(row=2, col=1, range=plot_settings.x_range, title=plot_settings.x_name) + fig.update_yaxes(row=2, col=1, range=plot_settings.y_range, title=plot_settings.y_name) + + if dark: + hist_settings = { + "marker_color": "#adadad", + } + else: + hist_settings = { + "marker_color": "#330C73", + } + + histx = go.Histogram( + x=df[plot_settings.x_name], + xbins=plot_settings.xbins, + name=plot_settings.x_name, + **hist_settings, + ) + fig.add_trace(histx, row=1, col=1) + fig.update_yaxes(row=1, col=1, title="counts") + + histy = go.Histogram( + y=df[plot_settings.y_name], + ybins=plot_settings.ybins, + name=plot_settings.y_name, + **hist_settings, + ) + fig.add_trace( + histy, + row=2, + col=2, + ) + fig.update_xaxes(row=2, col=2, title="counts") + fig.update_layout( + width=700, + height=700, + showlegend=False, + margin=dict(l=20, r=20, t=20, b=20), + template="plotly_dark" if dark else "plotly_white", + ) + + return fig + + +@solara.component +def FileFilterDialog( + burst_item: solara.Reactive[BurstItem], + on_close: Callable[[], None], +): + all_files = sorted(burst_item.value.df["filename"].unique()) + local_selected_files = solara.use_reactive(cast(list[str], burst_item.value.selected_files)) + error, set_error = solara.use_state("") + regex, set_regex = solara.use_state("") + + def on_input(value: str): + try: + pattern = re.compile(value) + set_error("") + except Exception: + set_error("Invalid regex") + set_regex(value) + return + new_selected = [f for f in all_files if pattern.search(f)] + local_selected_files.set(new_selected) + + def on_save(): + if not local_selected_files.value: + return + burst_item.update(selected_files=local_selected_files.value) + # selected_files.set(local_selected_files.value) + on_close() + + with solara.Card("File Filter"): + with solara.Row(style="align-items: center;"): + solara.InputText( + label="regex", value=regex, on_value=on_input, continuous_update=True, error=error + ) + solara.Button(label="Select All", on_click=lambda: local_selected_files.set(all_files)) + solara.Button(label="Select None", on_click=lambda: local_selected_files.set([])) + with solara.v.List(nav=True): + with solara.v.ListItemGroup( + v_model=local_selected_files.value, + on_v_model=local_selected_files.set, + multiple=True, + ): + for filename in all_files: + with solara.v.ListItem(value=filename): + with solara.v.ListItemAction(): + solara.Checkbox(value=filename in local_selected_files.value) + solara.v.ListItemTitle(children=[filename]) + + with solara.CardActions(): + solara.v.Spacer() + solara.Button( + "Save", + icon_name="mdi-content-save", + on_click=on_save, + disabled=not local_selected_files.value, + ) + solara.Button("Close", icon_name="mdi-window-close", on_click=on_close) + + +@solara.component +def PlotSettingsEditDialog( + plot_settings: solara.Reactive[BurstPlotSettings], + df: pl.DataFrame, + on_close: Callable[[], None], + duration: Optional[float] = None, +): + copy = solara.use_reactive(plot_settings.value) + img, set_img = solara.use_state(cast(Optional[BinnedImage], None)) + items = list(df.columns) + + drop_cols = ["filename", "burst_index"] + for col in drop_cols: + if col in items: + items.remove(col) + + def on_save(): + plot_settings.value = copy.value + on_close() + + def autolimit_xy(field: str, axis: Literal["x"] | Literal["y"]): + if field in ["E_app", "S_app"]: + update = {f"{axis}_min": 0.0, f"{axis}_max": 1.0} + elif field.startswith("time") and duration is not None: + update = {f"{axis}_min": 0.0, f"{axis}_max": duration} + # elif field in time .. (autolimit to 0, aquisition duration) + else: + update = {f"{axis}_min": df[field].min(), f"{axis}_max": df[field].max()} + copy.update(**update) + + def autolimit_z(): + if img is not None: + copy.update(z_min=0.0, z_max=img.img_data.max()) + + # TODO z limits should only update when: + # they were `None` (first redraw) + # any of the 'redraw' attributes changed + def rebin(): + if ( + copy.value == plot_settings.value + and copy.value.z_min is not None + and copy.value.z_max is not None + ): + return + img = BinnedImage.from_settings(df, copy.value) + set_img(img) + copy.update(z_min=0.0, z_max=img.img_data.max()) + + # only *some* of copy's attributes should trigger the redraw (specifically not z_min, z_max) + redraw_attrs = ["x_name", "y_name", "x_min", "x_max", "y_min", "y_max", "nbinsx", "nbinsy"] + bin_result = solara.use_thread( + rebin, dependencies=[getattr(copy.value, attr) for attr in redraw_attrs] + ) + disabled = bin_result.state == solara.ResultState.RUNNING + with solara.Card("Plot Settings"): + solara.Select( + label="X value", + value=copy.value.x_name, + values=items, + on_value=lambda val: copy.update(x_name=val), + ) + + solara.Select( + label="Y value", + value=copy.value.y_name, + values=items, + on_value=lambda val: copy.update(y_name=val), + ) + + with solara.Row(): + RangeInputField( + label="X min", + value=copy.value.x_min, + on_value=lambda val: copy.update(x_min=val), + vtype=float, + vmax=copy.value.x_max, + enable_restore=False, + ) + + RangeInputField( + label="X max", + value=copy.value.x_max, + on_value=lambda val: copy.update(x_max=val), + vtype=float, + vmin=copy.value.x_min, + enable_restore=False, + ) + solara.IconButton( + icon_name="mdi-auto-fix", + on_click=lambda *args: autolimit_xy(copy.value.x_name, "x"), + ) + + with solara.Row(): + RangeInputField( + label="Y min", + value=copy.value.y_min, + on_value=lambda val: copy.update(y_min=val), + vtype=float, + vmax=copy.value.y_max, + enable_restore=False, + ) + + RangeInputField( + label="Y max", + value=copy.value.y_max, + on_value=lambda val: copy.update(y_max=val), + vtype=float, + vmin=copy.value.y_min, + enable_restore=False, + ) + + solara.IconButton( + icon_name="mdi-auto-fix", + on_click=lambda *args: autolimit_xy(copy.value.y_name, "y"), + ) + + # this should never be `True` after rebin thread finished for the first time + if copy.value.z_min is not None and copy.value.z_max is not None: + with solara.Row(): + RangeInputField( + label="Z min", + value=copy.value.z_min, + on_value=lambda val: copy.update(z_min=val), + vtype=float, + vmax=copy.value.z_max, + enable_restore=False, + disabled=disabled, + ) + RangeInputField( + label="Z max", + value=copy.value.z_max, + on_value=lambda val: copy.update(z_max=val), + vtype=float, + vmin=copy.value.z_min, + enable_restore=False, + disabled=disabled, + ) + + solara.IconButton( + icon_name="mdi-auto-fix", + on_click=lambda *args: autolimit_z(), + disabled=disabled, + ) + + with solara.GridFixed(columns=2): + solara.InputInt( + label="N bins X", + value=copy.value.nbinsx, + on_value=lambda val: copy.update(nbinsx=val), + ) + solara.InputInt( + label="N bins Y", + value=copy.value.nbinsy, + on_value=lambda val: copy.update(nbinsy=val), + ) + + with solara.CardActions(): + rv.Spacer() + solara.Button("Save", icon_name="mdi-content-save", on_click=on_save, disabled=disabled) + solara.Button("Close", icon_name="mdi-window-close", on_click=on_close) + + +# burst item model could have plotsettings instance as child to keep the state +@solara.component +def BurstFigure( + fret_nodes: ReactiveFRETNodes, + global_filters: solara.Reactive[list[BurstFilterItem]], + node_idx: Optional[solara.Reactive[int]] = None, + burst_idx: Optional[solara.Reactive[int]] = None, +): + node_idx = solara.use_reactive(node_idx if node_idx is not None else 0) + burst_idx = solara.use_reactive(burst_idx if burst_idx is not None else 0) + + figure, set_figure = solara.use_state(cast(Optional[go.Figure], None)) + edit_filter, set_edit_filter = solara.use_state(False) + edit_settings, set_edit_settings = solara.use_state(False) + plot_settings = solara.use_reactive(BurstPlotSettings()) + + dark_effective = solara.lab.use_dark_effective() + + node_ref = Ref(fret_nodes.fields[node_idx.value]) + burst_ref = Ref(fret_nodes.fields[node_idx.value].bursts[burst_idx.value]) + + has_bursts = (node for node in fret_nodes.value if node.bursts) + node_values = [{"text": node.name, "value": i} for i, node in enumerate(has_bursts)] + + burst_item_values = [ + {"text": burst_item.name, "value": i} for i, burst_item in enumerate(node_ref.value.bursts) + ] + + file_filter = pl.col("filename").is_in(burst_ref.value.selected_files) + f_expr = chain_filters(global_filters.value) & file_filter + + # this is triggered twice ? + def redraw(): + filtered_df = burst_ref.value.df.filter(f_expr) + img = BinnedImage.from_settings(filtered_df, plot_settings.value) + figure = generate_figure( + filtered_df, plot_settings.value, binned_image=img, dark=dark_effective + ) + set_figure(figure) + + # does hashability matter in speed? + fig_result = solara.use_thread( + redraw, + dependencies=[ + node_idx.value, + burst_idx.value, + plot_settings.value, + global_filters.value, + burst_ref.value.selected_files, + dark_effective, + ], + intrusive_cancel=False, # is much faster + ) + + def on_fret_node(value: int): + node_idx.set(value) + burst_idx.set(0) + + with solara.Card(): + with solara.Row(): + solara.Select( + label="Measurement", + value=node_idx.value, + on_value=on_fret_node, # type: ignore + values=node_values, # type: ignore + ) + + solara.Select( + label="Burst item", + value=burst_idx.value, + on_value=burst_idx.set, + values=burst_item_values, # type: ignore + ) + + solara.IconButton(icon_name="mdi-file-star", on_click=lambda: set_edit_filter(True)) + solara.IconButton(icon_name="mdi-settings", on_click=lambda: set_edit_settings(True)) + + solara.ProgressLinear(fig_result.state == solara.ResultState.RUNNING) + if figure is not None: + with solara.Div( + style="opacity: 0.3" if fig_result.state == solara.ResultState.RUNNING else None + ): + solara.FigurePlotly(figure) + + # dedent this and figure will flicker/be removed when opening the dialog + if edit_settings: + with rv.Dialog(v_model=edit_settings, max_width=750, on_v_model=set_edit_settings): + PlotSettingsEditDialog( + plot_settings, + burst_ref.value.df.filter(f_expr), # = filtered dataframe by global filter + on_close=lambda: set_edit_settings(False), + duration=burst_ref.value.duration, + ) + + if edit_filter: + with rv.Dialog(v_model=edit_filter, max_width=750, on_v_model=set_edit_filter): + FileFilterDialog( + burst_item=burst_ref, + on_close=lambda: set_edit_filter(False), + ) diff --git a/dont_fret/web/bursts/methods.py b/dont_fret/web/bursts/methods.py new file mode 100644 index 0000000..838f3c5 --- /dev/null +++ b/dont_fret/web/bursts/methods.py @@ -0,0 +1,54 @@ +from typing import Optional + +import plotly.graph_objs as go + + +def create_histogram( + data, xbins, arange: tuple[float, float], xrange: tuple[Optional[float], Optional[float]] +) -> go.Figure: + hist_trace = go.Histogram(x=data, xbins=xbins) + + layout = go.Layout( + modebar_remove=[ + "lasso2d", + "select2d", + "zoom2d", + "pan2d", + "zoomIn2d", + "zoomOut2d", + "autoScale2d", + "resetScale2d", + ], + xaxis=dict( + range=arange, + type="linear", + ), + yaxis=dict( + type="linear", + ), + ) + + # Create a figure with the histogram trace and layout + fig = go.Figure(data=[hist_trace], layout=layout) + fig.update_layout(dragmode=False, selectdirection="h") + + fig.add_vrect( + editable=False, + x0=arange[0], + x1=xrange[0] if xrange[0] is not None else arange[0], + fillcolor="gray", + opacity=0.5, + layer="above", + line_width=0, + ) + fig.add_vrect( + editable=False, + x0=xrange[1] if xrange[1] is not None else arange[1], + x1=arange[1], + fillcolor="gray", + opacity=0.5, + layer="above", + line_width=0, + ) + + return fig diff --git a/dont_fret/web/bursts/page.py b/dont_fret/web/bursts/page.py new file mode 100644 index 0000000..d6d2fd1 --- /dev/null +++ b/dont_fret/web/bursts/page.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +import polars as pl +import solara + +import dont_fret.web.state as state +from dont_fret.web.bursts.components import BurstFigure, BurstFilters +from dont_fret.web.methods import chain_filters + + +@solara.component +def BurstPage(): + solara.Title(f"{state.APP_TITLE} / Plot 2D") + # sidebar selected node and burst for filters + has_bursts = [node for node in state.fret_nodes.value if node.bursts] + fret_node, set_fret_node = solara.use_state(has_bursts[0]) + burst_item, set_burst_item = solara.use_state(fret_node.bursts[0]) + + def on_fret_node(node_id: str): + new_node = state.fret_nodes.get_node(node_id) + set_fret_node(new_node) + set_burst_item(new_node.bursts[0]) + + def on_burst_item(item_name: str): + new_item = state.fret_nodes.get_item(fret_node.id, "bursts", item_name) + set_burst_item(new_item) + + def get_dataframe() -> tuple[pl.DataFrame, pl.DataFrame]: + return burst_item.df.filter(chain_filters(state.filters.value)), burst_item.df + + filtered_df, original_df = solara.use_memo( + get_dataframe, + dependencies=[ + burst_item, + state.filters.value, + ], # TODO do they need id's for faster hashing? + ) + with solara.Sidebar(): + with solara.Card("Burst plot settings", margin=0, elevation=0): + solara.Select( + label="Measurement", + value=fret_node.id, + on_value=on_fret_node, # type: ignore + values=[ + {"text": fret_node.name, "value": fret_node.id} + for fret_node in state.fret_nodes.value + if fret_node.bursts # type: ignore + ], + ) + + # check what happens if fret node is changed; should make sure to select a new burst item + solara.Select( + label="Burst item", + value=burst_item.name, + on_value=on_burst_item, + values=[ph.name for ph in fret_node.bursts], + ) + solara.Info(f"Number of bursts: {len(original_df)}", dense=True) + solara.Info(f"After filtering: {len(filtered_df)}", dense=True) + items = list(filtered_df.columns) + try: + items.remove("filename") + except ValueError: + pass + + BurstFilters(state.filters, original_df) + with solara.Card(): + solara.FileDownload( + filtered_df.write_csv, + filename=f"{burst_item.name}{'_filtered' if state.filters.value else ''}_bursts.csv", + children=[ + solara.Button( + "Download bursts csv", + block=True, + ) + ], + ) + + with solara.GridFixed(columns=2): + BurstFigure( + state.fret_nodes, + state.filters, + node_idx=state.burst_figure_selection[0][0], + burst_idx=state.burst_figure_selection[0][1], + ) + BurstFigure( + state.fret_nodes, + state.filters, + node_idx=state.burst_figure_selection[1][0], + burst_idx=state.burst_figure_selection[1][1], + ) diff --git a/dont_fret/web/components.py b/dont_fret/web/components.py new file mode 100644 index 0000000..7e6d3f1 --- /dev/null +++ b/dont_fret/web/components.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +from typing import Callable, Optional, Type + +import solara +import solara.lab +from solara.alias import rv + +import dont_fret.web.state as state + +# from dont_fret.web.methods import burst_search, combine_bursts, get_dataframe + + +@solara.component +def Snackbar(): + bar = state.snackbar.value + + close_btn = solara.Button( + label="close", + color=bar.btn_color, + text=True, + on_click=lambda: state.snackbar.update(show=False), + ) + children = [bar.message, close_btn] + rv.Snackbar( + v_model=bar.show, + color=bar.color, + timeout=bar.timeout, + on_v_model=lambda x: state.snackbar.update(show=x), + children=children, + ) + + +@solara.component +def RangeInputField( + label: str, + value: float | int, + on_value: Callable[[float | int | None], None], + vtype: Type[float] | Type[int], + vmin: Optional[float | int] = None, + vmax: Optional[float | int] = None, + disabled: bool = False, + enable_restore: bool = True, +): + """Input field for a float value with a range slider.""" + + if vmin is not None and vmax is not None and vmin >= vmax: + raise ValueError("vmin must be smaller than vmax") + + error, set_error = solara.use_state(False) + message, set_message = solara.use_state("") + + def inputtext_cb(new_value: str): + try: + value = vtype(new_value) + + except ValueError: + if vtype == int: + set_message("Input must be an integer") + else: + set_message("Input must be a number") + set_error(True) + return + if vmin is not None and value < vmin: + set_message(f"Input must be >= {vmin}") + set_error(True) + return + if vmax is not None and value > vmax: + set_message(f"Input must be <= {vmax}") + set_error(True) + return + set_error(False) + set_message("") + on_value(value) + + text_field = solara.InputText( + label=label, + value=str(value), + error=error, + message=message, + disabled=disabled, + on_value=inputtext_cb, + ) + + # TODO restore to external component + if enable_restore: + with solara.Row(): + (text_field,) # type: ignore + solara.IconButton( + icon_name="mdi-restore", on_click=lambda *args: on_value(vmin or vmax) + ) + else: + text_field # type: ignore diff --git a/dont_fret/web/home/__init__.py b/dont_fret/web/home/__init__.py new file mode 100644 index 0000000..76b2686 --- /dev/null +++ b/dont_fret/web/home/__init__.py @@ -0,0 +1,3 @@ +from dont_fret.web.home.page import HomePage + +__all__ = ["HomePage"] diff --git a/dont_fret/web/home/burst_settings.py b/dont_fret/web/home/burst_settings.py new file mode 100644 index 0000000..f283604 --- /dev/null +++ b/dont_fret/web/home/burst_settings.py @@ -0,0 +1,110 @@ +from functools import partial + +import humanize +import solara +from solara.alias import rv + +from dont_fret.config import cfg +from dont_fret.web.reactive import BurstSettingsReactive + + +@solara.component +def BurstColorSettingForm( + burst_settings: BurstSettingsReactive, settings_name: str, color_idx: int +): + burst_color = burst_settings.get_color(settings_name, color_idx) + setter = partial(burst_settings.update_color, settings_name, color_idx) + with solara.ColumnsResponsive([8, 4]): + with solara.Card("Search Thresholds"): + with solara.Column(): + # TODO figure out why + with solara.Tooltip("Minimal number of 'burst' photons."): # type: ignore + # TODO doesnt keep its value when losing focus; ONLY when pressing enter (-> continous_update) + solara.InputInt( + label="L", + value=burst_color.L, + on_value=lambda val: setter(L=val), + continuous_update=True, + ) + with solara.Tooltip("Number of neighbours in interval."): # type: ignore + solara.InputInt( + label="M", + value=burst_color.M, + on_value=lambda val: setter(M=val), + continuous_update=True, + ) + with solara.Tooltip("Interval size in seconds."): # type: ignore + solara.InputFloat( + label="T", + value=burst_color.T, + on_value=lambda val: setter(T=val), + continuous_update=True, + ) + + with solara.Card("Photon Streams"): + with rv.List(): + with solara.Tooltip("Select the streams to include in this color."): # type: ignore + for stream in cfg.streams: + + def on_value(val, stream: str = stream): + new_streams = burst_color.streams.copy() + if val: + new_streams.append(stream) + else: + new_streams.remove(stream) + setter(streams=new_streams) + + with rv.ListItem(): + rv.ListItemTitle(children=[stream]) + with rv.ListItemAction(): + solara.Checkbox( + value=stream in burst_color.streams, on_value=on_value + ) + + +@solara.component +def BurstSettingsDialog(burst_settings: BurstSettingsReactive, settings_name, on_close): + tab, set_tab = solara.use_state(0) + title = f"Editing burst search settings: '{settings_name}'" + + with solara.Card(title): + + def on_tab_change(val): + set_tab(val) + + def on_tab_remove(*args): + burst_settings.remove_color(settings_name) + + def on_tab_add(*args): + burst_settings.add_color(settings_name) + + try: + n_tabs = len(burst_settings.value[settings_name]) + # This can happen if the settings are reset while a new set was added and is selected + except KeyError: + return + + with solara.ColumnsResponsive([1, 10, 1]): + solara.IconButton(icon_name="mdi-minus", color="primary", on_click=on_tab_remove) + with solara.Row(): + with rv.Tabs( + centered=True, grow=True, v_model=tab, on_v_model=on_tab_change, height=30 + ): + for i in range(n_tabs): # Ref / fields ? + rv.Tab(children=[f"Color {humanize.apnumber(i + 1)}"]) + solara.IconButton(icon_name="mdi-plus", color="primary", on_click=on_tab_add) + + with rv.TabsItems(v_model=tab): + for i in range(n_tabs): + with rv.TabItem(): + BurstColorSettingForm(burst_settings, settings_name, i) + + with solara.CardActions(): + # todo align center + with rv.Layout(row=True): + solara.Button( + label="Save & close", + on_click=lambda *args: on_close(False), + text=True, + classes=["centered"], + ) diff --git a/dont_fret/web/home/fret_tree.vue b/dont_fret/web/home/fret_tree.vue new file mode 100644 index 0000000..3afd659 --- /dev/null +++ b/dont_fret/web/home/fret_tree.vue @@ -0,0 +1,18 @@ + + diff --git a/dont_fret/web/home/info_cards.py b/dont_fret/web/home/info_cards.py new file mode 100644 index 0000000..5ca9a5d --- /dev/null +++ b/dont_fret/web/home/info_cards.py @@ -0,0 +1,452 @@ +"""Solara components for the home page.""" + +from datetime import datetime +from pathlib import Path +from typing import Callable + +import polars as pl +import solara +import solara.lab +import yaml +from solara.alias import rv + +import dont_fret.web.state as state +from dont_fret.web.home.burst_settings import BurstSettingsDialog +from dont_fret.web.home.methods import task_burst_search +from dont_fret.web.methods import format_size +from dont_fret.web.models import BurstItem, FRETNode, PhotonFileItem +from dont_fret.web.reactive import ( + BurstSettingsReactive, + ReactiveFRETNodes, +) + + +@solara.component +def FRETNodeInfoCard( + fret_node: FRETNode, + on_name: Callable[[str], None], + on_description: Callable[[str], None], + on_delete: Callable[[], None], +): + title = solara.use_reactive(fret_node.name) + editing, set_editing = solara.use_state(False) + + def update_title(value): + title.set(value) + on_name(value) + set_editing(False) + + title_elem = ( + fret_node.name + if not editing + else solara.InputText(label="", value=title, on_value=update_title, continuous_update=False) + ) + with solara.Card(title_elem): # type: ignore + rv.Textarea(label="Description", v_model=fret_node.description, on_v_model=on_description) + with solara.CardActions(): + rv.Spacer() + solara.IconButton("edit", on_click=lambda: set_editing(True)) + solara.IconButton("delete", on_click=lambda *args: on_delete()) + + +@solara.component +def PhotonInfoCard( + fret_nodes: ReactiveFRETNodes, + measurement_id: str, + filebrowser_folder: solara.Reactive[Path], + burst_settings: BurstSettingsReactive, + open_: solara.Reactive[list[str]], +): + bs_name, set_bs_name = solara.use_state(next(iter(burst_settings.value.keys()))) + add_new, set_add_new = solara.use_state(False) + show_settings_dialog, set_show_settings_dialog = solara.use_state(False) + + open_confirmation_dialog = solara.use_reactive(False) + + # open the current node so that users can see the files they're adding + def open_node(): + ph_node_id = f"{measurement_id}:photons" + + if ph_node_id not in open_.value: + new_open = open_.value.copy() + new_open.append(ph_node_id) + open_.value = new_open + + def filebrowser_filter(pth: Path) -> bool: + """Return True if the path is a ptu file or a folder.""" + if pth.is_dir(): + return True + elif pth.suffix == ".ptu": + return True + else: + return False + + def add_single_file(ptu_pth: Path): + if ptu_pth.is_dir(): + return + elif ptu_pth.suffix != ".ptu": + return + + add_files([ptu_pth]) + + def add_all_files(*ignore): + filebrowser_folder.value + ptu_files = list(filebrowser_folder.value.glob("*.ptu")) # TODO allow other file types + add_files(ptu_files) + + def add_files(files: list[Path]): + open_node() + current_names = [ph.name for ph in fret_nodes.get_node(measurement_id).photons] + to_add = [PhotonFileItem(file_path=pth) for pth in files if pth.name not in current_names] + fret_nodes.add_photon_files(measurement_id, to_add) + + def remove_all_files(*ignore): + fret_nodes.remove_all_photon_files(measurement_id) + + def confirm_burst_search(): + if not state.fret_nodes.get_node(measurement_id).photons: + state.snackbar.warning("No photon files selected", timeout=0) + return + if bs_name in [b.name for b in fret_node.bursts]: + open_confirmation_dialog.value = True + else: + do_burst_search() + + def do_burst_search(): + # open the bursts node if not open already + bursts_node_id = f"{measurement_id}:bursts:" + if bursts_node_id not in open_.value: + new_open = open_.value.copy() + new_open.append(bursts_node_id) + open_.value = new_open + + task_burst_search(bs_name, measurement_id) + + def on_new_settings(new_name): + if new_name in burst_settings.settings_names: + return + set_add_new(False) + burst_settings.add_settings(new_name) + set_bs_name(new_name) + + fret_node = fret_nodes.get_node(measurement_id) + with solara.Card(f"{fret_node.name} / Photons"): + with solara.Columns([3, 1], gutters=False, gutters_dense=True): + with solara.Card(margin=0): + solara.Text("Click files to add") + solara.FileBrowser( + directory=filebrowser_folder, + on_file_open=add_single_file, + filter=filebrowser_filter, + directory_first=True, + ) + + with solara.Card("Burst Search", margin=0): + with solara.Column(): + if add_new: + solara.InputText( + label="New settings name", value="", on_value=on_new_settings + ) + else: + solara.Select( + label="Burst search settings", + value=bs_name, + on_value=set_bs_name, + values=list( + burst_settings.value.keys() + ), # color names? colorset names? + ) + with solara.Row(gap="0px", justify="space-evenly"): + with solara.Tooltip("Edit current settings"): # type: ignore + solara.IconButton( + icon_name="mdi-pencil", + on_click=lambda *args: set_show_settings_dialog(True), + ) + + with solara.Tooltip("Add new settings"): # type: ignore + solara.IconButton( + icon_name="mdi-plus", on_click=lambda *args: set_add_new(True) + ) + + def remove_settings(): + new_names = burst_settings.settings_names + if len(new_names) == 1: + return + + new_names.remove(bs_name) + set_bs_name(new_names[0]) + burst_settings.remove_settings(bs_name) + + with solara.Tooltip("Delete current settings"): # type: ignore + solara.IconButton( + icon_name="mdi-delete", on_click=lambda *args: remove_settings() + ) + + def reset(): + burst_settings.reset() + # possibly we enter an illegal state, so set name to the first entry. + set_bs_name(next(iter(burst_settings.value.keys()))) + + with solara.Tooltip("Restore default settings"): # type: ignore + solara.IconButton( + icon_name="mdi-restore", + on_click=lambda *args: reset(), + ) + + solara.Button( + "Search!", + on_click=confirm_burst_search, + disabled=task_burst_search.pending, + block=True, + ) + + solara.lab.ConfirmationDialog( + open_confirmation_dialog, + content=f"Burst search result {bs_name!r} already exists. Overwrite?", + on_ok=do_burst_search, + ) + + solara.ProgressLinear( + task_burst_search.progress if task_burst_search.pending else False # type: ignore + ) + + with solara.CardActions(): + solara.Button("Add all files", text=True, on_click=add_all_files) + solara.Button("Remove all files", text=True, on_click=remove_all_files) + + with rv.Dialog( + v_model=show_settings_dialog, + on_v_model=set_show_settings_dialog, + persistent=False, + max_width=800, + ): + BurstSettingsDialog(burst_settings, bs_name, on_close=set_show_settings_dialog) + + +@solara.component +def BurstInfoCard( + fret_nodes: ReactiveFRETNodes, + measurement_id: str, + filebrowser_folder: solara.Reactive[Path], + open_: solara.Reactive[list[str]], +): + # TODO different file types for bursts + def filebrowser_filter(pth: Path) -> bool: + """Return True if the path is a .csv file or a folder.""" + if pth.is_dir(): + return True + elif pth.suffix == ".csv": + return True + else: + return False + + def add_single_file(burst_pth: Path): + if burst_pth.is_dir(): + return + + bursts_node_id = f"{measurement_id}:bursts" + + # open the leaf we are adding to + if bursts_node_id not in open_.value: + new_open = open_.value.copy() + new_open.append(bursts_node_id) + open_.value = new_open + + # check if the item already exists: + if burst_pth.stem in [b.name for b in fret_nodes.get_node(measurement_id).bursts]: + state.snackbar.warning(f"Burst result name {burst_pth.stem!r} already exists.") + return + + # TODO check already added + fret_nodes.add_burst_items(measurement_id, [BurstItem.from_path(burst_pth)]) + + fret_node = fret_nodes.get_node(measurement_id) + with solara.Card(f"{fret_node.name} / Bursts"): + solara.Text("Click files to add") + solara.FileBrowser( + directory=filebrowser_folder.value, + on_directory_change=filebrowser_folder.set, + on_file_open=add_single_file, + filter=filebrowser_filter, + directory_first=True, + ) + + +@solara.component +def PhotonFileInfoCard(ph_file_item: PhotonFileItem, node_name: str, on_delete: Callable[[], None]): + # prevent flicker by setting `loaded` to `True` if we find ourselves in this component + # with the photon loading future already completed + loaded, set_loaded = solara.use_state(False) + + def load_info(): + if ph_file_item.photons is not None: + set_loaded(True) + else: + set_loaded(False) + info = ph_file_item.get_info() + return info + + result = solara.lab.use_task(load_info, dependencies=[ph_file_item], prefer_threaded=True) + + with solara.Card(f"{node_name} / Photon file / {ph_file_item.name}"): + solara.ProgressLinear(result.pending and not loaded) + + if result.pending and not loaded: + solara.Div(style="height: 20px;") + solara.Info("Loading photons...") + + elif result.finished: + info: dict = result.value # type: ignore + solara.Text("General info:") + dt = datetime.strptime(info["creation_time"], "%Y-%m-%d %H:%M:%S") + + headers = [ + {"text": "Property", "value": "property", "width": "50%"}, + {"text": "Value", "value": "value", "width": "50%"}, + ] + + items = [ + { + "property": "Creation date", + "value": f"{dt:%d %B %Y}", + }, + { + "property": "Creation time", + "value": f"{dt:%H:%M %Ss}", + }, + # TODO humanize file size + { + "property": "File size", + "value": format_size(ph_file_item.size), + }, + { + "property": "Number of photons", + "value": info["number_of_photons"], + }, + { + "property": "Acquisition duration", + "value": f"{info['acquisition_duration']} s", + }, + { + "property": "Power diode", + "value": f"{info['power_diode']:.2f} au", + }, + ] + + rv.DataTable( + headers=headers, + items=items, + hide_default_footer=True, + hide_default_header=True, + calculate_widths=True, + ) + + solara.Div(style="height: 20px;") + solara.Text("Count rates:") + headers = [ + {"text": "Stream", "value": "stream"}, + {"text": "Count rate (Hz)", "value": "count_rate", "width": "50%"}, + ] + + items = [ + { + "stream": "All", + "count_rate": f"{info['cps']:.2f}", + } + ] + stream_items = [ + {"stream": stream_name, "count_rate": f"{cps:.2f}"} + for stream_name, cps in info["stream_cps"].items() + ] + + rv.DataTable( + headers=headers, + items=items + stream_items, + hide_default_footer=True, + # hide_default_header=True, + calculate_widths=True, + ) + + solara.Div(style="height: 20px;") + + if comment := info.get("comment", None): + solara.Text(f"User comment: {comment}") + + with solara.CardActions(): + rv.Spacer() + solara.IconButton("delete", on_click=lambda *args: on_delete()) + + +@solara.component +def BurstItemInfoCard(burst_item: BurstItem, node_name: str, on_delete: Callable[[], None]): + headers = [ + {"text": "Filename", "value": "filename"}, + {"text": "Number of bursts", "value": "bursts"}, + {"text": "Photons per burst", "value": "n_photons"}, + {"text": "Duration (ms)", "value": "time_length"}, + {"text": "Interburst time (s)", "value": "ibt"}, + {"text": "Bursts per second (Hz)", "value": "bps"}, + {"text": "In-burst countrate (kHz)", "value": "burst_cps"}, + ] + + filenames = burst_item.df["filename"].unique().sort() + items = [] + for fname in filenames: + item = {"filename": fname} + df = burst_item.df.filter(pl.col("filename") == fname) + + item["bursts"] = len(df) + + inter_burst_times = df["time_mean"].diff().drop_nulls() + ibt_mean, ibt_std = inter_burst_times.mean(), inter_burst_times.std() + item["ibt"] = f"{ibt_mean:.2f} ± {ibt_std:.2f}" + items.append(item) + + bps_mean = 1 / ibt_mean # type: ignore + bps_std = (1 / (ibt_mean**2)) * ibt_std # type: ignore + + item["bps"] = f"{bps_mean:.2f} ± {bps_std:.2f}" + + item["n_photons"] = f"{df['n_photons'].mean():.1f} ± {df['n_photons'].std():.1f}" + item[ + "time_length" + ] = f"{df['time_length'].mean()*1e3:.2f} ± {df['time_length'].std()*1e3:.2f}" # type: ignore + + burst_cps = df["n_photons"] / df["time_length"] + item["burst_cps"] = f"{burst_cps.mean()*1e-3:.2f} ± {burst_cps.std()*1e-3:.2f}" # type: ignore + + with solara.Card(f"{node_name} / Bursts / {burst_item.name}"): + solara.Text(f"Total number of bursts: {len(burst_item.df)}", style="font-weight: bold;") + solara.HTML(tag="br") + solara.Text("Bursts statistics per file:") + + rv.DataTable( + headers=headers, + items=items, + calculate_widths=True, + hide_default_footer=len(items) <= 10, + ) + + with solara.CardActions(): + solara.FileDownload( + burst_item.df.write_csv, + filename=f"{burst_item.name}_bursts.csv", + children=[ + solara.Button( + "Download burst data.csv", + text=True, + ) + ], + ) + solara.FileDownload( + lambda: yaml.dump(burst_item.search_spec), + filename=f"{burst_item.name}_setttings.yaml", + children=[ + solara.Button( + "Download settings", + text=True, + ) + ], + ) + rv.Spacer() + solara.IconButton("delete", on_click=lambda *args: on_delete()) diff --git a/dont_fret/web/home/methods.py b/dont_fret/web/home/methods.py new file mode 100644 index 0000000..9d912b8 --- /dev/null +++ b/dont_fret/web/home/methods.py @@ -0,0 +1,50 @@ +import asyncio +from concurrent.futures import ThreadPoolExecutor + +import polars as pl +import solara +import solara.lab + +import dont_fret.web.state as state +from dont_fret.web.methods import burst_search +from dont_fret.web.models import BurstItem + + +@solara.lab.task(prefer_threaded=False) +async def task_burst_search(name: str, measurement_id: str) -> None: + # name: name of burst search settings as well as its output name + task_burst_search.progress = True + photon_file_items = state.fret_nodes.get_node(measurement_id).photons + burst_colors = list(state.burst_settings.value[name]) + + categories = [item.name for item in photon_file_items] + dtype = pl.Enum(categories) + + loop = asyncio.get_running_loop() + with ThreadPoolExecutor(max_workers=4) as executor: + futures = [] + for f in photon_file_items: + fut = loop.run_in_executor(executor, burst_search, f, burst_colors, dtype) + futures.append(fut) + + results = [] + for i, f in enumerate(asyncio.as_completed(futures)): + results.append(await f) + task_burst_search.progress = (i + 1) * (100 / len(futures)) + + task_burst_search.progress = True + + df = pl.concat( + results, how="vertical_relaxed" + ) # this should be very short such that the UI remains responsive + if len(df) == 0: + state.snackbar.warning("No bursts found", timeout=0) + else: + metadata = {fi.name: fi.get_info() for fi in photon_file_items} + burst_item = BurstItem(name=name, df=df, metadata=metadata) + state.fret_nodes.add_burst_items(measurement_id, [burst_item]) + state.snackbar.success( + f"Burst search completed, found {len(burst_item.df)} bursts", timeout=0 + ) + + task_burst_search.progress = False diff --git a/dont_fret/web/home/page.py b/dont_fret/web/home/page.py new file mode 100644 index 0000000..db6c08c --- /dev/null +++ b/dont_fret/web/home/page.py @@ -0,0 +1,119 @@ +from typing import cast + +import solara +import solara.lab +import solara.toestand + +import dont_fret.web.state as state +from dont_fret.web.home.info_cards import ( + BurstInfoCard, + BurstItemInfoCard, + FRETNodeInfoCard, + PhotonFileInfoCard, + PhotonInfoCard, +) +from dont_fret.web.methods import to_treeview + +welcome_text = """# Don't FRET! + +Add measurements on the left hand side. \n +Explore the nodes in the tree view to add photon files, perform burst search, inspect photon and burst files, +or reload burst .csv files. + +Then proceed to the `BURSTS` and `TRACE` pages to view bursts and photon files. +""" + + +@solara.component_vue("fret_tree.vue") +def FRETTreeView(items, value, active, open_): + pass + + +# Treeview reactives +_val = solara.reactive(None) +open_ = solara.Reactive(cast(list[str], [])) +active_id = solara.Reactive(cast(list[str], [])) + + +@solara.component +def HomePage(): + if len(state.fret_nodes.value) == 0: + state.fret_nodes.add_node() + + with solara.Columns([4, 8], style={"height": "100%"}): + with solara.Card("FRET Measurements"): + with solara.Column(): + FRETTreeView( + items=to_treeview(state.fret_nodes.value), + active=active_id.value, + on_active=active_id.set, # type: ignore + open_=open_.value, + on_open_=open_.set, # type: ignore + value=_val.value, + on_value=_val.set, # type: ignore + ) + + with solara.CardActions(): + solara.Button( + "Add new measurement", + text=True, + on_click=lambda *args: state.fret_nodes.add_node(), + ) + + split = active_id.value[0].split(":") if active_id.value else [] + if len(split) == 0: + with solara.Card(): + solara.Markdown(welcome_text, style="font-size: 18px") + elif len(split) == 1: + + def on_delete_node(node_id=split[0]): + # set the active id to the parent node before deleting + active_id.set([]) + state.fret_nodes.remove_node(node_id) + + def on_name(value: str, node_id=split[0]): + idx = state.fret_nodes.node_idx(node_id) + ref = solara.toestand.Ref(state.fret_nodes.fields[idx]) + ref.update(name=value) + + def on_description(value: str, node_id=split[0]): + idx = state.fret_nodes.node_idx(node_id) + ref = solara.toestand.Ref(state.fret_nodes.fields[idx]) + ref.update(description=value) + + fret_node = state.fret_nodes.get_node(split[0]) + FRETNodeInfoCard( + fret_node, on_name=on_name, on_description=on_description, on_delete=on_delete_node + ) + elif len(split) == 2: + node_id, dtype = split + if dtype == "photons": + PhotonInfoCard( + state.fret_nodes, node_id, state.filebrowser_folder, state.burst_settings, open_ + ) + elif dtype == "bursts": + BurstInfoCard(state.fret_nodes, node_id, state.filebrowser_folder, open_) + elif len(split) == 3: + node_id, dtype, file_item_id = split + fret_node = state.fret_nodes.get_node(node_id) + + if dtype not in ["photons", "bursts"]: + raise ValueError(f"Invalid dtype {dtype}") + + item = state.fret_nodes.get_item(node_id, dtype, file_item_id) # type: ignore + if dtype == "photons": + + def on_delete(item_id=file_item_id): + # set the active id to the parent node before deleting + active_id.set([f"{node_id}:{dtype}"]) + state.fret_nodes.remove_item(node_id, "photons", item_id) + + PhotonFileInfoCard(item, fret_node.name, on_delete) + elif dtype == "bursts": + + def on_delete(item_id=file_item_id): + # set the active id to the parent node before deleting + active_id.set([f"{node_id}:{dtype}"]) + state.fret_nodes.remove_item(node_id, "bursts", item_id) + + BurstItemInfoCard(item, fret_node.name, on_delete) diff --git a/dont_fret/web/local_cluster.py b/dont_fret/web/local_cluster.py new file mode 100644 index 0000000..7b99c89 --- /dev/null +++ b/dont_fret/web/local_cluster.py @@ -0,0 +1,41 @@ +import time +from pathlib import Path +from urllib.parse import urlparse + +import yaml +from distributed import LocalCluster + +# import executor module such that sizeof functions are registered +from dont_fret.config import cfg + +root = Path(__file__).parent.parent.parent +data = yaml.safe_load((root / "default_testing.yaml").read_text()) +cfg.update(data) + +address = cfg.web.executor.address # type: ignore +parsed_adress = urlparse(address) + + +def blocking_cluster(): + """Start a dask LocalCluster and block until iterrupted""" + + try: + local_cluster = LocalCluster(n_workers=6, threads_per_worker=2) + print(f"Started local cluster at {local_cluster.scheduler_address}") + except OSError: + # print(f"Could not start local cluster with at port: {port}") + raise + try: + loop = True + while loop: + try: + time.sleep(2) + except KeyboardInterrupt: + print("Interrupted") + loop = False + finally: + local_cluster.close() + + +if __name__ == "__main__": + blocking_cluster() diff --git a/dont_fret/web/main.py b/dont_fret/web/main.py new file mode 100644 index 0000000..ea24d1d --- /dev/null +++ b/dont_fret/web/main.py @@ -0,0 +1,134 @@ +import argparse +import copy +import sys +from pathlib import Path + +import solara +import solara.lab +import solara.server.settings +import yaml + +import dont_fret.web.state as state +from dont_fret.config import cfg +from dont_fret.web.bursts import BurstPage +from dont_fret.web.components import Snackbar +from dont_fret.web.home import HomePage +from dont_fret.web.models import BurstColorList, FRETNode +from dont_fret.web.trace import TracePage + + +def disable_bursts(nodes: list[FRETNode]) -> bool: + return not bool([ph for node in nodes for ph in node.bursts]) + + +def disable_trace(nodes: list[FRETNode]) -> bool: + return not bool([b for node in nodes for b in node.photons]) + + +pages = [ + { + "name": "home", + "main": HomePage, + "sidebar": None, + "disabled": lambda x: False, + "show": lambda: True, + }, + { + "name": "bursts", + "main": BurstPage, + "sidebar": None, + "disabled": disable_bursts, + "show": lambda: True, + }, + { + "name": "trace", + "main": TracePage, + "sidebar": None, + "disabled": disable_trace, + "show": lambda: True, + }, +] + + +parser = argparse.ArgumentParser(description="Process config argument") +parser.add_argument("--config", help="Configuration file") + +if "--" in sys.argv: + extra_args = sys.argv[sys.argv.index("--") + 1 :] + parsed = parser.parse_args(extra_args) + + data = yaml.safe_load(Path(parsed.config).read_text()) + cfg.update(data) + + +@solara.component +def Page(): + tab_selection = solara.use_reactive(0) + authorized = solara.use_reactive(cfg.web.password is None) + login_failed = solara.use_reactive(False) + password = solara.use_reactive("") + + def initialize(): + state.burst_settings.set({k: BurstColorList(v) for k, v in cfg.burst_search.items()}) + + default_filters = copy.deepcopy(cfg.web.burst_filters) + state.filters.set(default_filters) + + state.filebrowser_folder.set(cfg.web.default_dir) + if cfg.web.protect_filebrowser: + + def check_parent(new_value: Path): + if new_value in cfg.web.default_dir.parents: + state.filebrowser_folder.set(cfg.web.default_dir) + + return state.filebrowser_folder.subscribe(check_parent) + + solara.use_effect(initialize, []) + + def authorize(value: str): + if value == cfg.web.password: + authorized.set(True) + else: + login_failed.set(True) + + # it is important we do not interrupt the height 100% chain + # to ensure independent scrolling for both columns + with solara.Column(style={"height": "100%"}): + Snackbar() + with solara.AppBar(): + solara.lab.ThemeToggle() + with solara.lab.Tabs( + value=tab_selection.value, on_value=tab_selection.set, align="center" + ): + for page in pages: + if page["show"](): + solara.lab.Tab( + page["name"], disabled=page["disabled"](state.fret_nodes.value) + ) + solara.Title(state.APP_TITLE) + + if authorized.value: + pages[tab_selection.value]["main"]() + sidebar = pages[tab_selection.value]["sidebar"] + if sidebar is not None: + sidebar() + else: + with solara.Row(style="align-items: center;"): + solara.InputText( + label="Password", password=True, value=password.value, on_value=authorize + ) + solara.Button(label="Submit", on_click=lambda: authorize(password.value)) + if login_failed.value: + solara.Error("Incorrect password") + else: + solara.Info("Please enter password") + + +@solara.component +def Layout(children): + dark_effective = solara.lab.use_dark_effective() + return solara.AppLayout( + children=children, + toolbar_dark=dark_effective, + color="None", # if dark_effective else "primary", + ) diff --git a/dont_fret/web/methods.py b/dont_fret/web/methods.py new file mode 100644 index 0000000..201eb82 --- /dev/null +++ b/dont_fret/web/methods.py @@ -0,0 +1,182 @@ +from __future__ import annotations + +import itertools +import math +from concurrent.futures import ThreadPoolExecutor +from functools import reduce +from operator import and_ +from pathlib import Path +from typing import Literal, Optional, TypedDict, Union + +import polars as pl + +from dont_fret import BinnedPhotonData +from dont_fret.config.config import BurstColor +from dont_fret.formatting import TRACE_COLORS +from dont_fret.web.models import ( + BurstFilterItem, + BurstItem, + FRETNode, + PhotonData, + PhotonFileItem, + TraceSettings, +) + + +def batch_burst_search( + photon_file_items: list[PhotonFileItem], burst_colors: str, max_workers: int = 4 +) -> BurstItem: + """ + Search all photon file items in batch threaded. + """ + dtype = pl.Enum([item.name for item in photon_file_items]) + + futures = [] + with ThreadPoolExecutor(max_workers=max_workers) as executor: + for f in photon_file_items: + fut = executor.submit(burst_search, f, burst_colors, dtype) + futures.append(fut) + + df = pl.concat([f.result() for f in futures], how="vertical_relaxed") + metadata = {fi.name: fi.get_info() for fi in photon_file_items} + + return BurstItem(name=burst_colors, df=df, metadata=metadata) + + +def burst_search( + ph_file_item: PhotonFileItem, burst_colors: str | list[BurstColor], dtype: pl.Enum +) -> pl.DataFrame: + photons = ph_file_item.get_photons() + bursts = photons.burst_search(burst_colors) + + t_unit = photons.timestamps_unit + with_columns = [ + # (pl.col("timestamps_mean") * t_unit).alias("time_mean"), + # ((pl.col("timestamps_max") - pl.col("timestamps_min")) * t_unit).alias("time_length"), + pl.lit(ph_file_item.name).alias("filename").cast(dtype), + ] + + drop_columns = ["timestamps_mean", "timestamps_min", "timestamps_max"] + df = bursts.burst_data.with_columns(with_columns).drop(drop_columns) + + return df + + +def chain_filters(filters: list[BurstFilterItem]) -> Union[pl.Expr, Literal[True]]: + """Chain a list of `BurstFilterItem` objects into a single `pl.Expr` object.""" + f_exprs = list(itertools.chain(*(f.as_expr() for f in filters))) + if f_exprs: + return reduce(and_, f_exprs) + else: + return True + + +# todo move to `dev` +def create_file_items(pth: Path) -> list[PhotonFileItem]: + """Return a list of `PhotonFileItem` objects from a directory containing ptu files.""" + return [PhotonFileItem(file_path=ptu_pth) for ptu_pth in pth.glob("*.ptu")] + + +# move to `dev` ? +def gen_fileitems(n: Optional[int] = None, directory: str = "ds2") -> list[PhotonFileItem]: + """Returns a list of the first `n` FileItem objects generated from + the data in `TEST_FILE_DIR`""" + + # root = Path(__file__).parent.parent.parent.parent + root = Path(*Path(__file__).parts[:5]) + + test_dir = root / "tests" / "test_data" / "input" / directory + + file_items = create_file_items(test_dir) + if n is None: + return file_items + else: + return file_items[:n] + + +def get_duration(metadata: list[dict]) -> Optional[float]: + """try to find the acquisition duraction of the photon files, and return as float only if they + are all equal, otherwise returns `None` + """ + + durations = [m.get("acquisition_duration", None) for m in metadata] + if len(set(durations)) == 1: + return durations[0] + else: + return None + + +def format_size(size_in_bytes: int) -> str: + if size_in_bytes == 0: + return "0B" + size_names = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") + i = int(math.floor(math.log(size_in_bytes, 1000))) + p = math.pow(1000, i) + s = round(size_in_bytes / p, 2) + return f"{s} {size_names[i]}" + + +class BurstResult(TypedDict): + name: str + df: pl.DataFrame + metadata: dict + + +def generate_traces( + photons: PhotonData, trace_settings: TraceSettings +) -> dict[str, BinnedPhotonData]: + t_bin = trace_settings.t_bin * 1e-3 + bounds = (trace_settings.t_min, trace_settings.t_max) + + traces = {} + for stream in TRACE_COLORS: + stream_data = PhotonData( + photons.data.filter(pl.col("stream") == stream), + metadata=photons.metadata, + cfg=photons.cfg, + ) + + traces[stream] = BinnedPhotonData(stream_data, binning_time=t_bin, bounds=bounds) + + return traces + + +def to_treeview(nodes: list[FRETNode]) -> list[dict]: + items = [] + for fret_node in nodes: + item = { + "name": fret_node.name, + "id": fret_node.id, + "icon": "mdi-ruler", + "children": [ + { + "name": "Photons", + "id": f"{fret_node.id}:photons", + "icon": "mdi-lightbulb", + "children": [ + { + "name": photon_file.name, + "id": f"{fret_node.id}:photons:{photon_file.name}", + "icon": "mdi-file-star", + } + for photon_file in fret_node.photons + ], + }, + { + "name": "Bursts", + "id": f"{fret_node.id}:bursts", + "icon": "mdi-flash", + "children": [ + { + "name": burst_item.name, + "id": f"{fret_node.id}:bursts:{burst_item.name}", + "icon": "mdi-file-chart", + } + for burst_item in fret_node.bursts + ], + }, + ], + } + + items.append(item) + return items diff --git a/dont_fret/web/models.py b/dont_fret/web/models.py new file mode 100644 index 0000000..08ca15b --- /dev/null +++ b/dont_fret/web/models.py @@ -0,0 +1,281 @@ +from __future__ import annotations + +import dataclasses +import uuid +from collections import UserList +from dataclasses import dataclass, field, make_dataclass +from pathlib import Path +from typing import Optional, Tuple, TypedDict + +import numpy as np +import polars as pl + +from dont_fret.config.config import BurstColor, BurstFilterItem +from dont_fret.fileIO import PhotonFile +from dont_fret.models import PhotonData + +BURST_SCHEMA = { + "E_app": pl.Float64, + "S_app": pl.Float64, + "n_photons": pl.UInt16, + "time_length": pl.Float64, + "time_mean": pl.Float64, + "time_min": pl.Float64, + "time_max": pl.Float64, + "n_DD": pl.UInt16, + "n_DA": pl.UInt16, + "n_AA": pl.UInt16, + "n_AD": pl.UInt8, + "tau_DD": pl.Float64, + "tau_DA": pl.Float64, + "tau_AA": pl.Float64, + "tau_AD": pl.Float64, +} + + +# refactor: move to methods +def get_info(photons: PhotonData) -> dict: + info = {} + info["creation_time"] = photons.metadata["creation_time"] + info["number_of_photons"] = len(photons) + info["acquisition_duration"] = photons.metadata["acquisition_duration"] + info["power_diode"] = photons.metadata["tags"]["UsrPowerDiode"]["value"] + + info["cps"] = photons.cps + t_max = photons.photon_times.max() + counts = photons.data["stream"].value_counts(sort=True) + info["stream_cps"] = {k: v / t_max for k, v in counts.iter_rows()} + + if comment := photons.comment: + info["comment"] = comment + return info + + +@dataclasses.dataclass +class PhotonFileItem: + file_path: Path + info: Optional[dict] = None + photons: Optional[PhotonData] = None + id: uuid.UUID = dataclasses.field(default_factory=lambda: uuid.uuid4()) + + @property + def name(self) -> str: + return self.file_path.name + + @property + def size(self) -> int: + return self.file_path.stat().st_size + + def get_info(self) -> dict: + if self.info is None: + self.get_photons() + assert self.info is not None + return self.info + else: + return self.info + + def get_photons(self) -> PhotonData: + """read the file if it hasn't been read yet, otherwise checks for cached rsult""" + + if self.photons is not None: + return self.photons + # todo cachhe + # elif self.id in CHACHE: + # return CHACHE[self.id] + else: + self.photons = PhotonData.from_file(PhotonFile(self.file_path)) + self.info = get_info(self.photons) + return self.photons + + def to_cache(self): + pass + + def from_cache(self): + pass + + +# move to config ? +class SearchParams(TypedDict): + L: int + M: int + T: float + + +# todo maybe don't use this class anymore with the new config settings +class BurstColorList(UserList[BurstColor]): + @classmethod + def from_dict(cls, search_spec: dict[str, SearchParams]) -> BurstColorList: + """Takes a dict of burst settings from cfg.burst_search and returns + a dict of list of BurstColor instances.""" + + colors = [ + BurstColor(streams=[s.strip() for s in streams.split("+")], **search_params) + for streams, search_params in search_spec.items() + ] + + return cls(colors) + + def to_dict(self) -> dict[str, SearchParams]: + """Takes a list of `BurstColor` instnaes and returns a dict of burst + settings for burst search.""" + + spec = {} + for color in self: + d = dataclasses.asdict(color) + streams = " + ".join(d.pop("streams")) + spec[streams] = d + + return spec + + +@dataclasses.dataclass +class BurstItem: + name: str + + df: pl.DataFrame + + selected_files: list[str] = dataclasses.field(default_factory=list) + + search_spec: Optional[dict[str, SearchParams]] = None + """Burst search settings used to generate the bursts""" + + metadata: dict = field(default_factory=dict) + + id: uuid.UUID = dataclasses.field(default_factory=lambda: uuid.uuid4()) + + def __post_init__(self): + if not self.selected_files: + self.selected_files = list(self.df["filename"].unique()) + + @property + def duration(self) -> Optional[float]: + durations = [m.get("acquisition_duration", None) for m in self.metadata.values()] + if len(set(durations)) == 1: + return durations[0] + else: + return None + + @classmethod + def from_path(cls, path: Path) -> BurstItem: + # todo add support for hdf5 files + if path.suffix == ".csv": + df = pl.read_csv(path) + elif path.suffix == ".pq": + df = pl.read_parquet(path) + else: + raise ValueError(f"Unsupported file type: {path.suffix}") + + # convert the filename column to Enum dtype + df = df.with_columns(pl.col("filename").cast(pl.Enum(df["filename"].unique().sort()))) + + return cls(name=path.stem, df=df) + + +@dataclasses.dataclass +class BurstPlotSettings: + # = burst plot settings + # TODO these state classes should be in a separate file; one per page + x_name: str = "E_app" + y_name: str = "S_app" + + x_min: float = 0.0 + x_max: float = 1.0 + + y_min: float = 0.0 + y_max: float = 1.0 + + z_min: Optional[float] = None + z_max: Optional[float] = None + + nbinsx: int = 40 + nbinsy: int = 40 + + @property + def xbins(self) -> dict: + return dict(start=self.x_min, end=self.x_max, size=(self.x_max - self.x_min) / self.nbinsx) + + @property + def ybins(self) -> dict: + return dict(start=self.y_min, end=self.y_max, size=(self.y_max - self.y_min) / self.nbinsy) + + @property + def x_range(self) -> Tuple[float, float]: + return self.x_min, self.x_max + + @property + def y_range(self) -> Tuple[float, float]: + return self.y_min, self.y_max + + +@dataclasses.dataclass(frozen=True) +class BinnedImage: + img_data: np.ndarray + x: np.ndarray + y: np.ndarray + + @classmethod + def from_settings(cls, df: pl.DataFrame, settings: BurstPlotSettings) -> BinnedImage: + x, y = df[settings.x_name], df[settings.y_name] + + x_edges = np.linspace( + settings.x_min, settings.x_max, num=settings.nbinsx + 1, endpoint=True + ) + y_edges = np.linspace( + settings.y_min, settings.y_max, num=settings.nbinsy + 1, endpoint=True + ) + + img_data, *_ = np.histogram2d(x, y, bins=(x_edges, y_edges)) # type: ignore + + img = BinnedImage( + img_data=img_data, + x=(x_edges[:-1] + x_edges[1:]) / 2, + y=(y_edges[:-1] + y_edges[1:]) / 2, + ) + + return img + + +FrozenBurstFilterItem = make_dataclass( + "FrozenBurstFilterItem", BurstFilterItem.__dataclass_fields__, frozen=True +) + + +@dataclasses.dataclass +class TraceSettings: + t_min: float = 0.0 + """Lower bound of the time window to plot in seconds.""" + + t_max: float = 10.0 + """Upper bound of the time window to plot in seconds.""" + + t_bin: float = 1.0 + """Binning time in milliseconds.""" + + @property + def num_dpts(self) -> int: + """Number of datapoints in the currently plotted traces.""" + return int((self.t_max - self.t_min) / (self.t_bin / 1000)) + + +@dataclasses.dataclass +class TCSPCSettings: + log_y: bool = True + + +@dataclasses.dataclass +class FRETNode: + name: str # displayed name + id: str = dataclasses.field(default_factory=lambda: uuid.uuid4().hex) # unique id + description: str = "" # description of the node + photons: list[PhotonFileItem] = dataclasses.field(default_factory=list) + bursts: list[BurstItem] = dataclasses.field(default_factory=list) + + +@dataclass +class SnackbarMessage: + message: str = "" + color: str = "primary" + timeout: int = 3000 + btn_color: str = "text-primary-color" + + show: bool = False diff --git a/dont_fret/web/reactive.py b/dont_fret/web/reactive.py new file mode 100644 index 0000000..aea50ee --- /dev/null +++ b/dont_fret/web/reactive.py @@ -0,0 +1,243 @@ +import dataclasses +import uuid +from typing import Dict, List, Literal, Optional, TypeVar, Union, overload + +import solara +from solara import Reactive +from solara.lab import Ref + +from dont_fret import cfg +from dont_fret.config.config import BurstColor +from dont_fret.web.models import ( + BurstColorList, + BurstItem, + FRETNode, + PhotonFileItem, + SnackbarMessage, +) + +S = TypeVar("S") + + +class ReactiveFRETNodes(solara.Reactive[list[FRETNode]]): + def add_node(self, name: Optional[str] = None) -> None: + name = name or self.default_name() + node = FRETNode(name=name, id=uuid.uuid4().hex) # todo uuid as UUID type + self.append(node) + + def append(self, item: FRETNode) -> None: + new_value = self.value.copy() + new_value.append(item) + self.value = new_value + + def extend(self, items: list[FRETNode]) -> None: + new_value = self.value.copy() + new_value.extend(items) + self.value = new_value + + def default_name(self) -> str: + num_dic = { + 1: "TWO", + 2: "THREE", + 3: "FOUR", + 4: "FIVE", + 5: "SIX", + 6: "SEVEN", + 7: "EIGHT", + 8: "NINE", + 9: "TEN", + } + + if len(self.value) == 0: + return "FRET NOT" + else: + return f"FRET {num_dic.get(len(self.value), len(self.value) + 1)}" + + def remove_node(self, node_id: str) -> None: + self.value = [node for node in self.value if node.id != node_id] + + def node_idx(self, node_id: str) -> int: + return [node.id for node in self.value].index(node_id) + + def get_node(self, node_id: str) -> FRETNode: + for node in self.value: + if node.id == node_id: + return node + + raise ValueError(f"Node with id {node_id} not found.") + + def remove_all_photon_files(self, node_id: str) -> None: + idx = self.node_idx(node_id) + ref = Ref(self.fields[idx].photons) + ref.value = [] + + def add_photon_files(self, node_id: str, photon_files: list[PhotonFileItem]) -> None: + idx = self.node_idx(node_id) + ref = Ref(self.fields[idx].photons) + ref.value = ref.value + photon_files + + def add_burst_items(self, node_id: str, burst_items: list[BurstItem]) -> None: + """ + adds a burst item. If the item already exists, it will be replaced + """ + + idx = self.node_idx(node_id) + ref = Ref(self.fields[idx].bursts) + new_names = [item.name for item in burst_items] + previous_items = [item for item in ref.value if item.name not in new_names] + ref.value = previous_items + burst_items + + @overload + def get_item( + self, node_id: str, item_type: Literal["photons"], item_name: str + ) -> PhotonFileItem: + ... + + @overload + def get_item(self, node_id: str, item_type: Literal["bursts"], item_name: str) -> BurstItem: + ... + + # overload for type checking + def get_item( + self, node_id: str, item_type: Literal["photons", "bursts"], item_name: str + ) -> Union[PhotonFileItem, BurstItem]: + idx = self.node_idx(node_id) + ref = Ref(getattr(self.fields[idx], item_type)) + for item in ref.value: + if item.name == item_name: + return item + + raise ValueError(f"Item with name {item_name} not found.") + + def remove_item( + self, node_id: str, item_type: Literal["photons", "bursts"], item_name: str + ) -> None: + idx = self.node_idx(node_id) + ref = Ref(getattr(self.fields[idx], item_type)) + ref.value = [item for item in ref.value if item.name != item_name] + + +class ReactiveList(solara.Reactive[list[S]]): + def add_item(self, item: S) -> None: + self.value = self.value + [item] + + def remove_item(self, idx: int) -> None: + new_items = self.value.copy() + del new_items[idx] + self.value = new_items + + # self.value = [item for i, item in enumerate(self.value) if i != idx] + + +class PhotonFileReactiveList(ReactiveList[PhotonFileItem]): + def __init__(self, *args, **kwargs): + raise DeprecationWarning("deprecated in favour of ReactiveFRETNodes") + + def add_file_items(self, file_items: list[PhotonFileItem]) -> None: + new_items = self.value + [f for f in file_items if f.file_info["name"] not in self.names] + if new_items: + self.value = new_items + + def remove_file_item(self, idx: int) -> None: + new_items = self.value.copy() + del new_items[idx] + self.value = new_items + + def remove_all(self) -> None: + self.value = [] + + def select_index(self, idx: list[int]) -> None: + self.value = [dataclasses.replace(f, selected=i in idx) for i, f in enumerate(self.value)] + + def select_none(self) -> None: + self.value = [dataclasses.replace(f, selected=False) for f in self.value] + + def select_all(self) -> None: + self.value = [dataclasses.replace(f, selected=True) for f in self.value] + + @property + def names(self) -> list[str]: + """Returns names of all files""" + return [item.file_info["name"] for item in self.value] + + @property + def selected(self) -> list[int]: + """Returns indices of selected files""" + return [i for i, f in enumerate(self.value) if f.selected] + + +class BurstItemsReactiveList(ReactiveList[BurstItem]): + @property + def names(self) -> list[str]: + """Returns names of all files""" + return [item.name for item in self.value] + + +class BurstSettingsReactive(Reactive[Dict[str, List[BurstColor]]]): + def reset(self) -> None: + self.value = {k: BurstColorList(v) for k, v in cfg.burst_search.items()} + + def add_settings(self, setting_name: str): + """Adds a new burst settings name with default settings.""" + new_value = self.value.copy() + new_value[setting_name] = [BurstColor()] + self.value = new_value + + def remove_settings(self, setting_name: str): + """Removes a burst settings name.""" + new_value = self.value.copy() + new_value.pop(setting_name) + self.value = new_value + + def remove_color(self, setting_name: str): + """Removes the last color from the list of colors for a given burst settings name.""" + colors_ref = Ref(self.fields[setting_name]) + new_colors = colors_ref.get().copy() + if len(new_colors) == 1: + return + new_colors.pop() + colors_ref.set(new_colors) # isnt this a copy? + + def update_color(self, settings_name: str, color_idx: int, **kwargs): + # calling update / setter is fine because there are no listeners attached + Ref(self.fields[settings_name][color_idx]).update(**kwargs) + + def get_color(self, settings_name: str, color_idx: int) -> BurstColor: + # this is not allowed because it creates a listener with a too high idx + # return Ref(self.fields[setting_name][color_idx]).get() + + # do this instead + return self.value[settings_name][color_idx] + + def add_color(self, settings_name: str): + colors_ref = Ref(self.fields[settings_name]) + colors_ref.set(colors_ref.get().copy() + [BurstColor()]) + + @property + def settings_names(self) -> List[str]: + return list(self.value.keys()) + + +class SnackbarReactive(solara.Reactive[SnackbarMessage]): + def __init__(self, value: SnackbarMessage = SnackbarMessage(), default_timeout: int = 5000): + self.default_timeout = default_timeout + super().__init__(value) + + def set_message(self, msg: str, timeout: Optional[int] = None, **kwargs): + timeout = timeout if timeout is not None else self.default_timeout + self.update(message=msg, timeout=timeout, show=True, **kwargs) + + def info(self, msg: str, timeout: Optional[int] = None): + self.set_message(msg, color="primary", btn_color="text-primary-color", timeout=timeout) + + def secondary(self, msg: str, timeout: Optional[int] = None): + self.set_message(msg, color="secondary", btn_color="text-secondary-color", timeout=timeout) + + def warning(self, msg: str, timeout: Optional[int] = None): + self.set_message(msg, color="warning", btn_color="text-warning-color", timeout=timeout) + + def error(self, msg: str, timeout: Optional[int] = None): + self.set_message(msg, color="error", btn_color="text-error-color", timeout=timeout) + + def success(self, msg: str, timeout: Optional[int] = None): + self.set_message(msg, color="success", btn_color="text-success-color", timeout=timeout) diff --git a/dont_fret/web/state.py b/dont_fret/web/state.py new file mode 100644 index 0000000..b917c39 --- /dev/null +++ b/dont_fret/web/state.py @@ -0,0 +1,23 @@ +import copy +from pathlib import Path + +import solara + +from dont_fret.config import cfg +from dont_fret.web.models import BurstColorList +from dont_fret.web.reactive import BurstSettingsReactive, ReactiveFRETNodes, SnackbarReactive + +APP_TITLE = "Don't FRET!" +fret_nodes = ReactiveFRETNodes([]) + +filebrowser_folder = solara.Reactive[Path](cfg.web.default_dir) +burst_settings = BurstSettingsReactive({k: BurstColorList(v) for k, v in cfg.burst_search.items()}) + +filters = solara.Reactive(copy.deepcopy(cfg.web.burst_filters)) +snackbar = SnackbarReactive() + +# selections for burst figures (node, burst) +burst_figure_selection = [ + (solara.Reactive(0), solara.Reactive(0)), + (solara.Reactive(0), solara.Reactive(0)), +] diff --git a/dont_fret/web/trace/__init__.py b/dont_fret/web/trace/__init__.py new file mode 100644 index 0000000..2302c47 --- /dev/null +++ b/dont_fret/web/trace/__init__.py @@ -0,0 +1,3 @@ +from dont_fret.web.trace.page import TracePage + +__all__ = ["TracePage"] diff --git a/dont_fret/web/trace/methods.py b/dont_fret/web/trace/methods.py new file mode 100644 index 0000000..97a5cd9 --- /dev/null +++ b/dont_fret/web/trace/methods.py @@ -0,0 +1,40 @@ +import plotly.graph_objects as go +import polars as pl +import solara +import solara.lab + +from dont_fret import PhotonData +from dont_fret.formatting import TRACE_COLORS +from dont_fret.web.models import TCSPCSettings + +# TODO move fret node / photon file reactives to module level +TCSPC_SETTINGS = solara.Reactive(TCSPCSettings()) +MAX_DATAPOINTS = 100_000 + + +def create_tcspc_histogram( + photons: PhotonData, settings: TCSPCSettings, dark: bool = False +) -> go.Figure: + if photons.nanotimes_unit is None: + raise ValueError("Provided photon data does not have TCSPC information.") + fig = go.Figure() + for stream in TRACE_COLORS: + x, y = ( + photons.data.filter(pl.col("stream") == stream)["nanotimes"] + .value_counts() + .sort(pl.col("nanotimes")) + ) + + line = dict(color=TRACE_COLORS[stream]) + fig.add_trace(go.Scatter(x=x, y=y, mode="lines", line=line, name=stream)) + + fig.update_layout( + xaxis_title="Time (ns)", + yaxis_title="Photons per bin", + template="plotly_dark" if dark else "plotly_white", + ) + + if settings.log_y: + fig.update_yaxes(type="log") + + return fig diff --git a/dont_fret/web/trace/page.py b/dont_fret/web/trace/page.py new file mode 100644 index 0000000..bab5f1e --- /dev/null +++ b/dont_fret/web/trace/page.py @@ -0,0 +1,210 @@ +from io import BytesIO +from typing import Optional, cast + +import pandas as pd +import plotly.graph_objects as go +import solara +import solara.lab +from solara.alias import rv + +import dont_fret.web.state as state +from dont_fret import BinnedPhotonData, PhotonData +from dont_fret.formatting import TRACE_COLORS, TRACE_SIGNS +from dont_fret.web.methods import generate_traces +from dont_fret.web.models import TCSPCSettings, TraceSettings +from dont_fret.web.trace.methods import create_tcspc_histogram + +# TODO move fret node / photon file reactives to module level +TCSPC_SETTINGS = solara.Reactive(TCSPCSettings()) +MAX_DATAPOINTS = 100_000 + + +@solara.component +def TCSPCFigure(photons: PhotonData, title: str): + settings = TCSPC_SETTINGS.value # not really need in the global state + figure, set_figure = solara.use_state(cast(Optional[go.Figure], None)) + dark_effective = solara.lab.use_dark_effective() + + def redraw(): + fig = create_tcspc_histogram(photons, settings, dark=dark_effective) + fig.update_layout(title=title) + set_figure(fig) + + fig_result = solara.use_thread(redraw, dependencies=[photons, settings, dark_effective]) + # TODO style="opacity: 0.3" if fig_result.state == solara.ResultState.RUNNING else None + with solara.Card(): + solara.ProgressLinear(fig_result.state == solara.ResultState.RUNNING) + if figure: + solara.FigurePlotly(figure) + else: + solara.SpinnerSolara(size="100px") + + +def to_csv(traces: dict[str, BinnedPhotonData], name: str) -> BytesIO: + """Return a CSV string for the traces.""" + # TODO this name does not do anything + df = pd.DataFrame( + index=next(iter(traces.values())).time, + data={tr_name: trace.photons for tr_name, trace in traces.items()}, + ) + + bio = BytesIO() + df.to_csv(bio) + bio.name = name + bio.seek(0) + return bio + + +@solara.component +def TracePage(): + solara.Title(f"{state.APP_TITLE} / Trace") + + # photons, set_photons = solara.use_state(cast(Optional[ChannelPhotonData], None)) + TRACE_SETTINGS: solara.Reactive[TraceSettings] = solara.use_reactive(TraceSettings()) + + # only fret nodes with photon file items added are shown + nodes_with_photons = [node for node in state.fret_nodes.value if node.photons] + fret_node, set_fret_node = solara.use_state(nodes_with_photons[0]) + + photon_file, set_photon_file = solara.use_state(fret_node.photons[0]) + dark_effective = solara.lab.use_dark_effective() + + def on_fret_node(node_id: str): + new_node = state.fret_nodes.get_node(node_id) + set_fret_node(new_node) + set_photon_file(new_node.photons[0]) + + def on_photon_file(photon_file_name: str): + photon_file_item = state.fret_nodes.get_item(fret_node.id, "photons", photon_file_name) + set_photon_file(photon_file_item) + + def load_photons() -> PhotonData: + photons = photon_file.get_photons() + return photons + + ph_result = solara.lab.use_task(load_photons, dependencies=[photon_file], prefer_threaded=True) + + def traces_memo(): + if ph_result.value is None: + return {} + if TRACE_SETTINGS.value.num_dpts > MAX_DATAPOINTS: + return {} + + return generate_traces(ph_result.value, TRACE_SETTINGS.value) + + traces: dict[str, BinnedPhotonData] = solara.use_memo( + traces_memo, dependencies=[ph_result.value, TRACE_SETTINGS.value] + ) + with solara.Sidebar(): + with solara.Card("Controls"): + solara.Select( + label="Measurement", + value=fret_node.id, + on_value=on_fret_node, # type: ignore + values=[ + {"text": fret_node.name, "value": fret_node.id} + for fret_node in state.fret_nodes.value + if fret_node.photons # type: ignore + ], + ) + + solara.Select( + label="Photon file", + value=photon_file.name, + on_value=on_photon_file, + values=[ph.name for ph in fret_node.photons], + ) + + solara.ProgressLinear(ph_result.pending) + if ph_result.finished is not None: + solara.Text("Trace settings") + rv.Divider() # noqa + solara.InputFloat( + label="Time start", + # hint="Start time point of the trace in seconds.", + value=TRACE_SETTINGS.value.t_min, + on_value=lambda x: TRACE_SETTINGS.update(t_min=x), + ) + solara.InputFloat( + label="Time end", + # hint="Ending time point of the trace in seconds.", + value=TRACE_SETTINGS.value.t_max, + on_value=lambda x: TRACE_SETTINGS.update(t_max=x), + ) + solara.InputFloat( + label="Binning time", + # hint="Binning time in milliseconds.", + value=TRACE_SETTINGS.value.t_bin, + on_value=lambda x: TRACE_SETTINGS.update(t_bin=x), + ) + + solara.Div(style="height: 25px") + + solara.Text("TCSPC histogram settings") + rv.Divider() + + solara.Checkbox( + label="logy", + value=TCSPC_SETTINGS.value.log_y, + on_value=lambda x: TCSPC_SETTINGS.update(log_y=x), + ) + rv.Divider() + + solara.Div(style="height: 25px") + + if TRACE_SETTINGS.value.num_dpts > MAX_DATAPOINTS: + solara.Warning( + "Too many datapoints to plot. Please increase the binning time or reduce the interval." + ) + + if ph_result.finished and ph_result.value is not None: + solara.Info(f"Loaded {len(ph_result.value)} photons.") + solara.Info(f"Duration: {ph_result.value.metadata['acquisition_duration']} s") + + if traces: + + def download_cb(): + return to_csv(traces, "filename.txt") + + solara.FileDownload( + download_cb, + filename=f"{photon_file.name}_traces.csv", + children=[ + solara.Button( + "Download binned data", + block=True, + ) + ], + ) + + with solara.Column(): + # move to component + if traces: # this doesnt work like this? use use_state? + fig = go.Figure() + + for tr in TRACE_COLORS: + line = dict(color=TRACE_COLORS[tr]) + fig.add_trace( + go.Scatter( + x=traces[tr].time, + y=TRACE_SIGNS[tr] * traces[tr].photons, + mode="lines", + line=line, + name=tr, + ) + ) + + fig.update_layout( + title=f"Time trace: {fret_node.name}/{photon_file.name}", + xaxis_title="Time (s)", + yaxis_title="Photons per bin", + template="plotly_dark" if dark_effective else "plotly_white", + ) + with solara.Card(): + solara.FigurePlotly(fig, dependencies=[traces, dark_effective]) + + if ph_result.finished: + assert ph_result.value is not None + TCSPCFigure( + ph_result.value, title=f"TCSPC histogram: {fret_node.name}/{photon_file.name}" + ) diff --git a/dont_fret/web/utils.py b/dont_fret/web/utils.py new file mode 100644 index 0000000..754a13e --- /dev/null +++ b/dont_fret/web/utils.py @@ -0,0 +1,17 @@ +from typing import Optional, TypeVar +import time + +T = TypeVar("T") + + +def not_none(*args: Optional[T]) -> T: + """Returns the first `not None` argument""" + for a in args: + if a is not None: + return a + raise ValueError("All arguments are `None`") + + +def some_task(param=3) -> int: + time.sleep(param / 2) + return param * 2 diff --git a/dont_fret/web/widgets.py b/dont_fret/web/widgets.py new file mode 100644 index 0000000..8257f1d --- /dev/null +++ b/dont_fret/web/widgets.py @@ -0,0 +1,141 @@ +import base64 + +import ipyvuetify as v + + +class FloatField(v.TextField): + def __init__(self, **kwargs): + attributes = kwargs.pop("attributes", {"step": "any"}) + super().__init__(type="number", attributes=attributes, **kwargs) + self.on_event("input", self.on_input) + + @property + def value(self) -> float: + return float(self.v_model) + + def on_input(self, widget, event, data): + vmin, vmax = self.attributes.get("min"), self.attributes.get("max") + self.error_messages = [] + try: + value = self.value + if vmin is not None and self.value < vmin: + self.error_messages = [f"Input value must be larger than {vmin}"] + elif vmax is not None and self.value > vmax: + self.error_messages = [f"Input value must be smaller than {vmax}"] + except ValueError: + self.error_messages = ["Input value must be a floating point number"] + + +class IntField(v.TextField): + def __init__(self, **kwargs): + attributes = kwargs.pop("attributes", {"step": 1}) + + super().__init__(type="number", attributes=attributes, **kwargs) + self.on_event("input", self.on_input) + + @property + def value(self) -> int: + return int(self.v_model) + + def on_input(self, widget, event, data): + vmin, vmax = self.attributes.get("min"), self.attributes.get("max") + self.error_messages = [] + try: + value = self.value + if vmin is not None and self.value < vmin: + self.error_messages = [f"Input value must be larger than {vmin}"] + elif vmax is not None and self.value > vmax: + self.error_messages = [f"Input value must be smaller than {vmax}"] + except ValueError: + self.error_messages = ["Input value must be an integer number"] + + +class ProgressLinearTasks(v.ProgressLinear): + def __init__(self, num_tasks: int = 10, height=25, **kwargs): + self.completed = 0 + self.num_tasks = num_tasks + self.val_txt = v.Html(children=[self.bar_text], tag="strong") + super().__init__( + value=0.0, + height=height, + v_slots=[{"name": "default", "children": self.val_txt}], + **kwargs, + ) + + self.observe(self.update_text, names="value") + + def update_text(self, *event): + pass + self.val_txt.children = [f"{self.completed}/{self.num_tasks}"] + + @property + def bar_text(self) -> str: + if self.num_tasks: + return f"{self.completed}/{self.num_tasks}" + else: + return "" + + def reset(self): + self.completed = 0 + self.num_tasks = 0 + self.value = 0 + + def increment(self): + if self.completed < self.num_tasks: + self.completed += 1 + self.value = (self.completed / self.num_tasks) * 100 + + +class FileDownloadBtn(v.Btn): + """A button which can be given data to download on click. + Usage: + btn = FileDownloadBtn(children=['Download BUTTON'], attributes={'download': 'test.txt'}) + + """ + + # from: Holoviz panel's FileDownload. + _mime_types = { + "application": {"pdf": "pdf", "zip": "zip"}, + "audio": {"mp3": "mp3", "ogg": "ogg", "wav": "wav", "webm": "webm"}, + "image": { + "apng": "apng", + "bmp": "bmp", + "gif": "gif", + "ico": "x-icon", + "cur": "x-icon", + "jpg": "jpeg", + "jpeg": "jpeg", + "png": "png", + "svg": "svg+xml", + "tif": "tiff", + "tiff": "tiff", + "webp": "webp", + }, + "text": { + "css": "css", + "csv": "plain;charset=UTF-8", + "js": "javascript", + "html": "html", + "txt": "plain;charset=UTF-8", + }, + "video": {"mp4": "mp4", "ogg": "ogg", "webm": "webm"}, + } + + def set_payload(self, byte_data, filename: str): + b64 = base64.b64encode(byte_data).decode("utf-8") + + ext = filename.split(".")[1] + for mtype, subtypes in self._mime_types.items(): + stype = None + if ext in subtypes: + stype = subtypes[ext] + break + if stype is None: + mime = "application/octet-stream" + else: + mime = "{type}/{subtype}".format(type=mtype, subtype=stype) + + href = "data:{mime};base64,{b64}".format(mime=mime, b64=b64) + + self.attributes["download"] = filename + self.href = href diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4ab0146 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,84 @@ +[build-system] +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[project] +name = "dont-fret" +description = "Analyze confocal solution smFRET data" +authors = [ + { name = "Jochem Smit", email = "jhsmit@gmail.com" }, +] +license = "MIT" +requires-python = ">=3.9" +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Intended Audience :: Science/Research", + +] +dependencies = [ + "numpy>=1.18.0", + "tqdm>=4.64.1", + "typing-extensions>=4.4.0", + "PyYAML>=6.0", + "pandas>=1.5.3", + "dacite", + "numba>=0.56.4", + "polars>=0.20.23", + "phconvert", + "click", + "solara>=1.19.0", + "plotly", +] +dynamic = ["version"] + +[project.scripts] +dont-fret = "dont_fret.__main__:cli" + +[project.optional-dependencies] +plot = [ + "matplotlib", + "kdepy", + +] +docs = [ + "mkdocs>=1.4.2", + "mkdocstrings[python]>=0.19.1", + "mkdocs-material>=8.5.11", + "pygments>=2.13.0", + "mkdocs-gen-files>=0.4.0", + "mkdocs-literate-nav>=0.5.0", +] +test = [ + "pytest>=7.2.0", + "pytest-playwright>=0.3.3", + "pytest-asyncio", +] + + +[project.urls] +Source = "https://github.com/Jhsmit/dont-fret/" + +[tool.hatch.build] +exclude = [ + "_versioneer.py" +] + +[tool.hatch.version] +source = "vcs" + +[tool.hatch.build.hooks.vcs] +version-file = "dont_fret/_version.py" + +[tool.flake8] +max-line-length = 100 +ignore = "D203" +exclude = [".git", "__pycache__", "build", "dist", "docs"] +max-complexity = 10 + +[tool.black] +line-length = 100 + +[tool.ruff] +line-length = 100 diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..25aee8a --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,9 @@ +{ + "exclude": [ + "**/node_modules", + "**/__pycache__", + ".venv*", + + ], + + } \ No newline at end of file diff --git a/requirements/requirements-macOS-latest-3.10.txt b/requirements/requirements-macOS-latest-3.10.txt new file mode 100644 index 0000000..5102108 --- /dev/null +++ b/requirements/requirements-macOS-latest-3.10.txt @@ -0,0 +1,328 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --extra=web --output-file=requirements-macOS-latest-3.10.txt pyproject.toml +# +anyio==4.3.0 + # via + # starlette + # watchfiles +appnope==0.1.4 + # via ipykernel +asttokens==2.4.1 + # via stack-data +attrs==23.2.0 + # via + # jsonschema + # referencing +cachetools==5.3.3 + # via solara-ui +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # dask + # distributed + # rich-click + # solara-server + # uvicorn +cloudpickle==3.0.0 + # via + # dask + # distributed +comm==0.2.2 + # via + # ipykernel + # ipywidgets +contourpy==1.2.1 + # via matplotlib +cycler==0.12.1 + # via matplotlib +dacite==1.8.1 + # via dont-fret (pyproject.toml) +dask==2024.5.0 + # via distributed +debugpy==1.8.1 + # via ipykernel +decorator==5.1.1 + # via ipython +distributed==2024.5.0 + # via dont-fret (pyproject.toml) +exceptiongroup==1.2.1 + # via + # anyio + # ipython +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.1 + # via nbformat +filelock==3.14.0 + # via solara-server +fonttools==4.51.0 + # via matplotlib +fsspec==2024.3.1 + # via dask +h11==0.14.0 + # via uvicorn +h5py==3.11.0 + # via dont-fret (pyproject.toml) +humanize==4.9.0 + # via solara-ui +idna==3.7 + # via + # anyio + # requests +importlib-metadata==7.1.0 + # via dask +ipykernel==6.29.4 + # via solara-server +ipympl==0.9.4 + # via dont-fret (pyproject.toml) +ipython==8.24.0 + # via + # ipykernel + # ipympl + # ipywidgets +ipython-genutils==0.2.0 + # via ipympl +ipyvue==1.11.1 + # via + # ipyvuetify + # solara-ui +ipyvuetify==1.9.4 + # via solara-ui +ipywidgets==8.1.2 + # via + # ipympl + # ipyvue + # reacton + # solara-ui +jedi==0.19.1 + # via ipython +jinja2==3.1.4 + # via + # distributed + # solara-server +jsonschema==4.22.0 + # via nbformat +jsonschema-specifications==2023.12.1 + # via jsonschema +jupyter-client==8.6.1 + # via + # ipykernel + # solara-server +jupyter-core==5.7.2 + # via + # ipykernel + # jupyter-client + # nbformat +jupyterlab-widgets==3.0.10 + # via ipywidgets +kdepy==1.1.9 + # via dont-fret (pyproject.toml) +kiwisolver==1.4.5 + # via matplotlib +llvmlite==0.42.0 + # via numba +locket==1.0.0 + # via + # distributed + # partd +markdown==3.6 + # via + # pymdown-extensions + # solara-ui +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.5 + # via jinja2 +matplotlib==3.8.4 + # via ipympl +matplotlib-inline==0.1.7 + # via + # ipykernel + # ipython +mdurl==0.1.2 + # via markdown-it-py +msgpack==1.0.8 + # via distributed +nbformat==5.10.4 + # via solara-server +nest-asyncio==1.6.0 + # via ipykernel +numba==0.59.1 + # via dont-fret (pyproject.toml) +numpy==1.26.4 + # via + # contourpy + # dont-fret (pyproject.toml) + # h5py + # ipympl + # kdepy + # matplotlib + # numba + # pandas + # scipy + # solara-ui +packaging==24.0 + # via + # dask + # distributed + # ipykernel + # matplotlib + # plotly +pandas==2.2.2 + # via dont-fret (pyproject.toml) +parso==0.8.4 + # via jedi +partd==1.4.2 + # via dask +pexpect==4.9.0 + # via ipython +pillow==10.3.0 + # via + # ipympl + # matplotlib + # solara-ui +platformdirs==4.2.1 + # via jupyter-core +plotly==5.22.0 + # via dont-fret (pyproject.toml) +polars==0.20.25 + # via dont-fret (pyproject.toml) +prompt-toolkit==3.0.43 + # via ipython +psutil==5.9.8 + # via + # distributed + # ipykernel +ptyprocess==0.7.0 + # via pexpect +pure-eval==0.2.2 + # via stack-data +pygments==2.18.0 + # via + # ipython + # rich + # solara-ui +pymdown-extensions==10.8.1 + # via solara-ui +pyparsing==3.1.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # jupyter-client + # matplotlib + # pandas +pytz==2024.1 + # via pandas +pyyaml==6.0.1 + # via + # dask + # distributed + # dont-fret (pyproject.toml) + # pymdown-extensions +pyzmq==26.0.3 + # via + # ipykernel + # jupyter-client +reacton==1.8.3 + # via solara-ui +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via solara-ui +rich==13.7.1 + # via rich-click +rich-click==1.8.1 + # via solara-server +rpds-py==0.18.1 + # via + # jsonschema + # referencing +scipy==1.13.0 + # via + # dont-fret (pyproject.toml) + # kdepy +six==1.16.0 + # via + # asttokens + # python-dateutil +sniffio==1.3.1 + # via anyio +solara==1.32.1 + # via dont-fret (pyproject.toml) +solara-server[dev,starlette]==1.32.1 + # via solara +solara-ui[all,cache,extra,markdown]==1.32.1 + # via + # solara + # solara-server +sortedcontainers==2.4.0 + # via distributed +stack-data==0.6.3 + # via ipython +starlette==0.37.2 + # via solara-server +tblib==3.0.0 + # via distributed +tenacity==8.3.0 + # via plotly +toolz==0.12.1 + # via + # dask + # distributed + # partd +tornado==6.4 + # via + # distributed + # ipykernel + # jupyter-client +tqdm==4.66.4 + # via dont-fret (pyproject.toml) +traitlets==5.14.3 + # via + # comm + # ipykernel + # ipympl + # ipython + # ipywidgets + # jupyter-client + # jupyter-core + # matplotlib-inline + # nbformat +typing-extensions==4.11.0 + # via + # anyio + # dont-fret (pyproject.toml) + # ipython + # reacton + # rich-click + # uvicorn +tzdata==2024.1 + # via pandas +urllib3==2.2.1 + # via + # distributed + # requests +uvicorn==0.29.0 + # via solara-server +watchdog==4.0.0 + # via solara-server +watchfiles==0.21.0 + # via solara-server +wcwidth==0.2.13 + # via prompt-toolkit +websockets==12.0 + # via solara-server +widgetsnbextension==4.0.10 + # via ipywidgets +zict==3.0.0 + # via distributed +zipp==3.18.1 + # via importlib-metadata diff --git a/requirements/requirements-macOS-latest-3.9.txt b/requirements/requirements-macOS-latest-3.9.txt new file mode 100644 index 0000000..0372f16 --- /dev/null +++ b/requirements/requirements-macOS-latest-3.9.txt @@ -0,0 +1,336 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --extra=web --output-file=requirements-macOS-latest-3.9.txt pyproject.toml +# +anyio==4.3.0 + # via + # starlette + # watchfiles +appnope==0.1.4 + # via ipykernel +asttokens==2.4.1 + # via stack-data +attrs==23.2.0 + # via + # jsonschema + # referencing +cachetools==5.3.3 + # via solara-ui +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # dask + # distributed + # rich-click + # solara-server + # uvicorn +cloudpickle==3.0.0 + # via + # dask + # distributed +comm==0.2.2 + # via + # ipykernel + # ipywidgets +contourpy==1.2.1 + # via matplotlib +cycler==0.12.1 + # via matplotlib +dacite==1.8.1 + # via dont-fret (pyproject.toml) +dask==2024.5.0 + # via distributed +debugpy==1.8.1 + # via ipykernel +decorator==5.1.1 + # via ipython +distributed==2024.5.0 + # via dont-fret (pyproject.toml) +exceptiongroup==1.2.1 + # via + # anyio + # ipython +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.1 + # via nbformat +filelock==3.14.0 + # via solara-server +fonttools==4.51.0 + # via matplotlib +fsspec==2024.3.1 + # via dask +h11==0.14.0 + # via uvicorn +h5py==3.11.0 + # via dont-fret (pyproject.toml) +humanize==4.9.0 + # via solara-ui +idna==3.7 + # via + # anyio + # requests +importlib-metadata==7.1.0 + # via + # dask + # jupyter-client + # markdown +importlib-resources==6.4.0 + # via matplotlib +ipykernel==6.29.4 + # via solara-server +ipympl==0.9.4 + # via dont-fret (pyproject.toml) +ipython==8.18.1 + # via + # ipykernel + # ipympl + # ipywidgets +ipython-genutils==0.2.0 + # via ipympl +ipyvue==1.11.1 + # via + # ipyvuetify + # solara-ui +ipyvuetify==1.9.4 + # via solara-ui +ipywidgets==8.1.2 + # via + # ipympl + # ipyvue + # reacton + # solara-ui +jedi==0.19.1 + # via ipython +jinja2==3.1.4 + # via + # distributed + # solara-server +jsonschema==4.22.0 + # via nbformat +jsonschema-specifications==2023.12.1 + # via jsonschema +jupyter-client==8.6.1 + # via + # ipykernel + # solara-server +jupyter-core==5.7.2 + # via + # ipykernel + # jupyter-client + # nbformat +jupyterlab-widgets==3.0.10 + # via ipywidgets +kdepy==1.1.9 + # via dont-fret (pyproject.toml) +kiwisolver==1.4.5 + # via matplotlib +llvmlite==0.42.0 + # via numba +locket==1.0.0 + # via + # distributed + # partd +markdown==3.6 + # via + # pymdown-extensions + # solara-ui +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.5 + # via jinja2 +matplotlib==3.8.4 + # via ipympl +matplotlib-inline==0.1.7 + # via + # ipykernel + # ipython +mdurl==0.1.2 + # via markdown-it-py +msgpack==1.0.8 + # via distributed +nbformat==5.10.4 + # via solara-server +nest-asyncio==1.6.0 + # via ipykernel +numba==0.59.1 + # via dont-fret (pyproject.toml) +numpy==1.26.4 + # via + # contourpy + # dont-fret (pyproject.toml) + # h5py + # ipympl + # kdepy + # matplotlib + # numba + # pandas + # scipy + # solara-ui +packaging==24.0 + # via + # dask + # distributed + # ipykernel + # matplotlib + # plotly +pandas==2.2.2 + # via dont-fret (pyproject.toml) +parso==0.8.4 + # via jedi +partd==1.4.2 + # via dask +pexpect==4.9.0 + # via ipython +pillow==10.3.0 + # via + # ipympl + # matplotlib + # solara-ui +platformdirs==4.2.1 + # via jupyter-core +plotly==5.22.0 + # via dont-fret (pyproject.toml) +polars==0.20.25 + # via dont-fret (pyproject.toml) +prompt-toolkit==3.0.43 + # via ipython +psutil==5.9.8 + # via + # distributed + # ipykernel +ptyprocess==0.7.0 + # via pexpect +pure-eval==0.2.2 + # via stack-data +pygments==2.18.0 + # via + # ipython + # rich + # solara-ui +pymdown-extensions==10.8.1 + # via solara-ui +pyparsing==3.1.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # jupyter-client + # matplotlib + # pandas +pytz==2024.1 + # via pandas +pyyaml==6.0.1 + # via + # dask + # distributed + # dont-fret (pyproject.toml) + # pymdown-extensions +pyzmq==26.0.3 + # via + # ipykernel + # jupyter-client +reacton==1.8.3 + # via solara-ui +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via solara-ui +rich==13.7.1 + # via rich-click +rich-click==1.8.1 + # via solara-server +rpds-py==0.18.1 + # via + # jsonschema + # referencing +scipy==1.13.0 + # via + # dont-fret (pyproject.toml) + # kdepy +six==1.16.0 + # via + # asttokens + # python-dateutil +sniffio==1.3.1 + # via anyio +solara==1.32.1 + # via dont-fret (pyproject.toml) +solara-server[dev,starlette]==1.32.1 + # via solara +solara-ui[all,cache,extra,markdown]==1.32.1 + # via + # solara + # solara-server +sortedcontainers==2.4.0 + # via distributed +stack-data==0.6.3 + # via ipython +starlette==0.37.2 + # via solara-server +tblib==3.0.0 + # via distributed +tenacity==8.3.0 + # via plotly +toolz==0.12.1 + # via + # dask + # distributed + # partd +tornado==6.4 + # via + # distributed + # ipykernel + # jupyter-client +tqdm==4.66.4 + # via dont-fret (pyproject.toml) +traitlets==5.14.3 + # via + # comm + # ipykernel + # ipympl + # ipython + # ipywidgets + # jupyter-client + # jupyter-core + # matplotlib-inline + # nbformat +typing-extensions==4.11.0 + # via + # anyio + # dont-fret (pyproject.toml) + # ipython + # reacton + # rich-click + # starlette + # uvicorn +tzdata==2024.1 + # via pandas +urllib3==2.2.1 + # via + # distributed + # requests +uvicorn==0.29.0 + # via solara-server +watchdog==4.0.0 + # via solara-server +watchfiles==0.21.0 + # via solara-server +wcwidth==0.2.13 + # via prompt-toolkit +websockets==12.0 + # via solara-server +widgetsnbextension==4.0.10 + # via ipywidgets +zict==3.0.0 + # via distributed +zipp==3.18.1 + # via + # importlib-metadata + # importlib-resources diff --git a/requirements/requirements-ubuntu-latest-3.10.txt b/requirements/requirements-ubuntu-latest-3.10.txt new file mode 100644 index 0000000..85a14f4 --- /dev/null +++ b/requirements/requirements-ubuntu-latest-3.10.txt @@ -0,0 +1,326 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --extra=web --output-file=requirements-ubuntu-latest-3.10.txt pyproject.toml +# +anyio==4.3.0 + # via + # starlette + # watchfiles +asttokens==2.4.1 + # via stack-data +attrs==23.2.0 + # via + # jsonschema + # referencing +cachetools==5.3.3 + # via solara-ui +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # dask + # distributed + # rich-click + # solara-server + # uvicorn +cloudpickle==3.0.0 + # via + # dask + # distributed +comm==0.2.2 + # via + # ipykernel + # ipywidgets +contourpy==1.2.1 + # via matplotlib +cycler==0.12.1 + # via matplotlib +dacite==1.8.1 + # via dont-fret (pyproject.toml) +dask==2024.5.0 + # via distributed +debugpy==1.8.1 + # via ipykernel +decorator==5.1.1 + # via ipython +distributed==2024.5.0 + # via dont-fret (pyproject.toml) +exceptiongroup==1.2.1 + # via + # anyio + # ipython +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.1 + # via nbformat +filelock==3.14.0 + # via solara-server +fonttools==4.51.0 + # via matplotlib +fsspec==2024.3.1 + # via dask +h11==0.14.0 + # via uvicorn +h5py==3.11.0 + # via dont-fret (pyproject.toml) +humanize==4.9.0 + # via solara-ui +idna==3.7 + # via + # anyio + # requests +importlib-metadata==7.1.0 + # via dask +ipykernel==6.29.4 + # via solara-server +ipympl==0.9.4 + # via dont-fret (pyproject.toml) +ipython==8.24.0 + # via + # ipykernel + # ipympl + # ipywidgets +ipython-genutils==0.2.0 + # via ipympl +ipyvue==1.11.1 + # via + # ipyvuetify + # solara-ui +ipyvuetify==1.9.4 + # via solara-ui +ipywidgets==8.1.2 + # via + # ipympl + # ipyvue + # reacton + # solara-ui +jedi==0.19.1 + # via ipython +jinja2==3.1.4 + # via + # distributed + # solara-server +jsonschema==4.22.0 + # via nbformat +jsonschema-specifications==2023.12.1 + # via jsonschema +jupyter-client==8.6.1 + # via + # ipykernel + # solara-server +jupyter-core==5.7.2 + # via + # ipykernel + # jupyter-client + # nbformat +jupyterlab-widgets==3.0.10 + # via ipywidgets +kdepy==1.1.9 + # via dont-fret (pyproject.toml) +kiwisolver==1.4.5 + # via matplotlib +llvmlite==0.42.0 + # via numba +locket==1.0.0 + # via + # distributed + # partd +markdown==3.6 + # via + # pymdown-extensions + # solara-ui +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.5 + # via jinja2 +matplotlib==3.8.4 + # via ipympl +matplotlib-inline==0.1.7 + # via + # ipykernel + # ipython +mdurl==0.1.2 + # via markdown-it-py +msgpack==1.0.8 + # via distributed +nbformat==5.10.4 + # via solara-server +nest-asyncio==1.6.0 + # via ipykernel +numba==0.59.1 + # via dont-fret (pyproject.toml) +numpy==1.26.4 + # via + # contourpy + # dont-fret (pyproject.toml) + # h5py + # ipympl + # kdepy + # matplotlib + # numba + # pandas + # scipy + # solara-ui +packaging==24.0 + # via + # dask + # distributed + # ipykernel + # matplotlib + # plotly +pandas==2.2.2 + # via dont-fret (pyproject.toml) +parso==0.8.4 + # via jedi +partd==1.4.2 + # via dask +pexpect==4.9.0 + # via ipython +pillow==10.3.0 + # via + # ipympl + # matplotlib + # solara-ui +platformdirs==4.2.1 + # via jupyter-core +plotly==5.22.0 + # via dont-fret (pyproject.toml) +polars==0.20.25 + # via dont-fret (pyproject.toml) +prompt-toolkit==3.0.43 + # via ipython +psutil==5.9.8 + # via + # distributed + # ipykernel +ptyprocess==0.7.0 + # via pexpect +pure-eval==0.2.2 + # via stack-data +pygments==2.18.0 + # via + # ipython + # rich + # solara-ui +pymdown-extensions==10.8.1 + # via solara-ui +pyparsing==3.1.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # jupyter-client + # matplotlib + # pandas +pytz==2024.1 + # via pandas +pyyaml==6.0.1 + # via + # dask + # distributed + # dont-fret (pyproject.toml) + # pymdown-extensions +pyzmq==26.0.3 + # via + # ipykernel + # jupyter-client +reacton==1.8.3 + # via solara-ui +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via solara-ui +rich==13.7.1 + # via rich-click +rich-click==1.8.1 + # via solara-server +rpds-py==0.18.1 + # via + # jsonschema + # referencing +scipy==1.13.0 + # via + # dont-fret (pyproject.toml) + # kdepy +six==1.16.0 + # via + # asttokens + # python-dateutil +sniffio==1.3.1 + # via anyio +solara==1.32.1 + # via dont-fret (pyproject.toml) +solara-server[dev,starlette]==1.32.1 + # via solara +solara-ui[all,cache,extra,markdown]==1.32.1 + # via + # solara + # solara-server +sortedcontainers==2.4.0 + # via distributed +stack-data==0.6.3 + # via ipython +starlette==0.37.2 + # via solara-server +tblib==3.0.0 + # via distributed +tenacity==8.3.0 + # via plotly +toolz==0.12.1 + # via + # dask + # distributed + # partd +tornado==6.4 + # via + # distributed + # ipykernel + # jupyter-client +tqdm==4.66.4 + # via dont-fret (pyproject.toml) +traitlets==5.14.3 + # via + # comm + # ipykernel + # ipympl + # ipython + # ipywidgets + # jupyter-client + # jupyter-core + # matplotlib-inline + # nbformat +typing-extensions==4.11.0 + # via + # anyio + # dont-fret (pyproject.toml) + # ipython + # reacton + # rich-click + # uvicorn +tzdata==2024.1 + # via pandas +urllib3==2.2.1 + # via + # distributed + # requests +uvicorn==0.29.0 + # via solara-server +watchdog==4.0.0 + # via solara-server +watchfiles==0.21.0 + # via solara-server +wcwidth==0.2.13 + # via prompt-toolkit +websockets==12.0 + # via solara-server +widgetsnbextension==4.0.10 + # via ipywidgets +zict==3.0.0 + # via distributed +zipp==3.18.1 + # via importlib-metadata diff --git a/requirements/requirements-ubuntu-latest-3.9.txt b/requirements/requirements-ubuntu-latest-3.9.txt new file mode 100644 index 0000000..72d66e2 --- /dev/null +++ b/requirements/requirements-ubuntu-latest-3.9.txt @@ -0,0 +1,334 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --extra=web --output-file=requirements-ubuntu-latest-3.9.txt pyproject.toml +# +anyio==4.3.0 + # via + # starlette + # watchfiles +asttokens==2.4.1 + # via stack-data +attrs==23.2.0 + # via + # jsonschema + # referencing +cachetools==5.3.3 + # via solara-ui +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # dask + # distributed + # rich-click + # solara-server + # uvicorn +cloudpickle==3.0.0 + # via + # dask + # distributed +comm==0.2.2 + # via + # ipykernel + # ipywidgets +contourpy==1.2.1 + # via matplotlib +cycler==0.12.1 + # via matplotlib +dacite==1.8.1 + # via dont-fret (pyproject.toml) +dask==2024.5.0 + # via distributed +debugpy==1.8.1 + # via ipykernel +decorator==5.1.1 + # via ipython +distributed==2024.5.0 + # via dont-fret (pyproject.toml) +exceptiongroup==1.2.1 + # via + # anyio + # ipython +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.1 + # via nbformat +filelock==3.14.0 + # via solara-server +fonttools==4.51.0 + # via matplotlib +fsspec==2024.3.1 + # via dask +h11==0.14.0 + # via uvicorn +h5py==3.11.0 + # via dont-fret (pyproject.toml) +humanize==4.9.0 + # via solara-ui +idna==3.7 + # via + # anyio + # requests +importlib-metadata==7.1.0 + # via + # dask + # jupyter-client + # markdown +importlib-resources==6.4.0 + # via matplotlib +ipykernel==6.29.4 + # via solara-server +ipympl==0.9.4 + # via dont-fret (pyproject.toml) +ipython==8.18.1 + # via + # ipykernel + # ipympl + # ipywidgets +ipython-genutils==0.2.0 + # via ipympl +ipyvue==1.11.1 + # via + # ipyvuetify + # solara-ui +ipyvuetify==1.9.4 + # via solara-ui +ipywidgets==8.1.2 + # via + # ipympl + # ipyvue + # reacton + # solara-ui +jedi==0.19.1 + # via ipython +jinja2==3.1.4 + # via + # distributed + # solara-server +jsonschema==4.22.0 + # via nbformat +jsonschema-specifications==2023.12.1 + # via jsonschema +jupyter-client==8.6.1 + # via + # ipykernel + # solara-server +jupyter-core==5.7.2 + # via + # ipykernel + # jupyter-client + # nbformat +jupyterlab-widgets==3.0.10 + # via ipywidgets +kdepy==1.1.9 + # via dont-fret (pyproject.toml) +kiwisolver==1.4.5 + # via matplotlib +llvmlite==0.42.0 + # via numba +locket==1.0.0 + # via + # distributed + # partd +markdown==3.6 + # via + # pymdown-extensions + # solara-ui +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.5 + # via jinja2 +matplotlib==3.8.4 + # via ipympl +matplotlib-inline==0.1.7 + # via + # ipykernel + # ipython +mdurl==0.1.2 + # via markdown-it-py +msgpack==1.0.8 + # via distributed +nbformat==5.10.4 + # via solara-server +nest-asyncio==1.6.0 + # via ipykernel +numba==0.59.1 + # via dont-fret (pyproject.toml) +numpy==1.26.4 + # via + # contourpy + # dont-fret (pyproject.toml) + # h5py + # ipympl + # kdepy + # matplotlib + # numba + # pandas + # scipy + # solara-ui +packaging==24.0 + # via + # dask + # distributed + # ipykernel + # matplotlib + # plotly +pandas==2.2.2 + # via dont-fret (pyproject.toml) +parso==0.8.4 + # via jedi +partd==1.4.2 + # via dask +pexpect==4.9.0 + # via ipython +pillow==10.3.0 + # via + # ipympl + # matplotlib + # solara-ui +platformdirs==4.2.1 + # via jupyter-core +plotly==5.22.0 + # via dont-fret (pyproject.toml) +polars==0.20.25 + # via dont-fret (pyproject.toml) +prompt-toolkit==3.0.43 + # via ipython +psutil==5.9.8 + # via + # distributed + # ipykernel +ptyprocess==0.7.0 + # via pexpect +pure-eval==0.2.2 + # via stack-data +pygments==2.18.0 + # via + # ipython + # rich + # solara-ui +pymdown-extensions==10.8.1 + # via solara-ui +pyparsing==3.1.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # jupyter-client + # matplotlib + # pandas +pytz==2024.1 + # via pandas +pyyaml==6.0.1 + # via + # dask + # distributed + # dont-fret (pyproject.toml) + # pymdown-extensions +pyzmq==26.0.3 + # via + # ipykernel + # jupyter-client +reacton==1.8.3 + # via solara-ui +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via solara-ui +rich==13.7.1 + # via rich-click +rich-click==1.8.1 + # via solara-server +rpds-py==0.18.1 + # via + # jsonschema + # referencing +scipy==1.13.0 + # via + # dont-fret (pyproject.toml) + # kdepy +six==1.16.0 + # via + # asttokens + # python-dateutil +sniffio==1.3.1 + # via anyio +solara==1.32.1 + # via dont-fret (pyproject.toml) +solara-server[dev,starlette]==1.32.1 + # via solara +solara-ui[all,cache,extra,markdown]==1.32.1 + # via + # solara + # solara-server +sortedcontainers==2.4.0 + # via distributed +stack-data==0.6.3 + # via ipython +starlette==0.37.2 + # via solara-server +tblib==3.0.0 + # via distributed +tenacity==8.3.0 + # via plotly +toolz==0.12.1 + # via + # dask + # distributed + # partd +tornado==6.4 + # via + # distributed + # ipykernel + # jupyter-client +tqdm==4.66.4 + # via dont-fret (pyproject.toml) +traitlets==5.14.3 + # via + # comm + # ipykernel + # ipympl + # ipython + # ipywidgets + # jupyter-client + # jupyter-core + # matplotlib-inline + # nbformat +typing-extensions==4.11.0 + # via + # anyio + # dont-fret (pyproject.toml) + # ipython + # reacton + # rich-click + # starlette + # uvicorn +tzdata==2024.1 + # via pandas +urllib3==2.2.1 + # via + # distributed + # requests +uvicorn==0.29.0 + # via solara-server +watchdog==4.0.0 + # via solara-server +watchfiles==0.21.0 + # via solara-server +wcwidth==0.2.13 + # via prompt-toolkit +websockets==12.0 + # via solara-server +widgetsnbextension==4.0.10 + # via ipywidgets +zict==3.0.0 + # via distributed +zipp==3.18.1 + # via + # importlib-metadata + # importlib-resources diff --git a/requirements/requirements-windows-latest-3.10.txt b/requirements/requirements-windows-latest-3.10.txt new file mode 100644 index 0000000..a939e4a --- /dev/null +++ b/requirements/requirements-windows-latest-3.10.txt @@ -0,0 +1,329 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --extra=web --output-file=requirements-windows-latest-3.10.txt pyproject.toml +# +anyio==4.3.0 + # via + # starlette + # watchfiles +asttokens==2.4.1 + # via stack-data +attrs==23.2.0 + # via + # jsonschema + # referencing +cachetools==5.3.3 + # via solara-ui +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # dask + # distributed + # rich-click + # solara-server + # uvicorn +cloudpickle==3.0.0 + # via + # dask + # distributed +colorama==0.4.6 + # via + # click + # ipython + # tqdm +comm==0.2.2 + # via + # ipykernel + # ipywidgets +contourpy==1.2.1 + # via matplotlib +cycler==0.12.1 + # via matplotlib +dacite==1.8.1 + # via dont-fret (pyproject.toml) +dask==2024.5.0 + # via distributed +debugpy==1.8.1 + # via ipykernel +decorator==5.1.1 + # via ipython +distributed==2024.5.0 + # via dont-fret (pyproject.toml) +exceptiongroup==1.2.1 + # via + # anyio + # ipython +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.1 + # via nbformat +filelock==3.14.0 + # via solara-server +fonttools==4.51.0 + # via matplotlib +fsspec==2024.3.1 + # via dask +h11==0.14.0 + # via uvicorn +h5py==3.11.0 + # via dont-fret (pyproject.toml) +humanize==4.9.0 + # via solara-ui +idna==3.7 + # via + # anyio + # requests +importlib-metadata==7.1.0 + # via dask +ipykernel==6.29.4 + # via solara-server +ipympl==0.9.4 + # via dont-fret (pyproject.toml) +ipython==8.24.0 + # via + # ipykernel + # ipympl + # ipywidgets +ipython-genutils==0.2.0 + # via ipympl +ipyvue==1.11.1 + # via + # ipyvuetify + # solara-ui +ipyvuetify==1.9.4 + # via solara-ui +ipywidgets==8.1.2 + # via + # ipympl + # ipyvue + # reacton + # solara-ui +jedi==0.19.1 + # via ipython +jinja2==3.1.4 + # via + # distributed + # solara-server +jsonschema==4.22.0 + # via nbformat +jsonschema-specifications==2023.12.1 + # via jsonschema +jupyter-client==8.6.1 + # via + # ipykernel + # solara-server +jupyter-core==5.7.2 + # via + # ipykernel + # jupyter-client + # nbformat +jupyterlab-widgets==3.0.10 + # via ipywidgets +kdepy==1.1.9 + # via dont-fret (pyproject.toml) +kiwisolver==1.4.5 + # via matplotlib +llvmlite==0.42.0 + # via numba +locket==1.0.0 + # via + # distributed + # partd +markdown==3.6 + # via + # pymdown-extensions + # solara-ui +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.5 + # via jinja2 +matplotlib==3.8.4 + # via ipympl +matplotlib-inline==0.1.7 + # via + # ipykernel + # ipython +mdurl==0.1.2 + # via markdown-it-py +msgpack==1.0.8 + # via distributed +nbformat==5.10.4 + # via solara-server +nest-asyncio==1.6.0 + # via ipykernel +numba==0.59.1 + # via dont-fret (pyproject.toml) +numpy==1.26.4 + # via + # contourpy + # dont-fret (pyproject.toml) + # h5py + # ipympl + # kdepy + # matplotlib + # numba + # pandas + # scipy + # solara-ui +packaging==24.0 + # via + # dask + # distributed + # ipykernel + # matplotlib + # plotly +pandas==2.2.2 + # via dont-fret (pyproject.toml) +parso==0.8.4 + # via jedi +partd==1.4.2 + # via dask +pillow==10.3.0 + # via + # ipympl + # matplotlib + # solara-ui +platformdirs==4.2.1 + # via jupyter-core +plotly==5.22.0 + # via dont-fret (pyproject.toml) +polars==0.20.25 + # via dont-fret (pyproject.toml) +prompt-toolkit==3.0.43 + # via ipython +psutil==5.9.8 + # via + # distributed + # ipykernel +pure-eval==0.2.2 + # via stack-data +pygments==2.18.0 + # via + # ipython + # rich + # solara-ui +pymdown-extensions==10.8.1 + # via solara-ui +pyparsing==3.1.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # jupyter-client + # matplotlib + # pandas +pytz==2024.1 + # via pandas +pywin32==306 + # via jupyter-core +pyyaml==6.0.1 + # via + # dask + # distributed + # dont-fret (pyproject.toml) + # pymdown-extensions +pyzmq==26.0.3 + # via + # ipykernel + # jupyter-client +reacton==1.8.3 + # via solara-ui +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via solara-ui +rich==13.7.1 + # via rich-click +rich-click==1.8.1 + # via solara-server +rpds-py==0.18.1 + # via + # jsonschema + # referencing +scipy==1.13.0 + # via + # dont-fret (pyproject.toml) + # kdepy +six==1.16.0 + # via + # asttokens + # python-dateutil +sniffio==1.3.1 + # via anyio +solara==1.32.1 + # via dont-fret (pyproject.toml) +solara-server[dev,starlette]==1.32.1 + # via solara +solara-ui[all,cache,extra,markdown]==1.32.1 + # via + # solara + # solara-server +sortedcontainers==2.4.0 + # via distributed +stack-data==0.6.3 + # via ipython +starlette==0.37.2 + # via solara-server +tblib==3.0.0 + # via distributed +tenacity==8.3.0 + # via plotly +toolz==0.12.1 + # via + # dask + # distributed + # partd +tornado==6.4 + # via + # distributed + # ipykernel + # jupyter-client +tqdm==4.66.4 + # via dont-fret (pyproject.toml) +traitlets==5.14.3 + # via + # comm + # ipykernel + # ipympl + # ipython + # ipywidgets + # jupyter-client + # jupyter-core + # matplotlib-inline + # nbformat +typing-extensions==4.11.0 + # via + # anyio + # dont-fret (pyproject.toml) + # ipython + # reacton + # rich-click + # uvicorn +tzdata==2024.1 + # via pandas +urllib3==2.2.1 + # via + # distributed + # requests +uvicorn==0.29.0 + # via solara-server +watchdog==4.0.0 + # via solara-server +watchfiles==0.21.0 + # via solara-server +wcwidth==0.2.13 + # via prompt-toolkit +websockets==12.0 + # via solara-server +widgetsnbextension==4.0.10 + # via ipywidgets +zict==3.0.0 + # via distributed +zipp==3.18.1 + # via importlib-metadata diff --git a/requirements/requirements-windows-latest-3.9.txt b/requirements/requirements-windows-latest-3.9.txt new file mode 100644 index 0000000..bbe8c9d --- /dev/null +++ b/requirements/requirements-windows-latest-3.9.txt @@ -0,0 +1,337 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --extra=web --output-file=requirements-windows-latest-3.9.txt pyproject.toml +# +anyio==4.3.0 + # via + # starlette + # watchfiles +asttokens==2.4.1 + # via stack-data +attrs==23.2.0 + # via + # jsonschema + # referencing +cachetools==5.3.3 + # via solara-ui +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # dask + # distributed + # rich-click + # solara-server + # uvicorn +cloudpickle==3.0.0 + # via + # dask + # distributed +colorama==0.4.6 + # via + # click + # ipython + # tqdm +comm==0.2.2 + # via + # ipykernel + # ipywidgets +contourpy==1.2.1 + # via matplotlib +cycler==0.12.1 + # via matplotlib +dacite==1.8.1 + # via dont-fret (pyproject.toml) +dask==2024.5.0 + # via distributed +debugpy==1.8.1 + # via ipykernel +decorator==5.1.1 + # via ipython +distributed==2024.5.0 + # via dont-fret (pyproject.toml) +exceptiongroup==1.2.1 + # via + # anyio + # ipython +executing==2.0.1 + # via stack-data +fastjsonschema==2.19.1 + # via nbformat +filelock==3.14.0 + # via solara-server +fonttools==4.51.0 + # via matplotlib +fsspec==2024.3.1 + # via dask +h11==0.14.0 + # via uvicorn +h5py==3.11.0 + # via dont-fret (pyproject.toml) +humanize==4.9.0 + # via solara-ui +idna==3.7 + # via + # anyio + # requests +importlib-metadata==7.1.0 + # via + # dask + # jupyter-client + # markdown +importlib-resources==6.4.0 + # via matplotlib +ipykernel==6.29.4 + # via solara-server +ipympl==0.9.4 + # via dont-fret (pyproject.toml) +ipython==8.18.1 + # via + # ipykernel + # ipympl + # ipywidgets +ipython-genutils==0.2.0 + # via ipympl +ipyvue==1.11.1 + # via + # ipyvuetify + # solara-ui +ipyvuetify==1.9.4 + # via solara-ui +ipywidgets==8.1.2 + # via + # ipympl + # ipyvue + # reacton + # solara-ui +jedi==0.19.1 + # via ipython +jinja2==3.1.4 + # via + # distributed + # solara-server +jsonschema==4.22.0 + # via nbformat +jsonschema-specifications==2023.12.1 + # via jsonschema +jupyter-client==8.6.1 + # via + # ipykernel + # solara-server +jupyter-core==5.7.2 + # via + # ipykernel + # jupyter-client + # nbformat +jupyterlab-widgets==3.0.10 + # via ipywidgets +kdepy==1.1.9 + # via dont-fret (pyproject.toml) +kiwisolver==1.4.5 + # via matplotlib +llvmlite==0.42.0 + # via numba +locket==1.0.0 + # via + # distributed + # partd +markdown==3.6 + # via + # pymdown-extensions + # solara-ui +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.5 + # via jinja2 +matplotlib==3.8.4 + # via ipympl +matplotlib-inline==0.1.7 + # via + # ipykernel + # ipython +mdurl==0.1.2 + # via markdown-it-py +msgpack==1.0.8 + # via distributed +nbformat==5.10.4 + # via solara-server +nest-asyncio==1.6.0 + # via ipykernel +numba==0.59.1 + # via dont-fret (pyproject.toml) +numpy==1.26.4 + # via + # contourpy + # dont-fret (pyproject.toml) + # h5py + # ipympl + # kdepy + # matplotlib + # numba + # pandas + # scipy + # solara-ui +packaging==24.0 + # via + # dask + # distributed + # ipykernel + # matplotlib + # plotly +pandas==2.2.2 + # via dont-fret (pyproject.toml) +parso==0.8.4 + # via jedi +partd==1.4.2 + # via dask +pillow==10.3.0 + # via + # ipympl + # matplotlib + # solara-ui +platformdirs==4.2.1 + # via jupyter-core +plotly==5.22.0 + # via dont-fret (pyproject.toml) +polars==0.20.25 + # via dont-fret (pyproject.toml) +prompt-toolkit==3.0.43 + # via ipython +psutil==5.9.8 + # via + # distributed + # ipykernel +pure-eval==0.2.2 + # via stack-data +pygments==2.18.0 + # via + # ipython + # rich + # solara-ui +pymdown-extensions==10.8.1 + # via solara-ui +pyparsing==3.1.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # jupyter-client + # matplotlib + # pandas +pytz==2024.1 + # via pandas +pywin32==306 + # via jupyter-core +pyyaml==6.0.1 + # via + # dask + # distributed + # dont-fret (pyproject.toml) + # pymdown-extensions +pyzmq==26.0.3 + # via + # ipykernel + # jupyter-client +reacton==1.8.3 + # via solara-ui +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via solara-ui +rich==13.7.1 + # via rich-click +rich-click==1.8.1 + # via solara-server +rpds-py==0.18.1 + # via + # jsonschema + # referencing +scipy==1.13.0 + # via + # dont-fret (pyproject.toml) + # kdepy +six==1.16.0 + # via + # asttokens + # python-dateutil +sniffio==1.3.1 + # via anyio +solara==1.32.1 + # via dont-fret (pyproject.toml) +solara-server[dev,starlette]==1.32.1 + # via solara +solara-ui[all,cache,extra,markdown]==1.32.1 + # via + # solara + # solara-server +sortedcontainers==2.4.0 + # via distributed +stack-data==0.6.3 + # via ipython +starlette==0.37.2 + # via solara-server +tblib==3.0.0 + # via distributed +tenacity==8.3.0 + # via plotly +toolz==0.12.1 + # via + # dask + # distributed + # partd +tornado==6.4 + # via + # distributed + # ipykernel + # jupyter-client +tqdm==4.66.4 + # via dont-fret (pyproject.toml) +traitlets==5.14.3 + # via + # comm + # ipykernel + # ipympl + # ipython + # ipywidgets + # jupyter-client + # jupyter-core + # matplotlib-inline + # nbformat +typing-extensions==4.11.0 + # via + # anyio + # dont-fret (pyproject.toml) + # ipython + # reacton + # rich-click + # starlette + # uvicorn +tzdata==2024.1 + # via pandas +urllib3==2.2.1 + # via + # distributed + # requests +uvicorn==0.29.0 + # via solara-server +watchdog==4.0.0 + # via solara-server +watchfiles==0.21.0 + # via solara-server +wcwidth==0.2.13 + # via prompt-toolkit +websockets==12.0 + # via solara-server +widgetsnbextension==4.0.10 + # via ipywidgets +zict==3.0.0 + # via distributed +zipp==3.18.1 + # via + # importlib-metadata + # importlib-resources diff --git a/templates/01_load_datafile_1.py b/templates/01_load_datafile_1.py new file mode 100644 index 0000000..b7111ff --- /dev/null +++ b/templates/01_load_datafile_1.py @@ -0,0 +1,26 @@ +# %% +from pathlib import Path + +from dont_fret.fileIO import PhotonFile +from dont_fret.models import PhotonData + +# %% +cwd = Path(__file__).parent +test_data_dir = cwd.parent / "tests" / "test_data" / "input" / "ds1" +ptu_file = "datafile_1.ptu" + +# %% +photons = PhotonData.from_file(PhotonFile(test_data_dir / ptu_file)) + + +# %% + +bursts = photons.burst_search("DCBS") + +# %% + +# Export to a dataframe, skip the number of photons but include photon count +# and mean life time per burst per photon stream. + +bursts.burst_data.head() +print(bursts.burst_data) diff --git a/templates/02_load_and_plot_bursts.py b/templates/02_load_and_plot_bursts.py new file mode 100644 index 0000000..1b5ef1d --- /dev/null +++ b/templates/02_load_and_plot_bursts.py @@ -0,0 +1,99 @@ +# %% + +from pathlib import Path + +import numpy as np +import proplot as pplt +from KDEpy.utils import cartesian + +from dont_fret.fileIO import load_bursts +from dont_fret.formatting import CONTOURF_KWARGS, HIST1D_KWARGS +from dont_fret.plot import ( + calculate_kde_2d, + reshape_grid, +) + +# %% +# __file__ = Path().resolve() / "templates" / 'script.py' # pycharm scientific mode + +# %% +cwd = Path(__file__).parent +test_data_dir = cwd.parent / "tests" / "test_data" / "output" / "ds1" +burst_file = test_data_dir / "dcbs_bursts.h5" +bursts = load_bursts(burst_file) + +# %% + +x, y = bursts.E_app, bursts.S_app +xlim = (0, 1) +ylim = (0, 1) + + +# %% +# KDE plot +NUM = 2**10 +lin_x = np.linspace(*xlim, NUM, endpoint=True) +lin_y = np.linspace(*ylim, NUM, endpoint=True) + +grid_points = cartesian([lin_x, lin_y]) +grid, kde_z = calculate_kde_2d(x, y, grid_points=grid_points) +grid_x, grid_y = reshape_grid(grid, (NUM, NUM)) +z = kde_z.reshape((NUM, NUM)).T + +fig, ax = pplt.subplots(axwidth="60mm") +ax.contourf(grid_x, grid_y, z, **CONTOURF_KWARGS) +ax.format(xlim=xlim, ylim=ylim, xlabel="Apparent FRET", ylabel="Apparent Stoichiometry") + +panel_x = ax.panel_axes("t", width="15mm") +panel_x.hist(x, **HIST1D_KWARGS) +panel_y = ax.panel_axes("r", width="15mm") +panel_y.histh(y, **HIST1D_KWARGS) +pplt.show() + +# %% + +# 2D histogram plot +xbins = np.linspace(*xlim, 30, endpoint=True) +ybins = np.linspace(*ylim, 30, endpoint=True) + +fig, ax = pplt.subplots(axwidth="60mm") +h, xedge, yedge, img = ax.hist2d(x, y, cmap="viridis", bins=[xbins, ybins], edgefix=False) +ax.colorbar(img, loc="b", label="Counts", width="2.5mm") +ax.format(xlim=xlim, ylim=ylim, xlabel="Apparent FRET", ylabel="Apparent Stoichiometry") + +kw = {k: v for k, v in HIST1D_KWARGS.items() if k != "bins"} + +panel_x = ax.panel_axes("t", width="15mm") +panel_x.hist(x, bins=xbins, **kw) +panel_y = ax.panel_axes("r", width="15mm") +panel_y.histh(y, bins=ybins, **kw) +pplt.show() + +# %% +# ISSUE 38 +# df = bursts.to_dataframe(photon_counts=True) +# df.columns +# +# #%% +# +# # import proplot as pplt +# # +# fig, axes = pplt.subplots(nrows=2, ncols=2, sharey=False, share=False) +# +# for ax, stream in zip(axes, bursts[0].stream_counts): +# if stream == "AD": +# continue +# ax.hist(df[f"n_{stream}"], bins="fd") +# mean = df[f"n_{stream}"].mean() +# print(f"{stream}: {mean:.2f} photons per burst") +# ax.axvline(mean, color="r", linestyle="--") +# ax.format(xlabel="Photons per burst", ylabel="Count", title=stream) +# +# green_ch = df["n_DD"] + df["n_DA"] +# axes[1, 1].hist(green_ch, bins="fd") +# mean = green_ch.mean() +# print(f"DD + DA: {mean:.2f} photons per burst") +# axes[1, 1].axvline(mean, color="r", linestyle="--") +# axes[1, 1].format(xlabel="Photons per burst", ylabel="Count", title="DD + DA") +# +# pplt.show() diff --git a/templates/03_binning_trace.py b/templates/03_binning_trace.py new file mode 100644 index 0000000..693fe5f --- /dev/null +++ b/templates/03_binning_trace.py @@ -0,0 +1,52 @@ +# %% + +from pathlib import Path + +# import proplot as pplt +import matplotlib.pyplot as pplt +import numpy as np +import polars as pl + +from dont_fret.fileIO import PhotonFile +from dont_fret.formatting import TRACE_COLORS, TRACE_SIGNS +from dont_fret.models import BinnedPhotonData, PhotonData + +# %% +cwd = Path(__file__).parent +test_data_dir = cwd.parent / "tests" / "test_data" / "input" / "ds1" +ptu_file = "datafile_1.ptu" + +# %% +photons = PhotonData.from_file(PhotonFile(test_data_dir / ptu_file)) + +fig, ax = pplt.subplots( + # refaspect=3.5, axwidth="140mm" +) +xlim = (10, 20) +for stream in TRACE_COLORS: + stream_photons = PhotonData( + photons.data.filter(pl.col("stream") == stream), metadata=photons.metadata, cfg=photons.cfg + ) + tr = BinnedPhotonData(stream_photons, bounds=xlim, binning_time=1e-3) + ax.plot(tr.time, TRACE_SIGNS[stream] * tr.photons, color=TRACE_COLORS[stream]) + +ylim = np.max(np.abs(ax.get_ylim())) +# ax.format(xlabel="Time (s)", ylabel="Photons / ms", ylim=(-ylim, ylim), xlim=xlim) +pplt.show() + +# %% + +fig, ax = pplt.subplots( + # refaspect=3.5, axwidth="140mm" +) +for stream in TRACE_COLORS: + df = photons.data.filter(pl.col("stream") == stream) + counts = np.bincount(df["nanotimes"]) + nanotime = np.arange(len(counts)) * photons.nanotimes_unit * 1e9 + ax.plot(nanotime, counts, lw=0.5, label=stream, color=TRACE_COLORS[stream]) +# +# ax.format(xlabel="TCSPC time (ns)", ylabel="Photons / TCSPC bin", yscale="log") +ax.set_yscale("log") +pplt.show() + +# %% diff --git a/templates/04_multiple_files.py b/templates/04_multiple_files.py new file mode 100644 index 0000000..dd3d83d --- /dev/null +++ b/templates/04_multiple_files.py @@ -0,0 +1,17 @@ +# %% +from pathlib import Path + +from dont_fret.process import batch_search_and_save, search_and_save + +# %% + +cwd = Path(__file__).parent +test_data_dir = cwd.parent / "tests" / "test_data" / "input" / "ds2" +ptu_files = list(test_data_dir.glob("*.ptu")) + +# %% +search_and_save(ptu_files[0], output_type=".csv") + +# %% +batch_search_and_save(ptu_files) +# %% diff --git a/templates/05_web_models.py b/templates/05_web_models.py new file mode 100644 index 0000000..c00000a --- /dev/null +++ b/templates/05_web_models.py @@ -0,0 +1,39 @@ +# %% + +"""template for creating web models and using them to launch solara parts +for interactive use only + +""" + +from __future__ import annotations + +from pathlib import Path + +import dont_fret.web.state as state +from dont_fret.web.bursts.components import BurstFigure +from dont_fret.web.methods import batch_burst_search +from dont_fret.web.models import FRETNode, PhotonFileItem + +# %% +ROOT = Path(__file__).parent.parent +pth = ROOT / "tests" / "test_data" / "input" / "ds2" +photon_file_items = [PhotonFileItem(file_path=ptu_pth) for ptu_pth in pth.glob("*.ptu")] + +# %% + +burst_settings = ["DCBS", "APBS"] +burst_items = [batch_burst_search(photon_file_items, name) for name in burst_settings] +# %% +state.fret_nodes.set([]) +node = FRETNode( + name="FRET NOT", + photons=photon_file_items, + bursts=burst_items, +) +state.fret_nodes.append(node) + +# %% + +BurstFigure(state.fret_nodes, state.filters) + +# %% diff --git a/templates/06_preload_web.py b/templates/06_preload_web.py new file mode 100644 index 0000000..ffe1726 --- /dev/null +++ b/templates/06_preload_web.py @@ -0,0 +1,56 @@ +# %% + +""" +launch dont-fret web with preloaded data +""" + +from __future__ import annotations + +from pathlib import Path + +import solara + +import dont_fret.web.state as state +from dont_fret.web.bursts.components import BurstFigure +from dont_fret.web.main import Page as MainPage +from dont_fret.web.methods import batch_burst_search +from dont_fret.web.models import FRETNode, PhotonFileItem + +# %% + +ROOT = Path(__file__).parent.parent +pth = ROOT / "tests" / "test_data" / "input" / "ds2" +photon_file_items = [PhotonFileItem(file_path=ptu_pth) for ptu_pth in pth.glob("*.ptu")] + +# %% + +burst_settings = ["DCBS", "APBS"] +burst_items = [batch_burst_search(photon_file_items, name) for name in burst_settings] +# %% +# %% +node = FRETNode( + name="FRET NOT", + photons=photon_file_items, + bursts=burst_items, +) + +# %% + + +@solara.component +def Page(): + def preload(): + state.fret_nodes.set([]) + if len(state.fret_nodes.value) == 0: + state.fret_nodes.extend([node]) + + solara.use_effect(preload, dependencies=[]) + nodes = state.fret_nodes.value + + if len(nodes) != 0: + MainPage() + else: + solara.Text("Loading fret nodes...") + + +# %% diff --git a/tests/generate_ds1_result.py b/tests/generate_ds1_result.py new file mode 100644 index 0000000..5f169b1 --- /dev/null +++ b/tests/generate_ds1_result.py @@ -0,0 +1,29 @@ +""" +Generates reference burst search results for tests +""" + +from pathlib import Path + +from dont_fret.fileIO import PTUFile, save +from dont_fret.models import ChannelPhotonData + +cwd = Path(__file__).parent + +DCBS_TEST = {"DD + DA": {"L": 50, "M": 35, "T": 0.0005}, "AA": {"L": 50, "M": 35, "T": 0.0005}} +APBS_TEST = {"DD + DA + AA": {"L": 50, "M": 35, "T": 0.0005}} + +input_data_dir = cwd / "test_data" / "input" / "ds1" +output_data_dir = cwd / "test_data" / "output" / "ds1" +output_data_dir.mkdir(exist_ok=True, parents=True) + +photons = ChannelPhotonData.from_file(PTUFile(input_data_dir / "datafile_1.ptu")) + +save(output_data_dir / "photons.h5", photons) + +bursts_dcbs = photons.burst_search(DCBS_TEST).filter("n_photons", (50, None)) +bursts_dcbs.to_dataframe().write_csv(output_data_dir / "dcbs_bursts.csv") +save(output_data_dir / "dcbs_bursts.h5", bursts_dcbs) + +bursts_apbs = photons.burst_search(APBS_TEST).filter("n_photons", (50, None)) +bursts_apbs.to_dataframe().write_csv(output_data_dir / "apbs_bursts.csv") +save(output_data_dir / "apbs_bursts.h5", bursts_apbs) diff --git a/tests/generate_ds2.py b/tests/generate_ds2.py new file mode 100644 index 0000000..5c3b0e6 --- /dev/null +++ b/tests/generate_ds2.py @@ -0,0 +1,19 @@ +import dont_fret.datagen as datagen +import numpy as np +from pathlib import Path + +# __file__ = Path().resolve() / "script.py" +cwd = Path(__file__).parent + +output_data_dir = cwd / "test_data" / "input" / "ds2" + +ds_names = [ + "twostate", + "threestate", + "twostate_static", +] + +for s in ds_names: + func = getattr(datagen, s) + data = func() + np.savetxt(output_data_dir / f"{s}.txt", data) diff --git a/tests/generate_web_data.py b/tests/generate_web_data.py new file mode 100644 index 0000000..5b8493f --- /dev/null +++ b/tests/generate_web_data.py @@ -0,0 +1,76 @@ +"""Generates data for testing the web app""" +from pathlib import Path + +from dont_fret.web.apps.burst_view import gen_fileitems +from dont_fret.web.apps.burst_view.state import PhotonFiles, BurstColors +import pandas as pd + +# %% +cwd = Path(__file__).parent +OUTPUT_PATH = cwd / "test_data" / "output" / "web" +OUTPUT_PATH.mkdir(exist_ok=True, parents=True) + +# %% +DCBS_TEST = {"DD + DA": {"L": 50, "M": 35, "T": 0.0005}, "AA": {"L": 50, "M": 35, "T": 0.0005}} + +# %% + +photon_file_items = gen_fileitems(2) + +colors = list(BurstColors.from_dict(DCBS_TEST)) +photon_files = PhotonFiles() +photon_files.add_file_items(photon_file_items) +bursts_item = photon_files.do_burst_search( + "my_bursts", colors, on_progress=lambda x: None, on_indeterminate=lambda x: None +) + +# %% + + +bursts_item.df.to_csv(OUTPUT_PATH / "bursts.csv", index=False) + + +# %% +bursts_item.df.dtypes + +# %% + +df_read = pd.read_csv(OUTPUT_PATH / "bursts.csv") +df_read + +# %% + +d = bursts_item.burst_sets["200924-FRET-MBP-2K-R0_36.ptu"].to_dict(photon_counts=True) + +d["n_photons"].dtype + +# %% +# +# d['n_DD'] +# +# #%% +# +# b = bursts_item.burst_sets['200924-FRET-MBP-2K-R0_36.ptu'] +# type(b[0].stream_counts['DD']) +# +# +# #%% +# +# +# #%% +# +# import numpy as np +# np.iinfo(np.uint8) +# +# #%% +# +# a = np.array([1,2,3, 4], dtype=np.int64) +# a +# +# #%% +# # +# b = a.astype(np.min_scalar_type(a.max())) +# +# b.base +# +# a[:2].base diff --git a/tests/helpers.py b/tests/helpers.py new file mode 100644 index 0000000..3a6cb73 --- /dev/null +++ b/tests/helpers.py @@ -0,0 +1,29 @@ +from typing import Type + +import numpy as np + +from dont_fret.models import Bursts, PhotonData + + +def assert_burstsets_equal(bs1: Bursts, bs2: Bursts) -> None: + # This does (probably) not check every datafield + # TODO add __eq__ on photon objects? + assert len(bs1) == len(bs2) + # todo this needs to be listed centrally for future GUI + for attr in ["n_photons", "E_app", "S_app", "time_length", "time_mean", "time_max"]: + assert np.allclose( + getattr(bs1, attr), getattr(bs2, attr), equal_nan=True + ), f"{attr!r} is not equal" + + +def assert_photons_equal(ph1: Type[PhotonData], ph2: Type[PhotonData]) -> None: + assert len(ph1) == len(ph2) + + for attr in ["timestamps", "nanotimes", "photon_times"]: + a1 = getattr(ph1, attr) + a2 = getattr(ph2, attr) + assert np.all(a1 == a2) + assert a1.dtype == a2.dtype + + for attr in ["monotonic", "timestamps_unit", "nanotimes_unit", "description"]: + assert getattr(ph1, attr) == getattr(ph2, attr) diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..f6af434 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1 @@ +"""Test config""" diff --git a/tests/test_data/input/readme.md b/tests/test_data/input/readme.md new file mode 100644 index 0000000..3b1fddf --- /dev/null +++ b/tests/test_data/input/readme.md @@ -0,0 +1,29 @@ +Datasets +======== + + +Download test data [here](https://kuleuven-my.sharepoint.com/:f:/g/personal/jochem_smit_kuleuven_be/EnLFfuf9lKdOuT8yF0wfq1MBSyr_sIPVOR1jpfz5gTPsng?e=KmEcac) + + +ds1 +--- + +Single file + +Picoquant ptu file +nanosecond ALEX green/red +Maltose Binding Protein + 1uM maltose + +config: default.yaml + + +ds2 +--- + +Multiple files + +Picoquant ptu file +nanosecond ALEX green/red +Maltose Binding Protein + 1uM maltose + +config: default.yaml \ No newline at end of file diff --git a/tests/test_data/output/ds1/apbs_bursts.csv b/tests/test_data/output/ds1/apbs_bursts.csv new file mode 100644 index 0000000..2386ff8 --- /dev/null +++ b/tests/test_data/output/ds1/apbs_bursts.csv @@ -0,0 +1,1138 @@ +E_app,S_app,n_photons,time_length,time_mean,time_min,time_max +0.6666666666666666,0.11214953271028037,107,0.00091165,0.006330069626168224,0.005849549999999999,0.006761199999999999 +0.09803921568627451,0.9935064935064936,156,0.0015531499999999999,0.031011177243589743,0.0302652,0.031818349999999995 +0.09375,1.0,96,0.00070955,0.057345391145833334,0.05699165,0.057701199999999994 +0.5,0.15384615384615385,52,0.00044925,0.07508074134615385,0.0748696,0.07531884999999999 +0.5686274509803921,0.32903225806451614,155,0.00110335,0.13178802225806452,0.1312365,0.13233984999999998 +0.7207792207792207,0.4265927977839335,362,0.0023003999999999998,0.13941528687845303,0.1382863,0.14058669999999998 +1.0,0.03225806451612903,62,0.0003358,0.17759866290322582,0.17740355,0.17773935 +0.16666666666666666,0.09836065573770492,61,0.00051565,0.2539031057377049,0.25360869999999996,0.25412435 +0.6619718309859155,0.5220588235294118,136,0.00061915,0.2711397099264706,0.2708046,0.27142374999999996 +0.7391304347826086,0.36507936507936506,63,0.0004748,0.34638609126984127,0.34613695,0.34661175 +0.7254901960784313,0.5795454545454546,88,0.00040905,0.4834575647727272,0.48322655,0.4836356 +0.475,0.6557377049180327,61,0.00047529999999999995,0.5290265057377048,0.52876695,0.52924225 +0.7,0.11627906976744186,86,0.0006638,0.7020140924418604,0.7016869,0.7023507 +0.6923076923076923,0.08227848101265822,158,0.0013959999999999999,0.7656293262658228,0.7648591,0.7662551 +1.0,0.16417910447761194,67,0.00054055,0.770675223880597,0.7703755999999999,0.7709161499999999 +0.7666666666666667,0.410958904109589,146,0.0012339,0.8629874215753425,0.8624584,0.8636923 +0.36363636363636365,0.6111111111111112,54,0.0005203499999999999,0.8721721472222221,0.871956,0.87247635 +0.45454545454545453,0.559322033898305,59,0.0005342,0.8728175389830509,0.8725611999999999,0.8730954 +0.07017543859649122,0.9827586206896551,59,0.00039989999999999996,0.9922284796610169,0.9920302,0.9924301 +0.9166666666666666,0.06557377049180328,183,0.0009527,1.0007082887978143,1.0002056,1.0011583 +1.0,0.10588235294117647,86,0.0006114499999999999,1.037604718604651,1.037315,1.03792645 +0.625,0.12598425196850394,127,0.0011825499999999999,1.0775944716535433,1.0770863,1.07826885 +0.06666666666666667,0.989010989010989,91,0.0007457,1.106080745054945,1.1057046,1.1064502999999999 +0.08695652173913043,0.971830985915493,71,0.0006577,1.172876482394366,1.17255055,1.1732082499999998 +0.11688311688311688,1.0,77,0.0005292,1.2044814967532467,1.2042346499999999,1.20476385 +0.8888888888888888,0.10227272727272728,89,0.00066385,1.2274856808988763,1.22714975,1.2278136 +0.5151515151515151,0.1774193548387097,186,0.0016277499999999999,1.4129398849462365,1.4120746499999999,1.4137024 +0.4,0.09259259259259259,55,0.0004277,1.4326648436363636,1.4324443999999998,1.4328721 +0.5,0.3137254901960784,103,0.00066,1.723331894660194,1.72300215,1.72366215 +1.0,0.1320754716981132,53,0.00041985,1.7266878122641511,1.7264819999999999,1.72690185 +0.11538461538461539,1.0,52,0.00055275,1.7496866259615382,1.7494024,1.74995515 +0.36363636363636365,0.11956521739130435,92,0.0007345,1.9901721994565218,1.9897485,1.990483 +0.09836065573770492,0.9838709677419355,64,0.00038909999999999997,2.21513080625,2.21496405,2.21535315 +0.7142857142857143,0.6086956521739131,92,0.0006721,2.317820966847826,2.31744265,2.31811475 +0.7441860465116279,0.6825396825396826,63,0.0004904,2.3204588976190474,2.32020585,2.3206962499999997 +1.0,0.017241379310344827,58,0.00053255,2.325219798275862,2.32494175,2.3254742999999998 +0.2857142857142857,0.109375,64,0.00051145,2.41701230078125,2.41669705,2.4172085 +0.14754098360655737,0.953125,66,0.0005388999999999999,2.4182351795454546,2.4179641,2.418503 +0.2727272727272727,0.3283582089552239,134,0.0008843,2.419660960447761,2.41920985,2.4200941499999997 +0.08196721311475409,1.0,61,0.0004683,2.5626017303278688,2.5623631,2.5628314 +0.9,0.06756756756756757,148,0.0011453499999999998,2.7353666128378378,2.7348387499999998,2.7359841 +0.07142857142857142,0.9655172413793104,58,0.00045855,2.7596138293103447,2.7594038,2.7598623499999997 +0.0759493670886076,1.0,79,0.0008068999999999999,2.7608409075949365,2.7604279999999997,2.7612349 +0.43636363636363634,0.7432432432432432,74,0.00043085,2.7622322533783783,2.76198985,2.7624207 +0.8571428571428571,0.1044776119402985,67,0.0006587499999999999,2.8112353320895522,2.8109059,2.8115646499999998 +0.75,0.07692307692307693,52,0.00038009999999999997,2.9951838423076924,2.9950055499999997,2.99538565 +0.03773584905660377,1.0,53,0.00039755,3.2670820603773585,3.26690295,3.2673004999999997 +0.3333333333333333,0.24770642201834864,109,0.0011093,3.294902781651376,3.2943548999999996,3.2954642 +0.7142857142857143,0.06140350877192982,114,0.0005487,3.3840448276315787,3.38371795,3.38426665 +0.8,0.09345794392523364,107,0.0006439,3.4113053864485976,3.41099835,3.41164225 +0.9545454545454546,0.088,250,0.0011324,3.4183761233999994,3.41778425,3.41891665 +0.06779661016949153,0.9833333333333333,60,0.00045955,3.479966725833333,3.4797738999999996,3.48023345 +0.0847457627118644,0.9516129032258065,62,0.0005532,3.480709237903226,3.4803992,3.4809524 +0.625,0.14035087719298245,57,0.00052865,3.512196446491228,3.5118818999999997,3.51241055 +0.6,0.06329113924050633,79,0.0006971,3.5142843854430375,3.5139361499999997,3.5146332499999997 +0.8717948717948718,0.39,100,0.0008152999999999999,3.6287659104999994,3.62837575,3.6291910499999998 +0.625,0.10256410256410256,78,0.00066415,3.7747895897435892,3.7744856999999996,3.77514985 +0.5,0.05405405405405406,74,0.00074015,3.9398009587837834,3.9394489999999998,3.9401891499999997 +0.6078431372549019,0.5368421052631579,95,0.0005863999999999999,4.116732065263158,4.11642495,4.117011349999999 +0.08235294117647059,0.9659090909090909,89,0.00081075,4.223933796629213,4.22352285,4.2243336 +0.06172839506172839,1.0,81,0.0007257499999999999,4.318728641358025,4.31840935,4.3191351 +0.6296296296296297,0.48214285714285715,57,0.00038784999999999997,4.555023683333333,4.5548079,4.55519575 +0.75,0.058823529411764705,68,0.00054055,5.080078361029412,5.07981355,5.0803541 +1.0,0.03773584905660377,53,0.00037454999999999997,5.081171846226415,5.08096475,5.0813393 +0.0759493670886076,0.9875,80,0.0005693,5.32531257875,5.3250909,5.3256602 +0.6033519553072626,0.40774487471526194,440,0.0026901,5.349525332045454,5.34821425,5.3509043499999995 +0.6146788990825688,0.367003367003367,297,0.0022489999999999997,5.451120277946128,5.45003895,5.45228795 +0.7272727272727273,0.3728813559322034,59,0.00046235,5.4527965,5.4525571,5.453019449999999 +0.5769230769230769,0.3466666666666667,151,0.001307,5.458195633112583,5.4575415,5.458848499999999 +0.3333333333333333,0.057692307692307696,53,0.0004362,5.471685485849056,5.47149,5.4719261999999995 +0.047619047619047616,0.3620689655172414,58,0.0005025,5.517119712931034,5.5168446499999995,5.51734715 +0.2875,0.8247422680412371,97,0.0007363,5.56931110670103,5.5689525,5.5696888 +0.1206896551724138,1.0,58,0.00043519999999999995,5.584800554310345,5.5845769,5.5850121 +0.4666666666666667,0.18518518518518517,81,0.0007919,5.622584588888889,5.6221808,5.6229727 +0.08403361344537816,1.0,119,0.00091075,5.638441549579832,5.6379715,5.63888225 +0.6847826086956522,0.4946236559139785,186,0.0012473999999999999,5.7063527026881715,5.70570855,5.706955949999999 +0.9285714285714286,0.06511627906976744,215,0.001535,5.750842199302325,5.75006275,5.75159775 +0.9166666666666666,0.1085972850678733,221,0.0009473,5.763171207918552,5.7626938,5.7636411 +0.5,0.5256410256410257,156,0.00112985,5.771985069551282,5.7713451,5.772474949999999 +0.6,0.5,60,0.0006178,5.785626775833333,5.78532795,5.78594575 +1.0,0.12727272727272726,55,0.00048745,5.880929773636363,5.8806864,5.88117385 +0.43157894736842106,0.5757575757575758,165,0.0014179,5.908449771515151,5.90771775,5.90913565 +0.36363636363636365,0.09565217391304348,115,0.0008453499999999999,5.9096350495652175,5.909162149999999,5.9100075 +0.41509433962264153,0.19776119402985073,269,0.0017009999999999998,5.91177033197026,5.9109020999999995,5.9126031 +1.0,0.04225352112676056,71,0.0006016,5.964674928873239,5.9644087,5.965010299999999 +0.8888888888888888,0.13043478260869565,69,0.0005444,6.0457430579710145,6.04551095,6.04605535 +1.0,0.09230769230769231,65,0.0004907,6.062104066153846,6.0618378,6.0623285 +1.0,0.047619047619047616,63,0.0006298,6.0752022071428575,6.074860849999999,6.07549065 +0.5,0.4824561403508772,228,0.00156445,6.123399869736842,6.122578799999999,6.1241432499999995 +0.7,0.1388888888888889,72,0.00059315,6.129444288888888,6.129171599999999,6.12976475 +1.0,0.02631578947368421,77,0.000793,6.131599398051947,6.131208549999999,6.13200155 +0.1,1.0,60,0.000518,6.141461329166666,6.14123005,6.1417480499999995 +0.04411764705882353,1.0,136,0.00087595,6.193294540808823,6.1928797,6.19375565 +0.6153846153846154,0.1566265060240964,83,0.00059235,6.405853293975904,6.40555995,6.4061523 +0.75,0.17391304347826086,69,0.00049035,6.624084395652174,6.62383715,6.6243275 +0.42857142857142855,0.11797752808988764,178,0.0016147,6.6761953106741565,6.67531205,6.67692675 +0.8461538461538461,0.14942528735632185,87,0.0005515,6.678707185057471,6.678446,6.6789974999999995 +1.0,0.07692307692307693,52,0.00040025,6.6998094605769225,6.699614599999999,6.70001485 +0.660377358490566,0.5578947368421052,190,0.0013808,6.7362973802631565,6.73559195,6.73697275 +0.8108108108108109,0.5873015873015873,63,0.00047995,6.7815686841269835,6.78132045,6.7818004 +0.8382352941176471,0.39080459770114945,174,0.0013311999999999998,6.790469675862069,6.789813049999999,6.791144249999999 +0.8076923076923077,0.4642857142857143,56,0.0005073499999999999,6.792947708035714,6.79270875,6.7932160999999995 +0.8,0.5084745762711864,118,0.00079145,6.79449798177966,6.794135949999999,6.7949274 +0.6144578313253012,0.386046511627907,216,0.00115175,6.799311380787037,6.7987223,6.79987405 +0.08888888888888889,0.47368421052631576,95,0.00079575,6.800735558947368,6.800344399999999,6.801140149999999 +0.09090909090909091,0.6395348837209303,86,0.00071955,6.80165655232558,6.8012761,6.801995649999999 +0.532258064516129,0.6262626262626263,99,0.0005298,6.8385298924242415,6.83824325,6.8387730499999995 +0.43478260869565216,0.6764705882352942,68,0.0005693499999999999,6.840098450735295,6.83979215,6.840361499999999 +1.0,0.08108108108108109,111,0.00075765,6.880090871621622,6.879747699999999,6.88050535 +0.09615384615384616,1.0,52,0.0003634,6.908278824038461,6.9080974,6.908460799999999 +0.07964601769911504,0.9912280701754386,114,0.00111985,7.006847392105263,7.00620535,7.0073251999999995 +0.8095238095238095,0.15,140,0.0010758,7.057316838928571,7.05676115,7.0578369499999996 +0.37037037037037035,0.15,180,0.00149095,7.061234325833333,7.060509049999999,7.061999999999999 +0.8076923076923077,0.4126984126984127,63,0.0005095,7.269118034920634,7.2688542499999995,7.269363749999999 +0.6410256410256411,0.43820224719101125,89,0.00064005,7.38284066011236,7.38251465,7.3831546999999995 +0.5517241379310345,0.5087719298245614,57,0.0004637,7.386931000877192,7.38670795,7.38717165 +0.09210526315789473,1.0,76,0.00066835,7.398835598684211,7.39849685,7.3991652 +0.8095238095238095,0.0707070707070707,297,0.00127725,7.458681296969696,7.4580603,7.45933755 +0.6666666666666666,0.034482758620689655,87,0.0008552,7.461022785632184,7.460543899999999,7.4613990999999995 +0.8571428571428571,0.11290322580645161,62,0.0004808,7.47207727016129,7.47184165,7.47232245 +0.7752808988764045,0.577922077922078,154,0.00133435,7.493087475974026,7.4923926,7.493726949999999 +0.5392156862745098,0.45739910313901344,223,0.0017611999999999999,7.500378198878923,7.4995072,7.5012684 +0.7428571428571429,0.546875,64,0.00046984999999999996,7.51885635390625,7.51862275,7.5190926 +0.8148148148148148,0.4909090909090909,55,0.0004927,7.589915331818181,7.589698149999999,7.59019085 +0.8292682926829268,0.5540540540540541,74,0.00052715,7.590587316891892,7.590217999999999,7.59074515 +0.8333333333333334,0.11214953271028037,107,0.0009307499999999999,7.631622993925233,7.6311484,7.63207915 +0.9939759036144579,0.7155172413793104,232,0.0009102,7.64306754849138,7.6426697,7.6435799 +0.09574468085106383,0.9353233830845771,202,0.0012492,7.66850363490099,7.667901349999999,7.6691505499999995 +0.668918918918919,0.48366013071895425,307,0.0020084499999999997,7.680503357328989,7.6795427,7.68155115 +0.13953488372093023,0.7049180327868853,61,0.00045034999999999997,7.682078986065574,7.6818121999999995,7.68226255 +0.8529411764705882,0.40476190476190477,84,0.0005554,7.69715300595238,7.6968863,7.6974417 +0.6842105263157895,0.16666666666666666,114,0.0009442,7.871876771929824,7.8714230999999995,7.8723673 +0.6666666666666666,0.09523809523809523,126,0.0006299,7.873901417460318,7.873594649999999,7.874224549999999 +0.5185185185185185,0.5192307692307693,52,0.00035275,7.882489346153846,7.882299649999999,7.8826524 +0.64,0.36231884057971014,69,0.0004517,7.895106975362318,7.89485805,7.89530975 +1.0,0.09333333333333334,75,0.000509,7.929668613333334,7.92942665,7.92993565 +0.64,0.4716981132075472,53,0.00043335,7.98989225,7.98968935,7.9901227 +0.6,0.05952380952380952,252,0.0016143499999999999,8.079034284325397,8.0782431,8.07985745 +0.8571428571428571,0.06140350877192982,114,0.0011420999999999998,8.081029535964912,8.0804744,8.081616499999999 +0.8,0.08196721311475409,122,0.00086025,8.086914354918033,8.0864391,8.08729935 +0.9230769230769231,0.08227848101265822,158,0.0012387,8.124549328797467,8.1239122,8.1251509 +0.5333333333333333,0.5,60,0.0005341,8.207504779999999,8.207241999999999,8.2077761 +0.5,0.07228915662650602,83,0.00059495,8.436196713855422,8.4359235,8.43651845 +0.7,0.3448275862068966,87,0.0007327,8.716115822413792,8.7157263,8.716459 +0.3181818181818182,0.4230769230769231,156,0.0009335,8.71943452724359,8.718996149999999,8.71992965 +0.76,0.5387931034482759,232,0.00127805,8.87480922650862,8.8741381,8.87541615 +0.9166666666666666,0.06857142857142857,175,0.00129295,8.994374066285713,8.9936964,8.994989349999999 +0.8217821782178217,0.505,200,0.00110455,9.027212465749999,9.0265152,9.02761975 +0.078125,0.9552238805970149,67,0.0006600999999999999,9.31013516716418,9.30984035,9.31050045 +1.0,0.07446808510638298,94,0.0007149999999999999,9.42528144361702,9.42493625,9.42565125 +0.8148148148148148,0.06699751861042183,403,0.00207355,9.585920767990075,9.5847692,9.586842749999999 +0.4583333333333333,0.41379310344827586,58,0.0005235,9.596844062931034,9.59656605,9.59708955 +0.5348837209302325,0.4742647058823529,272,0.0011378999999999998,9.631544168933823,9.6310348,9.6321727 +0.5,0.0547945205479452,73,0.00060815,9.678635812328768,9.6783289,9.67893705 +0.9090909090909091,0.048034934497816595,460,0.0019967,9.704791262065216,9.7037456,9.705742299999999 +0.06382978723404255,1.0,94,0.0009462499999999999,9.722534309574469,9.7220716,9.72301785 +0.7857142857142857,0.08284023668639054,169,0.0014031999999999998,10.22662821301775,10.22594325,10.227346449999999 +1.0,0.10526315789473684,76,0.0005476999999999999,10.355707485526315,10.3554344,10.3559821 +0.6666666666666666,0.06741573033707865,89,0.0006504,10.368414633146067,10.3680982,10.3687486 +0.625,0.09375,256,0.00183285,10.382084707617187,10.38109265,10.382925499999999 +0.7777777777777778,0.08108108108108109,111,0.0008976499999999999,10.424751626126126,10.42429015,10.4251878 +0.7,0.09433962264150944,106,0.0011214999999999999,10.767721492924528,10.7671976,10.7683191 +0.6730769230769231,0.5234899328859061,298,0.00131855,10.812402956208052,10.81175485,10.8130734 +0.6,0.07692307692307693,130,0.00114545,10.931598542307693,10.931020949999999,10.9321664 +1.0,0.06572769953051644,213,0.0011162,10.985278999530516,10.98468235,10.98579855 +0.8888888888888888,0.12413793103448276,147,0.0007982,10.988725207142856,10.988374349999999,10.98917255 +0.00909090909090909,1.0,111,0.0009073999999999999,11.0631957509009,11.062775,11.0636824 +0.8043478260869565,0.41818181818181815,110,0.0007184,11.179220700909092,11.178872199999999,11.1795906 +0.6578947368421053,0.46060606060606063,165,0.00130785,11.39523097,11.39462065,11.3959285 +0.6666666666666666,0.0375,81,0.0007368,11.428834204320987,11.4285123,11.4292491 +0.8148148148148148,0.28125,97,0.0007408,11.665358070103093,11.66496675,11.665707549999999 +0.717391304347826,0.5168539325842697,89,0.00058535,11.667230833707864,11.6669514,11.66753675 +0.7452830188679245,0.48291571753986334,439,0.0027797499999999997,11.679320146013666,11.6779528,11.68073255 +0.7894736842105263,0.4014084507042254,142,0.001127,11.73972323697183,11.739206699999999,11.740333699999999 +0.5737704918032787,0.6421052631578947,95,0.0008575,11.832902889473683,11.8324778,11.8333353 +0.6612903225806451,0.543859649122807,114,0.0011225999999999999,11.834023871052631,11.833486749999999,11.83460935 +0.7027027027027027,0.4868421052631579,76,0.0007299,12.16166388486842,12.1612906,12.162020499999999 +0.9090909090909091,0.12643678160919541,88,0.0007099,12.340894673863636,12.3405115,12.3412214 +0.03333333333333333,1.0,60,0.000567,12.837643462499999,12.837352749999999,12.83791975 +0.75,0.06779661016949153,59,0.00047539999999999995,12.914612640677966,12.9143724,12.914847799999999 +0.078125,1.0,65,0.000452,12.917317774615384,12.917086849999999,12.91753885 +0.9090909090909091,0.09565217391304348,115,0.0007157999999999999,12.985333098695651,12.984992349999999,12.985708149999999 +0.673469387755102,0.6242038216560509,157,0.0007739,12.99197047643312,12.99160685,12.992380749999999 +0.8085106382978723,0.4845360824742268,98,0.0006885999999999999,12.992894942857143,12.992512399999999,12.993201 +0.05084745762711865,0.9833333333333333,60,0.0004718,13.0267158425,13.026465949999999,13.02693775 +0.05555555555555555,1.0,72,0.0007415999999999999,13.038267793055555,13.03788165,13.038623249999999 +0.058823529411764705,0.9807692307692307,52,0.00047044999999999997,13.04521085576923,13.044983949999999,13.045454399999999 +0.5384615384615384,0.49056603773584906,53,0.0004284,13.054547217924528,13.0543394,13.054767799999999 +0.4727272727272727,0.5,110,0.0007165499999999999,13.062094332727272,13.0617383,13.06245485 +0.5964912280701754,0.4418604651162791,129,0.0008849,13.0906807624031,13.090220599999999,13.0911055 +0.7272727272727273,0.3333333333333333,66,0.0006514,13.09988996590909,13.0995683,13.1002197 +0.22580645161290322,0.7322834645669292,127,0.0010378,13.150007456299212,13.1495506,13.1505884 +0.7142857142857143,0.058333333333333334,120,0.0006177499999999999,13.190181842083334,13.189872,13.19048975 +0.875,0.053691275167785234,149,0.0011038999999999999,13.206508762751676,13.20593995,13.20704385 +0.15873015873015872,0.36,175,0.0014730499999999998,13.250601355999999,13.24991085,13.251383899999999 +0.6708860759493671,0.46745562130177515,169,0.00107985,13.60216772781065,13.601650399999999,13.602730249999999 +0.8888888888888888,0.0661764705882353,136,0.000874,13.677039117647057,13.6766094,13.6774834 +0.5,0.4788732394366197,71,0.00048205,13.722744069014084,13.7224753,13.72295735 +0.4166666666666667,0.06349206349206349,189,0.00136965,13.767985580158731,13.76736395,13.7687336 +0.75,0.0380952380952381,105,0.0005845,13.872680656190475,13.87240495,13.872989449999999 +0.9411764705882353,0.05167173252279635,329,0.0019599,13.897083617629178,13.8960286,13.897988499999999 +0.625,0.08080808080808081,99,0.00082365,13.999341789898988,13.99888945,13.9997131 +0.6923076923076923,0.15294117647058825,85,0.00061315,14.002458741764706,14.0021291,14.002742249999999 +1.0,0.011764705882352941,85,0.0006313499999999999,14.006426914117645,14.006082249999999,14.0067136 +1.0,0.06521739130434782,138,0.0010398,14.013328790942028,14.012822949999999,14.01386275 +1.0,0.017241379310344827,58,0.00047819999999999997,14.015705800862067,14.01547205,14.01595025 +0.9090909090909091,0.15492957746478872,71,0.00048219999999999996,14.01639859084507,14.0161562,14.0166384 +0.8,0.18,250,0.0015446499999999998,14.087019919800001,14.086300549999999,14.087845199999999 +0.8,0.04716981132075472,106,0.0009241999999999999,14.08838916226415,14.0878714,14.0887956 +0.08620689655172414,0.9133858267716536,127,0.0010842999999999998,14.106695053543307,14.106107549999999,14.10719185 +0.8108108108108109,0.39361702127659576,94,0.00093785,14.152528567021276,14.152027149999999,14.152965 +0.8571428571428571,0.04666666666666667,150,0.00108725,14.203926296,14.203421899999999,14.20450915 +0.6987951807228916,0.44385026737967914,187,0.00162065,14.261682405080213,14.26084315,14.262463799999999 +0.9130434782608695,0.3382352941176471,68,0.0005385499999999999,14.264156296323529,14.263904949999999,14.264443499999999 +0.7777777777777778,0.4897959183673469,147,0.0007465999999999999,14.274723264285713,14.27438505,14.275131649999999 +0.6811594202898551,0.5111111111111111,136,0.00089525,14.276753213970588,14.2762867,14.27718195 +0.5,0.5396825396825397,63,0.00038595,14.40789277301587,14.4076577,14.40804365 +0.7142857142857143,0.2441860465116279,86,0.00049345,14.436269020348837,14.4360003,14.436493749999999 +0.13114754098360656,0.9838709677419355,63,0.00041014999999999997,14.440516981746033,14.440323549999999,14.4407337 +0.07142857142857142,0.9882352941176471,85,0.0004937,14.460952346470588,14.460703899999999,14.4611976 +1.0,0.0449438202247191,89,0.00090005,14.627132878089887,14.6266965,14.62759655 +0.8518518518518519,0.21951219512195122,123,0.00080405,14.694358022357722,14.694014699999999,14.69481875 +0.8333333333333334,0.096,125,0.0007902499999999999,14.7174692776,14.717083299999999,14.71787355 +0.65625,0.20253164556962025,158,0.00106815,14.71877033449367,14.718207249999999,14.719275399999999 +0.07894736842105263,0.8636363636363636,88,0.0009975,14.776283926136362,14.7757863,14.776783799999999 +0.2037037037037037,0.7397260273972602,73,0.0005559,14.858226323972604,14.85795405,14.85850995 +0.19318181818181818,0.7719298245614035,114,0.0008198999999999999,14.952070363596489,14.9517095,14.9525294 +0.13559322033898305,1.0,59,0.000538,14.961240575423728,14.96097095,14.961508949999999 +0.23076923076923078,0.3170731707317073,123,0.0008719999999999999,15.44384804512195,15.443384049999999,15.44425605 +0.8,0.054945054945054944,91,0.0007067,15.444753533516483,15.4443643,15.445070999999999 +1.0,0.05357142857142857,168,0.00111255,15.454479231547618,15.45388535,15.454997899999999 +0.8888888888888888,0.060810810810810814,148,0.0011385,15.56978799189189,15.5692373,15.570375799999999 +1.0,0.11428571428571428,70,0.0007166,15.602717793571427,15.6024438,15.6031604 +0.3333333333333333,0.04838709677419355,63,0.0006297,15.606714455555554,15.606415649999999,15.60704535 +0.8333333333333334,0.1111111111111111,54,0.00038455,15.615194087037034,15.61502595,15.6154105 +NaN,0.0,77,0.0006717499999999999,15.631646105844155,15.63134865,15.6320204 +0.8,0.052083333333333336,96,0.00071115,15.662920921875,15.66256525,15.663276399999999 +0.6417910447761194,0.44666666666666666,300,0.00162115,15.665453291333332,15.6647643,15.66638545 +0.3333333333333333,0.047619047619047616,63,0.00053515,15.666786669047617,15.666497349999998,15.6670325 +0.8461538461538461,0.25,52,0.0004103,15.695242732692307,15.6950174,15.6954277 +0.6923076923076923,0.4727272727272727,55,0.0005285,15.739549417272729,15.73930365,15.73983215 +0.8125,0.4824120603015075,199,0.0009527,16.134559955778894,16.134113799999998,16.1350665 +0.8083832335329342,0.5186335403726708,323,0.0013298,16.139658063003097,16.13898445,16.14031425 +0.803030303030303,0.499054820415879,530,0.0021861999999999997,16.240073993962262,16.238916,16.2411022 +0.95,0.08403361344537816,238,0.00105755,16.283939933613446,16.283420749999998,16.2844783 +0.6875,0.11940298507462686,134,0.0009073499999999999,16.404629352985076,16.40420325,16.4051106 +0.5625,0.14285714285714285,112,0.00064045,16.40588809330357,16.40555585,16.406196299999998 +1.0,0.06422018348623854,110,0.00095345,16.746073226818183,16.745599549999998,16.746553 +0.09333333333333334,0.872093023255814,86,0.00063395,16.81381724244186,16.8134825,16.81411645 +0.06802721088435375,1.0,147,0.0012672,16.892681432312926,16.892072849999998,16.89334005 +1.0,0.109375,64,0.00049555,16.924968803125,16.92471955,16.9252151 +0.7692307692307693,0.07784431137724551,167,0.0013040999999999999,17.23083443682635,17.230215599999998,17.2315197 +1.0,0.09090909090909091,55,0.00035434999999999997,17.665658813636362,17.6655045,17.66585885 +0.8072289156626506,0.48823529411764705,170,0.0010777,17.837593074117645,17.83703735,17.83811505 +0.6363636363636364,0.11827956989247312,93,0.00068565,18.189395409677417,18.189067299999998,18.18975295 +0.6190476190476191,0.4666666666666667,91,0.0007791499999999999,18.296013336813186,18.295592499999998,18.296371649999998 +1.0,0.02127659574468085,94,0.0007902,18.31494584148936,18.314587799999998,18.315378 +0.625,0.07272727272727272,111,0.0007635,18.315963833333335,18.315605299999998,18.3163688 +0.6666666666666666,0.047619047619047616,63,0.00068355,18.3304821484127,18.330166,18.33084955 +0.5604395604395604,0.4945652173913043,184,0.0008451999999999999,18.395585079347825,18.39517135,18.39601655 +0.8651685393258427,0.3423076923076923,260,0.0017096499999999998,18.487164795576923,18.4863295,18.48803915 +1.0,0.1206896551724138,175,0.0008455499999999999,18.649642532571427,18.6492355,18.65008105 +0.75,0.05333333333333334,76,0.0005206,18.744590250657893,18.74435345,18.74487405 +0.056338028169014086,0.9726027397260274,73,0.0006143,18.915261780821915,18.9149581,18.9155724 +0.625,0.08247422680412371,97,0.0007320499999999999,19.13401746958763,19.1336385,19.13437055 +0.75,0.07017543859649122,57,0.00043355,19.134947564035087,19.1347169,19.135150449999998 +0.42857142857142855,0.058823529411764705,119,0.0007364,19.14667518697479,19.146298249999997,19.14703465 +0.6,0.09615384615384616,156,0.0010272,19.179821229166667,19.179355599999997,19.1803828 +0.6285714285714286,0.5555555555555556,63,0.00051705,19.191747026190477,19.1915184,19.19203545 +1.0,0.05084745762711865,59,0.00045684999999999996,19.210037727966103,19.209807599999998,19.21026445 +0.6666666666666666,0.15,60,0.00047444999999999996,19.340518909166665,19.34025045,19.340724899999998 +0.8333333333333334,0.09375,64,0.0005204,19.350664254687498,19.35039265,19.35091305 +0.8,0.4098360655737705,61,0.00043109999999999996,19.419204029508197,19.4189768,19.4194079 +1.0,0.1044776119402985,67,0.00055735,19.515822913432835,19.51558275,19.516140099999998 +0.057971014492753624,0.6388888888888888,109,0.0009324499999999999,19.804609594036698,19.80417835,19.805110799999998 +0.6538461538461539,0.5416666666666666,96,0.00066075,20.004055510416666,20.0037301,20.00439085 +0.5384615384615384,0.08666666666666667,150,0.0013036,20.10540347,20.10479925,20.10610285 +0.38095238095238093,0.30434782608695654,69,0.0004128,20.166473334057972,20.1662459,20.1666587 +0.5714285714285714,0.06306306306306306,222,0.00160995,20.170558234234235,20.169767699999998,20.17137765 +0.7222222222222222,0.15,120,0.0010125,20.24494647958333,20.24449645,20.245508949999998 +0.9230769230769231,0.1262135922330097,103,0.0007519499999999999,20.248860574271845,20.248507,20.249258949999998 +0.7878787878787878,0.3793103448275862,87,0.000785,20.33437073103448,20.333990099999998,20.334775099999998 +1.0,0.06756756756756757,74,0.0006077999999999999,20.717677569594596,20.717339499999998,20.7179473 +0.7142857142857143,0.1206896551724138,58,0.00048709999999999997,20.71864753275862,20.718418,20.7189051 +0.07407407407407407,0.972972972972973,111,0.0009429,21.069128573423423,21.06867315,21.06961605 +0.8421052631578947,0.10857142857142857,175,0.0013613499999999999,21.09604913942857,21.0954031,21.09676445 +0.7804878048780488,0.4939759036144578,83,0.0006313,21.100769293373496,21.10044455,21.101075849999997 +0.7777777777777778,0.12162162162162163,74,0.00042469999999999997,21.106953800675676,21.106690649999997,21.107115349999997 +0.5205479452054794,0.4866666666666667,151,0.0007632,21.449752009602648,21.449384849999998,21.45014805 +0.7368421052631579,0.3333333333333333,57,0.00052095,21.456142300877193,21.45586525,21.4563862 +0.9375,0.1322314049586777,121,0.00094995,21.483318028099173,21.482806999999998,21.48375695 +0.2222222222222222,0.7168141592920354,113,0.0009249,21.514541508849558,21.51411165,21.515036549999998 +0.36585365853658536,0.5125,160,0.001042,21.576709388125,21.5761578,21.5771998 +0.57,0.6211180124223602,163,0.00101965,21.610914222392637,21.6103584,21.61137805 +0.11029411764705882,0.9855072463768116,139,0.00110615,21.65594208417266,21.655362049999997,21.6564682 +1.0,0.037037037037037035,81,0.0006650499999999999,21.792084830246914,21.79168915,21.7923542 +1.0,0.0821917808219178,73,0.00046055,21.87024599246575,21.87001875,21.8704793 +0.6551724137931034,0.38926174496644295,149,0.0007813,21.920073680536913,21.9197039,21.920485199999998 +0.52,0.31645569620253167,79,0.0005064,21.921016498734176,21.920747249999998,21.92125365 +0.0,0.02127659574468085,94,0.00067455,21.990697209574467,21.99036095,21.9910355 +0.5161290322580645,0.5636363636363636,55,0.0004655,22.01959588,22.0193406,22.0198061 +0.75,0.06557377049180328,122,0.000643,22.124256455327867,22.1239687,22.1246117 +0.5116279069767442,0.5512820512820513,78,0.00060825,22.293240762179487,22.2929274,22.29353565 +0.9090909090909091,0.1506849315068493,73,0.0007673,22.408430641780818,22.40804675,22.40881405 +0.06349206349206349,0.984375,64,0.00049225,22.46922604453125,22.469027,22.469519249999998 +0.1016949152542373,0.9516129032258065,63,0.0005447,22.470896418253965,22.4706095,22.471154199999997 +0.09259259259259259,1.0,54,0.00045034999999999997,22.57106448611111,22.5708393,22.57128965 +0.04081632653061224,0.9423076923076923,52,0.00041175,22.57784522596154,22.57761245,22.578024199999998 +0.8,0.5084745762711864,59,0.00035785,22.665973302542373,22.66584905,22.6662069 +0.6857142857142857,0.546875,64,0.0003747,22.68791462421875,22.68774285,22.688117549999998 +0.5,0.45161290322580644,62,0.0005599499999999999,22.690036752419356,22.68975235,22.6903123 +0.7962962962962963,0.46956521739130436,115,0.00075015,22.754080025652172,22.753726099999998,22.75447625 +0.65,0.30303030303030304,66,0.0006255,22.766605912878788,22.76628105,22.766906549999998 +0.05737704918032787,0.976,125,0.0011434,22.7866420712,22.786115199999998,22.787258599999998 +0.061224489795918366,0.98989898989899,99,0.0009651,22.788128310101012,22.78756935,22.78853445 +0.15789473684210525,1.0,57,0.00043975,22.810934016666664,22.8107103,22.81115005 +0.5714285714285714,0.056910569105691054,123,0.00107465,22.901470944715445,22.90098655,22.9020612 +0.875,0.10666666666666667,75,0.0006085,22.916739728,22.91644915,22.91705765 +1.0,0.05,61,0.0005980499999999999,22.941454183606556,22.9411705,22.94176855 +0.8333333333333334,0.09836065573770492,61,0.0006175,22.942232777868853,22.9419359,22.942553399999998 +0.8571428571428571,0.08092485549132948,174,0.0011469499999999999,23.459056948563216,23.45844095,23.4595879 +0.75,0.06936416184971098,174,0.00130245,23.461046023850574,23.4604201,23.461722549999998 +1.0,0.07042253521126761,71,0.00059495,23.462606195774647,23.46228905,23.462884 +0.5,0.038834951456310676,103,0.00072775,23.501303855825242,23.500930099999998,23.501657849999997 +1.0,0.08695652173913043,69,0.0004455,23.50852108043478,23.508288649999997,23.50873415 +0.4,0.054945054945054944,91,0.0008792499999999999,23.630680584615384,23.630256199999998,23.63113545 +0.4482758620689655,0.5523809523809524,105,0.0005784,23.647209776666667,23.6469374,23.647515799999997 +0.7692307692307693,0.13978494623655913,93,0.00073045,23.65795560483871,23.65759485,23.658325299999998 +0.8,0.2631578947368421,172,0.00122835,23.681620136918603,23.6809865,23.682214849999998 +0.43243243243243246,0.33035714285714285,112,0.0007452499999999999,23.685042801785713,23.68463645,23.6853817 +0.6153846153846154,0.1511627906976744,86,0.0006223,23.818074429069764,23.817767699999997,23.818389999999997 +0.07058823529411765,1.0,85,0.0005945,24.010643070588234,24.01029235,24.01088685 +0.08396946564885496,1.0,131,0.0009335,24.0127520740458,24.0123115,24.013244999999998 +1.0,0.08064516129032258,63,0.00052825,24.082898119841268,24.082621449999998,24.0831497 +0.5,0.06779661016949153,59,0.0005122999999999999,24.213779043220338,24.21349095,24.214003249999998 +0.6351351351351351,0.5481481481481482,135,0.0006382499999999999,24.707735455185183,24.707446949999998,24.7080852 +0.6,0.05813953488372093,86,0.00063775,25.277577780813953,25.2772145,25.27785225 +0.6666666666666666,0.08737864077669903,104,0.00077285,25.649441683653844,25.6490945,25.649867349999997 +0.5454545454545454,0.34375,64,0.00047224999999999996,25.981963030468748,25.981692,25.98216425 +1.0,0.05172413793103448,58,0.0006024,25.98398257241379,25.983688949999998,25.98429135 +1.0,0.05660377358490566,53,0.00045674999999999996,25.985421123584903,25.9851691,25.985625849999998 +0.52,0.43103448275862066,58,0.00048649999999999995,26.07682909913793,26.0766082,26.0770947 +0.046153846153846156,0.9848484848484849,66,0.00049675,26.230836623484848,26.2306018,26.23109855 +0.8,0.0949367088607595,158,0.0010921,26.277783725,26.2772231,26.278315199999998 +0.2857142857142857,0.11475409836065574,61,0.00055245,26.299286672950817,26.29900945,26.2995619 +0.11392405063291139,1.0,79,0.00069445,26.343187066455695,26.34286625,26.343560699999998 +0.5169491525423728,0.5042735042735043,234,0.0015271999999999998,26.677178933547008,26.676482699999998,26.6780099 +0.0641025641025641,0.9629629629629629,81,0.00055465,26.89123570802469,26.890972299999998,26.89152695 +0.78125,0.3333333333333333,96,0.0007103,26.92872498958333,26.928415349999998,26.92912565 +0.7777777777777778,0.44171779141104295,163,0.00095155,26.929737767484664,26.9292506,26.93020215 +1.0,0.06451612903225806,93,0.0005681,26.940101052688167,26.939843999999997,26.9404121 +0.7464788732394366,0.5399239543726235,264,0.00164215,27.03973452935606,27.0388073,27.040449449999997 +0.9166666666666666,0.11538461538461539,104,0.00087345,27.11077543173077,27.110340949999998,27.111214399999998 +0.8709677419354839,0.12015503875968993,258,0.0014743999999999998,27.11424779263566,27.1134651,27.1149395 +1.0,0.0410958904109589,73,0.00063045,27.44278112876712,27.442475549999997,27.443106 +0.75,0.41379310344827586,87,0.0006139,27.465040627011494,27.464740449999997,27.46535435 +0.9,0.09174311926605505,109,0.00097025,27.677348207798165,27.6768969,27.677867149999997 +0.4491017964071856,0.6162361623616236,271,0.0013483,27.941582494280443,27.940981949999998,27.942330249999998 +0.75,0.07272727272727272,110,0.00062495,27.945833986363635,27.9455386,27.946163549999998 +1.0,0.125,56,0.0004998,27.94724431875,27.946983,27.9474828 +0.5,0.1,60,0.000486,27.948490689999996,27.94822005,27.94870605 +0.625,0.4528301886792453,53,0.0005061999999999999,28.03073324150943,28.030442599999997,28.030948799999997 +0.07692307692307693,0.9811320754716981,53,0.0005218,28.097374890566037,28.09710255,28.09762435 +0.0392156862745098,0.9807692307692307,52,0.00044385,28.139279282692307,28.13906055,28.1395044 +0.8888888888888888,0.05263157894736842,171,0.0008595499999999999,28.237184254093567,28.2368034,28.237662949999997 +0.08974358974358974,0.9873417721518988,79,0.00060125,28.33258635949367,28.3322841,28.332885349999998 +0.2,0.07142857142857142,70,0.0006778999999999999,28.338092147142856,28.3377987,28.3384766 +0.7164179104477612,0.43506493506493504,154,0.0011809,28.34396026785714,28.343366099999997,28.344547 +0.9230769230769231,0.1566265060240964,83,0.0005492499999999999,28.48255316204819,28.482270699999997,28.48281995 +1.0,0.03759398496240601,133,0.0008498,28.54160526766917,28.54116465,28.54201445 +0.75,0.2033898305084746,59,0.00042415,28.54767657711864,28.547463399999998,28.54788755 +0.9444444444444444,0.10404624277456648,173,0.0010888999999999999,28.585977382080923,28.58548635,28.58657525 +0.8205128205128205,0.23076923076923078,169,0.0013289999999999999,28.60010609556213,28.599399899999998,28.6007289 +0.6405228758169934,0.4135135135135135,371,0.00228275,28.60817474568733,28.6070548,28.60933755 +0.8333333333333334,0.0967741935483871,62,0.00060505,28.66129467177419,28.6609759,28.661580949999998 +1.0,0.05555555555555555,55,0.0005195,28.66253746545454,28.662259249999998,28.662778749999998 +0.8505747126436781,0.46524064171123,187,0.0015784,28.70818773449198,28.707323499999998,28.708901899999997 +0.5864197530864198,0.5934065934065934,273,0.0016501999999999999,28.718040314468865,28.7172411,28.7188913 +0.3958333333333333,0.8571428571428571,280,0.0023295,28.725427005892854,28.724400199999998,28.7267297 +0.7142857142857143,0.1206896551724138,58,0.00049225,28.732067914655172,28.7318313,28.73232355 +0.039473684210526314,0.5757575757575758,133,0.001217,28.765920657894736,28.765379449999998,28.766596449999998 +0.9,0.0847457627118644,118,0.0008364,28.86083575338983,28.86042525,28.86126165 +0.07407407407407407,0.9310344827586207,58,0.0005896499999999999,28.91094575689655,28.9106377,28.911227349999997 +0.27692307692307694,0.28888888888888886,225,0.0018456999999999998,28.913527563111106,28.91252345,28.91436915 +0.5,0.14814814814814814,54,0.00045065,28.91498861574074,28.9147549,28.91520555 +0.6666666666666666,0.09036144578313253,166,0.0009716,28.996619693072287,28.9961113,28.9970829 +0.25,0.06153846153846154,65,0.0005919499999999999,29.004867499230766,29.00458545,29.005177399999997 +1.0,0.012048192771084338,83,0.0006845499999999999,29.03109179819277,29.03073705,29.031421599999998 +0.7931034482758621,0.16292134831460675,178,0.0008736,29.033227008426962,29.032803649999998,29.03367725 +0.8695652173913043,0.10599078341013825,217,0.0009289,29.04086368617511,29.040394449999997,29.04132335 +0.06557377049180328,1.0,61,0.00048515,29.19517649672131,29.1949367,29.19542185 +0.09090909090909091,0.9850746268656716,67,0.0005256,29.235122301492535,29.23485465,29.23538025 +0.6666666666666666,0.10714285714285714,56,0.0004539,29.252178574107138,29.251989899999998,29.252443799999998 +0.6666666666666666,0.15384615384615385,78,0.0005764499999999999,29.25846366923077,29.25814775,29.2587242 +0.6571428571428571,0.5223880597014925,67,0.000605,29.259698266417907,29.259373999999998,29.259978999999998 +0.625,0.41025641025641024,78,0.0005064,29.262143039743588,29.26189205,29.26239845 +0.8214285714285714,0.09824561403508772,285,0.00201995,29.348486917017542,29.3474754,29.349495349999998 +0.55,0.12903225806451613,155,0.0008665999999999999,29.35047318032258,29.3500227,29.3508893 +0.75,0.047058823529411764,85,0.00065675,29.4170487317647,29.416729999999998,29.41738675 +1.0,0.09876543209876543,81,0.0006334,29.56769930987654,29.5673529,29.567986299999998 +1.0,0.1092896174863388,184,0.00118455,29.842331993749998,29.8417037,29.842888249999998 +0.5,0.024691358024691357,81,0.00065685,29.875122273456785,29.874806399999997,29.87546325 +0.78,0.5952380952380952,84,0.0007065999999999999,29.973857,29.973473549999998,29.97418015 +1.0,0.015625,64,0.0005522999999999999,30.053058560937497,30.05277785,30.053330149999997 +1.0,0.07692307692307693,52,0.0004541,30.055548509615384,30.055348499999997,30.0558026 +0.7142857142857143,0.08695652173913043,161,0.001064,30.05711880248447,30.0565477,30.0576117 +0.7777777777777778,0.10714285714285714,84,0.0005718,30.058501135119045,30.058217149999997,30.05878895 +0.7169811320754716,0.39552238805970147,134,0.00083475,30.136227038059697,30.1357985,30.13663325 +0.7903225806451613,0.6019417475728155,103,0.0007174999999999999,30.20831998543689,30.20796425,30.20868175 +0.8013698630136986,0.5069444444444444,288,0.00176075,30.21001773767361,30.2091595,30.210920249999997 +0.8333333333333334,0.27692307692307694,130,0.0012242,30.214913519999996,30.2142418,30.215466 +0.1111111111111111,0.33962264150943394,53,0.00039779999999999997,30.271327587735847,30.27113605,30.271533849999997 +0.06097560975609756,1.0,82,0.0007897,30.431837319512194,30.43146235,30.43225205 +0.13333333333333333,1.0,60,0.0003813,30.557302215833335,30.5570901,30.557471399999997 +0.13157894736842105,0.6785714285714286,56,0.0004749,30.564983169642858,30.564745849999998,30.565220749999998 +0.07344632768361582,0.8762376237623762,202,0.0015106,30.567087679950493,30.56639165,30.56790225 +0.896551724137931,0.3333333333333333,87,0.0005312,30.75808589770115,30.757836049999998,30.75836725 +0.8,0.08928571428571429,56,0.00045654999999999996,30.887785270535712,30.8875638,30.888020349999998 +0.40625,0.4444444444444444,72,0.0006293,30.99994758958333,30.999633099999997,31.000262399999997 +0.75,0.035398230088495575,113,0.0006520499999999999,31.107280958849554,31.106974299999997,31.10762635 +1.0,0.02564102564102564,78,0.00046265,31.110026264102565,31.109762749999998,31.110225399999997 +1.0,0.06550218340611354,229,0.00118275,31.111450778602617,31.11084055,31.112023299999997 +0.625,0.36363636363636365,67,0.00059265,31.153555170895523,31.153247649999997,31.1538403 +0.3939393939393939,0.35294117647058826,187,0.00103305,31.350664244919788,31.350099649999997,31.351132699999997 +NaN,0.0,57,0.0005287499999999999,31.42143487105263,31.42114505,31.421673799999997 +1.0,0.0989010989010989,91,0.0005733,31.483024078021977,31.48274055,31.48331385 +0.8333333333333334,0.1,60,0.0004702,31.492899744166664,31.49265495,31.493125149999997 +1.0,0.12195121951219512,82,0.00044735,31.494304235365853,31.494085249999998,31.4945326 +0.2777777777777778,0.34615384615384615,52,0.00047329999999999996,31.533665120192307,31.5334351,31.533908399999998 +0.7419354838709677,0.5344827586206896,58,0.00040185,31.547186866379306,31.54696525,31.5473671 +0.04838709677419355,1.0,62,0.0004747,31.64470175241935,31.644422449999997,31.64489715 +0.7560975609756098,0.4823529411764706,85,0.00057475,31.724486418823528,31.724185199999997,31.72475995 +0.6,0.14492753623188406,69,0.00074005,31.736273893478263,31.735893049999998,31.7366331 +0.09523809523809523,0.3559322033898305,59,0.0004392,31.737139455084744,31.7369219,31.737361099999998 +0.6111111111111112,0.1935483870967742,93,0.0005794,31.738917426881716,31.7386292,31.739208599999998 +0.0625,0.8,60,0.0003881,31.82156267,31.821348299999997,31.8217364 +0.25,0.07017543859649122,57,0.00046239999999999996,31.822780507894734,31.8225271,31.8229895 +0.6966292134831461,0.43842364532019706,203,0.00095165,31.936376934236453,31.9358568,31.936808449999997 +0.7358490566037735,0.4732142857142857,112,0.00066285,31.939887916517858,31.939541549999998,31.9402044 +0.8235294117647058,0.2698412698412698,63,0.00055395,32.749195739682534,32.748891449999995,32.7494454 +0.05263157894736842,1.0,95,0.0006711999999999999,32.79559759263158,32.795255749999995,32.79592695 +0.06086956521739131,0.9913793103448276,116,0.00097765,32.80103867543104,32.80060645,32.8015841 +0.1348314606741573,0.9888888888888889,90,0.0007155999999999999,33.179163745,33.178790899999996,33.179506499999995 +0.08333333333333333,0.9863013698630136,73,0.0007270499999999999,33.193667843150685,33.1932875,33.19401455 +0.09090909090909091,0.9482758620689655,58,0.0004073,33.19504135862069,33.19487495,33.19528225 +0.8032786885245902,0.5922330097087378,103,0.0006780499999999999,33.30621154708738,33.3058791,33.306557149999996 +0.7058823529411765,0.06415094339622641,266,0.00232945,33.39347783233083,33.3922166,33.394546049999995 +0.5365853658536586,0.17446808510638298,237,0.00116815,33.60789826919831,33.607273049999996,33.6084412 +0.7769784172661871,0.3850415512465374,361,0.0017989999999999998,33.82747740166205,33.8265099,33.828308899999996 +0.8032786885245902,0.46923076923076923,390,0.0016542999999999998,33.83651996769231,33.835716999999995,33.8373713 +0.4383561643835616,0.7448979591836735,98,0.0007494499999999999,33.83996354081632,33.8395856,33.84033505 +0.6666666666666666,0.64,75,0.00050585,33.84777755933333,33.84752605,33.848031899999995 +0.7777777777777778,0.47368421052631576,95,0.00058055,33.887765874736836,33.88746605,33.888046599999996 +0.19117647058823528,0.7816091954022989,87,0.0004985,33.89081866436782,33.8905719,33.8910704 +0.11475409836065574,0.9838709677419355,62,0.00062835,33.91570385080645,33.9154225,33.91605085 +0.8181818181818182,0.0967741935483871,341,0.00287115,33.934815856304986,33.9334486,33.936319749999996 +0.5777777777777777,0.5487804878048781,82,0.00054915,34.005169019512195,34.0048828,34.00543195 +0.08333333333333333,0.9896907216494846,97,0.0006485999999999999,34.09572504742268,34.0954203,34.0960689 +0.08974358974358974,1.0,78,0.00058165,34.096497490384614,34.09618655,34.0967682 +0.7659574468085106,0.44339622641509435,106,0.0006985,34.2611264495283,34.2608041,34.2615026 +0.07142857142857142,0.2028985507246377,69,0.00050375,34.418649172463766,34.4184319,34.41893565 +0.3333333333333333,0.07692307692307693,78,0.0007616,34.45033814102564,34.44994545,34.45070705 +0.8333333333333334,0.10112359550561797,178,0.0012005499999999999,34.658871869662924,34.658268899999996,34.659469449999996 +1.0,0.09722222222222222,72,0.00053575,34.74162424861111,34.7413745,34.74191025 +0.88,0.36231884057971014,69,0.000452,34.75159598333333,34.75135605,34.75180805 +0.5263157894736842,0.5757575757575758,132,0.000767,34.75641472689394,34.75601775,34.75678475 +0.4873417721518987,0.5284280936454849,299,0.0022519,34.880205630100335,34.879148799999996,34.8814007 +0.5416666666666666,0.44813278008298757,483,0.00197855,34.890873284886126,34.88997525,34.891953799999996 +0.38095238095238093,0.40384615384615385,52,0.0004682,34.89494310576923,34.894706299999996,34.895174499999996 +0.038461538461538464,0.8387096774193549,93,0.0008726999999999999,34.89756319462365,34.8971298,34.8980025 +0.34782608695652173,0.17293233082706766,134,0.0012879999999999999,34.89889645634328,34.89820055,34.89948855 +0.6111111111111112,0.08490566037735849,212,0.00121655,35.0271817,35.0265572,35.02777375 +0.10679611650485436,0.9809523809523809,105,0.0006512499999999999,35.05883710571428,35.0585373,35.05918855 +0.47619047619047616,0.5490196078431373,153,0.0010025,35.07554248039216,35.0750198,35.0760223 +0.7307692307692307,0.2184873949579832,119,0.00105295,35.20018724957983,35.19965015,35.2007031 +0.11267605633802817,0.9861111111111112,72,0.0005695,35.37169085069444,35.3714057,35.3719752 +0.04,1.0,75,0.0006248499999999999,35.379740916,35.3794176,35.38004245 +0.018867924528301886,0.6708860759493671,79,0.00067015,35.4719030056962,35.471565999999996,35.47223615 +0.1111111111111111,0.984375,128,0.0008948999999999999,35.488909011328126,35.48849815,35.48939305 +0.6610169491525424,0.28095238095238095,210,0.0012389999999999999,35.561017766666666,35.560354499999995,35.5615935 +0.2916666666666667,0.16901408450704225,142,0.001168,35.566234656690135,35.5656362,35.5668042 +0.631578947368421,0.20652173913043478,184,0.0010965999999999999,35.787903189945645,35.7873441,35.788440699999995 +0.6190476190476191,0.13636363636363635,155,0.0008093999999999999,35.81458463451613,35.8142017,35.8150111 +0.09482758620689655,0.9830508474576272,118,0.0005314499999999999,35.95569447372881,35.955439649999995,35.9559711 +0.046296296296296294,0.9818181818181818,110,0.0009402,36.01949453863636,36.0190082,36.0199484 +0.14666666666666667,0.974025974025974,77,0.00069965,36.29226501233766,36.291917,36.29261665 +0.6119402985074627,0.5982142857142857,112,0.0006118499999999999,36.487004896875,36.48669535,36.4873072 +0.08823529411764706,1.0,137,0.0010320499999999999,36.52864979562043,36.52814865,36.5291807 +0.4117647058823529,0.5379746835443038,158,0.00100355,36.69830042025316,36.69783785,36.6988414 +0.09433962264150944,1.0,53,0.00037695,36.78978985754717,36.7895922,36.78996915 +0.75,0.35714285714285715,56,0.00045759999999999996,37.19247778482143,37.1922638,37.192721399999996 +0.8706896551724138,0.49678800856531047,468,0.002822,37.41274475950854,37.4114572,37.414279199999996 +0.6355140186915887,0.5194174757281553,206,0.00091245,37.5188001407767,37.5183453,37.51925775 +0.6363636363636364,0.5238095238095238,105,0.0006219999999999999,37.519876357142856,37.5195523,37.5201743 +0.78,0.390625,128,0.0008593499999999999,37.54187541601562,37.5414752,37.54233455 +0.6956521739130435,0.20909090909090908,110,0.0008918,37.581490620909086,37.581003249999995,37.58189505 +0.6666666666666666,0.037815126050420166,238,0.00159855,37.61831726743697,37.617387199999996,37.61898575 +1.0,0.07407407407407407,81,0.0004939499999999999,37.62254206851852,37.62227815,37.6227721 +0.2222222222222222,0.10714285714285714,84,0.0004296,37.625552763095236,37.625285399999996,37.625715 +0.14285714285714285,0.3723404255319149,96,0.0006455499999999999,37.683211390625,37.68287685,37.6835224 +0.3333333333333333,0.22340425531914893,94,0.0006589,37.77738220106383,37.77701945,37.777678349999995 +0.6811594202898551,0.3812154696132597,181,0.0010424499999999999,37.790356278729284,37.78987635,37.7909188 +0.6071428571428571,0.49469964664310956,283,0.0011838,37.801762546996464,37.801221049999995,37.802404849999995 +0.3333333333333333,0.4011627906976744,172,0.0009683999999999999,37.82785783633721,37.8274346,37.828403 +0.875,0.12121212121212122,66,0.00054385,37.9138015,37.9135165,37.91406035 +0.5925925925925926,0.5094339622641509,106,0.00055525,38.012334509905656,38.01205975,38.012615 +0.6071428571428571,0.5283018867924528,53,0.00045369999999999997,38.28224091509434,38.2820232,38.2824769 +0.5476190476190477,0.5384615384615384,79,0.00048229999999999996,38.33804074240506,38.33777345,38.338255749999995 +0.34782608695652173,0.5798319327731093,119,0.0007061999999999999,38.36128902352941,38.3609122,38.3616184 +0.4117647058823529,0.53125,64,0.00041254999999999997,38.4498652609375,38.44963155,38.4500441 +0.75,0.05517241379310345,145,0.0009414499999999999,38.45079703931034,38.4503178,38.45125925 +0.5686274509803921,0.504950495049505,203,0.0010006499999999998,38.47135838029557,38.470870149999996,38.4718708 +1.0,0.03731343283582089,134,0.0010272,38.50346699888059,38.5028469,38.5038741 +1.0,0.05084745762711865,59,0.00052865,38.69545626186441,38.6951959,38.69572455 +0.8076923076923077,0.37681159420289856,69,0.00046265,38.76046823913043,38.76025255,38.7607152 +0.058823529411764705,0.8947368421052632,115,0.0009161999999999999,38.79571043043478,38.795223549999996,38.796139749999995 +1.0,0.023529411764705882,85,0.00067275,38.86190645294118,38.86152885,38.8622016 +0.7472527472527473,0.5833333333333334,156,0.0008558999999999999,38.925354456410254,38.9249794,38.925835299999996 +0.45454545454545453,0.05045871559633028,218,0.0014715,38.932118222706414,38.931357,38.9328285 +0.625,0.041025641025641026,195,0.00133175,38.93445525794872,38.93379855,38.9351303 +NaN,0.0,54,0.0005415,38.935836250925924,38.935570299999995,38.9361118 +1.0,0.05660377358490566,53,0.00044645,38.943202163207545,38.942951449999995,38.9433979 +0.75,0.057971014492753624,70,0.00044225,38.95605399714285,38.95582015,38.9562624 +0.031578947368421054,0.9895833333333334,96,0.0007752999999999999,39.11292616510417,39.1124753,39.1132506 +0.47586206896551725,0.6904761904761905,210,0.0010098499999999999,39.11517434880952,39.1147863,39.11579615 +1.0,0.04285714285714286,210,0.00125385,39.13105959880952,39.1304817,39.131735549999995 +0.625,0.5970149253731343,67,0.0005155,39.13514074253731,39.1349489,39.1354644 +0.6153846153846154,0.4262295081967213,61,0.0004878,39.13576980245901,39.1355293,39.1360171 +0.8636363636363636,0.2391304347826087,92,0.00081295,39.15325072119565,39.1527883,39.15360125 +0.6666666666666666,0.11538461538461539,79,0.0008610499999999999,39.15653549936709,39.15609715,39.1569582 +0.6666666666666666,0.5567010309278351,97,0.0005363,39.39893715051546,39.398695149999995,39.399231449999995 +1.0,0.042328042328042326,189,0.00111485,39.47635962962963,39.47577895,39.4768938 +0.6774193548387096,0.43661971830985913,71,0.0006231499999999999,39.62222483802817,39.62189545,39.6225186 +0.7956989247311828,0.5224719101123596,358,0.0020912,39.62420345027933,39.623173,39.6252642 +0.6111111111111112,0.43548387096774194,124,0.000888,39.62686834717742,39.6264077,39.6272957 +0.8571428571428571,0.06763285024154589,207,0.00168535,39.65138063285024,39.6505909,39.65227625 +0.5227272727272727,0.14332247557003258,308,0.0013491,39.763196444967534,39.76254515,39.76389425 +0.8,0.5555555555555556,171,0.0009341,39.781526804678364,39.78108415,39.78201825 +0.746031746031746,0.504,125,0.00090325,39.7906736692,39.7902414,39.79114465 +0.7864077669902912,0.515,200,0.0007335,39.8216722665,39.82127235,39.82200585 +0.7662337662337663,0.48427672955974843,159,0.0009234499999999999,39.82470842798742,39.824247899999996,39.82517135 +0.7419354838709677,0.3875,81,0.00039035,39.929162574691354,39.928964,39.92935435 +0.9130434782608695,0.07540983606557378,305,0.0020141,39.94352582491803,39.942407849999995,39.94442195 +0.5,0.08602150537634409,93,0.00067745,39.94549955645161,39.945201999999995,39.94587945 +0.8571428571428571,0.625,56,0.00042045,40.07444802410714,40.074239999999996,40.074660449999996 +0.75,0.4931506849315068,73,0.00042774999999999996,40.13925446369863,40.13903905,40.1394668 +1.0,0.3157894736842105,57,0.00043539999999999996,40.143651591228064,40.14345575,40.143891149999995 +0.8,0.12,125,0.00096015,40.1685558784,40.168041349999996,40.1690015 +0.7272727272727273,0.08870967741935484,124,0.00079085,40.28554854677419,40.285172249999995,40.2859631 +0.66,0.1976284584980237,253,0.0015082499999999998,40.28721653023715,40.2863877,40.28789595 +1.0,0.13513513513513514,111,0.00078995,40.30458843918919,40.3042088,40.304998749999996 +0.056338028169014086,0.9861111111111112,144,0.00091475,40.32104660798611,40.320620649999995,40.321535399999995 +0.11382113821138211,1.0,123,0.0008298,40.35733809430894,40.35693535,40.35776515 +0.7105263157894737,0.5,76,0.00068585,40.41264966315789,40.412315549999995,40.4130014 +0.84,0.32051282051282054,78,0.000847,40.415350491025634,40.414933649999995,40.415780649999995 +0.8333333333333334,0.1875,96,0.00082825,40.41895846302083,40.4185966,40.41942485 +0.11578947368421053,0.979381443298969,97,0.0005899,40.464874168041234,40.4645594,40.4651493 +0.75,0.05,80,0.00077655,40.480520678750004,40.48008995,40.4808665 +1.0,0.078125,64,0.0004678,40.48286184921875,40.482604699999996,40.4830725 +1.0,0.04918032786885246,61,0.00047165,40.4855420262295,40.485254149999996,40.4857258 +0.08264462809917356,1.0,121,0.00092045,40.52833280289256,40.5279313,40.52885175 +0.07936507936507936,1.0,63,0.00047855,40.55993637222222,40.5597005,40.560179049999995 +0.4634146341463415,0.6259541984732825,131,0.0008853499999999999,40.5633860851145,40.56297765,40.563863 +0.32575757575757575,0.7135135135135136,185,0.001483,40.564827722162164,40.564075849999995,40.565558849999995 +0.4666666666666667,0.5294117647058824,85,0.0006692499999999999,40.56929735647059,40.568953799999996,40.56962305 +0.575,0.547945205479452,73,0.0004829,40.59054415479452,40.5903095,40.5907924 +0.06944444444444445,0.96,75,0.0007683,40.65089785666667,40.65049245,40.65126075 +0.07627118644067797,1.0,118,0.0007894,40.82299119322034,40.82258425,40.82337365 +0.11926605504587157,0.9646017699115044,114,0.0007904499999999999,40.94002519649123,40.93960925,40.9403997 +0.09803921568627451,0.9807692307692307,52,0.0005105999999999999,40.98025267115384,40.98003395,40.98054455 +0.24489795918367346,0.6282051282051282,78,0.0006355499999999999,40.9810207525641,40.980700299999995,40.98133585 +0.040983606557377046,0.9606299212598425,127,0.0009216,40.99887625472441,40.9984195,40.999341099999995 +1.0,0.07352941176470588,68,0.0005344999999999999,41.13119334191176,41.13093155,41.13146605 +0.8636363636363636,0.08870967741935484,248,0.00165975,41.134415304637095,41.133539649999996,41.1351994 +0.7741935483870968,0.17318435754189945,179,0.000953,41.13605114189944,41.135580399999995,41.1365334 +0.8,0.189873417721519,79,0.0006674,41.350946193670886,41.3506262,41.3512936 +0.9230769230769231,0.07738095238095238,168,0.0010325,41.42425508065476,41.42377545,41.424807949999995 +1.0,0.057971014492753624,69,0.00042655,41.44078049637681,41.4405656,41.44099215 +0.8205128205128205,0.4642857142857143,84,0.00089315,41.45115973988095,41.45073255,41.4516257 +0.10465116279069768,1.0,86,0.0006257999999999999,41.52804458139534,41.5277585,41.5283843 +0.05454545454545454,0.9649122807017544,114,0.0012997,41.57980621798246,41.5791512,41.580450899999995 +0.6666666666666666,0.046875,64,0.0003519,41.62367450625,41.62347275,41.623824649999996 +0.125,1.0,72,0.0006707999999999999,41.79113704513889,41.7907923,41.7914631 +0.07142857142857142,1.0,56,0.00040314999999999996,41.80134607857143,41.80112475,41.801527899999996 +0.9166666666666666,0.057971014492753624,207,0.00106335,41.98802927439613,41.98742635,41.988489699999995 +0.5,0.06593406593406594,91,0.0005683,42.292178477472525,42.291885799999996,42.2924541 +1.0,0.03076923076923077,65,0.0005668,42.29489761769231,42.29462025,42.295187049999996 +0.6,0.35714285714285715,112,0.0010766999999999999,42.42479543660714,42.424264099999995,42.4253408 +0.46875,0.5161290322580645,125,0.0009973,42.6013377396,42.600866599999996,42.6018639 +0.11594202898550725,0.69,100,0.0008572499999999999,42.603008022000004,42.6025819,42.60343915 +0.10256410256410256,0.975,160,0.0013725999999999999,42.7889604178125,42.78834395,42.78971655 +0.5,0.11320754716981132,53,0.0004346,42.8093071509434,42.809084399999996,42.809518999999995 +0.7142857142857143,0.06930693069306931,101,0.0007660999999999999,42.8134790470297,42.8131007,42.8138668 +0.6538461538461539,0.23008849557522124,113,0.00069265,43.02414490619469,43.023798649999996,43.0244913 +0.625,0.13114754098360656,61,0.0003924,43.165481140163934,43.16530075,43.165693149999996 +0.8421052631578947,0.07480314960629922,254,0.0015268,43.24531123484252,43.244558749999996,43.24608555 +0.8888888888888888,0.08490566037735849,106,0.00061475,43.26388671273585,43.26358525,43.264199999999995 +0.23529411764705882,0.29310344827586204,59,0.00043805,43.390830157627114,43.39059615,43.3910342 +0.6666666666666666,0.05660377358490566,53,0.0005489,43.82710802264151,43.8268134,43.8273623 +0.5591397849462365,0.5054347826086957,184,0.00119985,43.845848741576084,43.8453021,43.84650195 +0.375,0.5423728813559322,59,0.00054985,43.84681063305084,43.8465251,43.84707495 +0.4444444444444444,0.0782608695652174,115,0.0009396,43.982833239130436,43.9824123,43.983351899999995 +0.058333333333333334,1.0,120,0.00088395,44.04637886458333,44.0459713,44.04685525 +0.07575757575757576,1.0,66,0.0005312,44.08016688030303,44.07988985,44.08042105 +0.4189189189189189,0.6981132075471698,106,0.0009557,44.214503480188675,44.214017,44.2149727 +0.7169811320754716,0.5047619047619047,105,0.0007878,44.22811818523809,44.22767315,44.22846095 +0.06756756756756757,0.961038961038961,77,0.00051865,44.23278972467533,44.2325223,44.233040949999996 +0.2247191011235955,0.5632911392405063,158,0.0010086,44.23763273512658,44.237068699999995,44.2380773 +0.6,0.07142857142857142,71,0.00063765,44.29207927253521,44.2917509,44.29238855 +0.8636363636363636,0.09523809523809523,232,0.0008329499999999999,44.29316624310345,44.2926742,44.293507149999996 +0.5753424657534246,0.5509433962264151,265,0.0011136499999999999,44.30866283716981,44.30808005,44.309193699999994 +0.6363636363636364,0.4888888888888889,90,0.0006747999999999999,44.31029261388889,44.30999035,44.31066515 +0.23684210526315788,0.3958333333333333,96,0.00071715,44.3247752609375,44.32442175,44.3251389 +0.75,0.07547169811320754,53,0.00036909999999999997,44.342757112264145,44.34259135,44.34296045 +0.5833333333333334,0.10526315789473684,114,0.0009808,44.3441634495614,44.34365615,44.344636949999995 +0.25,0.06060606060606061,66,0.0005748,44.34690756060606,44.34662695,44.347201749999996 +0.5161290322580645,0.4305555555555556,72,0.00061005,44.63862588055556,44.6383444,44.63895445 +0.5964912280701754,0.3048128342245989,187,0.0012296,44.639739752941175,44.63908995,44.64031955 +0.6774193548387096,0.4246575342465753,74,0.00048699999999999997,44.6901120777027,44.689889449999995,44.690376449999995 +0.6056338028169014,0.5,142,0.0006215999999999999,44.69143850669013,44.691164549999996,44.69178615 +0.7142857142857143,0.06422018348623854,109,0.0008948999999999999,44.69378022568807,44.6932937,44.6941886 +0.8571428571428571,0.08045977011494253,87,0.00053275,44.699835209195406,44.6995406,44.70007335 +1.0,0.09090909090909091,99,0.0006493,44.70205159090909,44.701726449999995,44.702375749999995 +1.0,0.037383177570093455,107,0.0007731999999999999,44.95626396542056,44.955911449999995,44.95668465 +0.616,0.5482456140350878,228,0.00171755,44.98831820964912,44.9874234,44.98914095 +0.5714285714285714,0.5384615384615384,52,0.00047979999999999995,44.99027409423077,44.99003085,44.99051065 +0.5131578947368421,0.44574780058651026,341,0.00163725,45.00575966730205,45.0049497,45.00658695 +0.8636363636363636,0.38596491228070173,57,0.00045835,45.04340023157894,45.0431278,45.043586149999996 +0.07407407407407407,1.0,54,0.0003959,45.0445333,45.04433605,45.04473195 +0.05194805194805195,1.0,77,0.000687,45.046848659740256,45.04652695,45.04721395 +0.75,0.5765765765765766,111,0.000882,45.21407956666667,45.213688499999996,45.2145705 +0.8676470588235294,0.4788732394366197,142,0.00067545,45.23530260915493,45.23497785,45.235653299999996 +0.8888888888888888,0.38571428571428573,70,0.0004473,45.38486450357142,45.38465095,45.38509825 +0.7424242424242424,0.44594594594594594,296,0.0009734499999999999,45.50586859662162,45.505379999999995,45.50635345 +1.0,0.0425531914893617,94,0.0007245999999999999,45.54859909840425,45.5482574,45.548981999999995 +0.8333333333333334,0.1,60,0.0005309,46.027144095,46.0269008,46.0274317 +0.09090909090909091,0.9565217391304348,92,0.00056525,46.513500801630435,46.51323145,46.5137967 +0.375,0.3076923076923077,52,0.00051205,46.56332013557692,46.5630728,46.56358485 +0.8333333333333334,0.061224489795918366,98,0.0007647,46.61452658010204,46.6141545,46.614919199999996 +0.5,0.10144927536231885,138,0.0012288499999999999,46.76122783369565,46.76064385,46.7618727 +0.07446808510638298,0.9894736842105263,95,0.0005396,46.79856017157895,46.798307,46.7988466 +0.84,0.3787878787878788,66,0.00050345,46.831751537121214,46.83148945,46.831992899999996 +0.6176470588235294,0.4,170,0.0010959,46.876617291176466,46.87608445,46.877180349999996 +0.7972027972027972,0.5070921985815603,282,0.001741,46.90852416152482,46.90773005,46.90947105 +0.7857142857142857,0.509090909090909,55,0.00055665,46.947769733636356,46.94746035,46.948017 +0.5909090909090909,0.5569620253164557,79,0.00058305,47.037033524683544,47.03670165,47.0372847 +0.12087912087912088,1.0,92,0.00065865,47.07607476847826,47.075791699999996,47.076450349999995 +0.6341463414634146,0.40594059405940597,101,0.0009664,47.08529378168317,47.08480685,47.085773249999995 +0.15151515151515152,0.9295774647887324,71,0.00072125,47.0863069028169,47.0858998,47.08662105 +0.06,0.9615384615384616,52,0.0004895,47.42519479807692,47.4249295,47.425419 +0.5833333333333334,0.18461538461538463,130,0.0009838,47.459880057307686,47.4594114,47.4603952 +0.875,0.07339449541284404,109,0.0006203,47.48293721513761,47.4826623,47.483282599999995 +0.10144927536231885,0.971830985915493,71,0.00058095,47.56554196056338,47.56527645,47.5658574 +0.8571428571428571,0.12727272727272726,55,0.0006215,47.592050029999996,47.5917422,47.5923637 +0.8484848484848485,0.11418685121107267,289,0.0010182,47.595713602076124,47.595158149999996,47.59617635 +0.625,0.125,64,0.0005003,47.629112415624995,47.62884185,47.62934215 +0.7142857142857143,0.04929577464788732,142,0.0012238499999999999,47.63786470316901,47.637237299999995,47.63846115 +0.7391304347826086,0.1270718232044199,181,0.0013425,47.675228149999995,47.674579,47.6759215 +0.6458333333333334,0.631578947368421,76,0.00053425,47.75346465789473,47.753188449999996,47.7537227 +0.7678571428571429,0.5490196078431373,102,0.0009316999999999999,47.778237355882354,47.7777731,47.7787048 +0.7,0.12422360248447205,161,0.00078455,47.80277335931677,47.802350749999995,47.8031353 +0.7931034482758621,0.46774193548387094,62,0.0003415,47.81818814677419,47.818052949999995,47.81839445 +0.8536585365853658,0.33064516129032256,124,0.0008422999999999999,47.81948519233871,47.819030749999996,47.81987305 +0.6590909090909091,0.5207100591715976,170,0.00127165,47.83046771235294,47.82973595,47.8310076 +0.6486486486486487,0.5211267605633803,73,0.00043424999999999996,47.834950971917806,47.8347421,47.83517635 +0.5620915032679739,0.3347921225382932,457,0.0033927999999999996,47.97063055328227,47.968999249999996,47.972392049999996 +0.8214285714285714,0.27184466019417475,103,0.0007165499999999999,47.97384264174757,47.973490299999995,47.974206849999995 +0.32075471698113206,0.8833333333333333,60,0.0004368,47.996482002499995,47.99626345,47.996700249999996 +1.0,0.07758620689655173,116,0.0007010499999999999,48.26289718793103,48.26256215,48.2632632 +1.0,0.1206896551724138,58,0.0005015999999999999,48.26464216810345,48.264360249999996,48.264861849999996 +0.5,0.06521739130434782,92,0.0005452499999999999,48.29720276521739,48.296958,48.29750325 +0.02857142857142857,0.958904109589041,74,0.00037775,48.32885150135135,48.328640199999995,48.32901795 +0.6666666666666666,0.05660377358490566,53,0.00042754999999999996,48.468521240566034,48.468304849999996,48.4687324 +1.0,0.07462686567164178,67,0.0006552999999999999,48.473708255970145,48.47337775,48.474033049999996 +0.6666666666666666,0.1111111111111111,54,0.0004675,48.60035464259259,48.600142049999995,48.600609549999994 +0.5681818181818182,0.5057471264367817,87,0.00076005,48.68402173563218,48.683644199999996,48.68440425 +0.08695652173913043,1.0,69,0.0005892,48.80562139710145,48.8052883,48.8058775 +0.11510791366906475,0.6150442477876106,226,0.00131205,48.94216639933628,48.9415712,48.94288325 +0.04054054054054054,0.9736842105263158,76,0.0005741,49.181634821052626,49.18135925,49.18193335 +0.5,0.5714285714285714,63,0.00051985,49.3108236579365,49.310544199999995,49.31106405 +0.42105263157894735,0.4578313253012048,83,0.0005526999999999999,49.314923105421684,49.314607849999994,49.315160549999995 +1.0,0.016129032258064516,62,0.000576,49.47192295806451,49.471661399999995,49.4722374 +0.8888888888888888,0.0989010989010989,91,0.0008701,49.47346531923077,49.47298015,49.47385025 +0.5806451612903226,0.4,155,0.0008784,49.67035571451613,49.66988525,49.67076365 +0.6557377049180327,0.4674329501915709,783,0.0021636999999999997,49.97919781321839,49.978075149999995,49.98023885 +0.5609756097560976,0.41836734693877553,98,0.00078365,49.98095071887755,49.980565049999996,49.9813487 +0.8,0.08333333333333333,60,0.0005448499999999999,49.98379293916666,49.983513949999995,49.9840588 +0.625,0.46153846153846156,52,0.00042774999999999996,49.994367377884615,49.99411045,49.9945382 +0.8125,0.09411764705882353,170,0.0012656499999999999,50.02890880735294,50.0282887,50.02955435 +0.88,0.07668711656441718,326,0.0016717,50.03077427392638,50.02994655,50.03161825 +0.8333333333333334,0.075,80,0.0006853,50.032660376875,50.03227525,50.03296055 +0.6666666666666666,0.44594594594594594,74,0.00065785,50.0758213027027,50.075517749999996,50.0761756 +0.12244897959183673,0.9751243781094527,202,0.00163675,50.13920422227723,50.138403499999995,50.14004025 +0.49,0.44642857142857145,224,0.0012491499999999999,50.183770716517856,50.183182949999996,50.184432099999995 +0.09375,0.9014084507042254,71,0.00048659999999999996,50.20442800985916,50.204177349999995,50.20466395 +0.6078431372549019,0.6455696202531646,79,0.00069275,50.26248743037974,50.262142499999996,50.262835249999995 +0.38181818181818183,0.4700854700854701,117,0.0008382499999999999,50.26868732905982,50.2682431,50.26908135 +0.6309523809523809,0.4666666666666667,182,0.0009310999999999999,50.4223489032967,50.4219742,50.4229053 +0.09859154929577464,0.9466666666666667,75,0.0007004499999999999,50.618911977333326,50.6185839,50.61928435 +0.11320754716981132,1.0,53,0.0006188999999999999,50.62104375188679,50.62076605,50.62138495 +0.6923076923076923,0.06806282722513089,191,0.0018479,50.72170274502618,50.7207326,50.7225805 +0.5,0.05405405405405406,74,0.00057575,50.86674484527027,50.8664011,50.86697685 +0.9090909090909091,0.05789473684210526,190,0.00125955,50.91079026736842,50.910072549999995,50.911332099999996 +1.0,0.03597122302158273,139,0.00081335,50.9801058618705,50.97965515,50.9804685 +0.0703125,0.9846153846153847,130,0.0007771,51.24597500038461,51.24556965,51.24634675 +0.11538461538461539,1.0,53,0.00042259999999999997,51.29054175188679,51.29028485,51.29070745 +0.75,0.5063291139240507,79,0.00047454999999999996,51.332185080379745,51.3319568,51.33243135 +0.14545454545454545,0.9821428571428571,56,0.00034925,51.33414036428571,51.3339734,51.33432265 +0.109375,0.9696969696969697,66,0.000488,51.350637621212115,51.35039285,51.350880849999996 +1.0,0.05434782608695652,92,0.0006755999999999999,51.42103885597826,51.42069875,51.42137435 +0.05,0.967741935483871,62,0.00041894999999999996,51.47308576451613,51.4729102,51.47332915 +0.08333333333333333,0.631578947368421,76,0.0007453,51.53742557960526,51.5370481,51.5377934 +0.8571428571428571,0.11864406779661017,59,0.0004971999999999999,51.61434538474576,51.614098399999996,51.614595599999994 +0.4,0.1724137931034483,116,0.001034,51.65491937284482,51.6543852,51.6554192 +0.7816091954022989,0.4416243654822335,197,0.0011979,51.667747480964465,51.667181549999995,51.668379449999996 +0.64,0.4098360655737705,61,0.00031779999999999997,51.676916972131146,51.67670765,51.677025449999995 +0.7727272727272727,0.4230769230769231,52,0.0004607,51.75427638461538,51.75406065,51.75452135 +0.21428571428571427,0.417910447761194,67,0.0003924,51.788629523134325,51.788432549999996,51.78882495 +0.5454545454545454,0.0990990990990991,222,0.0011656499999999998,51.82172850225225,51.821154449999995,51.8223201 +0.7306122448979592,0.4253472222222222,579,0.0023186,51.98830762331606,51.98710535,51.989423949999996 +0.7058823529411765,0.2833333333333333,60,0.0005103999999999999,52.014194253333336,52.0139687,52.014479099999996 +1.0,0.01282051282051282,78,0.00058435,52.086269381410254,52.0859994,52.086583749999996 +1.0,0.05421686746987952,166,0.001031,52.087304040361445,52.08679485,52.087825849999994 +0.6111111111111112,0.5142857142857142,70,0.0005612,52.12050334357143,52.1202275,52.1207887 +0.6363636363636364,0.06832298136645963,161,0.00083645,52.37476145093167,52.37438175,52.3752182 +0.6,0.038461538461538464,130,0.0007846,52.40721559807692,52.406798249999994,52.40758285 +0.46875,0.5714285714285714,57,0.00039989999999999996,52.53359915701754,52.53334465,52.533744549999994 +0.9772727272727273,0.4631578947368421,96,0.0006774,52.543576967708326,52.54323695,52.543914349999994 +0.8857142857142857,0.47297297297297297,74,0.000589,52.55993686418919,52.5596356,52.5602246 +1.0,0.6153846153846154,52,0.00040845,52.562855647115384,52.56264295,52.5630514 +0.5555555555555556,0.061224489795918366,147,0.0010592499999999999,52.565634106122445,52.5651237,52.56618295 +1.0,0.43956043956043955,91,0.00057305,52.578474405494504,52.5782132,52.57878625 +0.03225806451612903,0.9841269841269841,63,0.00060855,52.807056707142856,52.8067393,52.80734785 +0.10869565217391304,1.0,92,0.001003,53.39130736847826,53.390800899999995,53.3918039 +0.5517241379310345,0.5,58,0.00048439999999999996,53.40984283103448,53.40958215,53.410066549999996 +0.5147058823529411,0.5112781954887218,133,0.0010574999999999998,53.41156722894737,53.41103845,53.412095949999994 +0.6666666666666666,0.037037037037037035,81,0.00060675,53.48077565432098,53.480460199999996,53.48106695 +0.8181818181818182,0.07534246575342465,146,0.0010747999999999999,53.48991014965753,53.489409249999994,53.49048405 +0.0625,0.9846153846153847,65,0.00032175,53.70026493384615,53.70014055,53.7004623 +0.7878787878787878,0.532258064516129,62,0.0003302,53.7809667266129,53.780801,53.7811312 +0.8461538461538461,0.43333333333333335,60,0.00050525,53.782037395833335,53.78181575,53.782320999999996 +0.6333333333333333,0.47619047619047616,63,0.0004117,53.802665019841264,53.80246705,53.80287875 +0.6666666666666666,0.05172413793103448,58,0.0004544,54.091086445689655,54.0908716,54.091325999999995 +0.7,0.11235955056179775,90,0.0007132,54.200965342222226,54.20059145,54.20130465 +0.7857142857142857,0.16666666666666666,84,0.0006431,54.43127062797619,54.43097425,54.431617349999996 +0.7142857142857143,0.125,56,0.00028425,54.43499797410714,54.434855649999996,54.435139899999996 +0.52,0.5747126436781609,87,0.0004931499999999999,54.67296782241379,54.672698849999996,54.673192 +0.868421052631579,0.4935064935064935,77,0.00059765,54.7927077525974,54.7924073,54.79300495 +0.08148148148148149,0.9712230215827338,139,0.0009419999999999999,54.81213723273381,54.811701649999996,54.81264365 +0.8181818181818182,0.11224489795918367,98,0.0005557,54.860953065816325,54.8607449,54.8613006 +0.47058823529411764,0.19767441860465115,86,0.0006134,54.927959743604646,54.92764245,54.92825585 +0.057692307692307696,1.0,52,0.0005522,55.12990636153845,55.1296253,55.130177499999995 +0.8888888888888888,0.0782608695652174,461,0.0026993499999999997,55.1983222329718,55.197068699999996,55.199768049999996 +0.5,0.02857142857142857,70,0.00061555,55.246878299285704,55.246550049999996,55.247165599999995 +0.08333333333333333,0.9836065573770492,62,0.00043769999999999996,55.265842957258066,55.265607499999994,55.2660452 +0.7,0.05649717514124294,178,0.0013260499999999998,55.35396051432584,55.3532015,55.35452755 +1.0,0.03333333333333333,120,0.0008084499999999999,55.51878019916666,55.51836915,55.5191776 +0.5581395348837209,0.5375,80,0.0006566499999999999,55.666032735625,55.665717799999996,55.66637445 +0.1694915254237288,0.8676470588235294,68,0.0005595,55.78456370073529,55.7843119,55.7848714 +0.016129032258064516,1.0,62,0.00044259999999999997,55.787068840322576,55.7868862,55.7873288 +0.17647058823529413,0.9066666666666666,75,0.000625,55.81099226133333,55.810677399999996,55.811302399999995 +0.875,0.0761904761904762,105,0.00086935,55.84013474095238,55.8396656,55.84053495 +0.5,0.4675324675324675,77,0.0007371,55.87720673246753,55.8768124,55.8775495 +0.44776119402985076,0.536,125,0.0008167,55.8817823276,55.881343949999994,55.882160649999996 +0.12727272727272726,0.9482758620689655,58,0.00070145,56.03406567672413,56.033679649999996,56.0343811 +0.5454545454545454,0.55,60,0.00046689999999999996,56.04456039416667,56.0443335,56.0448004 +0.7359550561797753,0.572347266881029,311,0.0010138,56.08485181270096,56.0844046,56.085418399999995 +0.06179775280898876,1.0,178,0.00132315,56.10189773483146,56.10118215,56.1025053 +0.12030075187969924,0.9851851851851852,135,0.00073105,56.10321687296296,56.102866899999995,56.103597949999994 +0.6666666666666666,0.045454545454545456,66,0.00044889999999999996,56.10904714242424,56.108822249999996,56.10927115 +1.0,0.11475409836065574,61,0.0004619,56.139764688524586,56.139527449999996,56.13998935 +1.0,0.08771929824561403,57,0.00038344999999999997,56.14056527368421,56.140390499999995,56.140773949999996 +0.5416666666666666,0.41379310344827586,58,0.00047034999999999997,56.21146473103448,56.211225999999996,56.21169635 +0.14754098360655737,0.8714285714285714,70,0.00059625,56.28709575571428,56.2868165,56.287412749999994 +0.04411764705882353,1.0,68,0.0005153,56.388588397794116,56.388329199999994,56.3888445 +0.0972972972972973,0.9840425531914894,189,0.0015294,56.39635691719577,56.395637,56.397166399999996 +0.2682926829268293,0.5616438356164384,73,0.00044955,56.49804488150684,56.497870549999995,56.4983201 +0.6666666666666666,0.11392405063291139,80,0.0006855,56.50350859562499,56.503209649999995,56.50389515 +1.0,0.07272727272727272,55,0.00049345,56.521731690909085,56.5214601,56.52195355 +0.6666666666666666,0.07692307692307693,78,0.0004793,56.566853719871794,56.5666331,56.5671124 +0.7777777777777778,0.15789473684210525,57,0.0005134,56.572307999122806,56.5720527,56.572566099999996 +0.8,0.05102040816326531,98,0.00070595,56.5929139,56.59250375,56.593209699999996 +0.8301886792452831,0.5760869565217391,92,0.0008334,56.69106944130435,56.6906034,56.6914368 +0.057692307692307696,1.0,53,0.0005566,56.7987859235849,56.798537249999995,56.79909385 +0.7368421052631579,0.11242603550295859,169,0.00110105,57.141350366272185,57.14077735,57.141878399999996 +1.0,0.1111111111111111,99,0.00063105,57.17693378989899,57.1766076,57.17723865 +0.7142857142857143,0.05343511450381679,131,0.0010188999999999999,57.17870694312976,57.178226349999996,57.179245249999994 +0.5185185185185185,0.48214285714285715,56,0.0004723,57.185431184821425,57.1851845,57.1856568 +0.3888888888888889,0.5294117647058824,68,0.00052045,57.187817530882356,57.187560749999996,57.1880812 +0.8333333333333334,0.08,76,0.00055485,57.18943339210526,57.189169449999994,57.189724299999995 +0.5,0.10526315789473684,57,0.00045735,57.31834138684211,57.318129799999994,57.31858715 +0.8571428571428571,0.11475409836065574,61,0.00062035,57.32034280819672,57.320082799999994,57.32070315 +0.21052631578947367,0.3584905660377358,107,0.00090555,57.34955194439252,57.349047549999995,57.3499531 +0.5333333333333333,0.46153846153846156,65,0.00062435,57.41722104461539,57.41690525,57.417529599999995 +0.8333333333333334,0.06666666666666667,90,0.0009574999999999999,57.47973943888888,57.47927685,57.480234349999996 +0.8,0.49107142857142855,112,0.00076425,57.48206265714285,57.48165555,57.482419799999995 +0.75,0.0425531914893617,94,0.00097275,57.7476604893617,57.74719345,57.7481662 +0.5,0.07692307692307693,52,0.00050455,57.74889169615384,57.748647899999995,57.74915245 +0.8,0.390625,64,0.00046275,57.803784095312494,57.80356845,57.8040312 +0.48148148148148145,0.38028169014084506,73,0.00052465,57.80712524178082,57.8068673,57.807391949999996 +0.5405405405405406,0.5211267605633803,71,0.00061725,57.83278244577465,57.83245445,57.8330717 +0.8421052631578947,0.5,76,0.0005222499999999999,58.11204495065789,58.1117892,58.11231145 +0.676056338028169,0.5419847328244275,132,0.00062145,58.174667758712125,58.1743416,58.174963049999995 +0.6428571428571429,0.5384615384615384,156,0.00123945,58.25818190416666,58.257507999999994,58.258747449999994 +0.625,0.47761194029850745,67,0.000489,58.26224624328358,58.262028699999995,58.2625177 +0.6666666666666666,0.04054054054054054,74,0.0005802499999999999,58.29619356013514,58.295917599999996,58.296497849999994 +0.42105263157894735,0.57,100,0.0005713,58.341286361499996,58.340999149999995,58.34157045 +0.5561797752808989,0.6716981132075471,265,0.00205005,58.59415568603773,58.593253399999995,58.595303449999996 +0.7804878048780488,0.5189873417721519,79,0.0004889,58.60624401898734,58.606026,58.6065149 +0.8095238095238095,0.5185185185185185,81,0.0005493,58.896335678395054,58.896059949999994,58.89660925 +0.6666666666666666,0.5357142857142857,84,0.0007425,58.89951465,58.899177349999995,58.899919849999996 +0.53125,0.3595505617977528,89,0.0007792,58.90033421910112,58.899984249999996,58.90076345 +0.6666666666666666,0.10344827586206896,59,0.0005131,58.906189290677965,58.905976349999996,58.906489449999995 +0.8,0.03816793893129771,131,0.0007019999999999999,58.931698993129764,58.93138225,58.932084249999996 +1.0,0.1076923076923077,65,0.0005563,58.94949036,58.949181349999996,58.949737649999996 +0.6666666666666666,0.06521739130434782,139,0.00099205,58.95960809352518,58.9591121,58.96010415 +1.0,0.029850746268656716,67,0.0007813999999999999,58.96628854477611,58.96589445,58.966675849999994 +0.8571428571428571,0.109375,64,0.00047484999999999997,58.97001504296875,58.96978215,58.970257 +0.8,0.10273972602739725,146,0.00097215,58.971541177739724,58.971064049999995,58.9720362 +0.0,0.038461538461538464,52,0.0006117,58.97235968076922,58.9720735,58.9726852 +0.75,0.11428571428571428,70,0.0005735,58.97428212642856,58.97395595,58.97452945 +1.0,0.05970149253731343,67,0.00052605,59.019327406716414,59.019049949999996,59.019576 +0.7777777777777778,0.05084745762711865,177,0.0009614,59.34770191638418,59.34722015,59.34818155 +0.7333333333333333,0.06072874493927125,247,0.00212235,59.349741994129545,59.34868985,59.3508122 +0.8,0.07462686567164178,67,0.00038679999999999997,59.38384389776119,59.3836831,59.3840699 +0.5,0.07017543859649122,57,0.00044219999999999996,59.59772711403508,59.5975125,59.597954699999995 +0.6,0.09090909090909091,55,0.0004789,59.65231733272727,59.65207805,59.65255695 +0.8245614035087719,0.4978165938864629,229,0.0016094,59.8250671521834,59.824125249999994,59.825734649999994 +0.4861111111111111,0.3850267379679144,187,0.0014164499999999999,59.82807731978609,59.827356449999996,59.8287729 +0.7555555555555555,0.3515625,128,0.00071915,59.83107717695312,59.83070565,59.8314248 +0.408,0.5341880341880342,235,0.00177255,59.8367135406383,59.83574755,59.8375201 +0.75,0.1,81,0.00049095,59.91594784012346,59.915712649999996,59.916203599999996 +0.8076923076923077,0.36619718309859156,71,0.00061305,59.92599748943661,59.925692749999996,59.926305799999994 +0.08695652173913043,1.0,92,0.00078115,60.2068062076087,60.2064386,60.20721975 +1.0,0.032520325203252036,123,0.00091355,60.24193126463414,60.241498899999996,60.242412449999996 +0.6111111111111112,0.20930232558139536,86,0.00076565,60.52897255174418,60.5285817,60.529347349999995 +NaN,0.0,121,0.0007189,60.621135735537194,60.620809949999995,60.62152885 +1.0,0.03867403314917127,181,0.0009998,60.635393020718226,60.6348348,60.635834599999995 +0.8181818181818182,0.08661417322834646,127,0.0006010999999999999,60.64380397283465,60.643509949999995,60.64411105 +0.9,0.10638297872340426,94,0.0005962,60.64876835159574,60.6484704,60.6490666 +1.0,0.0423728813559322,118,0.0008943499999999999,60.649895758050846,60.64946995,60.6503643 +0.875,0.06557377049180328,123,0.0008489999999999999,60.651492759756096,60.651050899999994,60.6518999 +0.8378378378378378,0.6379310344827587,58,0.0004969999999999999,60.823560213793094,60.823337599999995,60.8238346 +0.5964912280701754,0.4830508474576271,118,0.0005980499999999999,60.82580701355931,60.825488699999994,60.826086749999995 +0.42424242424242425,0.4489795918367347,147,0.00084695,61.20751447585034,61.207081499999994,61.20792845 +0.5714285714285714,0.5185185185185185,54,0.0004149,61.338039620370374,61.337825949999996,61.33824085 +0.09859154929577464,1.0,142,0.0007519499999999999,61.525373838380276,61.525017,61.52576895 +0.0625,0.96,100,0.0008083,61.5300216065,61.529642149999994,61.53045045 +0.9215686274509803,0.4722222222222222,108,0.00075,61.54872025324074,61.54832485,61.54907485 +1.0,0.03333333333333333,60,0.0004615,61.572219267499996,61.57200125,61.57246275 +0.0847457627118644,1.0,60,0.00048185,61.611989278333326,61.61176055,61.6122424 +0.3333333333333333,0.037037037037037035,81,0.0005811999999999999,61.61955257901234,61.619236449999995,61.619817649999995 +0.04697986577181208,0.9867549668874173,151,0.0013541999999999998,61.64870726986755,61.647984699999995,61.6493389 +0.45,0.11049723756906077,181,0.0008464999999999999,61.72115522596685,61.72075785,61.72160435 +0.21875,0.24427480916030533,132,0.0006523,61.72201468674242,61.721642749999994,61.72229505 +0.05084745762711865,1.0,59,0.0005210999999999999,61.72978914915254,61.7295297,61.730050799999994 +0.057692307692307696,1.0,104,0.0008235,61.75297520144231,61.75253375,61.75335725 +0.5185185185185185,0.5192307692307693,52,0.00046265,61.82020072211538,61.819964899999995,61.82042755 +0.7222222222222222,0.1978021978021978,91,0.0008573999999999999,61.84275567747253,61.84233345,61.84319085 +0.43333333333333335,0.5172413793103449,58,0.0004519,61.860103170689655,61.8599274,61.8603793 +0.0136986301369863,1.0,73,0.0005645499999999999,62.008778582876715,62.00848835,62.0090529 +0.8571428571428571,0.1,140,0.0008363499999999999,62.14797819178571,62.147573449999996,62.148409799999996 +0.7142857142857143,0.06306306306306306,111,0.0009142999999999999,62.15463553468468,62.154152249999996,62.155066549999994 +0.6833333333333333,0.631578947368421,96,0.0005133,62.279011628125,62.278775749999994,62.279289049999996 +0.6578947368421053,0.4892703862660944,233,0.00087515,62.3800142332618,62.379643349999995,62.380518499999994 +0.591304347826087,0.47717842323651455,242,0.0010096,62.39009721900826,62.38958175,62.390591349999994 +0.6071428571428571,0.5833333333333334,96,0.0005876,62.392787801041656,62.392484249999995,62.39307185 +0.10891089108910891,0.9901960784313726,102,0.00067055,62.42540809656863,62.425060849999994,62.4257314 +1.0,0.11764705882352941,68,0.00054265,62.80376179044118,62.803538749999994,62.804081399999994 +0.8636363636363636,0.07913669064748201,278,0.00191615,62.95329116708633,62.95247585,62.954392 +0.6666666666666666,0.10344827586206896,58,0.0005291,63.018711320689654,63.01842,63.0189491 +1.0,0.0547945205479452,73,0.0005942,63.07675705479452,63.076505049999994,63.077099249999996 +0.3333333333333333,0.034482758620689655,88,0.0008074,63.23177695511364,63.231409449999994,63.23221685 +0.1111111111111111,0.7627118644067796,59,0.0005256,63.4144985559322,63.4142283,63.414753899999994 +0.13333333333333333,0.9523809523809523,63,0.00061095,63.42655896190476,63.4262301,63.42684105 +0.75,0.12698412698412698,63,0.00053695,63.571066461111116,63.570804149999994,63.5713411 +0.6265060240963856,0.5496688741721855,151,0.0008334,63.590758962582775,63.5903159,63.5911493 +0.75,0.058823529411764705,68,0.0005991999999999999,63.7097790125,63.7094763,63.710075499999995 +1.0,0.04411764705882353,68,0.00055855,63.714613815441176,63.7143904,63.71494895 +0.6595744680851063,0.3700787401574803,127,0.00074805,63.90569837283464,63.9053082,63.90605625 +0.0,0.2857142857142857,56,0.0004563,63.99736133839285,63.997110649999996,63.99756695 +0.11827956989247312,0.96875,96,0.0005614999999999999,64.00585231354167,64.0055612,64.00612269999999 +0.3333333333333333,0.19607843137254902,153,0.0009498999999999999,64.15725963562092,64.15675829999999,64.1577082 +0.6666666666666666,0.08,75,0.0005105999999999999,64.163872122,64.1636182,64.1641288 +0.5588235294117647,0.5396825396825397,63,0.000636,64.26474100396825,64.264437,64.265073 +0.8,0.09615384615384616,52,0.00031539999999999997,64.35360920480768,64.35347715,64.35379255 +0.5882352941176471,0.12142857142857143,280,0.0011208499999999998,64.35663941749999,64.35606204999999,64.3571829 +0.4375,0.43243243243243246,74,0.0005097,64.35848421891892,64.3582129,64.3587226 +0.7777777777777778,0.1111111111111111,81,0.00041359999999999997,64.58271930185185,64.58255285,64.58296645 +0.5276073619631901,0.5,326,0.0017399499999999999,64.6785227773006,64.67767479999999,64.67941474999999 +0.75,0.10810810810810811,149,0.001308,64.74363575201342,64.74295605,64.74426405 +0.7619047619047619,0.14893617021276595,141,0.001003,64.74552113829786,64.74502635,64.74602935 +0.5714285714285714,0.4827586206896552,145,0.0009218999999999999,64.75373482896552,64.7533119,64.7542338 +0.8571428571428571,0.08433734939759036,83,0.0006730999999999999,64.75470096746989,64.75438985,64.75506295 +1.0,0.078125,64,0.00062,65.05600783515625,65.05572149999999,65.0563415 +0.8,0.05555555555555555,90,0.0006075,65.06058949888889,65.06026394999999,65.06087145 +0.7878787878787878,0.44594594594594594,74,0.00037765,65.18577691013513,65.18557315,65.1859508 +0.631578947368421,0.5507246376811594,138,0.00075335,65.5946072021739,65.5942267,65.59498005 +0.8235294117647058,0.21518987341772153,79,0.0005476,65.72155318417721,65.72126879999999,65.7218164 +0.10909090909090909,0.9322033898305084,59,0.00066495,65.8014482279661,65.80107155,65.8017365 +0.734375,0.37209302325581395,172,0.0008572499999999999,65.94665987180232,65.9462259,65.94708315 +0.71900826446281,0.49387755102040815,245,0.0011738999999999999,65.94989970224489,65.94927535,65.95044924999999 +0.5217391304347826,0.39655172413793105,58,0.00041545,65.9572293275862,65.95704185,65.9574573 +0.8,0.06172839506172839,81,0.0008715499999999999,65.95848187345679,65.9580757,65.95894725 +0.7391304347826086,0.4107142857142857,56,0.0004499,65.99647481964286,65.99624109999999,65.996691 +0.72,0.4032258064516129,186,0.0010012,66.13844559112903,66.1379302,66.13893139999999 +0.35714285714285715,0.1728395061728395,82,0.0008056,66.26821599329267,66.2678457,66.2686513 +0.6964285714285714,0.32941176470588235,341,0.00231855,66.27264731217008,66.27130945,66.273628 +0.7096774193548387,0.5486725663716814,113,0.00085085,66.27455417079646,66.2741772,66.27502805 +1.0,0.016129032258064516,62,0.00042485,66.35469808548388,66.35446449999999,66.35488935 +0.75,0.07766990291262135,103,0.00077195,66.43287649805825,66.43250195,66.4332739 +1.0,0.08823529411764706,68,0.00048669999999999996,66.60231741764706,66.60207414999999,66.60256085 +0.09230769230769231,1.0,260,0.002546,66.88764408403846,66.8864309,66.8889769 +0.7049180327868853,0.46923076923076923,130,0.0010569499999999999,66.9164120626923,66.9158496,66.91690655 +0.5789473684210527,0.41304347826086957,92,0.00085485,67.10439940597826,67.10393235,67.10478719999999 +0.7083333333333334,0.5052631578947369,95,0.0006877,67.15002186368422,67.14967639999999,67.15036409999999 +0.19402985074626866,0.7613636363636364,89,0.0006960499999999999,67.3657455011236,67.3654092,67.36610525 +0.5,0.17094017094017094,117,0.0010335,67.76021884273504,67.7597049,67.7607384 +1.0,0.018867924528301886,53,0.000374,67.8750229764151,67.8748477,67.8752217 +0.08108108108108109,0.9652173913043478,115,0.00107185,68.37532942869565,68.37477704999999,68.3758489 +0.07142857142857142,1.0,84,0.00073485,68.38020814523809,68.37984095,68.3805758 +0.42857142857142855,0.49122807017543857,57,0.0004547,68.48585948245614,68.48562989999999,68.4860846 +0.5555555555555556,0.5714285714285714,63,0.00052525,68.48731464841269,68.4870615,68.48758674999999 +0.8,0.09090909090909091,55,0.00047635,68.58961485181818,68.5893778,68.58985415 +0.030303030303030304,1.0,66,0.00053895,68.65161774848485,68.65137349999999,68.65191245 +0.828125,0.5203252032520326,123,0.00058565,69.00513703861789,69.004862,69.00544765 +0.7941176470588235,0.576271186440678,59,0.0005980499999999999,69.00631269745762,69.0060084,69.00660644999999 +0.75,0.06153846153846154,65,0.0005733499999999999,69.06078612538461,69.06051335,69.06108669999999 +0.7741935483870968,0.5714285714285714,217,0.00133325,69.28950003387097,69.28884235,69.2901756 +0.7457627118644068,0.5412844036697247,109,0.00078215,69.32127783348623,69.32091915,69.3217013 +0.7397260273972602,0.33640552995391704,217,0.0011125,69.39532475552996,69.39475784999999,69.39587035 +0.875,0.1038961038961039,77,0.00059505,69.39767205064935,69.39739365,69.3979887 +0.8214285714285714,0.06698564593301436,418,0.0021478499999999998,69.47007151770335,69.46906775,69.4712156 +0.8571428571428571,0.04666666666666667,150,0.00078575,69.475022185,69.4746186,69.47540434999999 +1.0,0.0547945205479452,73,0.0006047499999999999,69.48287937054795,69.48255809999999,69.48316285 +1.0,0.07368421052631578,95,0.0009374499999999999,69.83920838052632,69.8387731,69.83971054999999 +0.6333333333333333,0.4838709677419355,62,0.0005195,69.89175149193548,69.89149125,69.89201075 +0.4897959183673469,0.48756218905472637,201,0.00107285,69.89412114402985,69.8935782,69.89465105 +0.5454545454545454,0.5641025641025641,117,0.0006602,69.90035505940172,69.89997205,69.90063225 +0.8421052631578947,0.07818930041152264,244,0.0015141,69.93883558913934,69.93812725,69.93964135 +0.8,0.04310344827586207,116,0.0008581,69.946702825,69.9463191,69.9471772 +0.75,0.417910447761194,67,0.00061785,70.06034138507462,70.06005284999999,70.0606707 +0.9230769230769231,0.16455696202531644,79,0.0006403499999999999,70.06113456202532,70.06081315,70.0614535 +0.46875,0.49230769230769234,65,0.0004679,70.10478362461538,70.104513,70.1049809 +0.8571428571428571,0.1044776119402985,67,0.00070515,70.2431146858209,70.24277875,70.2434839 +0.8333333333333334,0.11320754716981132,106,0.0008457499999999999,70.26349342830189,70.26303399999999,70.26387975 +1.0,0.015384615384615385,66,0.0005365,70.3247596280303,70.3245208,70.3250573 +0.6,0.06666666666666667,75,0.0005702,70.45191603066665,70.451628,70.4521982 +1.0,0.05555555555555555,72,0.00049875,70.45899321597221,70.45870934999999,70.4592081 +0.75,0.07643312101910828,157,0.0016457,70.48073298503184,70.4799323,70.481578 +0.6956521739130435,0.4423076923076923,52,0.0005032999999999999,70.71971185865384,70.71948474999999,70.71998805 +0.15384615384615385,0.8387096774193549,62,0.0005898499999999999,70.78843789032257,70.7881462,70.78873605 +0.5365853658536586,0.4823529411764706,86,0.0005980499999999999,70.79083280813954,70.79050765,70.7911057 +0.9166666666666666,0.125,96,0.00084185,70.895599471875,70.89517995,70.8960218 +0.6976744186046512,0.4725274725274725,91,0.0006355999999999999,70.97654305769231,70.976218,70.9768536 +0.8,0.22167487684729065,203,0.0014315999999999999,70.97780094852216,70.9771957,70.9786273 +0.5,0.027777777777777776,72,0.0007109999999999999,71.00586392222222,71.00546489999999,71.0061759 +0.6666666666666666,0.03225806451612903,93,0.0005786,71.04673552526882,71.0464659,71.0470445 +1.0,0.11864406779661017,59,0.00037549999999999997,71.04862546779661,71.04845995,71.04883545 +0.5,0.15841584158415842,101,0.0006094,71.3047209569307,71.304434,71.3050434 +0.5555555555555556,0.13043478260869565,69,0.00070395,71.30596984637681,71.30567909999999,71.30638305 +0.5757575757575758,0.4925373134328358,67,0.0003364,71.41070634253731,71.4105749,71.4109113 +0.7419354838709677,0.543859649122807,57,0.0003414,71.42312692017543,71.4229442,71.4232856 +0.5769230769230769,0.24074074074074073,108,0.0007702,71.55786019537037,71.55744185,71.55821205 +0.6666666666666666,0.10344827586206896,58,0.0004707,71.62594041206896,71.62569005,71.62616075 +0.08256880733944955,1.0,109,0.00088005,71.63737166238532,71.6369332,71.63781325 +0.25,0.03669724770642202,109,0.0008355999999999999,71.64292695504587,71.64247064999999,71.64330625 +0.12162162162162163,0.9866666666666667,75,0.0008062999999999999,71.67950513133333,71.67909415,71.67990044999999 +0.07407407407407407,0.9310344827586207,58,0.00051815,71.68143529051724,71.68118414999999,71.6817023 +0.8571428571428571,0.09090909090909091,77,0.0007433,71.70359557142856,71.7031916,71.7039349 +0.07407407407407407,0.9878048780487805,82,0.00071925,71.73323513963415,71.7328728,71.73359205 +0.75,0.5154639175257731,194,0.0010739999999999999,71.73590190128866,71.73534785,71.73642185 +0.12962962962962962,0.9818181818181818,110,0.0007213499999999999,71.81454876772726,71.8141898,71.81491115 +0.8269230769230769,0.23963133640552994,217,0.0016631999999999999,71.83884434884793,71.83801185,71.83967505 +0.8888888888888888,0.3050847457627119,59,0.0006455499999999999,71.84177032711864,71.8414617,71.84210725 +0.052083333333333336,0.9795918367346939,98,0.00070835,71.98282439489796,71.98247795,71.9831863 +0.7297297297297297,0.4111111111111111,90,0.0007341,72.04067700055555,72.0403137,72.0410478 +0.6666666666666666,0.05172413793103448,58,0.00039815,72.04635218017242,72.04616279999999,72.04656095 +0.75,0.05333333333333334,150,0.00127425,72.199093505,72.198452,72.19972625 +0.7272727272727273,0.0990990990990991,111,0.0008211,72.20102221261261,72.200625,72.2014461 +0.4444444444444444,0.20930232558139536,87,0.0006225,72.29063187241378,72.29033435,72.29095685 +0.7428571428571429,0.14767932489451477,238,0.00178495,72.31497007184873,72.31409459999999,72.31587954999999 +0.8148148148148148,0.2621359223300971,103,0.0005371999999999999,72.32548233592232,72.32521295,72.32575014999999 +0.8103448275862069,0.5888324873096447,197,0.0014688,72.40682489720811,72.4061823,72.4076511 +0.5454545454545454,0.1506849315068493,73,0.0005852499999999999,72.59094309999999,72.59066145,72.5912467 +0.8192771084337349,0.5684931506849316,146,0.0009446,72.72003316267123,72.71962984999999,72.72057445 +0.08,1.0,75,0.00072665,72.72245868933332,72.72210625,72.7228329 +0.06756756756756757,0.9487179487179487,78,0.0007425,72.74907065833334,72.74869715,72.74943965 +0.5454545454545454,0.3548387096774194,62,0.00045615,72.84757764919354,72.84734645,72.8478026 +0.6666666666666666,0.4153846153846154,65,0.00045615,72.85095691538461,72.85073824999999,72.8511944 +0.573170731707317,0.6259541984732825,132,0.00057425,72.93127753484849,72.93101505,72.9315893 +1.0,0.03937007874015748,127,0.00078805,73.0930271011811,73.09262995,73.093418 +0.7948717948717948,0.375,104,0.00062095,73.29251099759615,73.29222515,73.29284609999999 +0.10218978102189781,1.0,137,0.0011698499999999998,73.3867885770073,73.38624109999999,73.38741095 +0.06962025316455696,0.9875,160,0.0012590499999999998,73.397470989375,73.3968732,73.39813225 +0.10091743119266056,0.990909090909091,110,0.00124205,73.41527909363636,73.4146578,73.41589985 +0.6590909090909091,0.46808510638297873,94,0.0006589,73.42318992287234,73.42281535,73.42347425 +0.8333333333333334,0.08571428571428572,70,0.00054635,73.50954196571429,73.509297,73.50984335 +0.8333333333333334,0.06896551724137931,87,0.00054755,73.51454926666666,73.51428605,73.5148336 +0.8,0.072992700729927,137,0.0011780999999999999,73.53215799051095,73.5315834,73.53276149999999 +0.17721518987341772,0.8144329896907216,97,0.00071505,73.56428592628866,73.56391835,73.56463339999999 +0.5660377358490566,0.43089430894308944,123,0.0006070499999999999,73.60837385447154,73.6080617,73.60866874999999 +0.7777777777777778,0.10714285714285714,84,0.0005538,73.64339791726191,73.64315429999999,73.6437081 +0.5714285714285714,0.059322033898305086,118,0.0010184,73.66052085211864,73.6599859,73.6610043 +0.7666666666666667,0.4918032786885246,61,0.00069545,73.92133733032786,73.92099005,73.9216855 +0.7454545454545455,0.5,110,0.00086685,73.9260791959091,73.9256711,73.92653795 +0.5,0.023255813953488372,86,0.0007987,73.94724234011628,73.9468732,73.9476719 +0.7083333333333334,0.08955223880597014,268,0.0023596999999999997,73.95120038880597,73.950113,73.9524727 +1.0,0.07058823529411765,85,0.0005586,73.95475249941175,73.95444524999999,73.95500385 +0.6666666666666666,0.0759493670886076,80,0.00050775,73.95821913875,73.9579158,73.95842354999999 +0.3333333333333333,0.3181818181818182,66,0.00052995,74.0340187431818,74.0337555,74.03428545 +0.1111111111111111,0.25961538461538464,104,0.0008684999999999999,74.29327358653846,74.2928352,74.2937037 +0.75,0.05555555555555555,72,0.00064675,74.38385348194444,74.3835735,74.38422025 +0.6333333333333333,0.3333333333333333,90,0.00067525,74.47627471333332,74.47597569999999,74.47665094999999 +1.0,0.07954545454545454,88,0.0006032,74.58371876477273,74.58345924999999,74.58406244999999 +0.11538461538461539,0.8666666666666667,90,0.0007063999999999999,74.72152218111111,74.72114945,74.72185585 +0.07368421052631578,0.8962264150943396,106,0.0009330499999999999,74.72273778113207,74.722313,74.72324605 +0.10135135135135136,0.736318407960199,201,0.0015394,74.72406428930348,74.72330145,74.72484084999999 +0.6666666666666666,0.03409090909090909,88,0.0008874,74.77022432784091,74.7697997,74.7706871 +NaN,0.0,65,0.0004914,74.77313090461539,74.77282805,74.77331945 +0.034482758620689655,0.9666666666666667,60,0.00065485,74.87678679666666,74.8764489,74.87710375 +0.75,0.07407407407407407,54,0.00030685,74.88108714074073,74.88092025,74.88122709999999 +0.8333333333333334,0.08955223880597014,67,0.00059245,74.88269582089552,74.8824187,74.88301115 +0.7368421052631579,0.152,125,0.0010428,74.90687091160001,74.90633869999999,74.9073815 +0.7666666666666667,0.379746835443038,79,0.00052165,74.90834634367087,74.9081078,74.90862944999999 +0.6865671641791045,0.42138364779874216,159,0.0008334,74.90922443144653,74.90876385,74.90959724999999 +0.6,0.11627906976744186,86,0.0007360499999999999,74.9686382732558,74.96829629999999,74.96903234999999 +0.057692307692307696,1.0,52,0.00035959999999999996,74.98377124326923,74.98360625,74.98396584999999 +0.10975609756097561,1.0,82,0.00061675,75.01509696219512,75.01481505,75.0154318 +0.6097560975609756,0.42487046632124353,193,0.0015251,75.01743276839377,75.016624,75.0181491 +0.45454545454545453,0.22297297297297297,148,0.00083185,75.25564972533783,75.25523224999999,75.2560641 +0.06451612903225806,0.43661971830985913,71,0.0007461,75.28017819366197,75.2797574,75.2805035 +0.08527131782945736,0.9772727272727273,133,0.001063,75.28367412180451,75.2831617,75.2842247 +0.08955223880597014,0.9710144927536232,69,0.000646,75.30913024130435,75.30878025,75.30942625 +0.5573770491803278,0.3935483870967742,155,0.0012006,75.41021272903225,75.40955575,75.41075635 +0.10810810810810811,0.9367088607594937,79,0.0007199,75.6208588170886,75.62050239999999,75.6212223 +0.08791208791208792,0.9680851063829787,94,0.0009933,75.6220080787234,75.62146849999999,75.6224618 +0.6666666666666666,0.031578947368421054,95,0.0006853499999999999,75.6952795168421,75.69492925,75.6956146 +0.7692307692307693,0.1111111111111111,117,0.0010056499999999999,75.71281296709401,75.71230324999999,75.7133089 +0.875,0.07017543859649122,115,0.0008876,75.71723344043477,75.71677745,75.71766505 +0.09333333333333334,0.9615384615384616,81,0.00068185,75.76853897530864,75.7681853,75.76886714999999 +0.4827586206896552,0.5,59,0.00030805,75.77768934745762,75.7775537,75.77786175 +0.6037735849056604,0.38686131386861317,137,0.0009394,75.78388058686132,75.7834409,75.7843803 +0.46153846153846156,0.09090909090909091,144,0.0011993,75.79107521041666,75.790482,75.7916813 +0.8333333333333334,0.09375,65,0.0005158,75.82682782538461,75.826574,75.8270898 +0.5,0.030303030303030304,132,0.0007752,75.84642355151514,75.84602319999999,75.8467984 +0.875,0.11594202898550725,69,0.00039995,75.86966576884058,75.86949555,75.8698955 +0.875,0.05194805194805195,154,0.00123175,75.87208692045455,75.87148085,75.8727126 +1.0,0.0875,160,0.00096645,75.873448739375,75.8729322,75.87389865 +0.6046511627906976,0.7166666666666667,181,0.00087685,75.89738529889502,75.89694535,75.8978222 +1.0,0.07692307692307693,65,0.00048794999999999996,76.14366971076923,76.14342285,76.1439108 +0.825,0.28169014084507044,142,0.0010703499999999999,76.17157826584507,76.171035,76.17210535 +0.6666666666666666,0.11278195488721804,133,0.0007371999999999999,76.29348663383459,76.29311419999999,76.2938514 +0.6333333333333333,0.43795620437956206,137,0.0009129499999999999,76.29792969489051,76.29744645,76.2983594 +0.5357142857142857,0.4117647058823529,136,0.00098275,76.33235477169117,76.3318749,76.33285765 +0.7941176470588235,0.5483870967741935,62,0.00054495,76.38727941129032,76.3869825,76.38752745 +0.14754098360655737,0.9838709677419355,62,0.0005558,76.41371745645162,76.41342145,76.41397725 +0.06349206349206349,0.984375,64,0.00040909999999999997,76.44535196484375,76.44512594999999,76.44553504999999 +0.875,0.14545454545454545,55,0.0004008,76.45073117545454,76.45052835,76.45092915 +0.7272727272727273,0.5555555555555556,99,0.0006925,76.58014678585857,76.5798149,76.5805074 +0.5681818181818182,0.4536082474226804,97,0.0006655999999999999,76.58471490979382,76.5843691,76.5850347 +0.6422018348623854,0.4052044609665427,269,0.0011183,76.6721588048327,76.67150455,76.67262285 +0.7333333333333333,0.436046511627907,172,0.0012311,76.7308551369186,76.73035014999999,76.73158124999999 +1.0,0.11764705882352941,68,0.0004941,76.7707686382353,76.77050849999999,76.7710026 +0.5555555555555556,0.08653846153846154,104,0.0007696,76.78280327836538,76.78241405,76.78318365 +0.5,0.14545454545454545,55,0.00039234999999999997,77.24441708545454,77.24422145,77.2446138 +0.7391304347826086,0.5433070866141733,127,0.0009285,77.26084277283465,77.2603786,77.2613071 +0.6102941176470589,0.46735395189003437,291,0.0011179999999999999,77.26249736082474,77.26194095,77.26305895 +1.0,0.05063291139240506,79,0.0006811,77.26676224556962,77.26637025,77.26705135 +0.29411764705882354,0.3269230769230769,52,0.00027604999999999996,77.28088586826922,77.28073069999999,77.28100675 +0.09259259259259259,0.9818181818181818,55,0.00039395,77.33310330909092,77.33291535,77.3333093 +NaN,0.0,73,0.0007739499999999999,77.40833406438357,77.40797305,77.40874699999999 +0.5882352941176471,0.3695652173913043,92,0.00064005,77.41044893369565,77.41009509999999,77.41073515 +0.7142857142857143,0.04713804713804714,297,0.00150775,77.46182646649831,77.4610601,77.46256785 +0.9534883720930233,0.5443037974683544,158,0.00108475,77.47758945632911,77.47706744999999,77.4781522 +0.5454545454545454,0.11702127659574468,188,0.0012973499999999998,77.70920580079786,77.70858754999999,77.70988489999999 +0.27906976744186046,0.2097560975609756,205,0.0019278,77.71113689804878,77.71015835,77.71208614999999 +0.875,0.06201550387596899,129,0.0006979,77.76194972325581,77.76164545,77.76234335 +1.0,0.034722222222222224,144,0.0008342,77.76625310069444,77.76581055,77.76664475 +0.55,0.37037037037037035,54,0.0004306,77.85351128796296,77.8533118,77.8537424 +0.5833333333333334,0.46511627906976744,129,0.0006144,77.89576838023255,77.89541485,77.89602925 +0.6666666666666666,0.05454545454545454,55,0.00040849999999999995,77.90911598636363,77.908883,77.9092915 +0.8888888888888888,0.06521739130434782,138,0.0009092999999999999,77.91008806847826,77.9096189,77.9105282 +1.0,0.11607142857142858,112,0.00071395,77.94004291071428,77.93968484999999,77.9403988 +1.0,0.09859154929577464,71,0.00055955,77.94104803309858,77.9408061,77.94136565 +0.4897959183673469,0.44954128440366975,109,0.00085155,77.94981218486238,77.9493545,77.95020604999999 +0.7142857142857143,0.1,71,0.0007003,77.96549641267606,77.96514595,77.96584625 +0.12142857142857143,0.9210526315789473,152,0.0011599499999999999,78.00277686118422,78.00221075,78.00337069999999 +0.24,0.23809523809523808,105,0.00102335,78.05522529523809,78.05472735,78.05575069999999 +0.3684210526315789,0.2111111111111111,90,0.0008361,78.05775093,78.05732814999999,78.05816424999999 +0.35294117647058826,0.30357142857142855,56,0.00046814999999999997,78.07808482857142,78.0778505,78.07831865 +0.8040540540540541,0.5,296,0.0012567,78.24812926587838,78.247511,78.2487677 +0.75,0.037383177570093455,107,0.00087015,78.44694437289719,78.44652105,78.4473912 +0.0962962962962963,0.9782608695652174,139,0.00131775,78.50406564568345,78.50343445,78.5047522 +0.8116883116883117,0.49517684887459806,311,0.0015592,78.53035445434084,78.52957545,78.53113465 +0.4594594594594595,0.5648854961832062,131,0.0009578999999999999,78.63795498396946,78.63750639999999,78.6384643 +0.08536585365853659,0.9761904761904762,85,0.0007771,78.65322026411765,78.65285435,78.65363144999999 +0.7352941176470589,0.07925407925407925,431,0.0025475999999999997,78.73888858665893,78.7377832,78.7403308 +0.8285714285714286,0.5645161290322581,62,0.00043474999999999997,78.80293156048387,78.80272844999999,78.8031632 +0.3181818181818182,0.25882352941176473,85,0.0006198999999999999,79.00216316235294,79.00185855,79.00247845 +0.8461538461538461,0.06951871657754011,187,0.0011565,79.00336899224598,79.00285865,79.00401515 +1.0,0.1005586592178771,179,0.00105995,79.00492667681564,79.0043196,79.00537955 +0.75,0.09876543209876543,81,0.0005859,79.01941499753086,79.0191222,79.0197081 +0.5,0.07142857142857142,56,0.0004193,79.16909939017857,79.16888535,79.16930465 +0.6666666666666666,0.16981132075471697,53,0.0004892,79.22142937830188,79.22118594999999,79.22167515 +0.8076923076923077,0.3611111111111111,144,0.0007936499999999999,79.27782706736112,79.27743674999999,79.2782304 +0.81,0.423728813559322,236,0.001493,79.29162718114407,79.29088525,79.29237825 +0.8333333333333334,0.06521739130434782,276,0.0014217,79.2940436606884,79.2933778,79.2947995 +1.0,0.07407407407407407,108,0.00069545,79.49856591574074,79.49817605,79.49887149999999 +0.8787878787878788,0.5789473684210527,114,0.0003449,79.55040438245614,79.55020085,79.55054575 +0.625,0.5333333333333333,105,0.0004275,79.59391505238095,79.59370955,79.59413705 +0.6142857142857143,0.5263157894736842,134,0.0008068999999999999,79.75216562686568,79.75178235,79.75258925 +1.0,0.06015037593984962,133,0.0008820999999999999,79.82220951541353,79.8217944,79.8226765 +0.5,0.07547169811320754,53,0.00046154999999999997,79.84773075377358,79.84750005,79.84796159999999 +1.0,0.06666666666666667,121,0.0008514,79.85152173016529,79.8511439,79.8519953 +0.03571428571428571,1.0,56,0.0003649,79.93836667767857,79.9381543,79.9385192 +0.1864406779661017,0.44029850746268656,134,0.00125215,79.94554507350746,79.94487989999999,79.94613205 +0.8048780487804879,0.43157894736842106,96,0.00059315,79.9507908171875,79.95046289999999,79.95105604999999 +0.49206349206349204,0.5779816513761468,327,0.001927,80.12107593914372,80.1201109,80.1220379 +0.7692307692307693,0.11711711711711711,111,0.0006992999999999999,80.34001814504504,80.3396819,80.3403812 +0.75,0.0975609756097561,82,0.0004938,80.36405101585365,80.36382755,80.36432135 +0.6,0.0970873786407767,103,0.0010525999999999999,80.36594516116504,80.36537315,80.36642574999999 +0.6388888888888888,0.5142857142857142,70,0.00058105,80.37514421142856,80.37488404999999,80.3754651 +0.7666666666666667,0.43478260869565216,69,0.0003696,80.47491098985508,80.47475075,80.47512035 +0.8888888888888888,0.08333333333333333,216,0.0012240999999999999,80.54855760949074,80.54787125,80.54909535 +0.6923076923076923,0.24528301886792453,159,0.0011780999999999999,80.58481202672955,80.5842073,80.58538539999999 +0.8148148148148148,0.34615384615384615,78,0.00053275,80.67727724102564,80.67703725,80.67757 +0.041666666666666664,1.0,72,0.00045904999999999996,80.70553619305555,80.70530035,80.70575939999999 +0.8571428571428571,0.06572769953051644,213,0.00152105,80.72717053521126,80.72645245,80.72797349999999 +1.0,0.0423728813559322,118,0.0007816499999999999,80.72960147542372,80.72925364999999,80.7300353 +0.05,0.975609756097561,82,0.00072405,80.77976067804877,80.77945305,80.7801771 +0.7777777777777778,0.16666666666666666,54,0.0004875,80.82553359166666,80.82529635,80.82578385 +0.48,0.6666666666666666,75,0.0005806499999999999,80.91932060399999,80.91901374999999,80.9195944 +0.875,0.04938271604938271,162,0.0008378,80.93678949012346,80.93637654999999,80.93721434999999 +0.12162162162162163,0.9736842105263158,78,0.00059765,81.02381321730768,81.02354799999999,81.02414565 +0.06569343065693431,1.0,138,0.0009251999999999999,81.0254081365942,81.02492649999999,81.02585169999999 +0.8333333333333334,0.09523809523809523,63,0.00040019999999999997,81.37179326111111,81.3715986,81.3719988 diff --git a/tests/test_data/output/ds1/apbs_bursts.h5 b/tests/test_data/output/ds1/apbs_bursts.h5 new file mode 100644 index 0000000..28147ad Binary files /dev/null and b/tests/test_data/output/ds1/apbs_bursts.h5 differ diff --git a/tests/test_data/output/ds1/apbs_bursts.txt b/tests/test_data/output/ds1/apbs_bursts.txt new file mode 100644 index 0000000..d6a4afa --- /dev/null +++ b/tests/test_data/output/ds1/apbs_bursts.txt @@ -0,0 +1,1138 @@ + E_app S_app n_photons time_length time_mean time_min time_max +0 0.666667 0.112150 124 0.000912 0.006332 0.005850 0.006761 +1 0.098039 0.993506 185 0.001553 0.031014 0.030265 0.031818 +2 0.093750 1.000000 109 0.000710 0.057345 0.056992 0.057701 +3 0.500000 0.153846 60 0.000449 0.075088 0.074870 0.075319 +4 0.568627 0.329032 176 0.001103 0.131788 0.131237 0.132340 +5 0.720779 0.426593 405 0.002300 0.139421 0.138286 0.140587 +6 1.000000 0.032258 69 0.000336 0.177595 0.177404 0.177739 +7 0.166667 0.098361 71 0.000516 0.253899 0.253609 0.254124 +8 0.661972 0.522059 148 0.000619 0.271139 0.270805 0.271424 +9 0.739130 0.365079 72 0.000475 0.346384 0.346137 0.346612 +10 0.725490 0.579545 96 0.000409 0.483457 0.483227 0.483636 +11 0.475000 0.655738 69 0.000475 0.529026 0.528767 0.529242 +12 0.700000 0.116279 99 0.000664 0.702013 0.701687 0.702351 +13 0.692308 0.082278 184 0.001396 0.765615 0.764859 0.766255 +14 1.000000 0.164179 75 0.000541 0.770677 0.770376 0.770916 +15 0.766667 0.410959 170 0.001234 0.862997 0.862458 0.863692 +16 0.363636 0.611111 64 0.000520 0.872179 0.871956 0.872476 +17 0.454545 0.559322 69 0.000534 0.872820 0.872561 0.873095 +18 0.070175 0.982759 67 0.000400 0.992229 0.992030 0.992430 +19 0.916667 0.065574 201 0.000953 1.000706 1.000206 1.001158 +20 1.000000 0.107143 96 0.000611 1.037604 1.037315 1.037926 +21 0.625000 0.125984 147 0.001183 1.077598 1.077086 1.078269 +22 0.066667 0.989011 105 0.000746 1.106077 1.105705 1.106450 +23 0.086957 0.971831 84 0.000658 1.172879 1.172551 1.173208 +24 0.116883 1.000000 87 0.000529 1.204484 1.204235 1.204764 +25 0.888889 0.102273 101 0.000664 1.227487 1.227150 1.227814 +26 0.515152 0.177419 218 0.001628 1.412933 1.412075 1.413702 +27 0.400000 0.092593 63 0.000428 1.432663 1.432444 1.432872 +28 0.500000 0.313725 115 0.000660 1.723336 1.723002 1.723662 +29 1.000000 0.132075 61 0.000420 1.726689 1.726482 1.726902 +30 0.115385 1.000000 61 0.000553 1.749684 1.749402 1.749955 +31 0.363636 0.119565 105 0.000735 1.990169 1.989748 1.990483 +32 0.098361 0.983871 71 0.000389 2.215134 2.214964 2.215353 +33 0.714286 0.608696 105 0.000672 2.317815 2.317443 2.318115 +34 0.744186 0.682540 73 0.000490 2.320459 2.320206 2.320696 +35 1.000000 0.017241 68 0.000533 2.325220 2.324942 2.325474 +36 0.285714 0.109375 73 0.000511 2.417011 2.416697 2.417209 +37 0.147541 0.953125 77 0.000539 2.418234 2.417964 2.418503 +38 0.272727 0.328358 150 0.000884 2.419659 2.419210 2.420094 +39 0.081967 1.000000 70 0.000468 2.562603 2.562363 2.562831 +40 0.900000 0.067568 170 0.001145 2.735370 2.734839 2.735984 +41 0.071429 0.965517 67 0.000459 2.759616 2.759404 2.759862 +42 0.075949 1.000000 94 0.000807 2.760841 2.760428 2.761235 +43 0.436364 0.743243 81 0.000431 2.762232 2.761990 2.762421 +44 0.857143 0.104478 79 0.000659 2.811233 2.810906 2.811565 +45 0.750000 0.076923 59 0.000380 2.995186 2.995006 2.995386 +46 0.037736 1.000000 61 0.000398 3.267084 3.266903 3.267300 +47 0.333333 0.247706 131 0.001109 3.294902 3.294355 3.295464 +48 0.714286 0.061404 124 0.000549 3.384040 3.383718 3.384267 +49 0.800000 0.093458 119 0.000644 3.411308 3.410998 3.411642 +50 0.954545 0.088000 272 0.001132 3.418373 3.417784 3.418917 +51 0.067797 0.983333 69 0.000460 3.479973 3.479774 3.480233 +52 0.084746 0.951613 73 0.000553 3.480705 3.480399 3.480952 +53 0.625000 0.140351 67 0.000529 3.512188 3.511882 3.512411 +54 0.600000 0.063291 92 0.000697 3.514288 3.513936 3.514633 +55 0.871795 0.390000 114 0.000815 3.628768 3.628376 3.629191 +56 0.625000 0.102564 91 0.000664 3.774794 3.774486 3.775150 +57 0.500000 0.054054 87 0.000740 3.939800 3.939449 3.940189 +58 0.607843 0.536842 107 0.000586 4.116730 4.116425 4.117011 +59 0.082353 0.965909 105 0.000811 4.223932 4.223523 4.224334 +60 0.061728 1.000000 94 0.000726 4.318729 4.318409 4.319135 +61 0.629630 0.482143 64 0.000388 4.555022 4.554808 4.555196 +62 0.750000 0.058824 77 0.000541 5.080081 5.079814 5.080354 +63 1.000000 0.037736 60 0.000375 5.081168 5.080965 5.081339 +64 0.075949 0.987500 90 0.000569 5.325316 5.325091 5.325660 +65 0.603352 0.407745 489 0.002690 5.349529 5.348214 5.350904 +66 0.614679 0.367003 338 0.002249 5.451129 5.450039 5.452288 +67 0.727273 0.372881 68 0.000462 5.452797 5.452557 5.453019 +68 0.576923 0.346667 177 0.001307 5.458197 5.457541 5.458848 +69 0.333333 0.057692 61 0.000436 5.471690 5.471490 5.471926 +70 0.047619 0.362069 67 0.000503 5.517118 5.516845 5.517347 +71 0.287500 0.824742 111 0.000736 5.569310 5.568952 5.569689 +72 0.120690 1.000000 67 0.000435 5.584800 5.584577 5.585012 +73 0.466667 0.185185 96 0.000792 5.622584 5.622181 5.622973 +74 0.084034 1.000000 137 0.000911 5.638439 5.637971 5.638882 +75 0.684783 0.494624 208 0.001247 5.706349 5.705709 5.706956 +76 0.928571 0.065116 245 0.001535 5.750838 5.750063 5.751598 +77 0.916667 0.108597 239 0.000947 5.763170 5.762694 5.763641 +78 0.500000 0.525641 178 0.001130 5.771975 5.771345 5.772475 +79 0.600000 0.500000 72 0.000618 5.785627 5.785328 5.785946 +80 1.000000 0.127273 63 0.000487 5.880934 5.880686 5.881174 +81 0.431579 0.575758 190 0.001418 5.908454 5.907718 5.909136 +82 0.363636 0.095652 130 0.000845 5.909626 5.909162 5.910007 +83 0.415094 0.197761 302 0.001701 5.911769 5.910902 5.912603 +84 1.000000 0.042254 83 0.000602 5.964682 5.964409 5.965010 +85 0.888889 0.130435 80 0.000544 6.045751 6.045511 6.046055 +86 1.000000 0.092308 74 0.000491 6.062101 6.061838 6.062328 +87 1.000000 0.047619 75 0.000630 6.075196 6.074861 6.075491 +88 0.500000 0.482456 259 0.001564 6.123396 6.122579 6.124143 +89 0.700000 0.138889 83 0.000593 6.129446 6.129172 6.129765 +90 1.000000 0.026316 92 0.000793 6.131601 6.131209 6.132002 +91 0.100000 1.000000 70 0.000518 6.141464 6.141230 6.141748 +92 0.044118 1.000000 153 0.000876 6.193296 6.192880 6.193756 +93 0.615385 0.156627 94 0.000592 6.405852 6.405560 6.406152 +94 0.750000 0.173913 79 0.000490 6.624083 6.623837 6.624327 +95 0.428571 0.117978 209 0.001615 6.676184 6.675312 6.676927 +96 0.846154 0.149425 98 0.000552 6.678710 6.678446 6.678997 +97 1.000000 0.076923 60 0.000400 6.699808 6.699615 6.700015 +98 0.660377 0.557895 216 0.001381 6.736294 6.735592 6.736973 +99 0.810811 0.587302 73 0.000480 6.781569 6.781320 6.781800 +100 0.838235 0.390805 200 0.001331 6.790471 6.789813 6.791144 +101 0.807692 0.464286 66 0.000507 6.792953 6.792709 6.793216 +102 0.800000 0.508475 134 0.000791 6.794501 6.794136 6.794927 +103 0.614458 0.386047 238 0.001152 6.799314 6.798722 6.799874 +104 0.088889 0.473684 109 0.000796 6.800739 6.800344 6.801140 +105 0.090909 0.639535 99 0.000720 6.801658 6.801276 6.801996 +106 0.532258 0.626263 109 0.000530 6.838527 6.838243 6.838773 +107 0.434783 0.676471 78 0.000569 6.840097 6.839792 6.840361 +108 1.000000 0.081081 125 0.000758 6.880095 6.879748 6.880505 +109 0.096154 1.000000 59 0.000363 6.908277 6.908097 6.908461 +110 0.079646 0.991228 133 0.001120 7.006841 7.006205 7.007325 +111 0.809524 0.150000 161 0.001076 7.057315 7.056761 7.057837 +112 0.370370 0.150000 208 0.001491 7.061237 7.060509 7.062000 +113 0.807692 0.412698 72 0.000509 7.269118 7.268854 7.269364 +114 0.641026 0.438202 101 0.000640 7.382837 7.382515 7.383155 +115 0.551724 0.508772 66 0.000464 7.386931 7.386708 7.387172 +116 0.092105 1.000000 89 0.000668 7.398832 7.398497 7.399165 +117 0.809524 0.070707 322 0.001277 7.458684 7.458060 7.459338 +118 0.666667 0.034483 103 0.000855 7.461018 7.460544 7.461399 +119 0.857143 0.112903 71 0.000481 7.472077 7.471842 7.472322 +120 0.775281 0.577922 180 0.001334 7.493081 7.492393 7.493727 +121 0.539216 0.457399 255 0.001761 7.500379 7.499507 7.501268 +122 0.742857 0.546875 73 0.000470 7.518858 7.518623 7.519093 +123 0.814815 0.490909 64 0.000493 7.589919 7.589698 7.590191 +124 0.829268 0.554054 81 0.000527 7.590583 7.590218 7.590745 +125 0.833333 0.112150 123 0.000931 7.631618 7.631148 7.632079 +126 0.993976 0.715517 250 0.000910 7.643071 7.642670 7.643580 +127 0.095745 0.935323 227 0.001249 7.668507 7.667901 7.669151 +128 0.668919 0.483660 346 0.002008 7.680510 7.679543 7.681551 +129 0.139535 0.704918 70 0.000450 7.682075 7.681812 7.682263 +130 0.852941 0.404762 95 0.000555 7.697153 7.696886 7.697442 +131 0.684211 0.166667 133 0.000944 7.871880 7.871423 7.872367 +132 0.666667 0.095238 138 0.000630 7.873903 7.873595 7.874225 +133 0.518519 0.519231 59 0.000353 7.882490 7.882300 7.882652 +134 0.640000 0.362319 78 0.000452 7.895105 7.894858 7.895310 +135 1.000000 0.093333 85 0.000509 7.929670 7.929427 7.929936 +136 0.640000 0.471698 62 0.000433 7.989895 7.989689 7.990123 +137 0.600000 0.059524 282 0.001614 8.079034 8.078243 8.079857 +138 0.857143 0.061404 136 0.001142 8.081030 8.080474 8.081616 +139 0.800000 0.081967 139 0.000860 8.086911 8.086439 8.087299 +140 0.923077 0.082278 181 0.001239 8.124546 8.123912 8.125151 +141 0.533333 0.500000 71 0.000534 8.207506 8.207242 8.207776 +142 0.500000 0.072289 94 0.000595 8.436200 8.435923 8.436518 +143 0.700000 0.344828 101 0.000733 8.716115 8.715726 8.716459 +144 0.318182 0.423077 175 0.000933 8.719438 8.718996 8.719930 +145 0.760000 0.538793 256 0.001278 8.874806 8.874138 8.875416 +146 0.916667 0.068571 199 0.001293 8.994375 8.993696 8.994989 +147 0.821782 0.505000 221 0.001105 9.027199 9.026515 9.027620 +148 0.078125 0.955224 80 0.000660 9.310139 9.309840 9.310500 +149 1.000000 0.074468 108 0.000715 9.425281 9.424936 9.425651 +150 0.814815 0.066998 442 0.002074 9.585907 9.584769 9.586843 +151 0.458333 0.413793 69 0.000523 9.596841 9.596566 9.597090 +152 0.534884 0.474265 294 0.001138 9.631550 9.631035 9.632173 +153 0.500000 0.054795 85 0.000608 9.678633 9.678329 9.678937 +154 0.909091 0.048035 498 0.001997 9.704790 9.703746 9.705742 +155 0.063830 1.000000 112 0.000946 9.722536 9.722072 9.723018 +156 0.785714 0.082840 193 0.001403 10.226629 10.225943 10.227346 +157 1.000000 0.105263 87 0.000548 10.355708 10.355434 10.355982 +158 0.666667 0.067416 102 0.000650 10.368414 10.368098 10.368749 +159 0.625000 0.093750 292 0.001833 10.382075 10.381093 10.382925 +160 0.777778 0.081081 128 0.000898 10.424749 10.424290 10.425188 +161 0.700000 0.094340 126 0.001121 10.767721 10.767198 10.768319 +162 0.673077 0.523490 322 0.001319 10.812405 10.811755 10.813073 +163 0.600000 0.076923 152 0.001145 10.931596 10.931021 10.932166 +164 1.000000 0.065728 235 0.001116 10.985276 10.984682 10.985799 +165 0.888889 0.124138 163 0.000798 10.988730 10.988374 10.989173 +166 0.009091 1.000000 129 0.000907 11.063199 11.062775 11.063682 +167 0.804348 0.418182 124 0.000718 11.179223 11.178872 11.179591 +168 0.657895 0.460606 190 0.001308 11.395237 11.394621 11.395929 +169 0.666667 0.037500 95 0.000737 11.428842 11.428512 11.429249 +170 0.814815 0.281250 110 0.000741 11.665352 11.664967 11.665708 +171 0.717391 0.516854 100 0.000585 11.667233 11.666951 11.667537 +172 0.745283 0.482916 492 0.002780 11.679325 11.677953 11.680733 +173 0.789474 0.401408 164 0.001127 11.739730 11.739207 11.740334 +174 0.573770 0.642105 111 0.000857 11.832904 11.832478 11.833335 +175 0.661290 0.543860 134 0.001123 11.834029 11.833487 11.834609 +176 0.702703 0.486842 90 0.000730 12.161664 12.161291 12.162020 +177 0.909091 0.126437 102 0.000710 12.340890 12.340511 12.341221 +178 0.033333 1.000000 70 0.000567 12.837645 12.837353 12.837920 +179 0.750000 0.067797 69 0.000475 12.914612 12.914372 12.914848 +180 0.078125 1.000000 73 0.000452 12.917318 12.917087 12.917539 +181 0.909091 0.095652 128 0.000716 12.985339 12.984992 12.985708 +182 0.673469 0.624204 172 0.000774 12.991973 12.991607 12.992381 +183 0.808511 0.484536 111 0.000689 12.992892 12.992512 12.993201 +184 0.050847 0.983333 69 0.000472 13.026716 13.026466 13.026938 +185 0.055556 1.000000 86 0.000742 13.038266 13.037882 13.038623 +186 0.058824 0.980769 62 0.000470 13.045213 13.044984 13.045454 +187 0.538462 0.490566 60 0.000428 13.054549 13.054339 13.054768 +188 0.472727 0.500000 124 0.000717 13.062097 13.061738 13.062455 +189 0.596491 0.441860 146 0.000885 13.090679 13.090221 13.091105 +190 0.727273 0.333333 79 0.000651 13.099888 13.099568 13.100220 +191 0.225806 0.732283 146 0.001038 13.150015 13.149551 13.150588 +192 0.714286 0.058333 132 0.000618 13.190181 13.189872 13.190490 +193 0.875000 0.053691 171 0.001104 13.206508 13.205940 13.207044 +194 0.158730 0.360000 203 0.001473 13.250608 13.249911 13.251384 +195 0.670886 0.467456 190 0.001080 13.602171 13.601650 13.602730 +196 0.888889 0.066176 153 0.000874 13.677041 13.676609 13.677483 +197 0.500000 0.478873 80 0.000482 13.722742 13.722475 13.722957 +198 0.416667 0.063492 216 0.001370 13.767992 13.767364 13.768734 +199 0.750000 0.038095 116 0.000584 13.872682 13.872405 13.872989 +200 0.941176 0.051672 365 0.001960 13.897077 13.896029 13.897988 +201 0.625000 0.080808 115 0.000824 13.999334 13.998889 13.999713 +202 0.692308 0.152941 97 0.000613 14.002459 14.002129 14.002742 +203 1.000000 0.011765 97 0.000631 14.006424 14.006082 14.006714 +204 1.000000 0.065217 159 0.001040 14.013330 14.012823 14.013863 +205 1.000000 0.017241 68 0.000478 14.015708 14.015472 14.015950 +206 0.909091 0.154930 80 0.000482 14.016400 14.016156 14.016638 +207 0.800000 0.180000 277 0.001545 14.087020 14.086301 14.087845 +208 0.800000 0.047170 123 0.000924 14.088381 14.087871 14.088796 +209 0.086207 0.913386 148 0.001084 14.106692 14.106108 14.107192 +210 0.810811 0.393617 110 0.000938 14.152525 14.152027 14.152965 +211 0.857143 0.046667 171 0.001087 14.203930 14.203422 14.204509 +212 0.698795 0.443850 217 0.001621 14.261679 14.260843 14.262464 +213 0.913043 0.338235 79 0.000539 14.264158 14.263905 14.264443 +214 0.777778 0.489796 162 0.000747 14.274727 14.274385 14.275132 +215 0.681159 0.511111 153 0.000895 14.276754 14.276287 14.277182 +216 0.500000 0.539683 71 0.000386 14.407889 14.407658 14.408044 +217 0.714286 0.244186 95 0.000493 14.436267 14.436000 14.436494 +218 0.131148 0.983871 70 0.000410 14.440516 14.440324 14.440734 +219 0.071429 0.988235 94 0.000494 14.460952 14.460704 14.461198 +220 1.000000 0.044944 106 0.000900 14.627135 14.626696 14.627597 +221 0.851852 0.219512 139 0.000804 14.694366 14.694015 14.694819 +222 0.833333 0.096000 139 0.000790 14.717471 14.717083 14.717874 +223 0.656250 0.202532 179 0.001068 14.718764 14.718207 14.719275 +224 0.078947 0.863636 108 0.000997 14.776286 14.775786 14.776784 +225 0.203704 0.739726 84 0.000556 14.858228 14.857954 14.858510 +226 0.193182 0.771930 130 0.000820 14.952076 14.951709 14.952529 +227 0.135593 1.000000 69 0.000538 14.961239 14.960971 14.961509 +228 0.230769 0.317073 140 0.000872 15.443844 15.443384 15.444256 +229 0.800000 0.054945 105 0.000707 15.444748 15.444364 15.445071 +230 1.000000 0.053571 189 0.001113 15.454477 15.453885 15.454998 +231 0.888889 0.060811 170 0.001138 15.569789 15.569237 15.570376 +232 1.000000 0.114286 82 0.000717 15.602727 15.602444 15.603160 +233 0.333333 0.048387 76 0.000630 15.606718 15.606416 15.607045 +234 0.833333 0.111111 62 0.000385 15.615196 15.615026 15.615410 +235 NaN 0.000000 89 0.000672 15.631646 15.631349 15.632020 +236 0.800000 0.052083 110 0.000711 15.662921 15.662565 15.663276 +237 0.641791 0.446667 332 0.001621 15.665466 15.664764 15.666385 +238 0.333333 0.047619 73 0.000535 15.666784 15.666497 15.667032 +239 0.846154 0.250000 60 0.000410 15.695241 15.695017 15.695428 +240 0.692308 0.472727 65 0.000528 15.739553 15.739304 15.739832 +241 0.812500 0.482412 218 0.000953 16.134564 16.134114 16.135067 +242 0.808383 0.518634 349 0.001330 16.139657 16.138984 16.140314 +243 0.803030 0.499055 573 0.002186 16.240070 16.238916 16.241102 +244 0.950000 0.084034 259 0.001058 16.283941 16.283421 16.284478 +245 0.687500 0.119403 152 0.000907 16.404633 16.404203 16.405111 +246 0.562500 0.142857 124 0.000640 16.405888 16.405556 16.406196 +247 1.000000 0.064220 128 0.000953 16.746072 16.745600 16.746553 +248 0.093333 0.872093 98 0.000634 16.813815 16.813482 16.814116 +249 0.068027 1.000000 172 0.001267 16.892688 16.892073 16.893340 +250 1.000000 0.109375 74 0.000496 16.924967 16.924720 16.925215 +251 0.769231 0.077844 192 0.001304 17.230839 17.230216 17.231520 +252 1.000000 0.090909 62 0.000354 17.665662 17.665505 17.665859 +253 0.807229 0.488235 190 0.001078 17.837589 17.837037 17.838115 +254 0.636364 0.118280 106 0.000686 18.189398 18.189067 18.189753 +255 0.619048 0.466667 106 0.000779 18.296013 18.295592 18.296372 +256 1.000000 0.021277 110 0.000790 18.314951 18.314588 18.315378 +257 0.625000 0.072727 126 0.000763 18.315966 18.315605 18.316369 +258 0.666667 0.047619 75 0.000684 18.330487 18.330166 18.330850 +259 0.560440 0.494565 201 0.000845 18.395586 18.395171 18.396017 +260 0.865169 0.342308 292 0.001710 18.487169 18.486330 18.488039 +261 1.000000 0.120690 191 0.000846 18.649645 18.649235 18.650081 +262 0.750000 0.053333 86 0.000521 18.744592 18.744353 18.744874 +263 0.056338 0.972603 85 0.000614 18.915260 18.914958 18.915572 +264 0.625000 0.082474 112 0.000732 19.134016 19.133638 19.134371 +265 0.750000 0.070175 66 0.000434 19.134944 19.134717 19.135150 +266 0.428571 0.058824 133 0.000736 19.146675 19.146298 19.147035 +267 0.600000 0.096154 176 0.001027 19.179825 19.179356 19.180383 +268 0.628571 0.555556 72 0.000517 19.191749 19.191518 19.192035 +269 1.000000 0.050847 67 0.000457 19.210040 19.209808 19.210264 +270 0.666667 0.150000 69 0.000474 19.340516 19.340250 19.340725 +271 0.833333 0.093750 74 0.000520 19.350663 19.350393 19.350913 +272 0.800000 0.409836 70 0.000431 19.419202 19.418977 19.419408 +273 1.000000 0.104478 78 0.000557 19.515827 19.515583 19.516140 +274 0.057971 0.638889 127 0.000932 19.804615 19.804178 19.805111 +275 0.653846 0.541667 109 0.000661 20.004054 20.003730 20.004391 +276 0.538462 0.086667 176 0.001304 20.105410 20.104799 20.106103 +277 0.380952 0.304348 78 0.000413 20.166471 20.166246 20.166659 +278 0.571429 0.063063 254 0.001610 20.170560 20.169768 20.171378 +279 0.722222 0.150000 138 0.001012 20.244951 20.244496 20.245509 +280 0.923077 0.126214 118 0.000752 20.248863 20.248507 20.249259 +281 0.787879 0.379310 102 0.000785 20.334373 20.333990 20.334775 +282 1.000000 0.067568 86 0.000608 20.717673 20.717339 20.717947 +283 0.714286 0.120690 68 0.000487 20.718651 20.718418 20.718905 +284 0.074074 0.972973 129 0.000943 21.069129 21.068673 21.069616 +285 0.842105 0.108571 202 0.001361 21.096055 21.095403 21.096764 +286 0.780488 0.493976 95 0.000631 21.100770 21.100445 21.101076 +287 0.777778 0.121622 81 0.000425 21.106953 21.106691 21.107115 +288 0.520548 0.486667 166 0.000763 21.449754 21.449385 21.450148 +289 0.736842 0.333333 68 0.000521 21.456140 21.455865 21.456386 +290 0.937500 0.132231 139 0.000950 21.483314 21.482807 21.483757 +291 0.222222 0.716814 131 0.000925 21.514546 21.514112 21.515037 +292 0.365854 0.512500 179 0.001042 21.576708 21.576158 21.577200 +293 0.570000 0.621118 183 0.001020 21.610910 21.610358 21.611378 +294 0.110294 0.985507 160 0.001106 21.655938 21.655362 21.656468 +295 1.000000 0.037037 92 0.000665 21.792079 21.791689 21.792354 +296 1.000000 0.082192 82 0.000461 21.870245 21.870019 21.870479 +297 0.655172 0.389262 164 0.000781 21.920076 21.919704 21.920485 +298 0.520000 0.316456 89 0.000506 21.921014 21.920747 21.921254 +299 0.000000 0.021277 107 0.000675 21.990698 21.990361 21.991035 +300 0.516129 0.563636 64 0.000465 22.019594 22.019341 22.019806 +301 0.750000 0.065574 134 0.000643 22.124258 22.123969 22.124612 +302 0.511628 0.551282 90 0.000608 22.293238 22.292927 22.293536 +303 0.909091 0.150685 88 0.000767 22.408434 22.408047 22.408814 +304 0.063492 0.984375 73 0.000492 22.469232 22.469027 22.469519 +305 0.101695 0.951613 73 0.000545 22.470895 22.470609 22.471154 +306 0.092593 1.000000 63 0.000450 22.571064 22.570839 22.571290 +307 0.040816 0.942308 61 0.000412 22.577841 22.577612 22.578024 +308 0.800000 0.508475 66 0.000358 22.665980 22.665849 22.666207 +309 0.685714 0.546875 72 0.000375 22.687916 22.687743 22.688118 +310 0.500000 0.451613 72 0.000560 22.690035 22.689752 22.690312 +311 0.796296 0.469565 129 0.000750 22.754080 22.753726 22.754476 +312 0.650000 0.303030 79 0.000625 22.766604 22.766281 22.766907 +313 0.057377 0.976000 147 0.001143 22.786649 22.786115 22.787259 +314 0.061224 0.989899 116 0.000965 22.788124 22.787569 22.788534 +315 0.157895 1.000000 66 0.000440 22.810934 22.810710 22.811150 +316 0.571429 0.056911 143 0.001075 22.901476 22.900987 22.902061 +317 0.875000 0.106667 87 0.000608 22.916739 22.916449 22.917058 +318 1.000000 0.050000 72 0.000598 22.941453 22.941170 22.941769 +319 0.833333 0.098361 72 0.000617 22.942236 22.941936 22.942553 +320 0.857143 0.080925 197 0.001147 23.459053 23.458441 23.459588 +321 0.750000 0.069364 198 0.001302 23.461050 23.460420 23.461723 +322 1.000000 0.070423 83 0.000595 23.462602 23.462289 23.462884 +323 0.500000 0.038835 117 0.000728 23.501302 23.500930 23.501658 +324 1.000000 0.086957 77 0.000445 23.508519 23.508289 23.508734 +325 0.400000 0.054945 105 0.000879 23.630684 23.630256 23.631135 +326 0.448276 0.552381 116 0.000578 23.647212 23.646937 23.647516 +327 0.769231 0.139785 107 0.000730 23.657956 23.657595 23.658325 +328 0.800000 0.263158 196 0.001228 23.681619 23.680986 23.682215 +329 0.432432 0.330357 127 0.000745 23.685040 23.684636 23.685382 +330 0.615385 0.151163 98 0.000622 23.818073 23.817768 23.818390 +331 0.070588 1.000000 96 0.000594 24.010638 24.010292 24.010887 +332 0.083969 1.000000 149 0.000933 24.012755 24.012311 24.013245 +333 1.000000 0.080645 73 0.000528 24.082900 24.082621 24.083150 +334 0.500000 0.067797 69 0.000512 24.213773 24.213491 24.214003 +335 0.635135 0.548148 147 0.000638 24.707738 24.707447 24.708085 +336 0.600000 0.058140 99 0.000638 25.277573 25.277214 25.277852 +337 0.666667 0.087379 119 0.000773 25.649444 25.649095 25.649867 +338 0.545455 0.343750 74 0.000472 25.981958 25.981692 25.982164 +339 1.000000 0.051724 70 0.000602 25.983981 25.983689 25.984291 +340 1.000000 0.056604 62 0.000457 25.985416 25.985169 25.985626 +341 0.520000 0.431034 67 0.000486 26.076836 26.076608 26.077095 +342 0.046154 0.984848 76 0.000497 26.230840 26.230602 26.231099 +343 0.800000 0.094937 179 0.001092 26.277784 26.277223 26.278315 +344 0.285714 0.114754 72 0.000552 26.299287 26.299009 26.299562 +345 0.113924 1.000000 92 0.000694 26.343192 26.342866 26.343561 +346 0.516949 0.504274 262 0.001527 26.677187 26.676483 26.678010 +347 0.064103 0.962963 91 0.000555 26.891237 26.890972 26.891527 +348 0.781250 0.333333 110 0.000710 26.928730 26.928415 26.929126 +349 0.777778 0.441718 182 0.000952 26.929735 26.929251 26.930202 +350 1.000000 0.064516 104 0.000568 26.940102 26.939844 26.940412 +351 0.746479 0.539924 296 0.001642 27.039722 27.038807 27.040449 +352 0.916667 0.115385 121 0.000873 27.110773 27.110341 27.111214 +353 0.870968 0.120155 287 0.001474 27.114242 27.113465 27.114939 +354 1.000000 0.041096 86 0.000630 27.442783 27.442476 27.443106 +355 0.750000 0.413793 99 0.000614 27.465040 27.464740 27.465354 +356 0.900000 0.091743 128 0.000970 27.677354 27.676897 27.677867 +357 0.449102 0.616236 297 0.001348 27.941589 27.940982 27.942330 +358 0.750000 0.072727 122 0.000625 27.945836 27.945539 27.946164 +359 1.000000 0.125000 66 0.000500 27.947243 27.946983 27.947483 +360 0.500000 0.100000 70 0.000486 27.948487 27.948220 27.948706 +361 0.625000 0.452830 63 0.000506 28.030727 28.030443 28.030949 +362 0.076923 0.981132 63 0.000522 28.097372 28.097103 28.097624 +363 0.039216 0.980769 60 0.000444 28.139281 28.139061 28.139504 +364 0.888889 0.052632 186 0.000860 28.237187 28.236803 28.237663 +365 0.089744 0.987342 91 0.000601 28.332584 28.332284 28.332885 +366 0.200000 0.071429 82 0.000678 28.338095 28.337799 28.338477 +367 0.716418 0.435065 177 0.001181 28.343960 28.343366 28.344547 +368 0.923077 0.156627 94 0.000549 28.482554 28.482271 28.482820 +369 1.000000 0.037594 149 0.000850 28.541606 28.541165 28.542014 +370 0.750000 0.203390 67 0.000424 28.547675 28.547463 28.547888 +371 0.944444 0.104046 193 0.001089 28.585986 28.585486 28.586575 +372 0.820513 0.230769 194 0.001329 28.600101 28.599400 28.600729 +373 0.640523 0.413514 410 0.002283 28.608178 28.607055 28.609338 +374 0.833333 0.096774 73 0.000605 28.661294 28.660976 28.661581 +375 1.000000 0.055556 64 0.000519 28.662536 28.662259 28.662779 +376 0.850575 0.465241 216 0.001578 28.708181 28.707323 28.708902 +377 0.586420 0.593407 304 0.001650 28.718044 28.717241 28.718891 +378 0.395833 0.857143 324 0.002329 28.725444 28.724400 28.726730 +379 0.714286 0.120690 68 0.000492 28.732070 28.731831 28.732324 +380 0.039474 0.575758 150 0.001217 28.765929 28.765379 28.766596 +381 0.900000 0.084746 133 0.000836 28.860835 28.860425 28.861262 +382 0.074074 0.931034 70 0.000590 28.910942 28.910638 28.911227 +383 0.276923 0.288889 261 0.001846 28.913515 28.912523 28.914369 +384 0.500000 0.148148 63 0.000451 28.914990 28.914755 28.915206 +385 0.666667 0.090361 185 0.000972 28.996618 28.996111 28.997083 +386 0.250000 0.061538 77 0.000592 29.004869 29.004585 29.005177 +387 1.000000 0.012048 96 0.000685 29.031088 29.030737 29.031422 +388 0.793103 0.162921 196 0.000874 29.033228 29.032804 29.033677 +389 0.869565 0.105991 235 0.000929 29.040864 29.040394 29.041323 +390 0.065574 1.000000 71 0.000485 29.195178 29.194937 29.195422 +391 0.090909 0.985075 77 0.000526 29.235122 29.234855 29.235380 +392 0.666667 0.107143 65 0.000454 29.252181 29.251990 29.252444 +393 0.666667 0.153846 89 0.000576 29.258461 29.258148 29.258724 +394 0.657143 0.522388 78 0.000605 29.259695 29.259374 29.259979 +395 0.625000 0.410256 88 0.000506 29.262145 29.261892 29.262398 +396 0.821429 0.098246 322 0.002020 29.348481 29.347475 29.349495 +397 0.550000 0.129032 172 0.000867 29.350471 29.350023 29.350889 +398 0.750000 0.047059 98 0.000657 29.417051 29.416730 29.417387 +399 1.000000 0.098765 92 0.000633 29.567699 29.567353 29.567986 +400 1.000000 0.109290 207 0.001185 29.842326 29.841704 29.842888 +401 0.500000 0.024691 93 0.000657 29.875123 29.874806 29.875463 +402 0.780000 0.595238 98 0.000707 29.973854 29.973474 29.974180 +403 1.000000 0.015625 73 0.000552 30.053061 30.052778 30.053330 +404 1.000000 0.076923 60 0.000454 30.055550 30.055348 30.055803 +405 0.714286 0.086957 182 0.001064 30.057112 30.056548 30.057612 +406 0.777778 0.107143 95 0.000572 30.058501 30.058217 30.058789 +407 0.716981 0.395522 151 0.000835 30.136226 30.135798 30.136633 +408 0.790323 0.601942 116 0.000717 30.208319 30.207964 30.208682 +409 0.801370 0.506944 321 0.001761 30.210017 30.209159 30.210920 +410 0.833333 0.276923 154 0.001224 30.214901 30.214242 30.215466 +411 0.111111 0.339623 60 0.000398 30.271328 30.271136 30.271534 +412 0.060976 1.000000 97 0.000790 30.431839 30.431462 30.432252 +413 0.133333 1.000000 67 0.000381 30.557301 30.557090 30.557471 +414 0.131579 0.678571 65 0.000475 30.564981 30.564746 30.565221 +415 0.073446 0.876238 231 0.001511 30.567092 30.566392 30.567902 +416 0.896552 0.333333 98 0.000531 30.758087 30.757836 30.758367 +417 0.800000 0.089286 65 0.000457 30.887785 30.887564 30.888020 +418 0.406250 0.444444 84 0.000629 30.999946 30.999633 31.000262 +419 0.750000 0.035398 125 0.000652 31.107283 31.106974 31.107626 +420 1.000000 0.025641 87 0.000463 31.110023 31.109763 31.110225 +421 1.000000 0.065502 252 0.001183 31.111448 31.110841 31.112023 +422 0.625000 0.363636 79 0.000593 31.153552 31.153248 31.153840 +423 0.393939 0.352941 207 0.001033 31.350659 31.350100 31.351133 +424 NaN 0.000000 67 0.000529 31.421432 31.421145 31.421674 +425 1.000000 0.098901 102 0.000573 31.483025 31.482741 31.483314 +426 0.833333 0.100000 70 0.000470 31.492898 31.492655 31.493125 +427 1.000000 0.121951 91 0.000447 31.494304 31.494085 31.494533 +428 0.277778 0.346154 61 0.000473 31.533666 31.533435 31.533908 +429 0.741935 0.534483 66 0.000402 31.547184 31.546965 31.547367 +430 0.048387 1.000000 71 0.000475 31.644698 31.644422 31.644897 +431 0.756098 0.482353 97 0.000575 31.724484 31.724185 31.724760 +432 0.600000 0.144928 84 0.000740 31.736273 31.735893 31.736633 +433 0.095238 0.355932 68 0.000439 31.737139 31.736922 31.737361 +434 0.611111 0.193548 104 0.000579 31.738919 31.738629 31.739209 +435 0.062500 0.800000 68 0.000388 31.821560 31.821348 31.821736 +436 0.250000 0.070175 66 0.000462 31.822776 31.822527 31.822989 +437 0.696629 0.438424 221 0.000952 31.936373 31.935857 31.936808 +438 0.735849 0.473214 125 0.000663 31.939888 31.939542 31.940204 +439 0.823529 0.269841 74 0.000554 32.749190 32.748891 32.749445 +440 0.052632 1.000000 108 0.000671 32.795598 32.795256 32.795927 +441 0.060870 0.991379 134 0.000978 32.801041 32.800606 32.801584 +442 0.134831 0.988889 104 0.000716 33.179163 33.178791 33.179506 +443 0.083333 0.986301 87 0.000727 33.193665 33.193287 33.194015 +444 0.090909 0.948276 66 0.000407 33.195047 33.194875 33.195282 +445 0.803279 0.592233 116 0.000678 33.306212 33.305879 33.306557 +446 0.705882 0.064151 310 0.002329 33.393466 33.392217 33.394546 +447 0.536585 0.174468 259 0.001168 33.607895 33.607273 33.608441 +448 0.776978 0.385042 395 0.001799 33.827473 33.826510 33.828309 +449 0.803279 0.469231 422 0.001654 33.836522 33.835717 33.837371 +450 0.438356 0.744898 113 0.000749 33.839965 33.839586 33.840335 +451 0.666667 0.640000 85 0.000506 33.847778 33.847526 33.848032 +452 0.777778 0.473684 106 0.000581 33.887764 33.887466 33.888047 +453 0.191176 0.781609 96 0.000499 33.890818 33.890572 33.891070 +454 0.114754 0.983871 73 0.000628 33.915704 33.915422 33.916051 +455 0.818182 0.096774 394 0.002871 33.934830 33.933449 33.936320 +456 0.577778 0.548780 93 0.000549 34.005166 34.004883 34.005432 +457 0.083333 0.989691 109 0.000649 34.095727 34.095420 34.096069 +458 0.089744 1.000000 89 0.000582 34.096496 34.096187 34.096768 +459 0.765957 0.443396 118 0.000699 34.261131 34.260804 34.261503 +460 0.071429 0.202899 79 0.000504 34.418651 34.418432 34.418936 +461 0.333333 0.076923 92 0.000762 34.450337 34.449945 34.450707 +462 0.833333 0.101124 200 0.001201 34.658871 34.658269 34.659469 +463 1.000000 0.097222 82 0.000536 34.741625 34.741374 34.741910 +464 0.880000 0.362319 78 0.000452 34.751595 34.751356 34.751808 +465 0.526316 0.575758 146 0.000767 34.756416 34.756018 34.756785 +466 0.487342 0.528428 341 0.002252 34.880218 34.879149 34.881401 +467 0.541667 0.448133 521 0.001979 34.890878 34.889975 34.891954 +468 0.380952 0.403846 61 0.000468 34.894944 34.894706 34.895174 +469 0.038462 0.838710 110 0.000873 34.897563 34.897130 34.898002 +470 0.347826 0.172932 159 0.001288 34.898888 34.898201 34.899489 +471 0.611111 0.084906 235 0.001217 35.027179 35.026557 35.027774 +472 0.106796 0.980952 117 0.000651 35.058840 35.058537 35.059189 +473 0.476190 0.549020 173 0.001002 35.075538 35.075020 35.076022 +474 0.730769 0.218487 139 0.001053 35.200186 35.199650 35.200703 +475 0.112676 0.986111 83 0.000570 35.371689 35.371406 35.371975 +476 0.040000 1.000000 87 0.000625 35.379740 35.379418 35.380042 +477 0.018868 0.670886 91 0.000670 35.471908 35.471566 35.472236 +478 0.111111 0.984375 145 0.000895 35.488911 35.488498 35.489393 +479 0.661017 0.280952 234 0.001239 35.561014 35.560354 35.561594 +480 0.291667 0.169014 165 0.001168 35.566235 35.565636 35.566804 +481 0.631579 0.206522 205 0.001097 35.787901 35.787344 35.788441 +482 0.619048 0.136364 170 0.000809 35.814586 35.814202 35.815011 +483 0.094828 0.983051 129 0.000531 35.955696 35.955440 35.955971 +484 0.046296 0.981818 128 0.000940 36.019493 36.019008 36.019948 +485 0.146667 0.974026 91 0.000700 36.292267 36.291917 36.292617 +486 0.611940 0.598214 124 0.000612 36.487003 36.486695 36.487307 +487 0.088235 1.000000 155 0.001032 36.528648 36.528149 36.529181 +488 0.411765 0.537975 177 0.001004 36.698303 36.697838 36.698841 +489 0.094340 1.000000 61 0.000377 36.789789 36.789592 36.789969 +490 0.750000 0.357143 65 0.000458 37.192481 37.192264 37.192721 +491 0.870690 0.496788 523 0.002822 37.412757 37.411457 37.414279 +492 0.635514 0.519417 223 0.000912 37.518800 37.518345 37.519258 +493 0.636364 0.523810 116 0.000622 37.519875 37.519552 37.520174 +494 0.780000 0.390625 145 0.000859 37.541877 37.541475 37.542335 +495 0.695652 0.209091 127 0.000892 37.581486 37.581003 37.581895 +496 0.666667 0.037815 266 0.001599 37.618307 37.617387 37.618986 +497 1.000000 0.074074 90 0.000494 37.622541 37.622278 37.622772 +498 0.222222 0.107143 92 0.000430 37.625550 37.625285 37.625715 +499 0.142857 0.372340 109 0.000646 37.683210 37.682877 37.683522 +500 0.333333 0.223404 107 0.000659 37.777379 37.777019 37.777678 +501 0.681159 0.381215 200 0.001042 37.790357 37.789876 37.790919 +502 0.607143 0.494700 306 0.001184 37.801768 37.801221 37.802405 +503 0.333333 0.401163 190 0.000968 37.827863 37.827435 37.828403 +504 0.875000 0.121212 76 0.000544 37.913799 37.913517 37.914060 +505 0.592593 0.509434 115 0.000555 38.012333 38.012060 38.012615 +506 0.607143 0.528302 62 0.000454 38.282241 38.282023 38.282477 +507 0.547619 0.538462 89 0.000482 38.338039 38.337773 38.338256 +508 0.347826 0.579832 133 0.000706 38.361287 38.360912 38.361618 +509 0.411765 0.531250 72 0.000413 38.449863 38.449632 38.450044 +510 0.750000 0.055172 164 0.000941 38.450796 38.450318 38.451259 +511 0.568627 0.504950 222 0.001001 38.471360 38.470870 38.471871 +512 1.000000 0.037313 154 0.001027 38.503451 38.502847 38.503874 +513 1.000000 0.050847 69 0.000529 38.695455 38.695196 38.695725 +514 0.807692 0.376812 78 0.000463 38.760472 38.760253 38.760715 +515 0.058824 0.894737 133 0.000916 38.795709 38.795224 38.796140 +516 1.000000 0.023529 98 0.000673 38.861902 38.861529 38.862202 +517 0.747253 0.583333 172 0.000856 38.925358 38.924979 38.925835 +518 0.454545 0.050459 246 0.001471 38.932116 38.931357 38.932828 +519 0.625000 0.041026 219 0.001332 38.934452 38.933799 38.935130 +520 NaN 0.000000 64 0.000541 38.935836 38.935570 38.936112 +521 1.000000 0.056604 62 0.000446 38.943199 38.942951 38.943398 +522 0.750000 0.057971 78 0.000442 38.956054 38.955820 38.956262 +523 0.031579 0.989583 108 0.000775 39.112922 39.112475 39.113251 +524 0.475862 0.690476 230 0.001010 39.115184 39.114786 39.115796 +525 1.000000 0.042857 233 0.001254 39.131064 39.130482 39.131736 +526 0.625000 0.597015 75 0.000516 39.135144 39.134949 39.135464 +527 0.615385 0.426230 71 0.000488 39.135770 39.135529 39.136017 +528 0.863636 0.239130 108 0.000813 39.153240 39.152788 39.153601 +529 0.666667 0.115385 96 0.000861 39.156535 39.156097 39.156958 +530 0.666667 0.556701 108 0.000536 39.398940 39.398695 39.399231 +531 1.000000 0.042328 211 0.001115 39.476359 39.475779 39.476894 +532 0.677419 0.436620 83 0.000623 39.622224 39.621895 39.622519 +533 0.795699 0.522472 398 0.002091 39.624205 39.623173 39.625264 +534 0.611111 0.435484 141 0.000888 39.626867 39.626408 39.627296 +535 0.857143 0.067633 240 0.001685 39.651387 39.650591 39.652276 +536 0.522727 0.143322 334 0.001349 39.763199 39.762545 39.763894 +537 0.800000 0.555556 188 0.000934 39.781527 39.781084 39.782018 +538 0.746032 0.504000 142 0.000903 39.790676 39.790241 39.791145 +539 0.786408 0.515000 214 0.000733 39.821670 39.821272 39.822006 +540 0.766234 0.484277 177 0.000923 39.824709 39.824248 39.825171 +541 0.741935 0.387500 89 0.000390 39.929163 39.928964 39.929354 +542 0.913043 0.075410 344 0.002014 39.943514 39.942408 39.944422 +543 0.500000 0.086022 105 0.000677 39.945501 39.945202 39.945879 +544 0.857143 0.625000 65 0.000420 40.074448 40.074240 40.074660 +545 0.750000 0.493151 81 0.000428 40.139253 40.139039 40.139467 +546 1.000000 0.315789 66 0.000435 40.143654 40.143456 40.143891 +547 0.800000 0.120000 142 0.000960 40.168555 40.168041 40.169002 +548 0.727273 0.088710 140 0.000791 40.285551 40.285172 40.285963 +549 0.660000 0.197628 281 0.001508 40.287206 40.286388 40.287896 +550 1.000000 0.135135 127 0.000790 40.304592 40.304209 40.304999 +551 0.056338 0.986111 161 0.000915 40.321050 40.320621 40.321535 +552 0.113821 1.000000 138 0.000830 40.357342 40.356935 40.357765 +553 0.710526 0.500000 89 0.000686 40.412653 40.412316 40.413001 +554 0.840000 0.320513 94 0.000847 40.415352 40.414934 40.415781 +555 0.833333 0.187500 111 0.000828 40.418960 40.418597 40.419425 +556 0.115789 0.979381 108 0.000590 40.464871 40.464559 40.465149 +557 0.750000 0.050000 95 0.000777 40.480511 40.480090 40.480866 +558 1.000000 0.078125 74 0.000468 40.482859 40.482605 40.483072 +559 1.000000 0.049180 70 0.000472 40.485534 40.485254 40.485726 +560 0.082645 1.000000 138 0.000920 40.528345 40.527931 40.528852 +561 0.079365 1.000000 72 0.000479 40.559935 40.559700 40.560179 +562 0.463415 0.625954 148 0.000885 40.563388 40.562978 40.563863 +563 0.325758 0.713514 214 0.001483 40.564829 40.564076 40.565559 +564 0.466667 0.529412 98 0.000669 40.569297 40.568954 40.569623 +565 0.575000 0.547945 82 0.000483 40.590544 40.590309 40.590792 +566 0.069444 0.960000 90 0.000768 40.650891 40.650492 40.651261 +567 0.076271 1.000000 133 0.000789 40.822991 40.822584 40.823374 +568 0.119266 0.964602 130 0.000790 40.940023 40.939609 40.940400 +569 0.098039 0.980769 61 0.000511 40.980258 40.980034 40.980545 +570 0.244898 0.628205 90 0.000636 40.981020 40.980700 40.981336 +571 0.040984 0.960630 145 0.000922 40.998878 40.998419 40.999341 +572 1.000000 0.073529 78 0.000534 41.131193 41.130932 41.131466 +573 0.863636 0.088710 278 0.001660 41.134412 41.133540 41.135199 +574 0.774194 0.173184 197 0.000953 41.136051 41.135580 41.136533 +575 0.800000 0.189873 92 0.000667 41.350949 41.350626 41.351294 +576 0.923077 0.077381 187 0.001032 41.424258 41.423775 41.424808 +577 1.000000 0.057971 77 0.000427 41.440782 41.440566 41.440992 +578 0.820513 0.464286 101 0.000893 41.451161 41.450733 41.451626 +579 0.104651 1.000000 98 0.000626 41.528050 41.527758 41.528384 +580 0.054545 0.964912 140 0.001300 41.579805 41.579151 41.580451 +581 0.666667 0.046875 71 0.000352 41.623673 41.623473 41.623825 +582 0.125000 1.000000 85 0.000671 41.791136 41.790792 41.791463 +583 0.071429 1.000000 64 0.000403 41.801346 41.801125 41.801528 +584 0.916667 0.057971 228 0.001063 41.988021 41.987426 41.988490 +585 0.500000 0.065934 102 0.000568 42.292178 42.291886 42.292454 +586 1.000000 0.030769 76 0.000567 42.294896 42.294620 42.295187 +587 0.600000 0.357143 133 0.001077 42.424793 42.424264 42.425341 +588 0.468750 0.516129 144 0.000997 42.601342 42.600867 42.601864 +589 0.115942 0.690000 117 0.000857 42.603008 42.602582 42.603439 +590 0.102564 0.975000 186 0.001373 42.788970 42.788344 42.789717 +591 0.500000 0.113208 62 0.000435 42.809305 42.809084 42.809519 +592 0.714286 0.069307 116 0.000766 42.813481 42.813101 42.813867 +593 0.653846 0.230088 127 0.000693 43.024146 43.023799 43.024491 +594 0.625000 0.131148 67 0.000392 43.165481 43.165301 43.165693 +595 0.842105 0.074803 284 0.001527 43.245314 43.244559 43.246086 +596 0.888889 0.084906 118 0.000615 43.263885 43.263585 43.264200 +597 0.235294 0.293103 68 0.000438 43.390829 43.390596 43.391034 +598 0.666667 0.056604 64 0.000549 43.827106 43.826813 43.827362 +599 0.559140 0.505435 206 0.001200 43.845850 43.845302 43.846502 +600 0.375000 0.542373 69 0.000550 43.846812 43.846525 43.847075 +601 0.444444 0.078261 132 0.000940 43.982838 43.982412 43.983352 +602 0.058333 1.000000 138 0.000884 44.046383 44.045971 44.046855 +603 0.075758 1.000000 76 0.000531 44.080165 44.079890 44.080421 +604 0.418919 0.698113 123 0.000956 44.214505 44.214017 44.214973 +605 0.716981 0.504762 120 0.000788 44.228115 44.227673 44.228461 +606 0.067568 0.961039 87 0.000519 44.232789 44.232522 44.233041 +607 0.224719 0.563291 177 0.001009 44.237626 44.237069 44.238077 +608 0.600000 0.071429 83 0.000638 44.292078 44.291751 44.292389 +609 0.863636 0.095238 248 0.000833 44.293162 44.292674 44.293507 +610 0.575342 0.550943 286 0.001114 44.308661 44.308080 44.309194 +611 0.636364 0.488889 103 0.000675 44.310296 44.309990 44.310665 +612 0.236842 0.395833 109 0.000717 44.324774 44.324422 44.325139 +613 0.750000 0.075472 60 0.000369 44.342760 44.342591 44.342960 +614 0.583333 0.105263 133 0.000981 44.344164 44.343656 44.344637 +615 0.250000 0.060606 77 0.000575 44.346911 44.346627 44.347202 +616 0.516129 0.430556 84 0.000610 44.638628 44.638344 44.638954 +617 0.596491 0.304813 211 0.001230 44.639737 44.639090 44.640320 +618 0.677419 0.424658 83 0.000487 44.690113 44.689889 44.690376 +619 0.605634 0.500000 153 0.000622 44.691440 44.691165 44.691786 +620 0.714286 0.064220 127 0.000895 44.693774 44.693294 44.694189 +621 0.857143 0.080460 97 0.000533 44.699834 44.699541 44.700073 +622 1.000000 0.090909 112 0.000649 44.702052 44.701726 44.702376 +623 1.000000 0.037383 122 0.000773 44.956270 44.955911 44.956685 +624 0.616000 0.548246 260 0.001718 44.988316 44.987423 44.989141 +625 0.571429 0.538462 61 0.000480 44.990272 44.990031 44.990511 +626 0.513158 0.445748 373 0.001637 45.005759 45.004950 45.006587 +627 0.863636 0.385965 66 0.000458 45.043394 45.043128 45.043586 +628 0.074074 1.000000 61 0.000396 45.044533 45.044336 45.044732 +629 0.051948 1.000000 91 0.000687 45.046851 45.046527 45.047214 +630 0.750000 0.576577 129 0.000882 45.214087 45.213688 45.214571 +631 0.867647 0.478873 154 0.000675 45.235305 45.234978 45.235653 +632 0.888889 0.385714 79 0.000447 45.384864 45.384651 45.385098 +633 0.742424 0.445946 315 0.000973 45.505867 45.505380 45.506353 +634 1.000000 0.042553 108 0.000725 45.548602 45.548257 45.548982 +635 0.833333 0.100000 70 0.000531 46.027149 46.026901 46.027432 +636 0.090909 0.956522 103 0.000565 46.513502 46.513231 46.513797 +637 0.375000 0.307692 61 0.000512 46.563323 46.563073 46.563585 +638 0.833333 0.061224 112 0.000765 46.614524 46.614154 46.614919 +639 0.500000 0.101449 162 0.001229 46.761230 46.760644 46.761873 +640 0.074468 0.989474 105 0.000540 46.798561 46.798307 46.798847 +641 0.840000 0.378788 76 0.000503 46.831751 46.831489 46.831993 +642 0.617647 0.400000 190 0.001096 46.876616 46.876084 46.877180 +643 0.797203 0.507092 314 0.001741 46.908534 46.907730 46.909471 +644 0.785714 0.509091 66 0.000557 46.947764 46.947460 46.948017 +645 0.590909 0.556962 90 0.000583 47.037027 47.036702 47.037285 +646 0.120879 1.000000 104 0.000659 47.076080 47.075792 47.076450 +647 0.634146 0.405941 120 0.000966 47.085297 47.084807 47.085773 +648 0.151515 0.929577 84 0.000721 47.086303 47.085900 47.086621 +649 0.060000 0.961538 61 0.000490 47.425192 47.424929 47.425419 +650 0.583333 0.184615 149 0.000984 47.459881 47.459411 47.460395 +651 0.875000 0.073394 121 0.000620 47.482939 47.482662 47.483283 +652 0.101449 0.971831 82 0.000581 47.565545 47.565276 47.565857 +653 0.857143 0.127273 67 0.000621 47.592053 47.591742 47.592364 +654 0.848485 0.114187 309 0.001018 47.595709 47.595158 47.596176 +655 0.625000 0.125000 74 0.000500 47.629108 47.628842 47.629342 +656 0.714286 0.049296 166 0.001224 47.637861 47.637237 47.638461 +657 0.739130 0.127072 205 0.001342 47.675232 47.674579 47.675922 +658 0.645833 0.631579 86 0.000534 47.753463 47.753188 47.753723 +659 0.767857 0.549020 120 0.000932 47.778236 47.777773 47.778705 +660 0.700000 0.124224 176 0.000785 47.802769 47.802351 47.803135 +661 0.793103 0.467742 68 0.000342 47.818191 47.818053 47.818394 +662 0.853659 0.330645 140 0.000842 47.819480 47.819031 47.819873 +663 0.659091 0.520710 195 0.001272 47.830456 47.829736 47.831008 +664 0.648649 0.521127 81 0.000434 47.834952 47.834742 47.835176 +665 0.562092 0.334792 523 0.003393 47.970638 47.968999 47.972392 +666 0.821429 0.271845 117 0.000717 47.973845 47.973490 47.974207 +667 0.320755 0.883333 68 0.000437 47.996483 47.996263 47.996700 +668 1.000000 0.077586 129 0.000701 48.262899 48.262562 48.263263 +669 1.000000 0.120690 67 0.000502 48.264642 48.264360 48.264862 +670 0.500000 0.065217 103 0.000545 48.297204 48.296958 48.297503 +671 0.028571 0.958904 80 0.000378 48.328850 48.328640 48.329018 +672 0.666667 0.056604 61 0.000428 48.468519 48.468305 48.468732 +673 1.000000 0.074627 80 0.000655 48.473707 48.473378 48.474033 +674 0.666667 0.111111 63 0.000467 48.600357 48.600142 48.600610 +675 0.568182 0.505747 102 0.000760 48.684023 48.683644 48.684404 +676 0.086957 1.000000 79 0.000589 48.805620 48.805288 48.805878 +677 0.115108 0.615044 251 0.001312 48.942173 48.941571 48.942883 +678 0.040541 0.973684 87 0.000574 49.181636 49.181359 49.181933 +679 0.500000 0.571429 73 0.000520 49.310820 49.310544 49.311064 +680 0.421053 0.457831 93 0.000553 49.314920 49.314608 49.315161 +681 1.000000 0.016129 73 0.000576 49.471927 49.471661 49.472237 +682 0.888889 0.098901 108 0.000870 49.473460 49.472980 49.473850 +683 0.580645 0.400000 173 0.000878 49.670352 49.669885 49.670764 +684 0.655738 0.467433 824 0.002164 49.979195 49.978075 49.980239 +685 0.560976 0.418367 114 0.000784 49.980951 49.980565 49.981349 +686 0.800000 0.083333 71 0.000545 49.983793 49.983514 49.984059 +687 0.625000 0.461538 60 0.000428 49.994361 49.994110 49.994538 +688 0.812500 0.094118 191 0.001266 50.028911 50.028289 50.029554 +689 0.880000 0.076687 358 0.001672 50.030777 50.029947 50.031618 +690 0.833333 0.075000 94 0.000685 50.032654 50.032275 50.032961 +691 0.666667 0.445946 85 0.000658 50.075826 50.075518 50.076176 +692 0.122449 0.975124 232 0.001637 50.139204 50.138403 50.140040 +693 0.490000 0.446429 248 0.001249 50.183774 50.183183 50.184432 +694 0.093750 0.901408 80 0.000487 50.204427 50.204177 50.204664 +695 0.607843 0.653846 92 0.000693 50.262486 50.262142 50.262835 +696 0.381818 0.470085 132 0.000838 50.268685 50.268243 50.269081 +697 0.630952 0.466667 200 0.000931 50.422358 50.421974 50.422905 +698 0.098592 0.946667 88 0.000700 50.618915 50.618584 50.619284 +699 0.113208 1.000000 65 0.000619 50.621046 50.620766 50.621385 +700 0.692308 0.068063 227 0.001848 50.721697 50.720733 50.722580 +701 0.500000 0.054054 84 0.000576 50.866741 50.866401 50.866977 +702 0.909091 0.057895 213 0.001260 50.910780 50.910073 50.911332 +703 1.000000 0.035971 155 0.000813 50.980102 50.979655 50.980469 +704 0.070312 0.984615 143 0.000777 51.245978 51.245570 51.246347 +705 0.115385 1.000000 60 0.000423 51.290539 51.290285 51.290707 +706 0.750000 0.506329 88 0.000475 51.332186 51.331957 51.332431 +707 0.145455 0.982143 63 0.000349 51.334141 51.333973 51.334323 +708 0.109375 0.969697 75 0.000488 51.350637 51.350393 51.350881 +709 1.000000 0.054348 105 0.000676 51.421038 51.420699 51.421374 +710 0.050000 0.967742 70 0.000419 51.473090 51.472910 51.473329 +711 0.083333 0.631579 91 0.000745 51.537423 51.537048 51.537793 +712 0.857143 0.118644 69 0.000497 51.614344 51.614098 51.614596 +713 0.400000 0.172414 136 0.001034 51.654915 51.654385 51.655419 +714 0.781609 0.441624 220 0.001198 51.667750 51.667182 51.668379 +715 0.640000 0.409836 66 0.000318 51.676914 51.676708 51.677025 +716 0.772727 0.423077 61 0.000461 51.754279 51.754061 51.754521 +717 0.214286 0.417910 74 0.000392 51.788627 51.788433 51.788825 +718 0.545455 0.099099 245 0.001166 51.821728 51.821154 51.822320 +719 0.730612 0.425347 621 0.002319 51.988308 51.987105 51.989424 +720 0.705882 0.283333 70 0.000510 52.014196 52.013969 52.014479 +721 1.000000 0.012821 90 0.000584 52.086273 52.085999 52.086584 +722 1.000000 0.054217 186 0.001031 52.087306 52.086795 52.087826 +723 0.611111 0.514286 81 0.000561 52.120506 52.120227 52.120789 +724 0.636364 0.068323 177 0.000836 52.374766 52.374382 52.375218 +725 0.600000 0.038462 145 0.000785 52.407213 52.406798 52.407583 +726 0.468750 0.571429 64 0.000400 52.533594 52.533345 52.533745 +727 0.977273 0.463158 109 0.000677 52.543579 52.543237 52.543914 +728 0.885714 0.472973 85 0.000589 52.559935 52.559636 52.560225 +729 1.000000 0.615385 60 0.000408 52.562857 52.562643 52.563051 +730 0.555556 0.061224 168 0.001059 52.565637 52.565124 52.566183 +731 1.000000 0.439560 102 0.000573 52.578478 52.578213 52.578786 +732 0.032258 0.984127 75 0.000609 52.807054 52.806739 52.807348 +733 0.108696 1.000000 110 0.001003 53.391305 53.390801 53.391804 +734 0.551724 0.500000 68 0.000484 53.409839 53.409582 53.410067 +735 0.514706 0.511278 153 0.001057 53.411566 53.411038 53.412096 +736 0.666667 0.037037 93 0.000607 53.480776 53.480460 53.481067 +737 0.818182 0.075342 167 0.001075 53.489918 53.489409 53.490484 +738 0.062500 0.984615 72 0.000322 53.700268 53.700141 53.700462 +739 0.787879 0.532258 68 0.000330 53.780967 53.780801 53.781131 +740 0.846154 0.433333 69 0.000505 53.782041 53.781816 53.782321 +741 0.633333 0.476190 71 0.000412 53.802666 53.802467 53.802879 +742 0.666667 0.051724 67 0.000454 54.091090 54.090872 54.091326 +743 0.700000 0.112360 104 0.000713 54.200965 54.200591 54.201305 +744 0.785714 0.166667 97 0.000643 54.431272 54.430974 54.431617 +745 0.714286 0.125000 62 0.000284 54.434998 54.434856 54.435140 +746 0.520000 0.574713 96 0.000493 54.672966 54.672699 54.673192 +747 0.868421 0.493506 88 0.000598 54.792707 54.792407 54.793005 +748 0.081481 0.971223 156 0.000942 54.812141 54.811702 54.812644 +749 0.818182 0.112245 109 0.000556 54.860958 54.860745 54.861301 +750 0.470588 0.197674 98 0.000613 54.927958 54.927642 54.928256 +751 0.057692 1.000000 63 0.000552 55.129907 55.129625 55.130177 +752 0.888889 0.078261 513 0.002699 55.198330 55.197069 55.199768 +753 0.500000 0.028571 82 0.000616 55.246878 55.246550 55.247166 +754 0.083333 0.983607 70 0.000438 55.265840 55.265607 55.266045 +755 0.700000 0.056497 204 0.001326 55.353950 55.353201 55.354528 +756 1.000000 0.033333 135 0.000808 55.518779 55.518369 55.519178 +757 0.558140 0.537500 92 0.000657 55.666035 55.665718 55.666374 +758 0.169492 0.867647 79 0.000559 55.784569 55.784312 55.784871 +759 0.016129 1.000000 71 0.000443 55.787074 55.786886 55.787329 +760 0.176471 0.906667 87 0.000625 55.810993 55.810677 55.811302 +761 0.875000 0.076190 121 0.000869 55.840133 55.839666 55.840535 +762 0.500000 0.467532 91 0.000737 55.877205 55.876812 55.877550 +763 0.447761 0.536000 140 0.000817 55.881781 55.881344 55.882161 +764 0.127273 0.948276 70 0.000701 56.034066 56.033680 56.034381 +765 0.545455 0.550000 69 0.000467 56.044559 56.044334 56.044800 +766 0.735955 0.572347 331 0.001014 56.084856 56.084405 56.085418 +767 0.061798 1.000000 204 0.001323 56.101893 56.101182 56.102505 +768 0.120301 0.985185 149 0.000731 56.103220 56.102867 56.103598 +769 0.666667 0.045455 75 0.000449 56.109048 56.108822 56.109271 +770 1.000000 0.114754 70 0.000462 56.139766 56.139527 56.139989 +771 1.000000 0.087719 65 0.000383 56.140566 56.140391 56.140774 +772 0.541667 0.413793 67 0.000470 56.211464 56.211226 56.211696 +773 0.147541 0.871429 82 0.000596 56.287097 56.286817 56.287413 +774 0.044118 1.000000 78 0.000515 56.388588 56.388329 56.388844 +775 0.097297 0.984043 221 0.001529 56.396365 56.395637 56.397166 +776 0.268293 0.561644 81 0.000450 56.498050 56.497871 56.498320 +777 0.666667 0.113924 92 0.000686 56.503513 56.503210 56.503895 +778 1.000000 0.072727 65 0.000493 56.521727 56.521460 56.521954 +779 0.666667 0.076923 87 0.000479 56.566857 56.566633 56.567112 +780 0.777778 0.157895 65 0.000513 56.572308 56.572053 56.572566 +781 0.800000 0.051020 112 0.000706 56.592908 56.592504 56.593210 +782 0.830189 0.576087 108 0.000833 56.691062 56.690603 56.691437 +783 0.057692 1.000000 64 0.000557 56.798792 56.798537 56.799094 +784 0.736842 0.112426 191 0.001101 57.141347 57.140777 57.141878 +785 1.000000 0.111111 110 0.000631 57.176934 57.176608 57.177239 +786 0.714286 0.053435 151 0.001019 57.178713 57.178226 57.179245 +787 0.518519 0.482143 65 0.000472 57.185432 57.185184 57.185657 +788 0.388889 0.529412 78 0.000520 57.187817 57.187561 57.188081 +789 0.833333 0.080000 87 0.000555 57.189433 57.189169 57.189724 +790 0.500000 0.105263 66 0.000457 57.318342 57.318130 57.318587 +791 0.857143 0.114754 73 0.000620 57.320347 57.320083 57.320703 +792 0.210526 0.358491 123 0.000906 57.349552 57.349048 57.349953 +793 0.533333 0.461538 78 0.000624 57.417220 57.416905 57.417530 +794 0.833333 0.066667 108 0.000957 57.479742 57.479277 57.480234 +795 0.800000 0.491071 127 0.000764 57.482059 57.481656 57.482420 +796 0.750000 0.042553 113 0.000973 57.747661 57.747193 57.748166 +797 0.500000 0.076923 62 0.000505 57.748895 57.748648 57.749152 +798 0.800000 0.390625 73 0.000463 57.803783 57.803568 57.804031 +799 0.481481 0.380282 83 0.000525 57.807126 57.806867 57.807392 +800 0.540541 0.521127 83 0.000617 57.832782 57.832454 57.833072 +801 0.842105 0.500000 87 0.000522 58.112046 58.111789 58.112311 +802 0.676056 0.541985 144 0.000621 58.174666 58.174342 58.174963 +803 0.642857 0.538462 179 0.001239 58.258177 58.257508 58.258747 +804 0.625000 0.477612 76 0.000489 58.262249 58.262029 58.262518 +805 0.666667 0.040541 85 0.000580 58.296197 58.295918 58.296498 +806 0.421053 0.570000 111 0.000571 58.341285 58.340999 58.341570 +807 0.556180 0.671698 303 0.002050 58.594168 58.593253 58.595303 +808 0.780488 0.518987 89 0.000489 58.606246 58.606026 58.606515 +809 0.809524 0.518519 91 0.000549 58.896333 58.896060 58.896609 +810 0.666667 0.535714 99 0.000742 58.899521 58.899177 58.899920 +811 0.531250 0.359551 104 0.000779 58.900340 58.899984 58.900763 +812 0.666667 0.103448 69 0.000513 58.906197 58.905976 58.906489 +813 0.800000 0.038168 145 0.000702 58.931703 58.931382 58.932084 +814 1.000000 0.107692 76 0.000556 58.949489 58.949181 58.949738 +815 0.666667 0.065217 158 0.000992 58.959609 58.959112 58.960104 +816 1.000000 0.029851 81 0.000781 58.966289 58.965894 58.966676 +817 0.857143 0.109375 73 0.000475 58.970014 58.969782 58.970257 +818 0.800000 0.102740 164 0.000972 58.971540 58.971064 58.972036 +819 0.000000 0.038462 64 0.000612 58.972365 58.972074 58.972685 +820 0.750000 0.114286 81 0.000574 58.974279 58.973956 58.974529 +821 1.000000 0.059701 78 0.000526 59.019326 59.019050 59.019576 +822 0.777778 0.050847 196 0.000961 59.347703 59.347220 59.348182 +823 0.733333 0.060729 286 0.002122 59.349749 59.348690 59.350812 +824 0.800000 0.074627 75 0.000387 59.383848 59.383683 59.384070 +825 0.500000 0.070175 65 0.000442 59.597730 59.597513 59.597955 +826 0.600000 0.090909 65 0.000479 59.652319 59.652078 59.652557 +827 0.824561 0.497817 257 0.001609 59.825051 59.824125 59.825735 +828 0.486111 0.385027 214 0.001416 59.828076 59.827356 59.828773 +829 0.755556 0.351562 142 0.000719 59.831076 59.830706 59.831425 +830 0.408000 0.534188 269 0.001773 59.836703 59.835748 59.837520 +831 0.750000 0.100000 91 0.000491 59.915949 59.915713 59.916204 +832 0.807692 0.366197 82 0.000613 59.925994 59.925693 59.926306 +833 0.086957 1.000000 108 0.000781 60.206809 60.206439 60.207220 +834 1.000000 0.032520 141 0.000914 60.241933 60.241499 60.242412 +835 0.611111 0.209302 101 0.000766 60.528969 60.528582 60.529347 +836 NaN 0.000000 134 0.000719 60.621143 60.620810 60.621529 +837 1.000000 0.038674 200 0.001000 60.635388 60.634835 60.635835 +838 0.818182 0.086614 139 0.000601 60.643805 60.643510 60.644111 +839 0.900000 0.106383 106 0.000596 60.648770 60.648470 60.649067 +840 1.000000 0.042373 136 0.000894 60.649897 60.649470 60.650364 +841 0.875000 0.065574 140 0.000849 60.651490 60.651051 60.651900 +842 0.837838 0.637931 68 0.000497 60.823563 60.823338 60.823835 +843 0.596491 0.483051 130 0.000598 60.825804 60.825489 60.826087 +844 0.424242 0.448980 164 0.000847 61.207513 61.207081 61.207928 +845 0.571429 0.518519 62 0.000415 61.338039 61.337826 61.338241 +846 0.098592 1.000000 153 0.000752 61.525374 61.525017 61.525769 +847 0.062500 0.960000 116 0.000808 61.530023 61.529642 61.530450 +848 0.921569 0.472222 122 0.000750 61.548721 61.548325 61.549075 +849 1.000000 0.033333 69 0.000461 61.572223 61.572001 61.572463 +850 0.084746 1.000000 70 0.000482 61.611992 61.611761 61.612242 +851 0.333333 0.037037 93 0.000581 61.619550 61.619236 61.619818 +852 0.046980 0.986755 177 0.001354 61.648701 61.647985 61.649339 +853 0.450000 0.110497 198 0.000846 61.721158 61.720758 61.721604 +854 0.218750 0.244275 145 0.000652 61.722010 61.721643 61.722295 +855 0.050847 1.000000 70 0.000521 61.729790 61.729530 61.730051 +856 0.057692 1.000000 120 0.000824 61.752973 61.752534 61.753357 +857 0.518519 0.519231 61 0.000463 61.820202 61.819965 61.820428 +858 0.722222 0.197802 107 0.000857 61.842757 61.842333 61.843191 +859 0.433333 0.517241 67 0.000452 61.860109 61.859927 61.860379 +860 0.013699 1.000000 84 0.000565 62.008779 62.008488 62.009053 +861 0.857143 0.100000 154 0.000836 62.147982 62.147573 62.148410 +862 0.714286 0.063063 128 0.000914 62.154636 62.154152 62.155067 +863 0.683333 0.631579 106 0.000513 62.279014 62.278776 62.279289 +864 0.657895 0.489270 250 0.000875 62.380019 62.379643 62.380518 +865 0.591304 0.477178 262 0.001010 62.390097 62.389582 62.390591 +866 0.607143 0.583333 107 0.000588 62.392787 62.392484 62.393072 +867 0.108911 0.990196 115 0.000671 62.425407 62.425061 62.425731 +868 1.000000 0.117647 78 0.000543 62.803767 62.803539 62.804081 +869 0.863636 0.079137 315 0.001916 62.953306 62.952476 62.954392 +870 0.666667 0.103448 68 0.000529 63.018706 63.018420 63.018949 +871 1.000000 0.054795 85 0.000594 63.076761 63.076505 63.077099 +872 0.333333 0.034483 103 0.000807 63.231784 63.231409 63.232217 +873 0.111111 0.762712 69 0.000526 63.414499 63.414228 63.414754 +874 0.133333 0.952381 74 0.000611 63.426558 63.426230 63.426841 +875 0.750000 0.126984 73 0.000537 63.571067 63.570804 63.571341 +876 0.626506 0.549669 167 0.000833 63.590756 63.590316 63.591149 +877 0.750000 0.058824 80 0.000599 63.709778 63.709476 63.710075 +878 1.000000 0.044118 79 0.000559 63.714621 63.714390 63.714949 +879 0.659574 0.370079 142 0.000748 63.905697 63.905308 63.906056 +880 0.000000 0.285714 65 0.000456 63.997358 63.997111 63.997567 +881 0.118280 0.968750 106 0.000561 64.005849 64.005561 64.006123 +882 0.333333 0.196078 172 0.000950 64.157257 64.156758 64.157708 +883 0.666667 0.080000 85 0.000511 64.163871 64.163618 64.164129 +884 0.558824 0.539683 76 0.000636 64.264743 64.264437 64.265073 +885 0.800000 0.096154 58 0.000315 64.353610 64.353477 64.353793 +886 0.588235 0.121429 301 0.001121 64.356641 64.356062 64.357183 +887 0.437500 0.432432 84 0.000510 64.358483 64.358213 64.358723 +888 0.777778 0.111111 90 0.000414 64.582723 64.582553 64.582966 +889 0.527607 0.500000 357 0.001740 64.678522 64.677675 64.679415 +890 0.750000 0.108108 175 0.001308 64.743631 64.742956 64.744264 +891 0.761905 0.148936 160 0.001003 64.745521 64.745026 64.746029 +892 0.571429 0.482759 163 0.000922 64.753741 64.753312 64.754234 +893 0.857143 0.084337 96 0.000673 64.754706 64.754390 64.755063 +894 1.000000 0.078125 76 0.000620 65.056011 65.055721 65.056342 +895 0.800000 0.055556 101 0.000607 65.060592 65.060264 65.060871 +896 0.787879 0.445946 82 0.000378 65.185776 65.185573 65.185951 +897 0.631579 0.550725 152 0.000753 65.594606 65.594227 65.594980 +898 0.823529 0.215190 90 0.000548 65.721553 65.721269 65.721816 +899 0.109091 0.932203 69 0.000665 65.801449 65.801072 65.801737 +900 0.734375 0.372093 187 0.000857 65.946661 65.946226 65.947083 +901 0.719008 0.493878 268 0.001174 65.949895 65.949275 65.950449 +902 0.521739 0.396552 66 0.000415 65.957232 65.957042 65.957457 +903 0.800000 0.061728 97 0.000872 65.958484 65.958076 65.958947 +904 0.739130 0.410714 64 0.000450 65.996474 65.996241 65.996691 +905 0.720000 0.403226 206 0.001001 66.138446 66.137930 66.138931 +906 0.357143 0.172840 97 0.000806 66.268220 66.267846 66.268651 +907 0.696429 0.329412 385 0.002319 66.272629 66.271309 66.273628 +908 0.709677 0.548673 127 0.000851 66.274559 66.274177 66.275028 +909 1.000000 0.016129 70 0.000425 66.354694 66.354464 66.354889 +910 0.750000 0.077670 118 0.000772 66.432876 66.432502 66.433274 +911 1.000000 0.088235 77 0.000487 66.602320 66.602074 66.602561 +912 0.092308 1.000000 308 0.002546 66.887651 66.886431 66.888977 +913 0.704918 0.469231 149 0.001057 66.916409 66.915850 66.916907 +914 0.578947 0.413043 109 0.000855 67.104393 67.103932 67.104787 +915 0.708333 0.505263 107 0.000688 67.150026 67.149676 67.150364 +916 0.194030 0.761364 103 0.000696 67.365747 67.365409 67.366105 +917 0.500000 0.170940 137 0.001033 67.760217 67.759705 67.760738 +918 1.000000 0.018868 60 0.000374 67.875023 67.874848 67.875222 +919 0.081081 0.965217 136 0.001072 68.375324 68.374777 68.375849 +920 0.071429 1.000000 99 0.000735 68.380209 68.379841 68.380576 +921 0.428571 0.491228 65 0.000455 68.485854 68.485630 68.486085 +922 0.555556 0.571429 74 0.000525 68.487316 68.487061 68.487587 +923 0.800000 0.090909 64 0.000476 68.589615 68.589378 68.589854 +924 0.030303 1.000000 76 0.000539 68.651622 68.651373 68.651912 +925 0.828125 0.520325 134 0.000586 69.005139 69.004862 69.005448 +926 0.794118 0.576271 71 0.000598 69.006312 69.006008 69.006606 +927 0.750000 0.061538 76 0.000573 69.060790 69.060513 69.061087 +928 0.774194 0.571429 242 0.001333 69.289502 69.288842 69.290176 +929 0.745763 0.541284 124 0.000782 69.321283 69.320919 69.321701 +930 0.739726 0.336406 238 0.001112 69.395327 69.394758 69.395870 +931 0.875000 0.103896 89 0.000595 69.397674 69.397394 69.397989 +932 0.821429 0.066986 459 0.002148 69.470075 69.469068 69.471216 +933 0.857143 0.046667 165 0.000786 69.475022 69.474619 69.475404 +934 1.000000 0.054795 85 0.000605 69.482879 69.482558 69.483163 +935 1.000000 0.073684 113 0.000937 69.839212 69.838773 69.839711 +936 0.633333 0.483871 72 0.000519 69.891753 69.891491 69.892011 +937 0.489796 0.487562 222 0.001073 69.894118 69.893578 69.894651 +938 0.545455 0.564103 130 0.000660 69.900348 69.899972 69.900632 +939 0.842105 0.078189 273 0.001514 69.938842 69.938127 69.939641 +940 0.800000 0.043103 132 0.000858 69.946709 69.946319 69.947177 +941 0.750000 0.417910 79 0.000618 70.060345 70.060053 70.060671 +942 0.923077 0.164557 90 0.000640 70.061136 70.060813 70.061453 +943 0.468750 0.492308 73 0.000468 70.104780 70.104513 70.104981 +944 0.857143 0.104478 80 0.000705 70.243123 70.242779 70.243484 +945 0.833333 0.113208 122 0.000846 70.263491 70.263034 70.263880 +946 1.000000 0.015385 77 0.000536 70.324764 70.324521 70.325057 +947 0.600000 0.066667 86 0.000570 70.451916 70.451628 70.452198 +948 1.000000 0.055556 82 0.000499 70.458989 70.458709 70.459208 +949 0.750000 0.076433 188 0.001646 70.480742 70.479932 70.481578 +950 0.695652 0.442308 62 0.000503 70.719713 70.719485 70.719988 +951 0.153846 0.838710 74 0.000590 70.788436 70.788146 70.788736 +952 0.536585 0.482353 97 0.000598 70.790830 70.790508 70.791106 +953 0.916667 0.125000 111 0.000842 70.895600 70.895180 70.896022 +954 0.697674 0.472527 103 0.000636 70.976542 70.976218 70.976854 +955 0.800000 0.221675 231 0.001432 70.977815 70.977196 70.978627 +956 0.500000 0.027778 84 0.000711 71.005866 71.005465 71.006176 +957 0.666667 0.032258 104 0.000579 71.046738 71.046466 71.047044 +958 1.000000 0.118644 66 0.000375 71.048628 71.048460 71.048835 +959 0.500000 0.158416 113 0.000609 71.304722 71.304434 71.305043 +960 0.555556 0.130435 81 0.000704 71.305976 71.305679 71.306383 +961 0.575758 0.492537 74 0.000336 71.410709 71.410575 71.410911 +962 0.741935 0.543860 64 0.000341 71.423127 71.422944 71.423286 +963 0.576923 0.240741 123 0.000770 71.557857 71.557442 71.558212 +964 0.666667 0.103448 67 0.000471 71.625939 71.625690 71.626161 +965 0.082569 1.000000 127 0.000880 71.637372 71.636933 71.637813 +966 0.250000 0.036697 125 0.000836 71.642924 71.642471 71.643306 +967 0.121622 0.986667 91 0.000806 71.679507 71.679094 71.679900 +968 0.074074 0.931034 68 0.000518 71.681439 71.681184 71.681702 +969 0.857143 0.090909 91 0.000743 71.703592 71.703192 71.703935 +970 0.074074 0.987805 96 0.000719 71.733233 71.732873 71.733592 +971 0.750000 0.515464 213 0.001074 71.735901 71.735348 71.736422 +972 0.129630 0.981818 124 0.000721 71.814546 71.814190 71.814911 +973 0.826923 0.239631 248 0.001663 71.838847 71.838012 71.839675 +974 0.888889 0.305085 72 0.000646 71.841774 71.841462 71.842107 +975 0.052083 0.979592 112 0.000708 71.982824 71.982478 71.983186 +976 0.729730 0.411111 104 0.000734 72.040677 72.040314 72.041048 +977 0.666667 0.051724 66 0.000398 72.046353 72.046163 72.046561 +978 0.750000 0.053333 174 0.001274 72.199093 72.198452 72.199726 +979 0.727273 0.099099 125 0.000821 72.201024 72.200625 72.201446 +980 0.444444 0.209302 99 0.000623 72.290632 72.290334 72.290957 +981 0.742857 0.147679 273 0.001785 72.314972 72.314095 72.315880 +982 0.814815 0.262136 114 0.000537 72.325482 72.325213 72.325750 +983 0.810345 0.588832 225 0.001469 72.406836 72.406182 72.407651 +984 0.545455 0.150685 84 0.000585 72.590944 72.590661 72.591247 +985 0.819277 0.568493 164 0.000945 72.720041 72.719630 72.720574 +986 0.080000 1.000000 88 0.000727 72.722455 72.722106 72.722833 +987 0.067568 0.948718 93 0.000742 72.749068 72.748697 72.749440 +988 0.545455 0.354839 71 0.000456 72.847576 72.847346 72.847803 +989 0.666667 0.415385 74 0.000456 72.850955 72.850738 72.851194 +990 0.573171 0.625954 144 0.000574 72.931280 72.931015 72.931589 +991 1.000000 0.039370 142 0.000788 73.093026 73.092630 73.093418 +992 0.794872 0.375000 116 0.000621 73.292512 73.292225 73.292846 +993 0.102190 1.000000 159 0.001170 73.386794 73.386241 73.387411 +994 0.069620 0.987500 184 0.001259 73.397478 73.396873 73.398132 +995 0.100917 0.990909 132 0.001242 73.415274 73.414658 73.415900 +996 0.659091 0.468085 107 0.000659 73.423186 73.422815 73.423474 +997 0.833333 0.085714 81 0.000546 73.509547 73.509297 73.509843 +998 0.833333 0.068966 98 0.000548 73.514549 73.514286 73.514834 +999 0.800000 0.072993 158 0.001178 73.532162 73.531583 73.532761 +1000 0.177215 0.814433 111 0.000715 73.564286 73.563918 73.564633 +1001 0.566038 0.430894 135 0.000607 73.608374 73.608062 73.608669 +1002 0.777778 0.107143 95 0.000554 73.643400 73.643154 73.643708 +1003 0.571429 0.059322 138 0.001018 73.660517 73.659986 73.661004 +1004 0.766667 0.491803 73 0.000695 73.921334 73.920990 73.921685 +1005 0.745455 0.500000 126 0.000867 73.926084 73.925671 73.926538 +1006 0.500000 0.023256 101 0.000799 73.947243 73.946873 73.947672 +1007 0.708333 0.089552 313 0.002360 73.951218 73.950113 73.952473 +1008 1.000000 0.070588 95 0.000559 73.954751 73.954445 73.955004 +1009 0.666667 0.075949 89 0.000508 73.958216 73.957916 73.958424 +1010 0.333333 0.318182 76 0.000530 74.034017 74.033755 74.034285 +1011 0.111111 0.259615 121 0.000868 74.293270 74.292835 74.293704 +1012 0.750000 0.055556 84 0.000647 74.383860 74.383573 74.384220 +1013 0.633333 0.333333 104 0.000675 74.476280 74.475976 74.476651 +1014 1.000000 0.079545 99 0.000603 74.583723 74.583459 74.584062 +1015 0.115385 0.866667 104 0.000706 74.721521 74.721149 74.721856 +1016 0.073684 0.896226 124 0.000933 74.722745 74.722313 74.723246 +1017 0.101351 0.736318 231 0.001539 74.724065 74.723301 74.724841 +1018 0.666667 0.034091 105 0.000887 74.770225 74.769800 74.770687 +1019 NaN 0.000000 74 0.000491 74.773123 74.772828 74.773319 +1020 0.034483 0.966667 73 0.000655 74.876785 74.876449 74.877104 +1021 0.750000 0.074074 60 0.000307 74.881083 74.880920 74.881227 +1022 0.833333 0.089552 76 0.000592 74.882699 74.882419 74.883011 +1023 0.736842 0.152000 145 0.001043 74.906869 74.906339 74.907381 +1024 0.766667 0.379747 89 0.000522 74.908347 74.908108 74.908629 +1025 0.686567 0.421384 176 0.000833 74.909221 74.908764 74.909597 +1026 0.600000 0.116279 100 0.000736 74.968640 74.968296 74.969032 +1027 0.057692 1.000000 59 0.000360 74.983773 74.983606 74.983966 +1028 0.109756 1.000000 94 0.000617 75.015103 75.014815 75.015432 +1029 0.609756 0.424870 223 0.001525 75.017428 75.016624 75.018149 +1030 0.454545 0.222973 165 0.000832 75.255650 75.255232 75.256064 +1031 0.064516 0.436620 86 0.000746 75.280169 75.279757 75.280503 +1032 0.085271 0.977273 154 0.001063 75.283679 75.283162 75.284225 +1033 0.089552 0.971014 82 0.000646 75.309127 75.308780 75.309426 +1034 0.557377 0.393548 179 0.001201 75.410204 75.409556 75.410756 +1035 0.108108 0.936709 93 0.000720 75.620856 75.620502 75.621222 +1036 0.087912 0.968085 114 0.000993 75.622000 75.621468 75.622462 +1037 0.666667 0.031579 108 0.000685 75.695277 75.694929 75.695615 +1038 0.769231 0.111111 137 0.001006 75.712810 75.712303 75.713309 +1039 0.875000 0.070175 132 0.000888 75.717232 75.716777 75.717665 +1040 0.093333 0.961538 94 0.000682 75.768537 75.768185 75.768867 +1041 0.482759 0.500000 65 0.000308 75.777692 75.777554 75.777862 +1042 0.603774 0.386861 155 0.000939 75.783884 75.783441 75.784380 +1043 0.461538 0.090909 167 0.001199 75.791073 75.790482 75.791681 +1044 0.833333 0.093750 75 0.000516 75.826826 75.826574 75.827090 +1045 0.500000 0.030303 146 0.000775 75.846424 75.846023 75.846798 +1046 0.875000 0.115942 76 0.000400 75.869669 75.869496 75.869895 +1047 0.875000 0.051948 177 0.001232 75.872089 75.871481 75.872713 +1048 1.000000 0.087500 178 0.000966 75.873450 75.872932 75.873899 +1049 0.604651 0.716667 198 0.000877 75.897386 75.896945 75.897822 +1050 1.000000 0.076923 74 0.000488 76.143669 76.143423 76.143911 +1051 0.825000 0.281690 163 0.001070 76.171577 76.171035 76.172105 +1052 0.666667 0.112782 145 0.000737 76.293484 76.293114 76.293851 +1053 0.633333 0.437956 155 0.000913 76.297927 76.297446 76.298359 +1054 0.535714 0.411765 156 0.000983 76.332356 76.331875 76.332858 +1055 0.794118 0.548387 72 0.000545 76.387276 76.386983 76.387527 +1056 0.147541 0.983871 72 0.000556 76.413717 76.413421 76.413977 +1057 0.063492 0.984375 72 0.000409 76.445348 76.445126 76.445535 +1058 0.875000 0.145455 63 0.000401 76.450732 76.450528 76.450929 +1059 0.727273 0.555556 113 0.000692 76.580150 76.579815 76.580507 +1060 0.568182 0.453608 109 0.000666 76.584711 76.584369 76.585035 +1061 0.642202 0.405204 291 0.001118 76.672153 76.671505 76.672623 +1062 0.733333 0.436047 196 0.001231 76.730868 76.730350 76.731581 +1063 1.000000 0.117647 77 0.000494 76.770768 76.770508 76.771003 +1064 0.555556 0.086538 119 0.000770 76.782802 76.782414 76.783184 +1065 0.500000 0.145455 62 0.000392 77.244417 77.244221 77.244614 +1066 0.739130 0.543307 144 0.000928 77.260838 77.260379 77.261307 +1067 0.610294 0.467354 313 0.001118 77.262499 77.261941 77.263059 +1068 1.000000 0.050633 92 0.000681 77.266758 77.266370 77.267051 +1069 0.294118 0.326923 57 0.000276 77.280885 77.280731 77.281007 +1070 0.092593 0.981818 63 0.000394 77.333105 77.332915 77.333309 +1071 NaN 0.000000 87 0.000774 77.408342 77.407973 77.408747 +1072 0.588235 0.369565 105 0.000640 77.410444 77.410095 77.410735 +1073 0.714286 0.047138 326 0.001508 77.461825 77.461060 77.462568 +1074 0.953488 0.544304 179 0.001085 77.477589 77.477067 77.478152 +1075 0.545455 0.117021 214 0.001297 77.709209 77.708588 77.709885 +1076 0.279070 0.209756 242 0.001928 77.711136 77.710158 77.712086 +1077 0.875000 0.062016 142 0.000698 77.761954 77.761645 77.762343 +1078 1.000000 0.034722 160 0.000834 77.766250 77.765811 77.766645 +1079 0.550000 0.370370 62 0.000431 77.853512 77.853312 77.853742 +1080 0.583333 0.465116 141 0.000614 77.895763 77.895415 77.896029 +1081 0.666667 0.054545 63 0.000408 77.909110 77.908883 77.909291 +1082 0.888889 0.065217 156 0.000909 77.910089 77.909619 77.910528 +1083 1.000000 0.116071 126 0.000714 77.940043 77.939685 77.940399 +1084 1.000000 0.098592 82 0.000560 77.941054 77.940806 77.941366 +1085 0.489796 0.449541 125 0.000852 77.949807 77.949354 77.950206 +1086 0.714286 0.100000 85 0.000700 77.965496 77.965146 77.965846 +1087 0.121429 0.921053 175 0.001160 78.002779 78.002211 78.003371 +1088 0.240000 0.238095 123 0.001023 78.055236 78.054727 78.055751 +1089 0.368421 0.211111 106 0.000836 78.057751 78.057328 78.058164 +1090 0.352941 0.303571 65 0.000468 78.078088 78.077850 78.078319 +1091 0.804054 0.500000 321 0.001257 78.248130 78.247511 78.248768 +1092 0.750000 0.037383 123 0.000870 78.446945 78.446521 78.447391 +1093 0.096296 0.978261 164 0.001318 78.504069 78.503434 78.504752 +1094 0.811688 0.495177 340 0.001559 78.530357 78.529575 78.531135 +1095 0.459459 0.564885 150 0.000958 78.637958 78.637506 78.638464 +1096 0.085366 0.976190 100 0.000777 78.653223 78.652854 78.653631 +1097 0.735294 0.079254 480 0.002548 78.738906 78.737783 78.740331 +1098 0.828571 0.564516 71 0.000435 78.802933 78.802728 78.803163 +1099 0.318182 0.258824 95 0.000620 79.002164 79.001859 79.002478 +1100 0.846154 0.069519 210 0.001156 79.003377 79.002859 79.004015 +1101 1.000000 0.100559 198 0.001060 79.004920 79.004320 79.005380 +1102 0.750000 0.098765 92 0.000586 79.019415 79.019122 79.019708 +1103 0.500000 0.071429 63 0.000419 79.169102 79.168885 79.169305 +1104 0.666667 0.169811 62 0.000489 79.221431 79.221186 79.221675 +1105 0.807692 0.361111 159 0.000794 79.277827 79.277437 79.278230 +1106 0.810000 0.423729 264 0.001493 79.291630 79.290885 79.292378 +1107 0.833333 0.065217 304 0.001422 79.294047 79.293378 79.294799 +1108 1.000000 0.074074 122 0.000695 79.498561 79.498176 79.498871 +1109 0.878788 0.578947 121 0.000345 79.550402 79.550201 79.550546 +1110 0.625000 0.533333 113 0.000427 79.593914 79.593710 79.594137 +1111 0.614286 0.526316 149 0.000807 79.752166 79.751782 79.752589 +1112 1.000000 0.060150 150 0.000882 79.822212 79.821794 79.822677 +1113 0.500000 0.075472 62 0.000462 79.847731 79.847500 79.847962 +1114 1.000000 0.066667 138 0.000851 79.851528 79.851144 79.851995 +1115 0.035714 1.000000 63 0.000365 79.938365 79.938154 79.938519 +1116 0.186441 0.440299 156 0.001252 79.945543 79.944880 79.946132 +1117 0.804878 0.431579 106 0.000593 79.950791 79.950463 79.951056 +1118 0.492063 0.577982 364 0.001927 80.121075 80.120111 80.122038 +1119 0.769231 0.117117 125 0.000699 80.340021 80.339682 80.340381 +1120 0.750000 0.097561 91 0.000494 80.364054 80.363828 80.364321 +1121 0.600000 0.097087 122 0.001053 80.365941 80.365373 80.366426 +1122 0.638889 0.514286 80 0.000581 80.375148 80.374884 80.375465 +1123 0.766667 0.434783 76 0.000370 80.474913 80.474751 80.475120 +1124 0.888889 0.083333 239 0.001224 80.548552 80.547871 80.549095 +1125 0.692308 0.245283 182 0.001178 80.584811 80.584207 80.585385 +1126 0.814815 0.346154 88 0.000533 80.677280 80.677037 80.677570 +1127 0.041667 1.000000 81 0.000459 80.705536 80.705300 80.705759 +1128 0.857143 0.065728 241 0.001521 80.727182 80.726452 80.727973 +1129 1.000000 0.042373 133 0.000782 80.729607 80.729254 80.730035 +1130 0.050000 0.975610 95 0.000724 80.779764 80.779453 80.780177 +1131 0.777778 0.166667 63 0.000487 80.825536 80.825296 80.825784 +1132 0.480000 0.666667 86 0.000581 80.919317 80.919014 80.919594 +1133 0.875000 0.049383 178 0.000838 80.936789 80.936377 80.937214 +1134 0.121622 0.973684 89 0.000598 81.023817 81.023548 81.024146 +1135 0.065693 1.000000 155 0.000925 81.025410 81.024926 81.025852 +1136 0.833333 0.095238 70 0.000400 81.371794 81.371599 81.371999 \ No newline at end of file diff --git a/tests/test_data/output/ds1/data.npy b/tests/test_data/output/ds1/data.npy new file mode 100644 index 0000000..d490508 Binary files /dev/null and b/tests/test_data/output/ds1/data.npy differ diff --git a/tests/test_data/output/ds1/dcbs_bursts.csv b/tests/test_data/output/ds1/dcbs_bursts.csv new file mode 100644 index 0000000..dba249a --- /dev/null +++ b/tests/test_data/output/ds1/dcbs_bursts.csv @@ -0,0 +1,72 @@ +E_app,S_app,n_photons,time_length,time_mean,time_min,time_max +0.7090909090909091,0.5,110,0.00048459999999999996,0.13980058136363638,0.13952675,0.14001135 +0.6811594202898551,0.5433070866141733,127,0.00042534999999999996,0.2711507921259842,0.27095255,0.2713779 +0.484375,0.512,125,0.00057225,5.3500339403999995,5.349704399999999,5.35027665 +0.6111111111111112,0.43902439024390244,123,0.00039854999999999996,5.45090887804878,5.4506973,5.45109585 +0.6451612903225806,0.49206349206349204,126,0.0006198,6.799205194444444,6.79891695,6.79953675 +1.0,0.6931818181818182,176,0.00047484999999999997,7.643020693465909,7.642821,7.6432958499999994 +0.6875,0.5454545454545454,89,0.0003388,7.68005721741573,7.679917,7.680255799999999 +0.7230769230769231,0.48872180451127817,133,0.0005114,8.875054557142857,8.8747815,8.8752929 +0.8507462686567164,0.5037593984962406,133,0.00048324999999999996,9.027363400751879,9.02708825,9.027571499999999 +0.5233644859813084,0.5119617224880383,209,0.0006818999999999999,9.631469436363636,9.631135,9.631816899999999 +0.6833333333333333,0.5429864253393665,221,0.00076335,10.812502875339366,10.81211545,10.8128788 +0.7464788732394366,0.5440613026819924,261,0.0011979,11.679098574712642,11.6784802,11.6796781 +0.7761194029850746,0.5775862068965517,116,0.0003561,14.274684119827585,14.274547649999999,14.27490375 +0.6612903225806451,0.6169154228855721,201,0.0006135,15.665242684079601,15.6649169,15.6655304 +0.8051948051948052,0.47530864197530864,162,0.0006631,16.13452135679012,16.134190399999998,16.1348535 +0.80625,0.5405405405405406,297,0.00097375,16.13966915673401,16.13915475,16.1401285 +0.8089430894308943,0.5146443514644351,479,0.0016311499999999998,16.24012433235908,16.23931635,16.2409475 +0.5945945945945946,0.5034013605442177,147,0.0005979,18.39558926428571,18.39529185,18.39588975 +0.6226415094339622,0.4344262295081967,123,0.000354,21.44975622235772,21.449592449999997,21.44994645 +0.4090909090909091,0.5546218487394958,119,0.00048995,21.57673112142857,21.576482199999997,21.57697215 +0.9069767441860465,0.43434343434343436,99,0.0002866,24.707691356565658,24.7075447,24.7078313 +0.7380952380952381,0.5957446808510638,141,0.00060865,27.03995338333333,27.039644499999998,27.040253149999998 +0.6736842105263158,0.5191256830601093,183,0.00069175,27.941435280601087,27.94109425,27.941786 +0.7788461538461539,0.5123152709359606,203,0.0009737999999999999,30.209923131280785,30.20943845,30.210412249999997 +0.7125,0.43243243243243246,185,0.0007323999999999999,31.936385518378376,31.9360245,31.9367569 +0.7619047619047619,0.375,168,0.00051665,33.82778103809523,33.82750865,33.8280253 +0.7857142857142857,0.5185185185185185,216,0.00069445,33.836178661805555,33.83582485,33.8365193 +0.8076923076923077,0.416,125,0.0005398,33.836969426799996,33.8367024,33.8372422 +0.4942528735632184,0.4046511627906977,215,0.0007138,34.89043054465116,34.89005775,34.89077155 +0.5887850467289719,0.49537037037037035,217,0.00089345,34.89124286267281,34.8908157,34.89170915 +0.44,0.5,100,0.00052275,36.698327346499994,36.69804475,36.698567499999996 +0.8955223880597015,0.5317460317460317,126,0.00061965,37.41220079047619,37.41190175,37.412521399999996 +0.8904109589041096,0.5748031496062992,128,0.0006365,37.41300270390625,37.41267695,37.41331345 +0.65625,0.5052631578947369,190,0.0006563,37.51882042763158,37.518477149999995,37.51913345 +0.6148148148148148,0.5192307692307693,260,0.00088435,37.80172908557692,37.8012958,37.80218015 +0.5604395604395604,0.5321637426900585,172,0.0006432499999999999,38.471371495058136,38.47102355,38.4716668 +0.8135593220338984,0.47580645161290325,125,0.0005697499999999999,39.6247250892,39.6244356,39.625005349999995 +0.7878787878787878,0.518324607329843,191,0.0005775999999999999,39.82168185837696,39.821391049999995,39.821968649999995 +0.5789473684210527,0.6031746031746031,126,0.000596,43.8458555,43.8455412,43.8461372 +0.5611510791366906,0.549407114624506,253,0.0009234499999999999,44.30867967687747,44.30821985,44.309143299999995 +0.6029411764705882,0.5271317829457365,129,0.000405,44.69142211046511,44.6912046,44.6916096 +0.4878048780487805,0.45387453874538747,271,0.0012441,45.00568387250922,45.0050679,45.006312 +0.8870967741935484,0.4881889763779528,127,0.00045414999999999995,45.235279990944875,45.23505445,45.235508599999996 +0.7461538461538462,0.4727272727272727,275,0.00067855,45.50587177581818,45.50553305,45.5062116 +0.7761194029850746,0.5726495726495726,117,0.000511,46.9085413051282,46.9083322,46.9088432 +0.11320754716981132,0.49074074074074076,108,0.00048534999999999995,48.94197349907407,48.941736399999996,48.942221749999995 +0.6567164179104478,0.47183098591549294,710,0.00168645,49.97919105978873,49.9783106,49.979997049999994 +0.5,0.49382716049382713,162,0.0007032,50.18369730987654,50.183361399999995,50.1840646 +0.5873015873015873,0.47368421052631576,135,0.0005071,50.42227173851852,50.4220408,50.4225479 +0.686046511627907,0.4574468085106383,190,0.00049465,51.987542827894735,51.987300499999996,51.98779515 +0.7887323943661971,0.47651006711409394,298,0.0007825499999999999,51.98880205503356,51.9884072,51.989189749999994 +0.7298850574712644,0.58,300,0.0008093,56.084851627499994,56.084482949999995,56.085292249999995 +0.8412698412698413,0.5206611570247934,121,0.0004294,59.825407404132235,59.825189449999996,59.82561885 +0.5714285714285714,0.46226415094339623,106,0.00038554999999999997,60.825805465566035,60.82558565,60.8259712 +0.46153846153846156,0.49523809523809526,105,0.0004563,61.207551699523805,61.207296549999995,61.20775285 +0.6545454545454545,0.49327354260089684,223,0.0007509,62.3799978,62.379644899999995,62.380395799999995 +0.6,0.4807692307692308,209,0.00073055,62.39014625574163,62.3897644,62.39049495 +0.56,0.5725190839694656,131,0.0004984,64.67799408816794,64.6777913,64.6782897 +0.55,0.4580152671755725,131,0.0004729,64.67903556297709,64.67881344999999,64.67928635 +0.71875,0.47761194029850745,201,0.0007477,65.9499088363184,65.94951965,65.95026734999999 +0.7931034482758621,0.4715447154471545,123,0.00046929999999999997,66.13850722886178,66.13828275,66.13875205 +0.7619047619047619,0.42857142857142855,148,0.00054685,66.27317363141891,66.2729042,66.27345104999999 +0.8181818181818182,0.5045871559633027,109,0.00038485,69.00513898348623,69.00497134999999,69.0053562 +0.7450980392156863,0.5425531914893617,94,0.00034294999999999996,69.39520370265957,69.39500955,69.3953525 +0.47619047619047616,0.5,168,0.00070855,69.89411067321427,69.893765,69.89447355 +0.7741935483870968,0.47692307692307695,130,0.00058575,71.73599183192307,71.73566315,71.73624889999999 +0.68,0.45871559633027525,109,0.00043464999999999997,74.90923457798165,74.90898575,74.9094204 +0.6176470588235294,0.425,160,0.0004744,76.6723790678125,76.6721288,76.6726032 +0.6220472440944882,0.508,250,0.00072645,77.26250836759999,77.26212215,77.2628486 +0.7941176470588235,0.49097472924187724,277,0.00095305,78.24810627292418,78.24763775,78.2485908 +0.8151260504201681,0.49583333333333335,240,0.0009398999999999999,78.530338238125,78.5298456,78.5307855 diff --git a/tests/test_data/output/ds1/dcbs_bursts.h5 b/tests/test_data/output/ds1/dcbs_bursts.h5 new file mode 100644 index 0000000..c1679e3 Binary files /dev/null and b/tests/test_data/output/ds1/dcbs_bursts.h5 differ diff --git a/tests/test_data/output/ds1/dcbs_bursts.txt b/tests/test_data/output/ds1/dcbs_bursts.txt new file mode 100644 index 0000000..ef1b633 --- /dev/null +++ b/tests/test_data/output/ds1/dcbs_bursts.txt @@ -0,0 +1,72 @@ + E_app S_app n_photons time_length time_mean time_min time_max +0 0.709091 0.500000 119 0.000485 0.139799 0.139527 0.140011 +1 0.681159 0.543307 135 0.000425 0.271153 0.270953 0.271378 +2 0.484375 0.512000 136 0.000572 5.350030 5.349704 5.350277 +3 0.611111 0.439024 131 0.000399 5.450907 5.450697 5.451096 +4 0.645161 0.492063 138 0.000620 6.799208 6.798917 6.799537 +5 1.000000 0.693182 186 0.000475 7.643023 7.642821 7.643296 +6 0.687500 0.545455 95 0.000339 7.680058 7.679917 7.680256 +7 0.723077 0.488722 143 0.000511 8.875053 8.874781 8.875293 +8 0.850746 0.503759 142 0.000483 9.027361 9.027088 9.027571 +9 0.523364 0.511962 222 0.000682 9.631471 9.631135 9.631817 +10 0.683333 0.542986 236 0.000763 10.812504 10.812115 10.812879 +11 0.746479 0.544061 283 0.001198 11.679098 11.678480 11.679678 +12 0.776119 0.577586 123 0.000356 14.274686 14.274548 14.274904 +13 0.661290 0.616915 213 0.000614 15.665242 15.664917 15.665530 +14 0.805195 0.475309 175 0.000663 16.134520 16.134190 16.134853 +15 0.806250 0.540541 316 0.000974 16.139666 16.139155 16.140128 +16 0.808943 0.514644 511 0.001631 16.240126 16.239316 16.240948 +17 0.594595 0.503401 158 0.000598 18.395590 18.395292 18.395890 +18 0.622642 0.434426 130 0.000354 21.449757 21.449592 21.449946 +19 0.409091 0.554622 129 0.000490 21.576731 21.576482 21.576972 +20 0.906977 0.434343 104 0.000287 24.707691 24.707545 24.707831 +21 0.738095 0.595745 152 0.000609 27.039953 27.039644 27.040253 +22 0.673684 0.519126 197 0.000692 27.941437 27.941094 27.941786 +23 0.778846 0.512315 222 0.000974 30.209925 30.209438 30.210412 +24 0.712500 0.432432 199 0.000732 31.936385 31.936024 31.936757 +25 0.761905 0.375000 178 0.000517 33.827780 33.827509 33.828025 +26 0.785714 0.518519 230 0.000694 33.836178 33.835825 33.836519 +27 0.807692 0.416000 135 0.000540 33.836970 33.836702 33.837242 +28 0.494253 0.404651 229 0.000714 34.890430 34.890058 34.890772 +29 0.588785 0.495370 234 0.000893 34.891244 34.890816 34.891709 +30 0.440000 0.500000 110 0.000523 36.698324 36.698045 36.698567 +31 0.895522 0.531746 138 0.000620 37.412203 37.411902 37.412521 +32 0.890411 0.574803 140 0.000637 37.413002 37.412677 37.413313 +33 0.656250 0.505263 203 0.000656 37.518819 37.518477 37.519133 +34 0.614815 0.519231 277 0.000884 37.801729 37.801296 37.802180 +35 0.560440 0.532164 184 0.000643 38.471370 38.471024 38.471667 +36 0.813559 0.475806 136 0.000570 39.624723 39.624436 39.625005 +37 0.787879 0.518325 202 0.000578 39.821681 39.821391 39.821969 +38 0.578947 0.603175 137 0.000596 43.845854 43.845541 43.846137 +39 0.561151 0.549407 271 0.000923 44.308678 44.308220 44.309143 +40 0.597015 0.523438 136 0.000405 44.691421 44.691205 44.691610 +41 0.487805 0.453875 295 0.001244 45.005685 45.005068 45.006312 +42 0.887097 0.488189 136 0.000454 45.235281 45.235054 45.235509 +43 0.746154 0.472727 289 0.000679 45.505872 45.505533 45.506212 +44 0.776119 0.572650 127 0.000511 46.908546 46.908332 46.908843 +45 0.113208 0.490741 117 0.000485 48.941974 48.941736 48.942222 +46 0.656716 0.471831 743 0.001686 49.979189 49.978311 49.979997 +47 0.500000 0.493827 176 0.000703 50.183698 50.183361 50.184065 +48 0.587302 0.473684 145 0.000507 50.422274 50.422041 50.422548 +49 0.686047 0.457447 200 0.000495 51.987542 51.987300 51.987795 +50 0.788732 0.476510 313 0.000783 51.988801 51.988407 51.989190 +51 0.729885 0.580000 315 0.000809 56.084853 56.084483 56.085292 +52 0.841270 0.520661 130 0.000429 59.825407 59.825189 59.825619 +53 0.571429 0.462264 114 0.000386 60.825804 60.825586 60.825971 +54 0.461538 0.495238 113 0.000456 61.207550 61.207297 61.207753 +55 0.654545 0.493274 238 0.000751 62.380000 62.379645 62.380396 +56 0.600000 0.480769 223 0.000731 62.390146 62.389764 62.390495 +57 0.560000 0.572519 140 0.000498 64.677997 64.677791 64.678290 +58 0.550000 0.458015 140 0.000473 64.679037 64.678813 64.679286 +59 0.718750 0.477612 216 0.000748 65.949908 65.949520 65.950267 +60 0.793103 0.471545 132 0.000469 66.138508 66.138283 66.138752 +61 0.761905 0.428571 159 0.000547 66.273174 66.272904 66.273451 +62 0.818182 0.504587 116 0.000385 69.005140 69.004971 69.005356 +63 0.745098 0.542553 100 0.000343 69.395202 69.395010 69.395353 +64 0.476190 0.500000 182 0.000709 69.894111 69.893765 69.894474 +65 0.774194 0.476923 140 0.000586 71.735992 71.735663 71.736249 +66 0.680000 0.458716 117 0.000435 74.909233 74.908986 74.909420 +67 0.617647 0.425000 169 0.000474 76.672378 76.672129 76.672603 +68 0.622047 0.508000 265 0.000726 77.262507 77.262122 77.262849 +69 0.794118 0.490975 295 0.000953 78.248107 78.247638 78.248591 +70 0.815126 0.495833 257 0.000940 78.530338 78.529846 78.530785 \ No newline at end of file diff --git a/tests/test_data/output/ds1/detectors.npy b/tests/test_data/output/ds1/detectors.npy new file mode 100644 index 0000000..0b079fb Binary files /dev/null and b/tests/test_data/output/ds1/detectors.npy differ diff --git a/tests/test_data/output/ds1/metadata.yaml b/tests/test_data/output/ds1/metadata.yaml new file mode 100644 index 0000000..ed60181 --- /dev/null +++ b/tests/test_data/output/ds1/metadata.yaml @@ -0,0 +1,533 @@ +acquisition_duration: 36000.0 +creation_time: '2019-01-28 17:56:04' +hardware_name: TimeHarp260P +laser_repetition_rate: 20000000 +nanotimes_unit: 2.50000003337858e-11 +record_type: rtTimeHarp260PT3 +software: SymPhoTime 64 +software_version: '2.4' +tags: + CreatorSW_Modules: + idx: -1 + type: tyInt8 + value: 0 + CreatorSW_Name: + data: SymPhoTime 64 + idx: -1 + type: tyAnsiString + value: 16 + CreatorSW_SVNBuild: + idx: -1 + type: tyInt8 + value: 4874 + CreatorSW_Version: + data: '2.4' + idx: -1 + type: tyAnsiString + value: 8 + Fast_Load_End: + idx: -1 + type: tyEmpty8 + value: 0 + File_Comment: + data: 2000 dillution + idx: -1 + type: tyAnsiString + value: 16 + File_CreatingTime: + idx: -1 + type: tyTDateTime + value: '2019-01-28 17:56:04' + File_GUID: + data: '{7BB8CC14-384D-41EB-A4BC-139FFBA9E53C}' + idx: -1 + type: tyAnsiString + value: 40 + HWInpChan_CFDLevel: + idx: + - 0 + - 1 + type: + - tyInt8 + - tyInt8 + value: + - -100 + - -100 + HWInpChan_CFDZeroCross: + idx: + - 0 + - 1 + type: + - tyInt8 + - tyInt8 + value: + - -10 + - -10 + HWInpChan_Enabled: + idx: + - 0 + - 1 + type: + - tyBool8 + - tyBool8 + value: + - true + - true + HWInpChan_ModuleIdx: + idx: + - 0 + - 1 + type: + - tyInt8 + - tyInt8 + value: + - 1 + - 1 + HWInpChan_Offset: + idx: + - 0 + - 1 + type: + - tyInt8 + - tyInt8 + value: + - 0 + - 450 + HWInpChan_TdCode: + idx: + - 0 + - 1 + type: + - tyInt8 + - tyInt8 + value: + - 4 + - 4 + HWMarkers_Enabled: + idx: + - 0 + - 1 + - 2 + - 3 + type: + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + value: + - true + - true + - true + - true + HWMarkers_HoldOff: + idx: -1 + type: tyInt8 + value: 0 + HWMarkers_RisingEdge: + idx: + - 0 + - 1 + - 2 + - 3 + type: + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + value: + - false + - false + - false + - false + HWModule_TypeCode: + idx: 0 + type: tyInt8 + value: 1000 + HWModule_VersCode: + idx: 0 + type: tyInt8 + value: 18939908 + HWSync_CFDLevel: + idx: -1 + type: tyInt8 + value: -150 + HWSync_CFDZeroCross: + idx: -1 + type: tyInt8 + value: -10 + HWSync_Divider: + idx: -1 + type: tyInt8 + value: 8 + HWSync_Offset: + idx: -1 + type: tyInt8 + value: 74000 + HW_BaseResolution: + idx: -1 + type: tyFloat8 + value: 2.50000003337858e-11 + HW_ExternalRefClock: + idx: -1 + type: tyBool8 + value: false + HW_InpChannels: + idx: -1 + type: tyInt8 + value: 3 + HW_Markers: + idx: -1 + type: tyInt8 + value: 4 + HW_Modules: + idx: -1 + type: tyInt8 + value: 1 + HW_SerialNo: + data: '1034990' + idx: -1 + type: tyAnsiString + value: 8 + HW_Type: + data: TimeHarp260P + idx: -1 + type: tyAnsiString + value: 16 + HW_Version: + data: 0121-0422-02.01 + idx: -1 + type: tyAnsiString + value: 16 + Header_End: + idx: -1 + type: tyEmpty8 + value: 0 + ImgHdr_Dimensions: + idx: -1 + type: tyInt8 + value: 1 + ImgHdr_Ident: + idx: -1 + type: tyInt8 + value: 7 + ImgHdr_X0: + idx: -1 + type: tyFloat8 + value: 40.0 + ImgHdr_Y0: + idx: -1 + type: tyFloat8 + value: 40.0 + ImgHdr_Z0: + idx: -1 + type: tyFloat8 + value: 40.0 + MeasDesc_AcquisitionTime: + idx: -1 + type: tyInt8 + value: 36000000 + MeasDesc_BinningFactor: + idx: -1 + type: tyInt8 + value: 1 + MeasDesc_GlobalResolution: + idx: -1 + type: tyFloat8 + value: 5.0e-08 + MeasDesc_Resolution: + idx: -1 + type: tyFloat8 + value: 2.50000003337858e-11 + Measurement_Mode: + idx: -1 + type: tyInt8 + value: 3 + Measurement_SubMode: + idx: -1 + type: tyInt8 + value: 1 + Sep2_FirmwareVersion: + data: 1.05.420 + idx: -1 + type: tyAnsiString + value: 16 + Sep2_SCM_000_SerialNo: + data: '1034451' + idx: -1 + type: tyAnsiString + value: 8 + Sep2_SLM_200_CW_PulseDisable: + idx: -1 + type: tyBool8 + value: false + Sep2_SLM_200_FineIntensity: + idx: -1 + type: tyInt8 + value: 57 + Sep2_SLM_200_HeadType: + data: LASER + idx: -1 + type: tyAnsiString + value: 8 + Sep2_SLM_200_SerialNo: + data: '1035701' + idx: -1 + type: tyAnsiString + value: 8 + Sep2_SLM_200_TriggerIndex: + idx: -1 + type: tyInt8 + value: 7 + Sep2_SLM_300_CW_PulseDisable: + idx: -1 + type: tyBool8 + value: false + Sep2_SLM_300_FineIntensity: + idx: -1 + type: tyInt8 + value: 40 + Sep2_SLM_300_HeadType: + data: LASER + idx: -1 + type: tyAnsiString + value: 8 + Sep2_SLM_300_SerialNo: + data: '1035702' + idx: -1 + type: tyAnsiString + value: 8 + Sep2_SLM_300_TriggerIndex: + idx: -1 + type: tyInt8 + value: 7 + Sep2_SOM_100_BaseOscFreq: + idx: -1 + type: tyFloat8 + value: 80000000.0 + Sep2_SOM_100_Divider: + idx: -1 + type: tyInt8 + value: 2 + Sep2_SOM_100_InvSyncMask: + idx: -1 + type: tyBool8 + value: true + Sep2_SOM_100_PreSync: + idx: -1 + type: tyInt8 + value: 0 + Sep2_SOM_100_Seq_BurstLen: + idx: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + type: + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + value: + - 1 + - 1 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + Sep2_SOM_100_Seq_CoarseDelay: + idx: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + type: + - tyFloat8 + - tyFloat8 + - tyFloat8 + - tyFloat8 + - tyFloat8 + - tyFloat8 + - tyFloat8 + - tyFloat8 + value: + - 0.0 + - 0.7 + - 0.0 + - 0.0 + - 0.0 + - 0.0 + - 0.0 + - 0.0 + Sep2_SOM_100_Seq_EnaOutput: + idx: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + type: + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + value: + - true + - true + - false + - false + - false + - false + - false + - false + Sep2_SOM_100_Seq_EnaSync: + idx: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + type: + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + - tyBool8 + value: + - true + - false + - false + - false + - false + - false + - false + - false + Sep2_SOM_100_Seq_FineDelay: + idx: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + type: + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + - tyInt8 + value: + - 20 + - 20 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + Sep2_SOM_100_SerialNo: + data: '1035467' + idx: -1 + type: tyAnsiString + value: 8 + Sep2_SOM_100_SyncMask: + idx: -1 + type: tyInt8 + value: 1 + Sep2_SerialNo: + data: '1034451' + idx: -1 + type: tyAnsiString + value: 8 + TTResultFormat_BitsPerRecord: + idx: -1 + type: tyInt8 + value: 32 + TTResultFormat_TTTRRecType: + idx: -1 + type: tyInt8 + value: 66310 + TTResult_InputRate: + idx: + - 0 + - 1 + type: + - tyInt8 + - tyInt8 + value: + - 100 + - 80 + TTResult_NumberOfRecords: + idx: -1 + type: tyInt8 + value: 1132842 + TTResult_StopAfter: + idx: -1 + type: tyInt8 + value: 81440 + TTResult_StopReason: + idx: -1 + type: tyInt8 + value: 1 + TTResult_SyncRate: + idx: -1 + type: tyInt8 + value: 20000000 + UsrHeadName: + data: + - 520.0nm (LDH-D-C-520) + - 640.0nm (LDH-D-C-640) + idx: + - 0 + - 1 + type: + - tyAnsiString + - tyAnsiString + value: + - 24 + - 24 + UsrPowerDiode: + idx: -1 + type: tyFloat8 + value: 40207.30211684235 + UsrPulseCfg: + data: PIE + idx: -1 + type: tyAnsiString + value: 8 + UsrPulseCfgIdx: + idx: -1 + type: tyInt8 + value: 1 +timestamps_unit: 5.0e-08 diff --git a/tests/test_data/output/ds1/nanotimes.npy b/tests/test_data/output/ds1/nanotimes.npy new file mode 100644 index 0000000..01047aa Binary files /dev/null and b/tests/test_data/output/ds1/nanotimes.npy differ diff --git a/tests/test_data/output/ds1/photons.h5 b/tests/test_data/output/ds1/photons.h5 new file mode 100644 index 0000000..801b4aa Binary files /dev/null and b/tests/test_data/output/ds1/photons.h5 differ diff --git a/tests/test_data/output/ds1/timestamps.npy b/tests/test_data/output/ds1/timestamps.npy new file mode 100644 index 0000000..795fd06 Binary files /dev/null and b/tests/test_data/output/ds1/timestamps.npy differ diff --git a/tests/test_data/output/ds1/z_img_apbs.npy b/tests/test_data/output/ds1/z_img_apbs.npy new file mode 100644 index 0000000..b9144bc Binary files /dev/null and b/tests/test_data/output/ds1/z_img_apbs.npy differ diff --git a/tests/test_data/output/ds1/z_img_dcbs.npy b/tests/test_data/output/ds1/z_img_dcbs.npy new file mode 100644 index 0000000..4bfbd91 Binary files /dev/null and b/tests/test_data/output/ds1/z_img_dcbs.npy differ diff --git a/tests/test_data/output/web/DCBS_default_bursts.csv b/tests/test_data/output/web/DCBS_default_bursts.csv new file mode 100644 index 0000000..852fb31 --- /dev/null +++ b/tests/test_data/output/web/DCBS_default_bursts.csv @@ -0,0 +1,230 @@ +,E_app,S_app,n_photons,time_length,time_mean,time_min,time_max,n_DD,n_DA,n_AA,n_AD,tau_DD,tau_DA,tau_AA,tau_AD,filename +0,0.5394736842105263,0.5615763546798029,407,0.0016798499999999999,0.5821335101965601,0.58126295,0.5829428,105,123,178,1,2.6492857496574723e-09,3.726016309910254e-09,2.876685431666296e-08,2.7300000364494093e-08,f4.ptu +1,0.6666666666666666,0.5,126,0.00042964999999999995,0.6517511976190475,0.6515398,0.65196945,21,42,63,0,2.3464286027567528e-09,3.557738142738993e-09,2.9110714674384005e-08,,f4.ptu +2,0.5319148936170213,0.48205128205128206,195,0.00066875,0.8446144617948718,0.8442822999999999,0.8449510499999999,44,50,101,0,3.1789773151711712e-09,4.2075000561761505e-09,2.8527970677918958e-08,,f4.ptu +3,0.3805309734513274,0.46887966804979253,241,0.0007504499999999999,2.5442334163900413,2.5438294,2.54457985,70,43,128,0,2.7935714658696076e-09,3.2552326016015513e-09,2.810332068771957e-08,,f4.ptu +4,0.15,0.5813953488372093,172,0.00062635,2.9248856671511625,2.92461285,2.9252392,85,15,72,0,3.5211765176009833e-09,2.2600000301742365e-09,2.8517361491858714e-08,,f4.ptu +5,0.11888111888111888,0.6559633027522935,218,0.0006665999999999999,2.925925973853211,2.9255554999999998,2.9262221,126,17,75,0,3.1081349621329723e-09,2.947058862876867e-09,2.8762000384013887e-08,,f4.ptu +6,0.5,0.48484848484848486,132,0.00035585,3.1350279803030303,3.1348157,3.13517155,32,32,68,0,2.2718750303327845e-09,4.254687556806171e-09,2.879117685499167e-08,,f4.ptu +7,0.782051282051282,0.5454545454545454,143,0.0004764,3.2864551877622374,3.28621215,3.28668855,17,61,65,0,3.2617647494315826e-09,4.791393506594914e-09,2.972923116615857e-08,,f4.ptu +8,0.37037037037037035,0.574468085106383,94,0.00042395,4.077127779787234,4.0769307999999995,4.07735475,34,20,40,0,3.7433824029206915e-09,3.915000052270856e-09,2.82618753773365e-08,,f4.ptu +9,0.16901408450704225,0.5035460992907801,141,0.000419,4.077845644326241,4.0776496,4.0780686,59,12,70,0,3.604237336257322e-09,4.512500060248337e-09,2.8293214663469214e-08,,f4.ptu +10,0.6911764705882353,0.5271317829457365,129,0.00046919999999999997,4.637728455813953,4.6374898,4.6379589999999995,21,47,61,0,2.5059524144104338e-09,3.582446856341433e-09,2.854631185654397e-08,,f4.ptu +11,0.7027027027027027,0.5311004784688995,209,0.00066225,4.638759683014354,4.6384251,4.63908735,33,78,98,0,2.3568182132850794e-09,3.768910306730605e-09,2.8820153446014802e-08,,f4.ptu +12,0.9418604651162791,0.5584415584415584,154,0.00046215,5.617535639935065,5.6173267,5.61778885,5,81,68,0,2.4400000325774937e-09,3.808642026159466e-09,2.948419157012676e-08,,f4.ptu +13,0.8676470588235294,0.5528455284552846,370,0.0016384499999999998,6.18067671527027,6.17992095,6.181559399999999,27,177,165,1,2.444444477081278e-09,4.015819262656485e-09,2.888742462811272e-08,2.8400000379180668e-08,f4.ptu +14,0.1919191919191919,0.5892857142857143,168,0.00057135,6.445808167559524,6.445531,6.446102349999999,80,19,69,0,2.9609375395327556e-09,4.142105318460931e-09,2.7926449648220407e-08,,f4.ptu +15,0.4925373134328358,0.44370860927152317,151,0.0006914499999999999,6.848385460927152,6.8480377,6.84872915,34,33,84,0,2.9588235689162957e-09,3.2287879218968205e-09,2.869791704982495e-08,,f4.ptu +16,0.07246376811594203,0.5227272727272727,132,0.00050475,7.911861859090909,7.9116329,7.91213765,64,5,63,0,3.441796920952917e-09,2.810000037517524e-09,2.881746070221468e-08,,f4.ptu +17,0.9375,0.5594405594405595,143,0.00050435,9.351014051048951,9.3507822,9.35128655,5,75,63,0,3.5100000468635264e-09,3.1803333757953376e-09,2.8963889275598284e-08,,f4.ptu +18,0.603448275862069,0.464,125,0.00045545,9.416124843199999,9.41589775,9.4163532,23,35,67,0,3.019565257706824e-09,2.9250000390529385e-09,2.8533582470516418e-08,,f4.ptu +19,0.5573770491803278,0.46564885496183206,131,0.00048259999999999997,10.591812293511449,10.5915714,10.592054,27,34,70,0,2.4203704026857807e-09,2.7823529783248667e-09,2.8474286094458202e-08,,f4.ptu +20,0.7092198581560284,0.47315436241610737,298,0.00100585,10.737798576342282,10.737345099999999,10.73835095,41,100,157,0,2.6280488155760192e-09,3.591500047951668e-09,2.881799401533473e-08,,f4.ptu +21,0.684931506849315,0.5214285714285715,140,0.00055145,11.267530855714284,11.2672595,11.26781095,23,50,67,0,2.820652211572789e-09,3.877000051763502e-09,2.9170149643194604e-08,,f4.ptu +22,0.6842105263157895,0.5688622754491018,167,0.0006324999999999999,12.915512495508981,12.91519235,12.91582485,30,65,72,0,2.3883333652210034e-09,3.632692356193952e-09,2.918819483414864e-08,,f4.ptu +23,0.9137931034482759,0.42962962962962964,135,0.00045484999999999997,13.01316205222222,13.01293065,13.0133855,5,53,77,0,2.4850000331783087e-09,3.490566084339904e-09,2.890422116513352e-08,,f4.ptu +24,0.5,0.14285714285714285,14,7.785e-05,13.209333589285714,13.2092961,13.20937395,1,1,12,0,1.875000025033935e-09,2.8000000373840095e-09,2.7860417038643125e-08,,f4.ptu +25,0.07692307692307693,0.5098039215686274,51,0.0002939,13.211191288235295,13.2110265,13.2113204,24,2,25,0,3.043750040638421e-09,2.4500000327110083e-09,2.8557000381276842e-08,,f4.ptu +26,0.3333333333333333,0.5454545454545454,11,7.91e-05,13.211634327272726,13.21159615,13.211675249999999,4,2,5,0,2.918750038969492e-09,1.0112500135016356e-08,2.8335000378312825e-08,,f4.ptu +27,0.5853658536585366,0.48520710059171596,169,0.0005829499999999999,13.819275338461539,13.818995549999999,13.819578499999999,34,48,87,0,2.191911793971043e-09,3.1036458747714492e-09,2.8375287735172555e-08,,f4.ptu +28,0.3835616438356164,0.474025974025974,154,0.0007446,13.820477097077921,13.82005525,13.82079985,45,28,81,0,2.890000038585638e-09,2.9276786105172726e-09,2.8303086797639822e-08,,f4.ptu +29,0.4426229508196721,0.5292841648590022,461,0.00180065,14.382028272125813,14.38117995,14.3829806,136,108,217,0,2.7430147425055272e-09,3.0828704115311044e-09,2.8282258442124773e-08,,f4.ptu +30,0.8767123287671232,0.5289855072463768,138,0.0006378499999999999,14.587557782608696,14.58725675,14.587894599999998,9,64,65,0,2.1444444730758483e-09,3.257031293486031e-09,2.8316154224215047e-08,,f4.ptu +31,0.8865979381443299,0.5449438202247191,178,0.00070595,14.591304641011234,14.5909582,14.59166415,11,86,81,0,2.415909123164937e-09,3.649709351054427e-09,2.8890432484494072e-08,,f4.ptu +32,0.82,0.45454545454545453,110,0.00041224999999999996,14.592095303181818,14.591866399999999,14.592278649999999,9,41,60,0,3.069444485425923e-09,2.9365854050612786e-09,2.8923750386173482e-08,,f4.ptu +33,0.8617021276595744,0.5053763440860215,186,0.00068025,14.611716611021507,14.611367999999999,14.612048249999999,13,81,92,0,3.436538507421171e-09,3.520370417372356e-09,2.877663081899183e-08,,f4.ptu +34,0.8070175438596491,0.5277777777777778,109,0.0005647499999999999,15.581095133027523,15.5808369,15.58140165,11,46,51,1,4.152272782711514e-09,3.751087006604121e-09,2.95544121592996e-08,3.83750005123612e-08,f4.ptu +35,0.84375,0.47761194029850745,134,0.00049955,15.721624518656714,15.7213578,15.721857349999999,10,54,70,0,2.4850000331783087e-09,4.087037091604835e-09,2.940928610694179e-08,,f4.ptu +36,0.5645161290322581,0.4161073825503356,149,0.0005742,16.423194540604026,16.422921249999998,16.42349545,27,35,87,0,2.453703736464162e-09,3.1600000421905252e-09,2.8230172790706332e-08,,f4.ptu +37,0.3548387096774194,0.5344827586206896,116,0.00034155,19.08830083922414,19.08811115,19.088452699999998,40,22,54,0,2.7768750370752575e-09,4.2511364203951215e-09,2.864722260470366e-08,,f4.ptu +38,0.20703125,0.37481698389458273,683,0.0016120499999999999,20.147808349048315,20.1470772,20.14868925,203,53,427,0,3.424507434884639e-09,3.118867966169655e-09,2.873208469274796e-08,,f4.ptu +39,0.5365853658536586,0.4270833333333333,288,0.00100165,21.421523339583334,21.4210335,21.42203515,57,66,165,0,2.7074561764992935e-09,3.3965909544402617e-09,2.8214545831250036e-08,,f4.ptu +40,0.6527777777777778,0.5714285714285714,126,0.00043759999999999996,21.618915067460314,21.6186985,21.6191361,25,47,54,0,2.4990000333652285e-09,3.901595796772742e-09,2.8891204089442642e-08,,f4.ptu +41,0.6307692307692307,0.4744525547445255,138,0.0004298,21.759782247101448,21.75953675,21.759966549999998,24,41,72,1,2.5343750338375354e-09,2.862804916271325e-09,2.820208370987153e-08,3.9850000532054564e-08,f4.ptu +42,0.7647058823529411,0.5112781954887218,133,0.00062205,22.103106266541353,22.1027904,22.10341245,16,52,65,0,2.5609375341921828e-09,3.026442348099646e-09,2.8818077307839515e-08,,f4.ptu +43,0.6111111111111112,0.5228215767634855,241,0.00092895,22.430489224481324,22.43002025,22.4309492,49,77,115,0,2.3357143168994163e-09,3.4314935523088584e-09,2.841739168376069e-08,,f4.ptu +44,0.65,0.4968944099378882,161,0.00047149999999999997,22.617157853416145,22.6169248,22.6173963,28,52,81,0,2.42142860375811e-09,3.2009615811989587e-09,2.8524691738870168e-08,,f4.ptu +45,0.6947368421052632,0.5121293800539084,371,0.00132065,22.677137446495955,22.6764437,22.67776435,58,132,181,0,2.6112069314150756e-09,3.2920454984989756e-09,2.8710359499346508e-08,,f4.ptu +46,0.7023809523809523,0.6131386861313869,137,0.00045155,22.74959053759124,22.74937,22.74982155,25,59,53,0,3.2740000437125883e-09,3.7262712361917344e-09,2.9067453218280794e-08,,f4.ptu +47,0.08823529411764706,0.44933920704845814,227,0.0009239,23.55503067665198,23.55454245,23.55546635,93,9,125,0,3.543010799992439e-09,2.5777778121948023e-09,2.8357000378606556e-08,,f4.ptu +48,0.5478260869565217,0.5665024630541872,203,0.00062345,24.15185354384236,24.1515362,24.152159649999998,52,63,88,0,2.8153846529740316e-09,3.733730208580803e-09,2.863664810961298e-08,,f4.ptu +49,0.7,0.4968944099378882,161,0.000536,24.155670441614905,24.15540785,24.15594385,24,56,81,0,2.6541667021035926e-09,3.5294643328376877e-09,2.918456829089034e-08,,f4.ptu +50,0.18867924528301888,0.53,100,0.00046455,25.3026513765,25.30239765,25.3028622,43,10,47,0,3.0360465521634754e-09,3.895000052003828e-09,2.8594681232843762e-08,,f4.ptu +51,0.5403225806451613,0.49206349206349204,252,0.0008286,25.46154012361111,25.46112835,25.461956949999998,57,67,128,0,2.5859649468070365e-09,3.06082093638873e-09,2.8365625378721712e-08,,f4.ptu +52,0.7972972972972973,0.3978494623655914,186,0.0008005499999999999,26.24204197983871,26.24164745,26.242448,15,59,112,0,2.3716666983318126e-09,3.4733051311193593e-09,2.8539732523903437e-08,,f4.ptu +53,0.3835616438356164,0.49324324324324326,148,0.0005848,26.339037227027024,26.33874385,26.33932865,45,28,75,0,2.626111146173455e-09,3.6785714776856248e-09,2.8414667046043152e-08,,f4.ptu +54,0.5185185185185185,0.5454545454545454,99,0.00044554999999999996,26.767675527272726,26.76743315,26.767878699999997,26,28,45,0,2.7365384980751686e-09,2.6491071782265165e-09,2.8166667042732e-08,,f4.ptu +55,0.6811594202898551,0.45394736842105265,152,0.0005622499999999999,27.335942306907892,27.3356861,27.336248349999998,22,47,83,0,2.327272758345151e-09,3.605851111973063e-09,2.8808735324396898e-08,,f4.ptu +56,0.7454545454545455,0.44715447154471544,123,0.00048435,27.5337966402439,27.533520449999997,27.534004799999998,14,41,68,0,2.7178571791444275e-09,3.5713415110971563e-09,2.8916912150788063e-08,,f4.ptu +57,0.3108108108108108,0.4277456647398844,173,0.0006491,28.835067339595376,28.834718849999998,28.83536795,51,23,99,0,3.603431420659988e-09,3.5434783081800736e-09,2.9111111499786127e-08,,f4.ptu +58,0.5521739130434783,0.39182282793867124,587,0.0017437,28.8372608044293,28.83636245,28.838106149999998,103,127,357,0,3.039805865828479e-09,3.4647638257871173e-09,2.8860434558997588e-08,,f4.ptu +59,0.2876712328767123,0.45625,160,0.00064435,28.8530899484375,28.85276425,28.853408599999998,52,21,87,0,3.046153886824362e-09,3.4511905222688106e-09,2.9333621081301016e-08,,f4.ptu +60,0.35064935064935066,0.4723926380368098,164,0.00052,28.855552888414632,28.855291249999997,28.85581125,50,27,86,1,3.0295000404481633e-09,3.271296339972786e-09,2.858604689329256e-08,4.2100000562095286e-08,f4.ptu +61,0.8125,0.43243243243243246,37,0.00022495,29.263552058108107,29.2634261,29.26365105,3,13,21,0,3.7166667162894886e-09,3.146153888159505e-09,2.8782143241425677e-08,,f4.ptu +62,0.43356643356643354,0.5836734693877551,245,0.0007888,29.366160405714282,29.36578105,29.366569849999998,81,62,102,0,3.0854938683562547e-09,4.264516185969655e-09,2.836446116301989e-08,,f4.ptu +63,0.7962962962962963,0.3624161073825503,149,0.00043575,30.10236235067114,30.102122899999998,30.10255865,11,43,95,0,2.3318182129512936e-09,3.033139575380477e-09,2.8183684586818855e-08,,f4.ptu +64,0.6,0.5,150,0.00055315,30.505348113999997,30.505074949999997,30.5056281,30,45,75,0,2.3808333651208676e-09,3.5133333802413643e-09,2.889066705239844e-08,,f4.ptu +65,0.15730337078651685,0.6137931034482759,145,0.00047335,31.64941631206896,31.649206799999998,31.64968015,75,14,56,0,3.3400000445937825e-09,2.3714286030905386e-09,2.849464323758714e-08,,f4.ptu +66,0.3333333333333333,0.5960264900662252,151,0.0006406,32.15981011059603,32.1595089,32.160149499999996,60,30,61,0,2.845416704657054e-09,3.053333374099706e-09,2.8793443007384507e-08,,f4.ptu +67,0.3333333333333333,0.375,8,7.835e-05,32.41301610625,32.4129789,32.41305725,2,1,5,0,2.875000038385367e-09,2.2000000293731503e-09,3.043000040628407e-08,,f4.ptu +68,0.4410480349344978,0.5204545454545455,440,0.0012304,35.21902380579545,35.21838965,35.219620049999996,128,101,211,0,2.919531288979923e-09,3.9554455973653175e-09,2.8841114129146e-08,,f4.ptu +69,0.5851063829787234,0.618421052631579,152,0.0005434999999999999,35.42218565493421,35.4218681,35.4224116,39,55,58,0,2.913461577360422e-09,3.509090955942298e-09,2.8333621067949584e-08,,f4.ptu +70,0.5555555555555556,0.4090909090909091,154,0.00049155,37.726308175,37.7260492,37.72654075,28,35,91,0,2.973214325410954e-09,3.6764286205141572e-09,2.9238461928836866e-08,,f4.ptu +71,0.7272727272727273,0.4782608695652174,345,0.0008767499999999999,38.102571626231885,38.1021131,38.10298985,45,120,180,0,2.6238889239215626e-09,3.562708380900591e-09,2.8972639275715105e-08,,f4.ptu +72,0.6486486486486487,0.5606060606060606,133,0.00064965,38.36923358684211,38.36893315,38.369582799999996,26,48,58,1,2.633653881316896e-09,3.079687541118238e-09,2.8591810726569197e-08,3.4550000461291974e-08,f4.ptu +73,0.9690721649484536,0.6510067114093959,149,0.00048489999999999997,39.34529019664429,39.345073299999996,39.3455582,3,94,52,0,6.450000086116736e-09,3.3316489806524964e-09,2.9073077311244132e-08,,f4.ptu +74,0.5959595959595959,0.5,198,0.001032,40.23424834191919,40.23369555,40.234727549999995,40,59,99,0,2.7893750372421504e-09,3.3652542822190987e-09,2.865530341289236e-08,,f4.ptu +75,0.9134615384615384,0.45021645021645024,231,0.0006613999999999999,40.369481995021644,40.3691849,40.3698463,9,95,127,0,2.4805555886745245e-09,3.294473728196468e-09,2.9209646059281935e-08,,f4.ptu +76,0.7407407407407407,0.4682080924855491,173,0.0007645,40.37030513236994,40.369862149999996,40.37062665,21,60,92,0,2.726190512589023e-09,3.717916716306178e-09,2.9077717779533514e-08,,f4.ptu +77,0.5,0.45614035087719296,114,0.00041319999999999996,40.48003367982456,40.47982825,40.48024145,26,26,62,0,2.553846187943657e-09,3.502884662153141e-09,2.8756855222654866e-08,,f4.ptu +78,0.9047619047619048,0.5294117647058824,119,0.00044354999999999997,40.494585637394955,40.4943807,40.49482425,6,57,56,0,2.116666694927198e-09,3.604824609533079e-09,2.9059375387984268e-08,,f4.ptu +79,0.42990654205607476,0.45147679324894513,237,0.0007995,40.67092567341772,40.6704563,40.6712558,61,46,130,0,2.3668033102887375e-09,2.872282647044738e-09,2.8382115763557267e-08,,f4.ptu +80,0.5512820512820513,0.5416666666666666,144,0.0006095499999999999,40.855711693055554,40.855415699999995,40.856025249999995,35,43,66,0,3.0878571840844574e-09,3.0988372506762396e-09,2.8303030680916285e-08,,f4.ptu +81,0.46715328467153283,0.556910569105691,246,0.0009835,40.85737670406504,40.8569533,40.8579368,73,64,109,0,3.459589087286358e-09,3.281250043809386e-09,2.8547248087568655e-08,,f4.ptu +82,0.6153846153846154,0.5449101796407185,167,0.00063245,42.454439111077846,42.45412985,42.4547623,35,56,76,0,2.6935714645344645e-09,3.0526786121862016e-09,2.8179934586768793e-08,,f4.ptu +83,0.6689655172413793,0.5846774193548387,248,0.0006495,42.67430638709677,42.6739442,42.674593699999996,48,97,103,0,2.628125035089232e-09,2.998453648281072e-09,2.8450485816747925e-08,,f4.ptu +84,0.5742574257425742,0.5233160621761658,193,0.00065105,42.69856419326425,42.69827385,42.6989249,43,58,92,0,2.845348875198784e-09,2.9745690052319965e-09,2.828097863846112e-08,,f4.ptu +85,0.9,0.5633802816901409,284,0.0007343,42.98488619260563,42.98449565,42.98522995,16,144,124,0,2.4781250330865173e-09,3.880902829593387e-09,2.9253024584118147e-08,,f4.ptu +86,0.7477477477477478,0.4007220216606498,277,0.00092965,42.99480259079422,42.99433275,42.9952624,28,83,166,0,2.554464319820042e-09,3.268072332790071e-09,2.869954857595115e-08,,f4.ptu +87,0.07142857142857142,0.5,28,0.00016319999999999998,43.03175564107143,43.03168785,43.03185105,13,1,14,0,2.2807692612207657e-09,1.7750000236987917e-09,2.8766071812639674e-08,,f4.ptu +88,0.0,0.5454545454545454,22,0.00010879999999999999,43.032139336363635,43.03208275,43.03219155,12,0,10,0,2.620833368325211e-09,,2.7772500370802645e-08,,f4.ptu +89,0.8666666666666667,0.5357142857142857,84,0.00046344999999999996,43.45571499285714,43.4554773,43.455940749999996,6,39,39,0,2.9458333726644264e-09,3.752564152666207e-09,2.9477564496131795e-08,,f4.ptu +90,0.8142857142857143,0.5,140,0.0006619,43.45652058571429,43.45615695,43.45681885,13,57,70,0,2.4730769560960413e-09,3.3557017991893887e-09,2.8982857529819786e-08,,f4.ptu +91,0.5783783783783784,0.4920212765957447,376,0.0015259499999999999,43.69243048430851,43.6916492,43.693175149999995,78,107,191,0,2.6342949069664807e-09,3.896962668852462e-09,2.922002656813966e-08,,f4.ptu +92,0.7590361445783133,0.46368715083798884,179,0.0007042,44.27589455083798,44.275517099999995,44.276221299999996,20,63,96,0,2.241250029923897e-09,2.597222256898858e-09,2.8156771209266542e-08,,f4.ptu +93,0.7454545454545455,0.5238095238095238,105,0.0004893,44.35365143999999,44.35339245,44.35388175,14,41,50,0,2.1928571721349257e-09,3.4073171186632937e-09,2.8722500383486505e-08,,f4.ptu +94,0.7761194029850746,0.4249471458773784,473,0.0012506,44.355893730443974,44.35526945,44.35652005,45,156,272,0,2.642222257499672e-09,3.2004808119617707e-09,2.8379320231845734e-08,,f4.ptu +95,0.5277777777777778,0.4864864864864865,518,0.0018279,44.542024715444015,44.5411262,44.542954099999996,119,133,266,0,2.831932810919601e-09,3.7511278696318e-09,2.8645677074190127e-08,,f4.ptu +96,0.6595744680851063,0.47959183673469385,98,0.00045325,44.549770708163265,44.54953965,44.5499929,16,31,51,0,2.818750037634349e-09,4.6120968357716444e-09,2.936666705875372e-08,,f4.ptu +97,0.6436781609195402,0.58,151,0.0006680999999999999,44.55326150562914,44.552965449999995,44.55363355,31,56,63,1,2.843548425062217e-09,3.5575893332131968e-09,2.898055594248747e-08,3.637500048565834e-08,f4.ptu +98,0.6912751677852349,0.3983957219251337,374,0.00105625,44.915177765106954,44.91464865,44.9157049,46,103,225,0,2.4467391631022534e-09,3.726456360433139e-09,2.8769222606332533e-08,,f4.ptu +99,0.3163841807909605,0.5315315315315315,333,0.00089845,45.50582873453453,45.50531965,45.5062181,121,56,156,0,3.3702479788819336e-09,4.458482202384264e-09,2.895464782248344e-08,,f4.ptu +100,0.4117647058823529,0.5190839694656488,131,0.00045214999999999996,46.45097035267175,46.450738099999995,46.451190249999996,40,28,63,0,2.9443750393116227e-09,3.2687500436424932e-09,2.841785752227623e-08,,f4.ptu +101,0.1111111111111111,0.45918367346938777,196,0.0008277,46.662896001275506,46.662475099999995,46.6633028,80,10,106,0,2.5500000340461515e-09,2.2275000297403144e-09,2.8188915470702004e-08,,f4.ptu +102,0.5145631067961165,0.4735632183908046,435,0.00168575,47.9430433613793,47.942224949999996,47.943910699999996,100,106,229,0,2.5692500343031663e-09,3.4733491029779572e-09,2.8662773308453255e-08,,f4.ptu +103,0.6533333333333333,0.436046511627907,172,0.0007373999999999999,49.202366022965116,49.2019619,49.2026993,26,49,97,0,2.243269260720087e-09,3.342857187489073e-09,2.9130670492028944e-08,,f4.ptu +104,0.5,0.5373134328358209,67,0.0002804,49.60217505074627,49.602036899999995,49.602317299999996,18,18,31,0,2.7555555923461683e-09,2.834722260069823e-09,2.842983908925648e-08,,f4.ptu +105,0.18072289156626506,0.3824884792626728,217,0.000687,49.63986816290322,49.639540249999996,49.640227249999995,68,15,134,0,3.73970593228337e-09,3.290000043926211e-09,2.866660486035216e-08,,f4.ptu +106,0.4731182795698925,0.6480836236933798,287,0.0007833,50.00666215121951,50.006342149999995,50.00712545,98,88,101,0,2.5640306464783783e-09,2.9974432218383405e-09,2.8779950879303057e-08,,f4.ptu +107,0.6778523489932886,0.5033783783783784,296,0.00094285,50.385764453209454,50.3852775,50.386220349999995,48,101,147,0,3.0171875402837736e-09,3.3269802424397517e-09,2.8350170446542578e-08,,f4.ptu +108,0.20930232558139536,0.3805309734513274,113,0.00036304999999999996,50.90522852699115,50.905077649999996,50.9054407,34,9,70,0,4.26102946865555e-09,3.502777824544877e-09,2.8096786089418037e-08,,f4.ptu +109,0.5,0.6666666666666666,6,2.575e-05,50.905624616666664,50.90561525,50.905640999999996,2,2,2,0,4.512500060248337e-09,2.0375000272035426e-09,3.0637500409054497e-08,,f4.ptu +110,0.8950276243093923,0.48655913978494625,372,0.0014762,51.94293650080645,51.9421758,51.943652,19,162,191,0,2.0513158168616874e-09,3.1058642389985987e-09,2.8750262163961875e-08,,f4.ptu +111,0.7,0.42424242424242425,165,0.00064645,52.76915396060606,52.768864799999996,52.76951125,21,49,95,0,2.2511905062470927e-09,3.838265357368787e-09,2.9557368815685826e-08,,f4.ptu +112,0.5531914893617021,0.44976076555023925,209,0.0009620999999999999,52.77216551387559,52.771689249999994,52.77265135,42,52,115,0,2.391071460495656e-09,3.1413461957876234e-09,2.846956559750077e-08,,f4.ptu +113,0.17142857142857143,0.4,175,0.00050395,52.95185815342857,52.951587149999995,52.9520911,58,12,105,0,3.0577586615151114e-09,4.427083392441236e-09,2.9044762292551068e-08,,f4.ptu +114,0.11666666666666667,0.38461538461538464,156,0.00054135,54.27040434903846,54.27014885,54.2706902,53,7,96,0,2.8372641888249354e-09,2.542857176807927e-09,2.8677344132883604e-08,,f4.ptu +115,0.1694915254237288,0.35435435435435436,334,0.0012213,54.550410606137724,54.54973035,54.550951649999995,98,20,215,1,5.057908230795623e-09,5.285000070562318e-09,2.9800930630443235e-08,3.0025000400876745e-08,f4.ptu +116,0.6226415094339622,0.49074074074074076,108,0.00038134999999999997,54.642948249074074,54.642742899999995,54.64312425,20,33,55,0,2.686250035865284e-09,3.910606112818252e-09,2.8306364014294123e-08,,f4.ptu +117,0.14893617021276595,0.4845360824742268,97,0.0003361,54.643468467525764,54.643266149999995,54.64360225,40,7,50,0,3.3156250442683416e-09,4.196428627456902e-09,2.87905003843944e-08,,f4.ptu +118,0.4533762057877814,0.5174708818635607,602,0.00225365,54.650484004817265,54.649338799999995,54.651592449999995,170,141,290,1,3.201029454503032e-09,3.4507092659300652e-09,2.836353486145242e-08,4.640000061950644e-08,f4.ptu +119,0.5116279069767442,0.44559585492227977,193,0.0007350999999999999,54.65273091450777,54.6523295,54.6530646,42,44,107,0,4.558333394193611e-09,3.2897727711959043e-09,2.809556112277952e-08,,f4.ptu +120,0.8585858585858586,0.526595744680851,188,0.0008407499999999999,55.64281142765957,55.642402499999996,55.64324325,14,85,89,0,2.1928571721349257e-09,3.2885294556712825e-09,2.8296629591283667e-08,,f4.ptu +121,0.81,0.4716981132075472,424,0.0017059,55.92719289009434,55.9263444,55.928050299999995,38,162,224,0,2.2789473988482634e-09,3.060956830991613e-09,2.8381473593219025e-08,,f4.ptu +122,0.676056338028169,0.5725806451612904,124,0.0005603499999999999,55.97100817862903,55.970726299999995,55.971286649999996,23,48,53,0,3.4021739584673715e-09,3.230729209801527e-09,2.850613245606938e-08,,f4.ptu +123,0.5818181818181818,0.4700854700854701,117,0.0006104,56.109517774786326,56.109204049999995,56.109814449999995,23,32,62,0,2.3478261183033622e-09,3.853125051444736e-09,2.983427459187867e-08,,f4.ptu +124,0.7222222222222222,0.5,144,0.0007322,56.77475826041666,56.774402599999995,56.7751348,20,52,72,0,2.173750029022675e-09,3.1495192728198223e-09,2.8840625385063642e-08,,f4.ptu +125,0.4939759036144578,0.5460526315789473,152,0.0006464,56.88836104144736,56.8880488,56.8886952,42,41,69,0,2.576190510586308e-09,3.0567073578845903e-09,2.8277898928275077e-08,,f4.ptu +126,0.6931818181818182,0.6153846153846154,143,0.0005092499999999999,57.021123320629364,57.02087805,57.0213873,27,61,55,0,2.3740741057713627e-09,3.407786930744737e-09,2.8627727654948423e-08,,f4.ptu +127,0.7065217391304348,0.6524822695035462,141,0.00049915,57.47610904574468,57.47584525,57.476344399999995,27,65,49,0,2.718518554814634e-09,3.5980769711164027e-09,2.836581670525488e-08,,f4.ptu +128,0.6344827586206897,0.5160142348754448,281,0.0010586,58.29588915409252,58.295342749999996,58.29640135,53,92,136,0,2.4514151270695247e-09,3.1385869984263694e-09,2.8289338612997292e-08,,f4.ptu +129,0.5862068965517241,0.5337423312883436,163,0.0005429,58.74937125460123,58.749095399999995,58.7496383,36,51,76,0,2.970833372998212e-09,3.779902011251417e-09,2.8672368803869808e-08,,f4.ptu +130,0.5702479338842975,0.5761904761904761,210,0.0006611,59.05708218904762,59.056726149999996,59.05738725,52,69,89,0,2.4350961863581746e-09,2.8887681545112197e-09,2.813455093743429e-08,,f4.ptu +131,0.5526315789473685,0.6031746031746031,252,0.0007373,59.07075541507936,59.07040885,59.07114615,68,84,100,0,2.6547794472098123e-09,2.896726229151633e-09,2.8271500377465006e-08,,f4.ptu +0,0.4852941176470588,0.5074626865671642,134,0.0006167999999999999,0.08911167686567165,0.08879234999999999,0.08940914999999999,35,33,66,0,2.3742857459858285e-09,2.7174242787057473e-09,2.8120833708786725e-08,,f5.ptu +1,0.49122807017543857,0.5,115,0.00052085,0.693281542173913,0.6929847499999999,0.6935055999999999,29,28,57,1,3.431034528567844e-09,3.2482143290825975e-09,2.831842143072305e-08,2.7300000364494093e-08,f5.ptu +2,0.0,0.7333333333333333,15,7.465e-05,1.4107530633333332,1.4107178,1.41079245,11,0,4,0,2.875000038385367e-09,,2.7862500372004273e-08,,f5.ptu +3,0.46551724137931033,0.48333333333333334,121,0.0005157499999999999,1.5077220834710743,1.50746955,1.5079852999999999,31,27,62,1,3.26048391449987e-09,3.350000044727297e-09,2.824193586094125e-08,3.065000040922139e-08,f5.ptu +4,0.3333333333333333,0.4015748031496063,127,0.0004764,2.0681050783464565,2.0678719,2.0683483,34,17,76,0,3.018382393240903e-09,3.629411813163727e-09,2.8193092481681306e-08,,f5.ptu +5,0.37209302325581395,0.45989304812834225,187,0.000765,2.0757352112299463,2.07533515,2.07610015,54,32,101,0,3.5472222695827185e-09,3.1054687914624547e-09,2.815717859376043e-08,,f5.ptu +6,0.43243243243243246,0.3425925925925926,432,0.0013327,2.079810994560185,2.07923355,2.08056625,84,64,284,0,3.3255952824966964e-09,3.1664062922760576e-09,2.8369278547784575e-08,,f5.ptu +7,0.7441860465116279,0.49142857142857144,175,0.0005409999999999999,3.2670912902857143,3.2668277999999997,3.2673688,22,64,89,0,2.7840909462625092e-09,3.3109375442057568e-09,2.870196667534718e-08,,f5.ptu +8,0.5957446808510638,0.44339622641509435,106,0.00048475,4.3925374924528295,4.392282,4.39276675,19,28,59,0,2.5828947713274486e-09,3.276785758035496e-09,2.8105508849825055e-08,,f5.ptu +9,0.5471698113207547,0.4608695652173913,115,0.0004453,5.10318447173913,5.1029706,5.1034159,24,29,62,0,2.604166701436021e-09,3.019827626525919e-09,2.8408468121229207e-08,,f5.ptu +10,0.4854368932038835,0.38721804511278196,266,0.0006916499999999999,6.514980245112782,6.51462525,6.515316899999999,53,50,163,0,2.545283052851098e-09,4.1285000551213866e-09,2.8621472774775677e-08,,f5.ptu +11,0.34210526315789475,0.49673202614379086,153,0.000567,7.625833409477123,7.6255498,7.626116799999999,50,26,77,0,3.496500046683282e-09,3.60673081738579e-09,2.834188349528784e-08,,f5.ptu +12,0.58,0.4854368932038835,103,0.0003816,7.974767051941748,7.9745691,7.9749507,21,29,53,0,2.7000000360488663e-09,3.6681034972502954e-09,2.866084943926715e-08,,f5.ptu +13,0.47368421052631576,0.4470588235294118,85,0.00043275,9.144545437647059,9.1443342,9.14476695,20,18,47,0,3.757500050168006e-09,3.533333380508393e-09,2.852393655104816e-08,,f5.ptu +14,0.5161290322580645,0.4492753623188406,138,0.0007314499999999999,9.756791853985508,9.75643475,9.7571662,30,32,76,0,2.4658333662557395e-09,3.1835937925055353e-09,2.8423026695277576e-08,,f5.ptu +15,0.5,0.6187845303867403,181,0.0006030499999999999,11.749963713259667,11.7496668,11.750269849999999,56,56,69,0,2.779464322824114e-09,3.494642903801343e-09,2.8391304726890654e-08,,f5.ptu +16,0.364741641337386,0.5238853503184714,630,0.0028314,11.765248445079365,11.7639173,11.766748699999999,209,120,299,2,2.786722525245013e-09,3.2370833765530308e-09,2.8455267938446998e-08,3.605000048131912e-08,f5.ptu +17,0.4132231404958678,0.491869918699187,246,0.0007993999999999999,11.768057269715447,11.76768775,11.76848715,71,50,125,0,3.0598591957830787e-09,3.000000040054296e-09,2.8657600382619998e-08,,f5.ptu +18,0.5753424657534246,0.4515463917525773,486,0.0017597,13.332430587757202,13.331564949999999,13.33332465,93,126,266,1,2.5422043350173366e-09,2.9976190876415543e-09,2.830404173128294e-08,2.8250000377177953e-08,f5.ptu +19,0.5068493150684932,0.477124183006536,153,0.00057315,14.626009948039215,14.625746099999999,14.62631925,36,37,80,0,2.502777811193445e-09,3.939189241783006e-09,2.8862500385355705e-08,,f5.ptu +20,0.5454545454545454,0.46261682242990654,214,0.0006303,14.628214856542057,14.62787455,14.628504849999999,45,54,115,0,2.5122222557639865e-09,4.3759259843508034e-09,2.9008043865559786e-08,,f5.ptu +21,0.44,0.5,100,0.00045329999999999996,16.768321407,16.76811265,16.76856595,28,22,50,0,2.9776786111848442e-09,3.5193182288061194e-09,2.898700038701796e-08,,f5.ptu +22,0.49606299212598426,0.4980392156862745,255,0.0008707,16.769195165686273,16.76873745,16.76960815,64,63,128,0,2.592968784619846e-09,3.946031798716921e-09,2.846855506759597e-08,,f5.ptu +23,0.47619047619047616,0.5283018867924528,159,0.00065475,16.98523041855346,16.98491335,16.9855681,44,40,75,0,2.52386367006083e-09,3.392500045294733e-09,2.8522667047485113e-08,,f5.ptu +24,0.5,0.4,5,1.365e-05,17.138263369999997,17.138254,17.13826765,1,1,3,0,2.7750000370502237e-09,4.375000058412515e-09,2.8016667040729287e-08,,f5.ptu +25,0.7222222222222222,0.5217391304347826,138,0.0005635,17.209692888043477,17.209417549999998,17.20998105,20,52,66,0,2.5750000343799373e-09,3.175961580865173e-09,2.8412121591463717e-08,,f5.ptu +26,0.5460992907801419,0.5183823529411765,273,0.00120685,19.96056306208791,19.959995749999997,19.9612026,64,77,131,1,2.382812531813959e-09,3.1610390032433576e-09,2.8638359160989054e-08,4.255000056810343e-08,f5.ptu +27,0.696078431372549,0.5425531914893617,188,0.0006849499999999999,20.236021824468086,20.2356917,20.23637665,31,71,86,0,2.3508064829995357e-09,3.6690141334936345e-09,2.8668023638573116e-08,,f5.ptu +28,0.7428571428571429,0.5185185185185185,135,0.0006154,20.23682351962963,20.23649845,20.23711385,18,52,65,0,2.9694444840907797e-09,3.196634658064265e-09,2.887115423162509e-08,,f5.ptu +29,0.6,0.38461538461538464,13,8.329999999999999e-05,20.92415252307692,20.92411705,20.92420035,2,3,8,0,3.587500047898262e-09,3.7083333828448937e-09,2.8946875386482232e-08,,f5.ptu +30,0.38095238095238093,0.43448275862068964,145,0.00053015,20.92473878655172,20.924479899999998,20.925010049999997,39,24,82,0,3.0583333741664625e-09,2.9333333724975335e-09,2.8316463792699482e-08,,f5.ptu +31,0.5576923076923077,0.45414847161572053,229,0.0008036,20.926414966593885,20.9260186,20.9268222,46,58,125,0,2.5657609038217993e-09,3.4250000457286545e-09,2.8537000381009815e-08,,f5.ptu +32,0.7387387387387387,0.44047619047619047,252,0.0007701499999999999,20.94402479484127,20.943616249999998,20.9443864,29,82,141,0,2.2353448574312613e-09,3.1600610178010954e-09,2.8654433006691182e-08,,f5.ptu +33,0.3944954128440367,0.5190476190476191,210,0.0008181,21.739925945,21.73953925,21.74035735,66,43,101,0,2.5670454888191874e-09,2.90465120157195e-09,2.8213861762834066e-08,,f5.ptu +34,0.45,0.49586776859504134,121,0.00041529999999999996,21.742062458264463,21.741843199999998,21.7422585,33,27,61,0,2.508333366823175e-09,2.8212963339646417e-09,2.8159426605476863e-08,,f5.ptu +35,0.6075949367088608,0.4968553459119497,159,0.00064235,22.326253931132076,22.32593085,22.3265732,31,48,80,0,2.766129069189848e-09,4.034895887204971e-09,2.883500038498854e-08,,f5.ptu +36,0.575,0.41237113402061853,97,0.00049055,23.09945217680412,23.0992118,23.099702349999998,17,23,57,0,3.1573529833316537e-09,3.2369565649571354e-09,2.8520175819381675e-08,,f5.ptu +37,0.3684210526315789,0.4253731343283582,134,0.00042409999999999995,24.39973922947761,24.39953575,24.39995985,36,21,77,0,2.8951389275431387e-09,3.990476243754762e-09,2.8638312070674158e-08,,f5.ptu +38,0.47761194029850745,0.47183098591549294,142,0.0005882,24.53823494507042,24.53790625,24.538494449999998,35,32,75,0,2.558571462732021e-09,3.5984375480442934e-09,2.840033371251845e-08,,f5.ptu +39,0.6095238095238096,0.4751131221719457,221,0.0009567,26.266305195475113,26.26582475,26.26678145,41,64,116,0,2.6335366205273382e-09,3.2882812939032634e-09,2.8366164171832353e-08,,f5.ptu +40,0.4782608695652174,0.4726027397260274,146,0.0005207499999999999,27.12029227568493,27.12002085,27.1205416,36,33,77,0,2.4937500332951335e-09,3.0371212526711295e-09,2.7988636737324737e-08,,f5.ptu +41,0.875,0.5106382978723404,141,0.0004696,27.664543893971633,27.66428885,27.664758449999997,9,63,69,0,3.161111153316471e-09,3.651190524939097e-09,2.9059420677839946e-08,,f5.ptu +42,0.48854961832061067,0.5927601809954751,221,0.00071555,27.92877466402715,27.928383049999997,27.9290986,67,64,90,0,2.8134328733842524e-09,3.2546875434547387e-09,2.8460833713326215e-08,,f5.ptu +43,0.46153846153846156,0.37142857142857144,140,0.0004525,28.437483804285716,28.43724335,28.437695849999997,28,24,88,0,2.360714317233202e-09,2.8520833714127296e-09,2.809744355695928e-08,,f5.ptu +44,0.573170731707317,0.44086021505376344,186,0.0006634999999999999,28.75447188575269,28.75417105,28.754834549999998,35,47,104,0,2.7400000365829233e-09,3.779255369607406e-09,2.8304086916361622e-08,,f5.ptu +45,0.5086705202312138,0.4505208333333333,384,0.00111565,29.817958333463544,29.81741615,29.8185318,85,88,211,0,2.725882389335609e-09,3.1823864061257786e-09,2.862582976608207e-08,,f5.ptu +46,0.7432432432432432,0.5138888888888888,144,0.0005059999999999999,30.19215564861111,30.191889999999997,30.192396,19,55,70,0,2.4263158218684744e-09,3.5481818655551267e-09,2.918464324679963e-08,,f5.ptu +47,0.8255813953488372,0.6056338028169014,142,0.00048485,30.19535183028169,30.19509485,30.1955797,15,71,56,0,2.3366666978645126e-09,3.965493010691488e-09,2.9033928959073093e-08,,f5.ptu +48,0.639751552795031,0.5313531353135313,303,0.0007817999999999999,30.965410602970294,30.9649751,30.9657569,58,103,142,0,2.373275893755597e-09,3.346601986429501e-09,2.8993134189916754e-08,,f5.ptu +49,0.5692307692307692,0.4452054794520548,146,0.00056005,31.04872991746575,31.04843845,31.0489985,28,37,81,0,2.4830357474377965e-09,3.1972973399857947e-09,2.850308680031011e-08,,f5.ptu +50,0.29508196721311475,0.5169491525423728,118,0.0006190999999999999,31.27212399618644,31.2717966,31.2724157,43,18,57,0,2.964534923301716e-09,2.9472222615718593e-09,2.843552669544447e-08,,f5.ptu +51,0.7068965517241379,0.4915254237288136,118,0.00046655,33.05308340338983,33.05284165,33.0533082,17,41,60,0,2.627941211557366e-09,3.87804883226531e-09,2.841750037941432e-08,,f5.ptu +52,0.6451612903225806,0.49206349206349204,63,0.00037735,33.062999488888885,33.0628154,33.06319275,11,20,32,0,2.7409091275041523e-09,3.2387500432419503e-09,2.8463281630025563e-08,,f5.ptu +53,0.6091954022988506,0.58,151,0.000714,33.08902509039735,33.08868675,33.089400749999996,34,53,63,1,2.4992647392511157e-09,3.059434003111975e-09,2.8181746408012696e-08,2.772500037016845e-08,f5.ptu +54,0.4426229508196721,0.5,122,0.0005136,33.34201549385246,33.3417639,33.3422775,34,27,61,0,2.7852941548347235e-09,3.3027778218745905e-09,2.843401677307746e-08,,f5.ptu +55,0.7682926829268293,0.5655172413793104,290,0.0010274499999999998,33.34387511431034,33.343328899999996,33.34435635,38,126,126,0,3.034210566826845e-09,3.371230203740909e-09,2.8949008323018644e-08,,f5.ptu +56,0.7101449275362319,0.4423076923076923,156,0.0006541,34.45274865961538,34.4524131,34.4530672,20,49,87,0,3.000000040054296e-09,3.246938818861486e-09,2.8478736012415807e-08,,f5.ptu +57,0.7840909090909091,0.5269461077844312,167,0.0007524999999999999,34.55674930149701,34.55636415,34.55711665,19,69,79,0,2.3250000310420793e-09,3.7246377308886667e-09,2.9003797855596656e-08,,f5.ptu +58,0.691358024691358,0.4764705882352941,170,0.0006083,34.562754489411766,34.56243715,34.56304545,25,56,89,0,3.2570000434856138e-09,3.886160766171525e-09,2.8771629597625597e-08,,f5.ptu +59,0.7352941176470589,0.53125,129,0.0005336,34.58158944612403,34.581311899999996,34.5818455,18,50,60,1,2.715277814030624e-09,3.5775000477647476e-09,2.8927500386223547e-08,4.987500066590267e-08,f5.ptu +60,0.6923076923076923,0.4148936170212766,188,0.00079625,35.48936040718085,35.48899885,35.489795099999995,24,54,110,0,2.6416667019366997e-09,3.1814815239588147e-09,2.876113674763872e-08,,f5.ptu +61,0.5684210526315789,0.4947916666666667,192,0.0005714,35.54255734270833,35.542252,35.542823399999996,41,54,97,0,2.35731710464429e-09,2.8958333719968548e-09,2.8368299347829576e-08,,f5.ptu +62,0.5272727272727272,0.46218487394957986,119,0.00048045,35.78564016260504,35.785396399999996,35.78587685,26,29,64,0,2.796153883486504e-09,2.9163793492826677e-09,2.850273475555232e-08,,f5.ptu +63,0.43478260869565216,0.6571428571428571,35,0.00015635,35.86945125142857,35.86938045,35.8695368,13,10,12,0,2.5307692645586237e-09,3.935000052537885e-09,2.9141667055749647e-08,,f5.ptu +64,0.3790322580645161,0.4217687074829932,294,0.0012322499999999998,36.43600531598639,36.435352699999996,36.43658495,77,47,170,0,2.9941558841321123e-09,3.006914933763641e-09,2.8463088615317102e-08,,f5.ptu +65,0.3627450980392157,0.4722222222222222,216,0.0008685499999999999,36.53979275046296,36.5393408,36.54020935,65,37,114,0,3.0876923489174216e-09,3.2439189622298813e-09,2.8736184594195527e-08,,f5.ptu +66,0.21176470588235294,0.4913294797687861,173,0.0007634499999999999,37.236032522543354,37.23563255,37.236396,67,18,88,0,2.869776157718606e-09,3.359722267079325e-09,2.8331534469175645e-08,,f5.ptu +67,0.32142857142857145,0.45161290322580644,124,0.00055905,37.343197318548384,37.34293435,37.3434934,38,18,68,0,3.0598684619062566e-09,3.881944496273962e-09,2.856360332254147e-08,,f5.ptu +68,0.5652173913043478,0.5411764705882353,85,0.00028155,38.30112031352941,38.30093565,38.301217199999996,20,26,39,0,2.9125000388860456e-09,3.4653846616524624e-09,2.8284615762255505e-08,,f5.ptu +69,0.5935483870967742,0.5326460481099656,291,0.0012402,39.28324331821305,39.2826312,39.283871399999995,63,92,136,0,2.557936542088623e-09,3.771195702524775e-09,2.878327244312143e-08,,f5.ptu +70,0.8115942028985508,0.46621621621621623,148,0.0005518,41.68679008243243,41.68651505,41.68706685,13,56,79,0,2.726923113331405e-09,3.7227679068471386e-09,2.911234216084335e-08,,f5.ptu +71,0.7868852459016393,0.45185185185185184,135,0.0005547,41.68829379555555,41.6879801,41.6885348,13,48,74,0,2.6365384967400254e-09,3.408854212179751e-09,2.8580067949152394e-08,,f5.ptu +72,0.5849056603773585,0.5047619047619047,105,0.00042685,42.68550820047619,42.685332249999995,42.6857591,22,31,52,0,2.3920454864826868e-09,3.2967742375650436e-09,2.833076960902557e-08,,f5.ptu +73,0.21428571428571427,0.3684210526315789,38,0.00015255,43.93478923947368,43.934700799999995,43.93485335,11,3,24,0,3.6704545944603695e-09,2.4583333661556033e-09,2.851458371404385e-08,,f5.ptu +74,0.2838709677419355,0.39948453608247425,388,0.0013503,44.056695406701024,44.056005899999995,44.0573562,111,44,233,0,2.5853603948786233e-09,2.910227311582974e-09,2.844688879182386e-08,,f5.ptu +75,0.31343283582089554,0.4407894736842105,152,0.0007126999999999999,44.16442669078947,44.1640454,44.1647581,46,21,85,0,2.6635869920844392e-09,2.9630952776568024e-09,2.8717941559896227e-08,,f5.ptu +76,0.3010752688172043,0.627318718381113,594,0.0016085499999999998,44.20988538215488,44.20916235,44.2107709,260,112,221,1,3.480480815700171e-09,3.2991071869049477e-09,2.8366403093664222e-08,3.4425000459623045e-08,f5.ptu +77,0.4492753623188406,0.5679012345679012,243,0.00092735,44.21190302983539,44.2114744,44.21240175,76,62,105,0,2.974671092347697e-09,3.020564556457894e-09,2.857738133392991e-08,,f5.ptu +78,0.4050632911392405,0.5231788079470199,151,0.00044524999999999995,44.40955782119205,44.40933885,44.409784099999996,47,32,72,0,3.003723444359328e-09,2.890625038593983e-09,2.795034759539938e-08,,f5.ptu +79,0.5317460317460317,0.4581818181818182,275,0.0011355,44.99130482272727,44.99074175,44.991877249999995,59,67,149,0,2.562711898622653e-09,3.300746312726406e-09,2.8852684948983046e-08,,f5.ptu +80,0.5821596244131455,0.5195121951219512,410,0.00179415,44.99358852670731,44.9927162,44.99451035,89,124,197,0,2.6738764401944608e-09,3.508064562966717e-09,2.8544543528318648e-08,,f5.ptu +81,0.4444444444444444,0.6040268456375839,298,0.0010332,45.66544020704698,45.664881949999995,45.66591515,100,80,118,0,2.7980000373573067e-09,3.099375041381094e-09,2.8337500378346203e-08,,f5.ptu +82,0.6666666666666666,0.2727272727272727,11,7.07e-05,48.31098235909091,48.310942149999995,48.31101285,1,2,8,0,4.575000061082801e-09,1.7875000238656846e-09,2.8106250375258685e-08,,f5.ptu +83,0.23529411764705882,0.288135593220339,295,0.0006483999999999999,48.31344315169491,48.3131257,48.313774099999996,65,20,210,0,2.4900000332450653e-09,2.2837500304913325e-09,2.80436908506139e-08,,f5.ptu +84,0.5932203389830508,0.5190615835777126,341,0.0008273,49.949005867448676,49.9485637,49.949391,72,105,164,0,2.659027813279606e-09,4.1023810071536126e-09,2.9235671122045386e-08,,f5.ptu +85,0.16666666666666666,0.5,24,0.00010059999999999999,51.853378308333326,51.8533042,51.8534048,10,2,12,0,2.9075000388192885e-09,2.8000000373840095e-09,2.8410417045986413e-08,,f5.ptu +86,0.19402985074626866,0.46206896551724136,146,0.00057095,52.08590552739726,52.085634649999996,52.0862056,54,13,78,1,3.850925977341302e-09,3.0634615793631368e-09,2.9145513209647155e-08,2.7275000364160307e-08,f5.ptu +87,0.0873015873015873,0.490272373540856,257,0.0008307999999999999,52.08712855544747,52.08673555,52.087566349999996,115,11,131,0,3.888695704093569e-09,3.1181818598140106e-09,2.9661069098307817e-08,,f5.ptu +88,0.64,0.45454545454545453,110,0.00031895,54.30143566318181,54.301239499999994,54.301558449999995,18,32,60,0,2.7958333706617116e-09,3.1617187922134728e-09,2.805791704128003e-08,,f5.ptu +89,0.5833333333333334,0.42857142857142855,280,0.0009350999999999999,54.414427614642854,54.413981549999995,54.414916649999995,50,70,160,0,2.3200000309753222e-09,3.077142898227121e-09,2.8549844131181297e-08,,f5.ptu +90,0.6666666666666666,0.55,120,0.00053755,54.731234539999996,54.730965749999996,54.7315033,22,44,54,0,2.6920454904881164e-09,3.4426136823274586e-09,2.868842630895749e-08,,f5.ptu +91,0.33962264150943394,0.4690265486725664,113,0.00050175,55.283732448672566,55.283459199999996,55.28396095,35,18,60,0,3.6578571916947737e-09,2.850000038051581e-09,2.8300833711189985e-08,,f5.ptu +92,0.5384615384615384,0.5603448275862069,116,0.00043345,55.37193135043103,55.3717198,55.37215325,30,35,51,0,2.5625000342130444e-09,3.523571475616153e-09,2.905392195653891e-08,,f5.ptu +93,0.47435897435897434,0.4727272727272727,165,0.00068615,57.75650380333333,57.7561866,57.75687275,41,37,87,0,2.5835366198597666e-09,3.609459507650912e-09,2.819051761776308e-08,,f5.ptu +94,0.46875,0.5079365079365079,189,0.00071565,58.027801915079365,58.02743365,58.028149299999995,51,45,93,0,3.081372590160344e-09,3.63222227071759e-09,2.8269892850561825e-08,,f5.ptu +95,0.4444444444444444,0.44751381215469616,181,0.0006277,58.11850843232044,58.11822325,58.118850949999995,45,36,100,0,2.788888926124549e-09,3.59722227025029e-09,2.8413250379357574e-08,,f5.ptu +96,0.3541666666666667,0.46601941747572817,103,0.0005473,58.94836859126214,58.9480965,58.9486438,31,17,55,0,2.7927419727709747e-09,3.6352941661834407e-09,2.8350909469434323e-08,,f5.ptu diff --git a/tests/test_data/output/web/bursts.csv b/tests/test_data/output/web/bursts.csv new file mode 100644 index 0000000..852fb31 --- /dev/null +++ b/tests/test_data/output/web/bursts.csv @@ -0,0 +1,230 @@ +,E_app,S_app,n_photons,time_length,time_mean,time_min,time_max,n_DD,n_DA,n_AA,n_AD,tau_DD,tau_DA,tau_AA,tau_AD,filename +0,0.5394736842105263,0.5615763546798029,407,0.0016798499999999999,0.5821335101965601,0.58126295,0.5829428,105,123,178,1,2.6492857496574723e-09,3.726016309910254e-09,2.876685431666296e-08,2.7300000364494093e-08,f4.ptu +1,0.6666666666666666,0.5,126,0.00042964999999999995,0.6517511976190475,0.6515398,0.65196945,21,42,63,0,2.3464286027567528e-09,3.557738142738993e-09,2.9110714674384005e-08,,f4.ptu +2,0.5319148936170213,0.48205128205128206,195,0.00066875,0.8446144617948718,0.8442822999999999,0.8449510499999999,44,50,101,0,3.1789773151711712e-09,4.2075000561761505e-09,2.8527970677918958e-08,,f4.ptu +3,0.3805309734513274,0.46887966804979253,241,0.0007504499999999999,2.5442334163900413,2.5438294,2.54457985,70,43,128,0,2.7935714658696076e-09,3.2552326016015513e-09,2.810332068771957e-08,,f4.ptu +4,0.15,0.5813953488372093,172,0.00062635,2.9248856671511625,2.92461285,2.9252392,85,15,72,0,3.5211765176009833e-09,2.2600000301742365e-09,2.8517361491858714e-08,,f4.ptu +5,0.11888111888111888,0.6559633027522935,218,0.0006665999999999999,2.925925973853211,2.9255554999999998,2.9262221,126,17,75,0,3.1081349621329723e-09,2.947058862876867e-09,2.8762000384013887e-08,,f4.ptu +6,0.5,0.48484848484848486,132,0.00035585,3.1350279803030303,3.1348157,3.13517155,32,32,68,0,2.2718750303327845e-09,4.254687556806171e-09,2.879117685499167e-08,,f4.ptu +7,0.782051282051282,0.5454545454545454,143,0.0004764,3.2864551877622374,3.28621215,3.28668855,17,61,65,0,3.2617647494315826e-09,4.791393506594914e-09,2.972923116615857e-08,,f4.ptu +8,0.37037037037037035,0.574468085106383,94,0.00042395,4.077127779787234,4.0769307999999995,4.07735475,34,20,40,0,3.7433824029206915e-09,3.915000052270856e-09,2.82618753773365e-08,,f4.ptu +9,0.16901408450704225,0.5035460992907801,141,0.000419,4.077845644326241,4.0776496,4.0780686,59,12,70,0,3.604237336257322e-09,4.512500060248337e-09,2.8293214663469214e-08,,f4.ptu +10,0.6911764705882353,0.5271317829457365,129,0.00046919999999999997,4.637728455813953,4.6374898,4.6379589999999995,21,47,61,0,2.5059524144104338e-09,3.582446856341433e-09,2.854631185654397e-08,,f4.ptu +11,0.7027027027027027,0.5311004784688995,209,0.00066225,4.638759683014354,4.6384251,4.63908735,33,78,98,0,2.3568182132850794e-09,3.768910306730605e-09,2.8820153446014802e-08,,f4.ptu +12,0.9418604651162791,0.5584415584415584,154,0.00046215,5.617535639935065,5.6173267,5.61778885,5,81,68,0,2.4400000325774937e-09,3.808642026159466e-09,2.948419157012676e-08,,f4.ptu +13,0.8676470588235294,0.5528455284552846,370,0.0016384499999999998,6.18067671527027,6.17992095,6.181559399999999,27,177,165,1,2.444444477081278e-09,4.015819262656485e-09,2.888742462811272e-08,2.8400000379180668e-08,f4.ptu +14,0.1919191919191919,0.5892857142857143,168,0.00057135,6.445808167559524,6.445531,6.446102349999999,80,19,69,0,2.9609375395327556e-09,4.142105318460931e-09,2.7926449648220407e-08,,f4.ptu +15,0.4925373134328358,0.44370860927152317,151,0.0006914499999999999,6.848385460927152,6.8480377,6.84872915,34,33,84,0,2.9588235689162957e-09,3.2287879218968205e-09,2.869791704982495e-08,,f4.ptu +16,0.07246376811594203,0.5227272727272727,132,0.00050475,7.911861859090909,7.9116329,7.91213765,64,5,63,0,3.441796920952917e-09,2.810000037517524e-09,2.881746070221468e-08,,f4.ptu +17,0.9375,0.5594405594405595,143,0.00050435,9.351014051048951,9.3507822,9.35128655,5,75,63,0,3.5100000468635264e-09,3.1803333757953376e-09,2.8963889275598284e-08,,f4.ptu +18,0.603448275862069,0.464,125,0.00045545,9.416124843199999,9.41589775,9.4163532,23,35,67,0,3.019565257706824e-09,2.9250000390529385e-09,2.8533582470516418e-08,,f4.ptu +19,0.5573770491803278,0.46564885496183206,131,0.00048259999999999997,10.591812293511449,10.5915714,10.592054,27,34,70,0,2.4203704026857807e-09,2.7823529783248667e-09,2.8474286094458202e-08,,f4.ptu +20,0.7092198581560284,0.47315436241610737,298,0.00100585,10.737798576342282,10.737345099999999,10.73835095,41,100,157,0,2.6280488155760192e-09,3.591500047951668e-09,2.881799401533473e-08,,f4.ptu +21,0.684931506849315,0.5214285714285715,140,0.00055145,11.267530855714284,11.2672595,11.26781095,23,50,67,0,2.820652211572789e-09,3.877000051763502e-09,2.9170149643194604e-08,,f4.ptu +22,0.6842105263157895,0.5688622754491018,167,0.0006324999999999999,12.915512495508981,12.91519235,12.91582485,30,65,72,0,2.3883333652210034e-09,3.632692356193952e-09,2.918819483414864e-08,,f4.ptu +23,0.9137931034482759,0.42962962962962964,135,0.00045484999999999997,13.01316205222222,13.01293065,13.0133855,5,53,77,0,2.4850000331783087e-09,3.490566084339904e-09,2.890422116513352e-08,,f4.ptu +24,0.5,0.14285714285714285,14,7.785e-05,13.209333589285714,13.2092961,13.20937395,1,1,12,0,1.875000025033935e-09,2.8000000373840095e-09,2.7860417038643125e-08,,f4.ptu +25,0.07692307692307693,0.5098039215686274,51,0.0002939,13.211191288235295,13.2110265,13.2113204,24,2,25,0,3.043750040638421e-09,2.4500000327110083e-09,2.8557000381276842e-08,,f4.ptu +26,0.3333333333333333,0.5454545454545454,11,7.91e-05,13.211634327272726,13.21159615,13.211675249999999,4,2,5,0,2.918750038969492e-09,1.0112500135016356e-08,2.8335000378312825e-08,,f4.ptu +27,0.5853658536585366,0.48520710059171596,169,0.0005829499999999999,13.819275338461539,13.818995549999999,13.819578499999999,34,48,87,0,2.191911793971043e-09,3.1036458747714492e-09,2.8375287735172555e-08,,f4.ptu +28,0.3835616438356164,0.474025974025974,154,0.0007446,13.820477097077921,13.82005525,13.82079985,45,28,81,0,2.890000038585638e-09,2.9276786105172726e-09,2.8303086797639822e-08,,f4.ptu +29,0.4426229508196721,0.5292841648590022,461,0.00180065,14.382028272125813,14.38117995,14.3829806,136,108,217,0,2.7430147425055272e-09,3.0828704115311044e-09,2.8282258442124773e-08,,f4.ptu +30,0.8767123287671232,0.5289855072463768,138,0.0006378499999999999,14.587557782608696,14.58725675,14.587894599999998,9,64,65,0,2.1444444730758483e-09,3.257031293486031e-09,2.8316154224215047e-08,,f4.ptu +31,0.8865979381443299,0.5449438202247191,178,0.00070595,14.591304641011234,14.5909582,14.59166415,11,86,81,0,2.415909123164937e-09,3.649709351054427e-09,2.8890432484494072e-08,,f4.ptu +32,0.82,0.45454545454545453,110,0.00041224999999999996,14.592095303181818,14.591866399999999,14.592278649999999,9,41,60,0,3.069444485425923e-09,2.9365854050612786e-09,2.8923750386173482e-08,,f4.ptu +33,0.8617021276595744,0.5053763440860215,186,0.00068025,14.611716611021507,14.611367999999999,14.612048249999999,13,81,92,0,3.436538507421171e-09,3.520370417372356e-09,2.877663081899183e-08,,f4.ptu +34,0.8070175438596491,0.5277777777777778,109,0.0005647499999999999,15.581095133027523,15.5808369,15.58140165,11,46,51,1,4.152272782711514e-09,3.751087006604121e-09,2.95544121592996e-08,3.83750005123612e-08,f4.ptu +35,0.84375,0.47761194029850745,134,0.00049955,15.721624518656714,15.7213578,15.721857349999999,10,54,70,0,2.4850000331783087e-09,4.087037091604835e-09,2.940928610694179e-08,,f4.ptu +36,0.5645161290322581,0.4161073825503356,149,0.0005742,16.423194540604026,16.422921249999998,16.42349545,27,35,87,0,2.453703736464162e-09,3.1600000421905252e-09,2.8230172790706332e-08,,f4.ptu +37,0.3548387096774194,0.5344827586206896,116,0.00034155,19.08830083922414,19.08811115,19.088452699999998,40,22,54,0,2.7768750370752575e-09,4.2511364203951215e-09,2.864722260470366e-08,,f4.ptu +38,0.20703125,0.37481698389458273,683,0.0016120499999999999,20.147808349048315,20.1470772,20.14868925,203,53,427,0,3.424507434884639e-09,3.118867966169655e-09,2.873208469274796e-08,,f4.ptu +39,0.5365853658536586,0.4270833333333333,288,0.00100165,21.421523339583334,21.4210335,21.42203515,57,66,165,0,2.7074561764992935e-09,3.3965909544402617e-09,2.8214545831250036e-08,,f4.ptu +40,0.6527777777777778,0.5714285714285714,126,0.00043759999999999996,21.618915067460314,21.6186985,21.6191361,25,47,54,0,2.4990000333652285e-09,3.901595796772742e-09,2.8891204089442642e-08,,f4.ptu +41,0.6307692307692307,0.4744525547445255,138,0.0004298,21.759782247101448,21.75953675,21.759966549999998,24,41,72,1,2.5343750338375354e-09,2.862804916271325e-09,2.820208370987153e-08,3.9850000532054564e-08,f4.ptu +42,0.7647058823529411,0.5112781954887218,133,0.00062205,22.103106266541353,22.1027904,22.10341245,16,52,65,0,2.5609375341921828e-09,3.026442348099646e-09,2.8818077307839515e-08,,f4.ptu +43,0.6111111111111112,0.5228215767634855,241,0.00092895,22.430489224481324,22.43002025,22.4309492,49,77,115,0,2.3357143168994163e-09,3.4314935523088584e-09,2.841739168376069e-08,,f4.ptu +44,0.65,0.4968944099378882,161,0.00047149999999999997,22.617157853416145,22.6169248,22.6173963,28,52,81,0,2.42142860375811e-09,3.2009615811989587e-09,2.8524691738870168e-08,,f4.ptu +45,0.6947368421052632,0.5121293800539084,371,0.00132065,22.677137446495955,22.6764437,22.67776435,58,132,181,0,2.6112069314150756e-09,3.2920454984989756e-09,2.8710359499346508e-08,,f4.ptu +46,0.7023809523809523,0.6131386861313869,137,0.00045155,22.74959053759124,22.74937,22.74982155,25,59,53,0,3.2740000437125883e-09,3.7262712361917344e-09,2.9067453218280794e-08,,f4.ptu +47,0.08823529411764706,0.44933920704845814,227,0.0009239,23.55503067665198,23.55454245,23.55546635,93,9,125,0,3.543010799992439e-09,2.5777778121948023e-09,2.8357000378606556e-08,,f4.ptu +48,0.5478260869565217,0.5665024630541872,203,0.00062345,24.15185354384236,24.1515362,24.152159649999998,52,63,88,0,2.8153846529740316e-09,3.733730208580803e-09,2.863664810961298e-08,,f4.ptu +49,0.7,0.4968944099378882,161,0.000536,24.155670441614905,24.15540785,24.15594385,24,56,81,0,2.6541667021035926e-09,3.5294643328376877e-09,2.918456829089034e-08,,f4.ptu +50,0.18867924528301888,0.53,100,0.00046455,25.3026513765,25.30239765,25.3028622,43,10,47,0,3.0360465521634754e-09,3.895000052003828e-09,2.8594681232843762e-08,,f4.ptu +51,0.5403225806451613,0.49206349206349204,252,0.0008286,25.46154012361111,25.46112835,25.461956949999998,57,67,128,0,2.5859649468070365e-09,3.06082093638873e-09,2.8365625378721712e-08,,f4.ptu +52,0.7972972972972973,0.3978494623655914,186,0.0008005499999999999,26.24204197983871,26.24164745,26.242448,15,59,112,0,2.3716666983318126e-09,3.4733051311193593e-09,2.8539732523903437e-08,,f4.ptu +53,0.3835616438356164,0.49324324324324326,148,0.0005848,26.339037227027024,26.33874385,26.33932865,45,28,75,0,2.626111146173455e-09,3.6785714776856248e-09,2.8414667046043152e-08,,f4.ptu +54,0.5185185185185185,0.5454545454545454,99,0.00044554999999999996,26.767675527272726,26.76743315,26.767878699999997,26,28,45,0,2.7365384980751686e-09,2.6491071782265165e-09,2.8166667042732e-08,,f4.ptu +55,0.6811594202898551,0.45394736842105265,152,0.0005622499999999999,27.335942306907892,27.3356861,27.336248349999998,22,47,83,0,2.327272758345151e-09,3.605851111973063e-09,2.8808735324396898e-08,,f4.ptu +56,0.7454545454545455,0.44715447154471544,123,0.00048435,27.5337966402439,27.533520449999997,27.534004799999998,14,41,68,0,2.7178571791444275e-09,3.5713415110971563e-09,2.8916912150788063e-08,,f4.ptu +57,0.3108108108108108,0.4277456647398844,173,0.0006491,28.835067339595376,28.834718849999998,28.83536795,51,23,99,0,3.603431420659988e-09,3.5434783081800736e-09,2.9111111499786127e-08,,f4.ptu +58,0.5521739130434783,0.39182282793867124,587,0.0017437,28.8372608044293,28.83636245,28.838106149999998,103,127,357,0,3.039805865828479e-09,3.4647638257871173e-09,2.8860434558997588e-08,,f4.ptu +59,0.2876712328767123,0.45625,160,0.00064435,28.8530899484375,28.85276425,28.853408599999998,52,21,87,0,3.046153886824362e-09,3.4511905222688106e-09,2.9333621081301016e-08,,f4.ptu +60,0.35064935064935066,0.4723926380368098,164,0.00052,28.855552888414632,28.855291249999997,28.85581125,50,27,86,1,3.0295000404481633e-09,3.271296339972786e-09,2.858604689329256e-08,4.2100000562095286e-08,f4.ptu +61,0.8125,0.43243243243243246,37,0.00022495,29.263552058108107,29.2634261,29.26365105,3,13,21,0,3.7166667162894886e-09,3.146153888159505e-09,2.8782143241425677e-08,,f4.ptu +62,0.43356643356643354,0.5836734693877551,245,0.0007888,29.366160405714282,29.36578105,29.366569849999998,81,62,102,0,3.0854938683562547e-09,4.264516185969655e-09,2.836446116301989e-08,,f4.ptu +63,0.7962962962962963,0.3624161073825503,149,0.00043575,30.10236235067114,30.102122899999998,30.10255865,11,43,95,0,2.3318182129512936e-09,3.033139575380477e-09,2.8183684586818855e-08,,f4.ptu +64,0.6,0.5,150,0.00055315,30.505348113999997,30.505074949999997,30.5056281,30,45,75,0,2.3808333651208676e-09,3.5133333802413643e-09,2.889066705239844e-08,,f4.ptu +65,0.15730337078651685,0.6137931034482759,145,0.00047335,31.64941631206896,31.649206799999998,31.64968015,75,14,56,0,3.3400000445937825e-09,2.3714286030905386e-09,2.849464323758714e-08,,f4.ptu +66,0.3333333333333333,0.5960264900662252,151,0.0006406,32.15981011059603,32.1595089,32.160149499999996,60,30,61,0,2.845416704657054e-09,3.053333374099706e-09,2.8793443007384507e-08,,f4.ptu +67,0.3333333333333333,0.375,8,7.835e-05,32.41301610625,32.4129789,32.41305725,2,1,5,0,2.875000038385367e-09,2.2000000293731503e-09,3.043000040628407e-08,,f4.ptu +68,0.4410480349344978,0.5204545454545455,440,0.0012304,35.21902380579545,35.21838965,35.219620049999996,128,101,211,0,2.919531288979923e-09,3.9554455973653175e-09,2.8841114129146e-08,,f4.ptu +69,0.5851063829787234,0.618421052631579,152,0.0005434999999999999,35.42218565493421,35.4218681,35.4224116,39,55,58,0,2.913461577360422e-09,3.509090955942298e-09,2.8333621067949584e-08,,f4.ptu +70,0.5555555555555556,0.4090909090909091,154,0.00049155,37.726308175,37.7260492,37.72654075,28,35,91,0,2.973214325410954e-09,3.6764286205141572e-09,2.9238461928836866e-08,,f4.ptu +71,0.7272727272727273,0.4782608695652174,345,0.0008767499999999999,38.102571626231885,38.1021131,38.10298985,45,120,180,0,2.6238889239215626e-09,3.562708380900591e-09,2.8972639275715105e-08,,f4.ptu +72,0.6486486486486487,0.5606060606060606,133,0.00064965,38.36923358684211,38.36893315,38.369582799999996,26,48,58,1,2.633653881316896e-09,3.079687541118238e-09,2.8591810726569197e-08,3.4550000461291974e-08,f4.ptu +73,0.9690721649484536,0.6510067114093959,149,0.00048489999999999997,39.34529019664429,39.345073299999996,39.3455582,3,94,52,0,6.450000086116736e-09,3.3316489806524964e-09,2.9073077311244132e-08,,f4.ptu +74,0.5959595959595959,0.5,198,0.001032,40.23424834191919,40.23369555,40.234727549999995,40,59,99,0,2.7893750372421504e-09,3.3652542822190987e-09,2.865530341289236e-08,,f4.ptu +75,0.9134615384615384,0.45021645021645024,231,0.0006613999999999999,40.369481995021644,40.3691849,40.3698463,9,95,127,0,2.4805555886745245e-09,3.294473728196468e-09,2.9209646059281935e-08,,f4.ptu +76,0.7407407407407407,0.4682080924855491,173,0.0007645,40.37030513236994,40.369862149999996,40.37062665,21,60,92,0,2.726190512589023e-09,3.717916716306178e-09,2.9077717779533514e-08,,f4.ptu +77,0.5,0.45614035087719296,114,0.00041319999999999996,40.48003367982456,40.47982825,40.48024145,26,26,62,0,2.553846187943657e-09,3.502884662153141e-09,2.8756855222654866e-08,,f4.ptu +78,0.9047619047619048,0.5294117647058824,119,0.00044354999999999997,40.494585637394955,40.4943807,40.49482425,6,57,56,0,2.116666694927198e-09,3.604824609533079e-09,2.9059375387984268e-08,,f4.ptu +79,0.42990654205607476,0.45147679324894513,237,0.0007995,40.67092567341772,40.6704563,40.6712558,61,46,130,0,2.3668033102887375e-09,2.872282647044738e-09,2.8382115763557267e-08,,f4.ptu +80,0.5512820512820513,0.5416666666666666,144,0.0006095499999999999,40.855711693055554,40.855415699999995,40.856025249999995,35,43,66,0,3.0878571840844574e-09,3.0988372506762396e-09,2.8303030680916285e-08,,f4.ptu +81,0.46715328467153283,0.556910569105691,246,0.0009835,40.85737670406504,40.8569533,40.8579368,73,64,109,0,3.459589087286358e-09,3.281250043809386e-09,2.8547248087568655e-08,,f4.ptu +82,0.6153846153846154,0.5449101796407185,167,0.00063245,42.454439111077846,42.45412985,42.4547623,35,56,76,0,2.6935714645344645e-09,3.0526786121862016e-09,2.8179934586768793e-08,,f4.ptu +83,0.6689655172413793,0.5846774193548387,248,0.0006495,42.67430638709677,42.6739442,42.674593699999996,48,97,103,0,2.628125035089232e-09,2.998453648281072e-09,2.8450485816747925e-08,,f4.ptu +84,0.5742574257425742,0.5233160621761658,193,0.00065105,42.69856419326425,42.69827385,42.6989249,43,58,92,0,2.845348875198784e-09,2.9745690052319965e-09,2.828097863846112e-08,,f4.ptu +85,0.9,0.5633802816901409,284,0.0007343,42.98488619260563,42.98449565,42.98522995,16,144,124,0,2.4781250330865173e-09,3.880902829593387e-09,2.9253024584118147e-08,,f4.ptu +86,0.7477477477477478,0.4007220216606498,277,0.00092965,42.99480259079422,42.99433275,42.9952624,28,83,166,0,2.554464319820042e-09,3.268072332790071e-09,2.869954857595115e-08,,f4.ptu +87,0.07142857142857142,0.5,28,0.00016319999999999998,43.03175564107143,43.03168785,43.03185105,13,1,14,0,2.2807692612207657e-09,1.7750000236987917e-09,2.8766071812639674e-08,,f4.ptu +88,0.0,0.5454545454545454,22,0.00010879999999999999,43.032139336363635,43.03208275,43.03219155,12,0,10,0,2.620833368325211e-09,,2.7772500370802645e-08,,f4.ptu +89,0.8666666666666667,0.5357142857142857,84,0.00046344999999999996,43.45571499285714,43.4554773,43.455940749999996,6,39,39,0,2.9458333726644264e-09,3.752564152666207e-09,2.9477564496131795e-08,,f4.ptu +90,0.8142857142857143,0.5,140,0.0006619,43.45652058571429,43.45615695,43.45681885,13,57,70,0,2.4730769560960413e-09,3.3557017991893887e-09,2.8982857529819786e-08,,f4.ptu +91,0.5783783783783784,0.4920212765957447,376,0.0015259499999999999,43.69243048430851,43.6916492,43.693175149999995,78,107,191,0,2.6342949069664807e-09,3.896962668852462e-09,2.922002656813966e-08,,f4.ptu +92,0.7590361445783133,0.46368715083798884,179,0.0007042,44.27589455083798,44.275517099999995,44.276221299999996,20,63,96,0,2.241250029923897e-09,2.597222256898858e-09,2.8156771209266542e-08,,f4.ptu +93,0.7454545454545455,0.5238095238095238,105,0.0004893,44.35365143999999,44.35339245,44.35388175,14,41,50,0,2.1928571721349257e-09,3.4073171186632937e-09,2.8722500383486505e-08,,f4.ptu +94,0.7761194029850746,0.4249471458773784,473,0.0012506,44.355893730443974,44.35526945,44.35652005,45,156,272,0,2.642222257499672e-09,3.2004808119617707e-09,2.8379320231845734e-08,,f4.ptu +95,0.5277777777777778,0.4864864864864865,518,0.0018279,44.542024715444015,44.5411262,44.542954099999996,119,133,266,0,2.831932810919601e-09,3.7511278696318e-09,2.8645677074190127e-08,,f4.ptu +96,0.6595744680851063,0.47959183673469385,98,0.00045325,44.549770708163265,44.54953965,44.5499929,16,31,51,0,2.818750037634349e-09,4.6120968357716444e-09,2.936666705875372e-08,,f4.ptu +97,0.6436781609195402,0.58,151,0.0006680999999999999,44.55326150562914,44.552965449999995,44.55363355,31,56,63,1,2.843548425062217e-09,3.5575893332131968e-09,2.898055594248747e-08,3.637500048565834e-08,f4.ptu +98,0.6912751677852349,0.3983957219251337,374,0.00105625,44.915177765106954,44.91464865,44.9157049,46,103,225,0,2.4467391631022534e-09,3.726456360433139e-09,2.8769222606332533e-08,,f4.ptu +99,0.3163841807909605,0.5315315315315315,333,0.00089845,45.50582873453453,45.50531965,45.5062181,121,56,156,0,3.3702479788819336e-09,4.458482202384264e-09,2.895464782248344e-08,,f4.ptu +100,0.4117647058823529,0.5190839694656488,131,0.00045214999999999996,46.45097035267175,46.450738099999995,46.451190249999996,40,28,63,0,2.9443750393116227e-09,3.2687500436424932e-09,2.841785752227623e-08,,f4.ptu +101,0.1111111111111111,0.45918367346938777,196,0.0008277,46.662896001275506,46.662475099999995,46.6633028,80,10,106,0,2.5500000340461515e-09,2.2275000297403144e-09,2.8188915470702004e-08,,f4.ptu +102,0.5145631067961165,0.4735632183908046,435,0.00168575,47.9430433613793,47.942224949999996,47.943910699999996,100,106,229,0,2.5692500343031663e-09,3.4733491029779572e-09,2.8662773308453255e-08,,f4.ptu +103,0.6533333333333333,0.436046511627907,172,0.0007373999999999999,49.202366022965116,49.2019619,49.2026993,26,49,97,0,2.243269260720087e-09,3.342857187489073e-09,2.9130670492028944e-08,,f4.ptu +104,0.5,0.5373134328358209,67,0.0002804,49.60217505074627,49.602036899999995,49.602317299999996,18,18,31,0,2.7555555923461683e-09,2.834722260069823e-09,2.842983908925648e-08,,f4.ptu +105,0.18072289156626506,0.3824884792626728,217,0.000687,49.63986816290322,49.639540249999996,49.640227249999995,68,15,134,0,3.73970593228337e-09,3.290000043926211e-09,2.866660486035216e-08,,f4.ptu +106,0.4731182795698925,0.6480836236933798,287,0.0007833,50.00666215121951,50.006342149999995,50.00712545,98,88,101,0,2.5640306464783783e-09,2.9974432218383405e-09,2.8779950879303057e-08,,f4.ptu +107,0.6778523489932886,0.5033783783783784,296,0.00094285,50.385764453209454,50.3852775,50.386220349999995,48,101,147,0,3.0171875402837736e-09,3.3269802424397517e-09,2.8350170446542578e-08,,f4.ptu +108,0.20930232558139536,0.3805309734513274,113,0.00036304999999999996,50.90522852699115,50.905077649999996,50.9054407,34,9,70,0,4.26102946865555e-09,3.502777824544877e-09,2.8096786089418037e-08,,f4.ptu +109,0.5,0.6666666666666666,6,2.575e-05,50.905624616666664,50.90561525,50.905640999999996,2,2,2,0,4.512500060248337e-09,2.0375000272035426e-09,3.0637500409054497e-08,,f4.ptu +110,0.8950276243093923,0.48655913978494625,372,0.0014762,51.94293650080645,51.9421758,51.943652,19,162,191,0,2.0513158168616874e-09,3.1058642389985987e-09,2.8750262163961875e-08,,f4.ptu +111,0.7,0.42424242424242425,165,0.00064645,52.76915396060606,52.768864799999996,52.76951125,21,49,95,0,2.2511905062470927e-09,3.838265357368787e-09,2.9557368815685826e-08,,f4.ptu +112,0.5531914893617021,0.44976076555023925,209,0.0009620999999999999,52.77216551387559,52.771689249999994,52.77265135,42,52,115,0,2.391071460495656e-09,3.1413461957876234e-09,2.846956559750077e-08,,f4.ptu +113,0.17142857142857143,0.4,175,0.00050395,52.95185815342857,52.951587149999995,52.9520911,58,12,105,0,3.0577586615151114e-09,4.427083392441236e-09,2.9044762292551068e-08,,f4.ptu +114,0.11666666666666667,0.38461538461538464,156,0.00054135,54.27040434903846,54.27014885,54.2706902,53,7,96,0,2.8372641888249354e-09,2.542857176807927e-09,2.8677344132883604e-08,,f4.ptu +115,0.1694915254237288,0.35435435435435436,334,0.0012213,54.550410606137724,54.54973035,54.550951649999995,98,20,215,1,5.057908230795623e-09,5.285000070562318e-09,2.9800930630443235e-08,3.0025000400876745e-08,f4.ptu +116,0.6226415094339622,0.49074074074074076,108,0.00038134999999999997,54.642948249074074,54.642742899999995,54.64312425,20,33,55,0,2.686250035865284e-09,3.910606112818252e-09,2.8306364014294123e-08,,f4.ptu +117,0.14893617021276595,0.4845360824742268,97,0.0003361,54.643468467525764,54.643266149999995,54.64360225,40,7,50,0,3.3156250442683416e-09,4.196428627456902e-09,2.87905003843944e-08,,f4.ptu +118,0.4533762057877814,0.5174708818635607,602,0.00225365,54.650484004817265,54.649338799999995,54.651592449999995,170,141,290,1,3.201029454503032e-09,3.4507092659300652e-09,2.836353486145242e-08,4.640000061950644e-08,f4.ptu +119,0.5116279069767442,0.44559585492227977,193,0.0007350999999999999,54.65273091450777,54.6523295,54.6530646,42,44,107,0,4.558333394193611e-09,3.2897727711959043e-09,2.809556112277952e-08,,f4.ptu +120,0.8585858585858586,0.526595744680851,188,0.0008407499999999999,55.64281142765957,55.642402499999996,55.64324325,14,85,89,0,2.1928571721349257e-09,3.2885294556712825e-09,2.8296629591283667e-08,,f4.ptu +121,0.81,0.4716981132075472,424,0.0017059,55.92719289009434,55.9263444,55.928050299999995,38,162,224,0,2.2789473988482634e-09,3.060956830991613e-09,2.8381473593219025e-08,,f4.ptu +122,0.676056338028169,0.5725806451612904,124,0.0005603499999999999,55.97100817862903,55.970726299999995,55.971286649999996,23,48,53,0,3.4021739584673715e-09,3.230729209801527e-09,2.850613245606938e-08,,f4.ptu +123,0.5818181818181818,0.4700854700854701,117,0.0006104,56.109517774786326,56.109204049999995,56.109814449999995,23,32,62,0,2.3478261183033622e-09,3.853125051444736e-09,2.983427459187867e-08,,f4.ptu +124,0.7222222222222222,0.5,144,0.0007322,56.77475826041666,56.774402599999995,56.7751348,20,52,72,0,2.173750029022675e-09,3.1495192728198223e-09,2.8840625385063642e-08,,f4.ptu +125,0.4939759036144578,0.5460526315789473,152,0.0006464,56.88836104144736,56.8880488,56.8886952,42,41,69,0,2.576190510586308e-09,3.0567073578845903e-09,2.8277898928275077e-08,,f4.ptu +126,0.6931818181818182,0.6153846153846154,143,0.0005092499999999999,57.021123320629364,57.02087805,57.0213873,27,61,55,0,2.3740741057713627e-09,3.407786930744737e-09,2.8627727654948423e-08,,f4.ptu +127,0.7065217391304348,0.6524822695035462,141,0.00049915,57.47610904574468,57.47584525,57.476344399999995,27,65,49,0,2.718518554814634e-09,3.5980769711164027e-09,2.836581670525488e-08,,f4.ptu +128,0.6344827586206897,0.5160142348754448,281,0.0010586,58.29588915409252,58.295342749999996,58.29640135,53,92,136,0,2.4514151270695247e-09,3.1385869984263694e-09,2.8289338612997292e-08,,f4.ptu +129,0.5862068965517241,0.5337423312883436,163,0.0005429,58.74937125460123,58.749095399999995,58.7496383,36,51,76,0,2.970833372998212e-09,3.779902011251417e-09,2.8672368803869808e-08,,f4.ptu +130,0.5702479338842975,0.5761904761904761,210,0.0006611,59.05708218904762,59.056726149999996,59.05738725,52,69,89,0,2.4350961863581746e-09,2.8887681545112197e-09,2.813455093743429e-08,,f4.ptu +131,0.5526315789473685,0.6031746031746031,252,0.0007373,59.07075541507936,59.07040885,59.07114615,68,84,100,0,2.6547794472098123e-09,2.896726229151633e-09,2.8271500377465006e-08,,f4.ptu +0,0.4852941176470588,0.5074626865671642,134,0.0006167999999999999,0.08911167686567165,0.08879234999999999,0.08940914999999999,35,33,66,0,2.3742857459858285e-09,2.7174242787057473e-09,2.8120833708786725e-08,,f5.ptu +1,0.49122807017543857,0.5,115,0.00052085,0.693281542173913,0.6929847499999999,0.6935055999999999,29,28,57,1,3.431034528567844e-09,3.2482143290825975e-09,2.831842143072305e-08,2.7300000364494093e-08,f5.ptu +2,0.0,0.7333333333333333,15,7.465e-05,1.4107530633333332,1.4107178,1.41079245,11,0,4,0,2.875000038385367e-09,,2.7862500372004273e-08,,f5.ptu +3,0.46551724137931033,0.48333333333333334,121,0.0005157499999999999,1.5077220834710743,1.50746955,1.5079852999999999,31,27,62,1,3.26048391449987e-09,3.350000044727297e-09,2.824193586094125e-08,3.065000040922139e-08,f5.ptu +4,0.3333333333333333,0.4015748031496063,127,0.0004764,2.0681050783464565,2.0678719,2.0683483,34,17,76,0,3.018382393240903e-09,3.629411813163727e-09,2.8193092481681306e-08,,f5.ptu +5,0.37209302325581395,0.45989304812834225,187,0.000765,2.0757352112299463,2.07533515,2.07610015,54,32,101,0,3.5472222695827185e-09,3.1054687914624547e-09,2.815717859376043e-08,,f5.ptu +6,0.43243243243243246,0.3425925925925926,432,0.0013327,2.079810994560185,2.07923355,2.08056625,84,64,284,0,3.3255952824966964e-09,3.1664062922760576e-09,2.8369278547784575e-08,,f5.ptu +7,0.7441860465116279,0.49142857142857144,175,0.0005409999999999999,3.2670912902857143,3.2668277999999997,3.2673688,22,64,89,0,2.7840909462625092e-09,3.3109375442057568e-09,2.870196667534718e-08,,f5.ptu +8,0.5957446808510638,0.44339622641509435,106,0.00048475,4.3925374924528295,4.392282,4.39276675,19,28,59,0,2.5828947713274486e-09,3.276785758035496e-09,2.8105508849825055e-08,,f5.ptu +9,0.5471698113207547,0.4608695652173913,115,0.0004453,5.10318447173913,5.1029706,5.1034159,24,29,62,0,2.604166701436021e-09,3.019827626525919e-09,2.8408468121229207e-08,,f5.ptu +10,0.4854368932038835,0.38721804511278196,266,0.0006916499999999999,6.514980245112782,6.51462525,6.515316899999999,53,50,163,0,2.545283052851098e-09,4.1285000551213866e-09,2.8621472774775677e-08,,f5.ptu +11,0.34210526315789475,0.49673202614379086,153,0.000567,7.625833409477123,7.6255498,7.626116799999999,50,26,77,0,3.496500046683282e-09,3.60673081738579e-09,2.834188349528784e-08,,f5.ptu +12,0.58,0.4854368932038835,103,0.0003816,7.974767051941748,7.9745691,7.9749507,21,29,53,0,2.7000000360488663e-09,3.6681034972502954e-09,2.866084943926715e-08,,f5.ptu +13,0.47368421052631576,0.4470588235294118,85,0.00043275,9.144545437647059,9.1443342,9.14476695,20,18,47,0,3.757500050168006e-09,3.533333380508393e-09,2.852393655104816e-08,,f5.ptu +14,0.5161290322580645,0.4492753623188406,138,0.0007314499999999999,9.756791853985508,9.75643475,9.7571662,30,32,76,0,2.4658333662557395e-09,3.1835937925055353e-09,2.8423026695277576e-08,,f5.ptu +15,0.5,0.6187845303867403,181,0.0006030499999999999,11.749963713259667,11.7496668,11.750269849999999,56,56,69,0,2.779464322824114e-09,3.494642903801343e-09,2.8391304726890654e-08,,f5.ptu +16,0.364741641337386,0.5238853503184714,630,0.0028314,11.765248445079365,11.7639173,11.766748699999999,209,120,299,2,2.786722525245013e-09,3.2370833765530308e-09,2.8455267938446998e-08,3.605000048131912e-08,f5.ptu +17,0.4132231404958678,0.491869918699187,246,0.0007993999999999999,11.768057269715447,11.76768775,11.76848715,71,50,125,0,3.0598591957830787e-09,3.000000040054296e-09,2.8657600382619998e-08,,f5.ptu +18,0.5753424657534246,0.4515463917525773,486,0.0017597,13.332430587757202,13.331564949999999,13.33332465,93,126,266,1,2.5422043350173366e-09,2.9976190876415543e-09,2.830404173128294e-08,2.8250000377177953e-08,f5.ptu +19,0.5068493150684932,0.477124183006536,153,0.00057315,14.626009948039215,14.625746099999999,14.62631925,36,37,80,0,2.502777811193445e-09,3.939189241783006e-09,2.8862500385355705e-08,,f5.ptu +20,0.5454545454545454,0.46261682242990654,214,0.0006303,14.628214856542057,14.62787455,14.628504849999999,45,54,115,0,2.5122222557639865e-09,4.3759259843508034e-09,2.9008043865559786e-08,,f5.ptu +21,0.44,0.5,100,0.00045329999999999996,16.768321407,16.76811265,16.76856595,28,22,50,0,2.9776786111848442e-09,3.5193182288061194e-09,2.898700038701796e-08,,f5.ptu +22,0.49606299212598426,0.4980392156862745,255,0.0008707,16.769195165686273,16.76873745,16.76960815,64,63,128,0,2.592968784619846e-09,3.946031798716921e-09,2.846855506759597e-08,,f5.ptu +23,0.47619047619047616,0.5283018867924528,159,0.00065475,16.98523041855346,16.98491335,16.9855681,44,40,75,0,2.52386367006083e-09,3.392500045294733e-09,2.8522667047485113e-08,,f5.ptu +24,0.5,0.4,5,1.365e-05,17.138263369999997,17.138254,17.13826765,1,1,3,0,2.7750000370502237e-09,4.375000058412515e-09,2.8016667040729287e-08,,f5.ptu +25,0.7222222222222222,0.5217391304347826,138,0.0005635,17.209692888043477,17.209417549999998,17.20998105,20,52,66,0,2.5750000343799373e-09,3.175961580865173e-09,2.8412121591463717e-08,,f5.ptu +26,0.5460992907801419,0.5183823529411765,273,0.00120685,19.96056306208791,19.959995749999997,19.9612026,64,77,131,1,2.382812531813959e-09,3.1610390032433576e-09,2.8638359160989054e-08,4.255000056810343e-08,f5.ptu +27,0.696078431372549,0.5425531914893617,188,0.0006849499999999999,20.236021824468086,20.2356917,20.23637665,31,71,86,0,2.3508064829995357e-09,3.6690141334936345e-09,2.8668023638573116e-08,,f5.ptu +28,0.7428571428571429,0.5185185185185185,135,0.0006154,20.23682351962963,20.23649845,20.23711385,18,52,65,0,2.9694444840907797e-09,3.196634658064265e-09,2.887115423162509e-08,,f5.ptu +29,0.6,0.38461538461538464,13,8.329999999999999e-05,20.92415252307692,20.92411705,20.92420035,2,3,8,0,3.587500047898262e-09,3.7083333828448937e-09,2.8946875386482232e-08,,f5.ptu +30,0.38095238095238093,0.43448275862068964,145,0.00053015,20.92473878655172,20.924479899999998,20.925010049999997,39,24,82,0,3.0583333741664625e-09,2.9333333724975335e-09,2.8316463792699482e-08,,f5.ptu +31,0.5576923076923077,0.45414847161572053,229,0.0008036,20.926414966593885,20.9260186,20.9268222,46,58,125,0,2.5657609038217993e-09,3.4250000457286545e-09,2.8537000381009815e-08,,f5.ptu +32,0.7387387387387387,0.44047619047619047,252,0.0007701499999999999,20.94402479484127,20.943616249999998,20.9443864,29,82,141,0,2.2353448574312613e-09,3.1600610178010954e-09,2.8654433006691182e-08,,f5.ptu +33,0.3944954128440367,0.5190476190476191,210,0.0008181,21.739925945,21.73953925,21.74035735,66,43,101,0,2.5670454888191874e-09,2.90465120157195e-09,2.8213861762834066e-08,,f5.ptu +34,0.45,0.49586776859504134,121,0.00041529999999999996,21.742062458264463,21.741843199999998,21.7422585,33,27,61,0,2.508333366823175e-09,2.8212963339646417e-09,2.8159426605476863e-08,,f5.ptu +35,0.6075949367088608,0.4968553459119497,159,0.00064235,22.326253931132076,22.32593085,22.3265732,31,48,80,0,2.766129069189848e-09,4.034895887204971e-09,2.883500038498854e-08,,f5.ptu +36,0.575,0.41237113402061853,97,0.00049055,23.09945217680412,23.0992118,23.099702349999998,17,23,57,0,3.1573529833316537e-09,3.2369565649571354e-09,2.8520175819381675e-08,,f5.ptu +37,0.3684210526315789,0.4253731343283582,134,0.00042409999999999995,24.39973922947761,24.39953575,24.39995985,36,21,77,0,2.8951389275431387e-09,3.990476243754762e-09,2.8638312070674158e-08,,f5.ptu +38,0.47761194029850745,0.47183098591549294,142,0.0005882,24.53823494507042,24.53790625,24.538494449999998,35,32,75,0,2.558571462732021e-09,3.5984375480442934e-09,2.840033371251845e-08,,f5.ptu +39,0.6095238095238096,0.4751131221719457,221,0.0009567,26.266305195475113,26.26582475,26.26678145,41,64,116,0,2.6335366205273382e-09,3.2882812939032634e-09,2.8366164171832353e-08,,f5.ptu +40,0.4782608695652174,0.4726027397260274,146,0.0005207499999999999,27.12029227568493,27.12002085,27.1205416,36,33,77,0,2.4937500332951335e-09,3.0371212526711295e-09,2.7988636737324737e-08,,f5.ptu +41,0.875,0.5106382978723404,141,0.0004696,27.664543893971633,27.66428885,27.664758449999997,9,63,69,0,3.161111153316471e-09,3.651190524939097e-09,2.9059420677839946e-08,,f5.ptu +42,0.48854961832061067,0.5927601809954751,221,0.00071555,27.92877466402715,27.928383049999997,27.9290986,67,64,90,0,2.8134328733842524e-09,3.2546875434547387e-09,2.8460833713326215e-08,,f5.ptu +43,0.46153846153846156,0.37142857142857144,140,0.0004525,28.437483804285716,28.43724335,28.437695849999997,28,24,88,0,2.360714317233202e-09,2.8520833714127296e-09,2.809744355695928e-08,,f5.ptu +44,0.573170731707317,0.44086021505376344,186,0.0006634999999999999,28.75447188575269,28.75417105,28.754834549999998,35,47,104,0,2.7400000365829233e-09,3.779255369607406e-09,2.8304086916361622e-08,,f5.ptu +45,0.5086705202312138,0.4505208333333333,384,0.00111565,29.817958333463544,29.81741615,29.8185318,85,88,211,0,2.725882389335609e-09,3.1823864061257786e-09,2.862582976608207e-08,,f5.ptu +46,0.7432432432432432,0.5138888888888888,144,0.0005059999999999999,30.19215564861111,30.191889999999997,30.192396,19,55,70,0,2.4263158218684744e-09,3.5481818655551267e-09,2.918464324679963e-08,,f5.ptu +47,0.8255813953488372,0.6056338028169014,142,0.00048485,30.19535183028169,30.19509485,30.1955797,15,71,56,0,2.3366666978645126e-09,3.965493010691488e-09,2.9033928959073093e-08,,f5.ptu +48,0.639751552795031,0.5313531353135313,303,0.0007817999999999999,30.965410602970294,30.9649751,30.9657569,58,103,142,0,2.373275893755597e-09,3.346601986429501e-09,2.8993134189916754e-08,,f5.ptu +49,0.5692307692307692,0.4452054794520548,146,0.00056005,31.04872991746575,31.04843845,31.0489985,28,37,81,0,2.4830357474377965e-09,3.1972973399857947e-09,2.850308680031011e-08,,f5.ptu +50,0.29508196721311475,0.5169491525423728,118,0.0006190999999999999,31.27212399618644,31.2717966,31.2724157,43,18,57,0,2.964534923301716e-09,2.9472222615718593e-09,2.843552669544447e-08,,f5.ptu +51,0.7068965517241379,0.4915254237288136,118,0.00046655,33.05308340338983,33.05284165,33.0533082,17,41,60,0,2.627941211557366e-09,3.87804883226531e-09,2.841750037941432e-08,,f5.ptu +52,0.6451612903225806,0.49206349206349204,63,0.00037735,33.062999488888885,33.0628154,33.06319275,11,20,32,0,2.7409091275041523e-09,3.2387500432419503e-09,2.8463281630025563e-08,,f5.ptu +53,0.6091954022988506,0.58,151,0.000714,33.08902509039735,33.08868675,33.089400749999996,34,53,63,1,2.4992647392511157e-09,3.059434003111975e-09,2.8181746408012696e-08,2.772500037016845e-08,f5.ptu +54,0.4426229508196721,0.5,122,0.0005136,33.34201549385246,33.3417639,33.3422775,34,27,61,0,2.7852941548347235e-09,3.3027778218745905e-09,2.843401677307746e-08,,f5.ptu +55,0.7682926829268293,0.5655172413793104,290,0.0010274499999999998,33.34387511431034,33.343328899999996,33.34435635,38,126,126,0,3.034210566826845e-09,3.371230203740909e-09,2.8949008323018644e-08,,f5.ptu +56,0.7101449275362319,0.4423076923076923,156,0.0006541,34.45274865961538,34.4524131,34.4530672,20,49,87,0,3.000000040054296e-09,3.246938818861486e-09,2.8478736012415807e-08,,f5.ptu +57,0.7840909090909091,0.5269461077844312,167,0.0007524999999999999,34.55674930149701,34.55636415,34.55711665,19,69,79,0,2.3250000310420793e-09,3.7246377308886667e-09,2.9003797855596656e-08,,f5.ptu +58,0.691358024691358,0.4764705882352941,170,0.0006083,34.562754489411766,34.56243715,34.56304545,25,56,89,0,3.2570000434856138e-09,3.886160766171525e-09,2.8771629597625597e-08,,f5.ptu +59,0.7352941176470589,0.53125,129,0.0005336,34.58158944612403,34.581311899999996,34.5818455,18,50,60,1,2.715277814030624e-09,3.5775000477647476e-09,2.8927500386223547e-08,4.987500066590267e-08,f5.ptu +60,0.6923076923076923,0.4148936170212766,188,0.00079625,35.48936040718085,35.48899885,35.489795099999995,24,54,110,0,2.6416667019366997e-09,3.1814815239588147e-09,2.876113674763872e-08,,f5.ptu +61,0.5684210526315789,0.4947916666666667,192,0.0005714,35.54255734270833,35.542252,35.542823399999996,41,54,97,0,2.35731710464429e-09,2.8958333719968548e-09,2.8368299347829576e-08,,f5.ptu +62,0.5272727272727272,0.46218487394957986,119,0.00048045,35.78564016260504,35.785396399999996,35.78587685,26,29,64,0,2.796153883486504e-09,2.9163793492826677e-09,2.850273475555232e-08,,f5.ptu +63,0.43478260869565216,0.6571428571428571,35,0.00015635,35.86945125142857,35.86938045,35.8695368,13,10,12,0,2.5307692645586237e-09,3.935000052537885e-09,2.9141667055749647e-08,,f5.ptu +64,0.3790322580645161,0.4217687074829932,294,0.0012322499999999998,36.43600531598639,36.435352699999996,36.43658495,77,47,170,0,2.9941558841321123e-09,3.006914933763641e-09,2.8463088615317102e-08,,f5.ptu +65,0.3627450980392157,0.4722222222222222,216,0.0008685499999999999,36.53979275046296,36.5393408,36.54020935,65,37,114,0,3.0876923489174216e-09,3.2439189622298813e-09,2.8736184594195527e-08,,f5.ptu +66,0.21176470588235294,0.4913294797687861,173,0.0007634499999999999,37.236032522543354,37.23563255,37.236396,67,18,88,0,2.869776157718606e-09,3.359722267079325e-09,2.8331534469175645e-08,,f5.ptu +67,0.32142857142857145,0.45161290322580644,124,0.00055905,37.343197318548384,37.34293435,37.3434934,38,18,68,0,3.0598684619062566e-09,3.881944496273962e-09,2.856360332254147e-08,,f5.ptu +68,0.5652173913043478,0.5411764705882353,85,0.00028155,38.30112031352941,38.30093565,38.301217199999996,20,26,39,0,2.9125000388860456e-09,3.4653846616524624e-09,2.8284615762255505e-08,,f5.ptu +69,0.5935483870967742,0.5326460481099656,291,0.0012402,39.28324331821305,39.2826312,39.283871399999995,63,92,136,0,2.557936542088623e-09,3.771195702524775e-09,2.878327244312143e-08,,f5.ptu +70,0.8115942028985508,0.46621621621621623,148,0.0005518,41.68679008243243,41.68651505,41.68706685,13,56,79,0,2.726923113331405e-09,3.7227679068471386e-09,2.911234216084335e-08,,f5.ptu +71,0.7868852459016393,0.45185185185185184,135,0.0005547,41.68829379555555,41.6879801,41.6885348,13,48,74,0,2.6365384967400254e-09,3.408854212179751e-09,2.8580067949152394e-08,,f5.ptu +72,0.5849056603773585,0.5047619047619047,105,0.00042685,42.68550820047619,42.685332249999995,42.6857591,22,31,52,0,2.3920454864826868e-09,3.2967742375650436e-09,2.833076960902557e-08,,f5.ptu +73,0.21428571428571427,0.3684210526315789,38,0.00015255,43.93478923947368,43.934700799999995,43.93485335,11,3,24,0,3.6704545944603695e-09,2.4583333661556033e-09,2.851458371404385e-08,,f5.ptu +74,0.2838709677419355,0.39948453608247425,388,0.0013503,44.056695406701024,44.056005899999995,44.0573562,111,44,233,0,2.5853603948786233e-09,2.910227311582974e-09,2.844688879182386e-08,,f5.ptu +75,0.31343283582089554,0.4407894736842105,152,0.0007126999999999999,44.16442669078947,44.1640454,44.1647581,46,21,85,0,2.6635869920844392e-09,2.9630952776568024e-09,2.8717941559896227e-08,,f5.ptu +76,0.3010752688172043,0.627318718381113,594,0.0016085499999999998,44.20988538215488,44.20916235,44.2107709,260,112,221,1,3.480480815700171e-09,3.2991071869049477e-09,2.8366403093664222e-08,3.4425000459623045e-08,f5.ptu +77,0.4492753623188406,0.5679012345679012,243,0.00092735,44.21190302983539,44.2114744,44.21240175,76,62,105,0,2.974671092347697e-09,3.020564556457894e-09,2.857738133392991e-08,,f5.ptu +78,0.4050632911392405,0.5231788079470199,151,0.00044524999999999995,44.40955782119205,44.40933885,44.409784099999996,47,32,72,0,3.003723444359328e-09,2.890625038593983e-09,2.795034759539938e-08,,f5.ptu +79,0.5317460317460317,0.4581818181818182,275,0.0011355,44.99130482272727,44.99074175,44.991877249999995,59,67,149,0,2.562711898622653e-09,3.300746312726406e-09,2.8852684948983046e-08,,f5.ptu +80,0.5821596244131455,0.5195121951219512,410,0.00179415,44.99358852670731,44.9927162,44.99451035,89,124,197,0,2.6738764401944608e-09,3.508064562966717e-09,2.8544543528318648e-08,,f5.ptu +81,0.4444444444444444,0.6040268456375839,298,0.0010332,45.66544020704698,45.664881949999995,45.66591515,100,80,118,0,2.7980000373573067e-09,3.099375041381094e-09,2.8337500378346203e-08,,f5.ptu +82,0.6666666666666666,0.2727272727272727,11,7.07e-05,48.31098235909091,48.310942149999995,48.31101285,1,2,8,0,4.575000061082801e-09,1.7875000238656846e-09,2.8106250375258685e-08,,f5.ptu +83,0.23529411764705882,0.288135593220339,295,0.0006483999999999999,48.31344315169491,48.3131257,48.313774099999996,65,20,210,0,2.4900000332450653e-09,2.2837500304913325e-09,2.80436908506139e-08,,f5.ptu +84,0.5932203389830508,0.5190615835777126,341,0.0008273,49.949005867448676,49.9485637,49.949391,72,105,164,0,2.659027813279606e-09,4.1023810071536126e-09,2.9235671122045386e-08,,f5.ptu +85,0.16666666666666666,0.5,24,0.00010059999999999999,51.853378308333326,51.8533042,51.8534048,10,2,12,0,2.9075000388192885e-09,2.8000000373840095e-09,2.8410417045986413e-08,,f5.ptu +86,0.19402985074626866,0.46206896551724136,146,0.00057095,52.08590552739726,52.085634649999996,52.0862056,54,13,78,1,3.850925977341302e-09,3.0634615793631368e-09,2.9145513209647155e-08,2.7275000364160307e-08,f5.ptu +87,0.0873015873015873,0.490272373540856,257,0.0008307999999999999,52.08712855544747,52.08673555,52.087566349999996,115,11,131,0,3.888695704093569e-09,3.1181818598140106e-09,2.9661069098307817e-08,,f5.ptu +88,0.64,0.45454545454545453,110,0.00031895,54.30143566318181,54.301239499999994,54.301558449999995,18,32,60,0,2.7958333706617116e-09,3.1617187922134728e-09,2.805791704128003e-08,,f5.ptu +89,0.5833333333333334,0.42857142857142855,280,0.0009350999999999999,54.414427614642854,54.413981549999995,54.414916649999995,50,70,160,0,2.3200000309753222e-09,3.077142898227121e-09,2.8549844131181297e-08,,f5.ptu +90,0.6666666666666666,0.55,120,0.00053755,54.731234539999996,54.730965749999996,54.7315033,22,44,54,0,2.6920454904881164e-09,3.4426136823274586e-09,2.868842630895749e-08,,f5.ptu +91,0.33962264150943394,0.4690265486725664,113,0.00050175,55.283732448672566,55.283459199999996,55.28396095,35,18,60,0,3.6578571916947737e-09,2.850000038051581e-09,2.8300833711189985e-08,,f5.ptu +92,0.5384615384615384,0.5603448275862069,116,0.00043345,55.37193135043103,55.3717198,55.37215325,30,35,51,0,2.5625000342130444e-09,3.523571475616153e-09,2.905392195653891e-08,,f5.ptu +93,0.47435897435897434,0.4727272727272727,165,0.00068615,57.75650380333333,57.7561866,57.75687275,41,37,87,0,2.5835366198597666e-09,3.609459507650912e-09,2.819051761776308e-08,,f5.ptu +94,0.46875,0.5079365079365079,189,0.00071565,58.027801915079365,58.02743365,58.028149299999995,51,45,93,0,3.081372590160344e-09,3.63222227071759e-09,2.8269892850561825e-08,,f5.ptu +95,0.4444444444444444,0.44751381215469616,181,0.0006277,58.11850843232044,58.11822325,58.118850949999995,45,36,100,0,2.788888926124549e-09,3.59722227025029e-09,2.8413250379357574e-08,,f5.ptu +96,0.3541666666666667,0.46601941747572817,103,0.0005473,58.94836859126214,58.9480965,58.9486438,31,17,55,0,2.7927419727709747e-09,3.6352941661834407e-09,2.8350909469434323e-08,,f5.ptu diff --git a/tests/test_io.py b/tests/test_io.py new file mode 100644 index 0000000..444a666 --- /dev/null +++ b/tests/test_io.py @@ -0,0 +1,36 @@ +from pathlib import Path + +import pytest +import yaml + +# from helpers import assert_burstsets_equal, assert_photons_equal +from dont_fret.fileIO import ( + PhotonFile, + read_ptu_metadata, +) +from dont_fret.models import Bursts, PhotonData +from dont_fret.utils import clean_types + +cwd = Path(__file__).parent +input_data_dir = cwd / "test_data" / "input" +output_data_dir = cwd / "test_data" / "output" + + +@pytest.fixture +def ph_ds1() -> PhotonData: + return PhotonData.from_file(PhotonFile(input_data_dir / "ds1" / "datafile_1.ptu")) + + +@pytest.fixture +def dcbs_bursts(ph_ds1: PhotonData) -> Bursts: + return ph_ds1.burst_search("DCBS") + + +def test_read_ptu_metadata(): + metadata = read_ptu_metadata(input_data_dir / "ds1" / "datafile_1.ptu") + + m_saved = yaml.safe_load((output_data_dir / "ds1" / "metadata.yaml").read_text()) + m_saved["record_type"] = "rtTimeHarp260PT3" + m_clean = clean_types(metadata) + + assert m_saved == m_clean diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000..5f35f20 --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,142 @@ +from dataclasses import asdict +from pathlib import Path + +import numpy as np +import polars as pl +import pytest + +from dont_fret.config.config import BurstColor +from dont_fret.fileIO import PhotonFile +from dont_fret.models import BinnedPhotonData, Bursts, PhotonData + +cwd = Path(__file__).parent +input_data_dir = cwd / "test_data" / "input" +output_data_dir = cwd / "test_data" / "output" + +DCBS_TEST = {"DD + DA": {"L": 50, "M": 35, "T": 0.0005}, "AA": {"L": 50, "M": 35, "T": 0.0005}} +APBS_TEST = [ + BurstColor( + streams=["DD", "DA", "AA"], + L=50, + M=35, + T=0.0005, + ) +] + + +@pytest.fixture +def ph_ds1() -> PhotonData: + return PhotonData.from_file(PhotonFile(input_data_dir / "ds1" / "datafile_1.ptu")) + + +@pytest.fixture +def dcbs_bursts(ph_ds1: PhotonData) -> Bursts: + return ph_ds1.burst_search("DCBS") + + +@pytest.fixture +def apbs_bursts(ph_ds1: PhotonData) -> Bursts: + return ph_ds1.burst_search(APBS_TEST) + + +@pytest.fixture +def rnd_data() -> pl.DataFrame: + np.random.seed(42) + timestamps = np.cumsum(np.random.randint(20, 200, 1000)) + detectors = np.random.randint(0, 2, 1000) + nanotimes = np.random.randint(0, 2**12 + 1, 1000) + + data = pl.DataFrame({"timestamps": timestamps, "detectors": detectors, "nanotimes": nanotimes}) + + return data + + +def test_photon(rnd_data): + photon_data = PhotonData(rnd_data) + assert len(photon_data) == 1000 + + sl = photon_data[:500] + assert len(sl) == 500 + + +def test_load_save_photons(ph_ds1: PhotonData, tmp_path: Path): + test_dir_1 = tmp_path / "test_dir_1" + ph_ds1.save(test_dir_1) + + photons_load = PhotonData.load(test_dir_1) + assert ph_ds1.data.equals(photons_load.data) + assert ph_ds1.cfg == photons_load.cfg + assert asdict(ph_ds1.cfg) == asdict(photons_load.cfg) + assert ph_ds1.metadata == photons_load.metadata + + +def test_load_save_bursts(dcbs_bursts: Bursts, tmp_path: Path): + test_dir_1 = tmp_path / "test_dir_1" + dcbs_bursts.save(test_dir_1) + + bursts_load = Bursts.load(test_dir_1) + # for some reason, we require maintain_order=True on the aggregation operation + # although the data and cfg appear to be the same + assert dcbs_bursts.burst_data.equals(bursts_load.burst_data) + assert dcbs_bursts.photon_data.equals(bursts_load.photon_data) + assert dcbs_bursts.cfg == bursts_load.cfg + assert asdict(dcbs_bursts.cfg) == asdict(bursts_load.cfg) + assert dcbs_bursts.metadata == bursts_load.metadata + + +def test_burst_search(ph_ds1: PhotonData): + search_args = ["DCBS", APBS_TEST] + reference_files = ["dcbs_bursts.csv", "apbs_bursts.csv"] + + for bs_arg, ref_file in zip(search_args, reference_files): + bs = ph_ds1.burst_search(bs_arg) + pth = output_data_dir / "ds1" / ref_file + + # bs = ph_ds1.burst_search("DCBS") + # pth = output_data_dir / "ds1" / "dcbs_bursts.csv" + + df_ref = pl.read_csv(pth) + df_test = bs.burst_data.filter(pl.col("n_photons") > 50) + + for k in ["n_photons", "E_app", "S_app"]: + assert (df_ref[k] == df_test[k]).all() + + time_length = ( + df_test["timestamps_max"] - df_test["timestamps_min"] + ) * ph_ds1.timestamps_unit + assert (df_ref["time_length"] == time_length).all() + assert (df_ref["time_min"] == df_test["timestamps_min"] * ph_ds1.timestamps_unit).all() + assert (df_ref["time_max"] == df_test["timestamps_max"] * ph_ds1.timestamps_unit).all() + + +def test_binning(ph_ds1): + trace = BinnedPhotonData(ph_ds1, bounds=(0, ph_ds1.tmax)) + assert len(trace) == 81439 + trace.binning_time = 1e-2 + + # check if all photons are accounted for, last photon is omitted because of + # exclusive upper bound + assert trace.photons.sum() == len(trace.photon_data.timestamps) - 1 + assert len(trace) == 8144 + trace.binning_time = 1e-3 + assert len(trace) == 81439 + + trace = BinnedPhotonData(ph_ds1) + assert len(trace) == 10000 + trace.binning_time = 1e-2 + assert len(trace) == 1000 + trace.bounds = (10.0, 20.0) + + assert len(trace) == 1000 + assert trace.time.min() == 10 + 1e-2 / 2 + + +BURST_ATTRS = [ + "n_photons", + "E_app", + "S_app", + "time_length", + "time_mean", + "time_min", + "time_max", +] diff --git a/tests/test_ui.py b/tests/test_ui.py new file mode 100644 index 0000000..90b172e --- /dev/null +++ b/tests/test_ui.py @@ -0,0 +1,87 @@ +import pandas as pd +import playwright.sync_api +import yaml +from pathlib import Path +import pytest + +from dont_fret import cfg + +cwd = Path(__file__).parent +input_dir = cwd / "test_data" / "input" +data_dir = cwd / "test_data" / "output" / "web" + +CB_LOCATOR = "div:nth-child({}) > .v-list-item__action > .v-input > .v-input__control > .v-input__slot > .v-input--selection-controls__input > .v-input--selection-controls__ripple" + + +@pytest.mark.skip(reason="Takes too long") +def test_main_app( + page_session: playwright.sync_api.Page, solara_server, solara_app, tmp_path: Path +): + cfg.web.default_dir = input_dir + with solara_app("dont_fret.web.app"): + page_session.goto(solara_server.base_url) + + # switch between APBS / DCBS, fails on solara 1.12 + # Currently fails on timeout, but solara already errored + page_session.get_by_role("button", name="Burst search settings DCBS").click() + page_session.get_by_text("APBS").click() + page_session.get_by_role("button", name="Burst search settings APBS").click() + page_session.get_by_text("DCBS").click() + + page_session.get_by_text("ds2").click() + page_session.get_by_role("button", name="Add files").click() + page_session.get_by_role("button", name="Select None").click() + + for i in range(1, 6): + cb = page_session.locator(CB_LOCATOR.format(i)) + # expect(cb).not_to_be_checked() # is not a checkbox + + # Enable CBs 4 and 5 + for i in [4, 5]: + cb = page_session.locator(CB_LOCATOR.format(i)) + cb.click() + # expect(cb).to_be_checked() # is not a checkbox + + page_session.get_by_role("button", name="Search!").click() + page_session.get_by_text("Finished burst search.").wait_for() + + page_session.get_by_role("button", name="Burst set").click() + page_session.get_by_role("option", name="DCBS_default").get_by_text("DCBS_default").click() + with page_session.expect_download() as download_info: + page_session.get_by_role("button", name="Download csv").click() + download_info.value.save_as(tmp_path / "bursts.csv") + + compare_df = pd.read_csv(tmp_path / "bursts.csv") + reference_df = pd.read_csv(data_dir / "DCBS_default_bursts.csv") + + pd.testing.assert_frame_equal(reference_df, compare_df) + + with page_session.expect_download() as download_info: + page_session.get_by_role("button", name="Download settings").click() + + download_info.value.save_as(tmp_path / "settings.yaml") + burst_colors_dict = yaml.safe_load((tmp_path / "settings.yaml").read_text()) + assert burst_colors_dict == cfg.burst_search["DCBS"] + + # Set APBS settings really high, assert we dont find any bursts + page_session.get_by_role("button", name="Burst search settings DCBS").click() + page_session.get_by_text("APBS").click() + + page_session.locator(".v-sheet > .v-sheet > button").first.click() + page_session.get_by_label("L").click() + page_session.get_by_label("L").fill("5000") + page_session.get_by_label("L").press("Enter") + page_session.get_by_role("button", name="Save & close").click() + + page_session.get_by_role("button", name="Search!").click() + page_session.get_by_text("No bursts found.").wait_for() + + # open the burst view tab + page_session.get_by_role("tab", name="burst_view").click() + page_session.get_by_text("Number of bursts: 229").wait_for() + + # open the trace view tab + page_session.get_by_role("tab", name="trace_view").click() + page_session.get_by_role("button", name="Photon file").click() + page_session.get_by_text("f5.ptu").click() + page_session.get_by_text("Loaded 944419 photons.").click() diff --git a/tests/test_web.py b/tests/test_web.py new file mode 100644 index 0000000..48d3188 --- /dev/null +++ b/tests/test_web.py @@ -0,0 +1,108 @@ +import asyncio +import time +from pathlib import Path +from typing import List + +import ipyvuetify as v +import numpy as np +import plotly.graph_objects as go +import polars as pl +import pytest +import solara + +import dont_fret.web.state as state +from dont_fret import PhotonData, PhotonFile +from dont_fret.web.bursts.components import BurstFigure +from dont_fret.web.home.methods import task_burst_search +from dont_fret.web.methods import burst_search, create_file_items +from dont_fret.web.models import BurstItem, FRETNode, PhotonFileItem + +cwd = Path(__file__).parent +input_data_dir = cwd / "test_data" / "input" +output_data_dir = cwd / "test_data" / "output" + +DCBS_TEST = {"DD + DA": {"L": 50, "M": 35, "T": 0.0005}, "AA": {"L": 50, "M": 35, "T": 0.0005}} +APBS_TEST = {"DD + DA + AA": {"L": 50, "M": 35, "T": 0.0005}} + + +@pytest.fixture +def ph_ds1() -> PhotonData: + return PhotonData.from_file(PhotonFile(input_data_dir / "ds1" / "datafile_1.ptu")) + + +@pytest.fixture +def photon_file_items() -> List[PhotonFileItem]: + file_items = create_file_items(input_data_dir / "ds1") + return file_items + + +@pytest.fixture +def burst_items(photon_file_items: List[PhotonFileItem]) -> List[BurstItem]: + burst_settings = ["DCBS", "APBS"] + dtype = pl.Enum([item.name for item in photon_file_items]) + + burst_items = [] + for name in burst_settings: + # TODO there should be a function list[PhotonFileItem] -> BurstItem + dfs = [burst_search(f, name, dtype) for f in photon_file_items] + df = pl.concat(dfs, how="vertical_relaxed") + + burst_items.append(BurstItem(name=name, df=df)) + return burst_items + + +@pytest.mark.asyncio +async def test_burst_search(ph_ds1): + ph_item = PhotonFileItem(file_path=input_data_dir / "ds1" / "datafile_1.ptu") + + node = FRETNode( + name="FRET NOT", + photons=[ph_item], + ) + + state.fret_nodes.set([]) + assert len(state.fret_nodes.value) == 0 + state.fret_nodes.append(node) + assert len(state.fret_nodes.value) == 1 + + await task_burst_search.function("DCBS", node.id) # type: ignore + + new_node = state.fret_nodes.get_node(node.id) + assert new_node.bursts + burst_item = new_node.bursts[0] + assert burst_item.name == "DCBS" + assert burst_item.df.shape == (72, 15) + assert burst_item.df["filename"].unique()[0] == "datafile_1.ptu" + + await asyncio.sleep(0) + + +def test_burst_figure(photon_file_items, burst_items): + state.fret_nodes.set([]) + node = FRETNode( + name="FRET NOT", + photons=photon_file_items, + bursts=burst_items, + ) + state.fret_nodes.append(node) + + fig = BurstFigure(state.fret_nodes, state.filters) + box, rc = solara.render(fig) + + locator = rc.find(v.Select, label="Burst item") + assert locator.widget.v_model == 0 + assert locator.widget.items == [{"text": "DCBS", "value": 0}, {"text": "APBS", "value": 1}] + + find_figure = rc.find(go.FigureWidget).wait_for(timeout=5) + find_figure.assert_single() + img_test = find_figure.widget.data[0].z # type: ignore + img_ref = np.load(output_data_dir / "ds1" / "z_img_dcbs.npy") + assert np.allclose(img_test, img_ref) + + locator.widget.v_model = 1 + time.sleep(0.5) # wait for the redraw to start + find_figure = rc.find(go.FigureWidget).wait_for(timeout=5) + find_figure.assert_single() + img_test = find_figure.widget.data[0].z # type: ignore + img_ref = np.load(output_data_dir / "ds1" / "z_img_apbs.npy") + assert np.allclose(img_test, img_ref)