pywavelet 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {pywavelet-0.2.0 → pywavelet-0.2.2}/CHANGELOG.rst +50 -0
  2. {pywavelet-0.2.0 → pywavelet-0.2.2}/PKG-INFO +51 -2
  3. {pywavelet-0.2.0 → pywavelet-0.2.2}/pyproject.toml +1 -1
  4. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/_version.py +2 -2
  5. pywavelet-0.2.2/src/pywavelet/transforms/jax/inverse/__init__.py +0 -0
  6. pywavelet-0.2.2/src/pywavelet/transforms/jax/inverse/main.py +69 -0
  7. pywavelet-0.2.2/src/pywavelet/transforms/jax/inverse/to_freq.py +70 -0
  8. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet.egg-info/PKG-INFO +51 -2
  9. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet.egg-info/SOURCES.txt +3 -0
  10. {pywavelet-0.2.0 → pywavelet-0.2.2}/.github/workflows/ci.yml +0 -0
  11. {pywavelet-0.2.0 → pywavelet-0.2.2}/.github/workflows/docs.yml +0 -0
  12. {pywavelet-0.2.0 → pywavelet-0.2.2}/.github/workflows/pypi.yml +0 -0
  13. {pywavelet-0.2.0 → pywavelet-0.2.2}/.gitignore +0 -0
  14. {pywavelet-0.2.0 → pywavelet-0.2.2}/.pre-commit-config.yaml +0 -0
  15. {pywavelet-0.2.0 → pywavelet-0.2.2}/CITATION.cff +0 -0
  16. {pywavelet-0.2.0 → pywavelet-0.2.2}/README.rst +0 -0
  17. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/_config.yml +0 -0
  18. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/_static/demo.gif +0 -0
  19. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/_toc.yml +0 -0
  20. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/api.rst +0 -0
  21. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/example.ipynb +0 -0
  22. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/index.rst +0 -0
  23. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/logo.png +0 -0
  24. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/roundtrip_freq.png +0 -0
  25. {pywavelet-0.2.0 → pywavelet-0.2.2}/docs/roundtrip_time.png +0 -0
  26. {pywavelet-0.2.0 → pywavelet-0.2.2}/setup.cfg +0 -0
  27. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/__init__.py +0 -0
  28. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/backend.py +0 -0
  29. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/logger.py +0 -0
  30. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/__init__.py +0 -0
  31. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/jax/__init__.py +0 -0
  32. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/jax/forward/__init__.py +0 -0
  33. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/jax/forward/from_freq.py +0 -0
  34. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/jax/forward/from_time.py +0 -0
  35. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/jax/forward/main.py +0 -0
  36. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/__init__.py +0 -0
  37. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/forward/__init__.py +0 -0
  38. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/forward/from_freq.py +0 -0
  39. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/forward/from_time.py +0 -0
  40. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/forward/main.py +0 -0
  41. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/inverse/__init__.py +0 -0
  42. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/inverse/main.py +0 -0
  43. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/inverse/to_freq.py +0 -0
  44. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/numpy/inverse/to_time.py +0 -0
  45. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/transforms/phi_computer.py +0 -0
  46. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/types/__init__.py +0 -0
  47. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/types/common.py +0 -0
  48. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/types/frequencyseries.py +0 -0
  49. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/types/plotting.py +0 -0
  50. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/types/timeseries.py +0 -0
  51. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/types/wavelet.py +0 -0
  52. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/types/wavelet_bins.py +0 -0
  53. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet/utils.py +0 -0
  54. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet.egg-info/dependency_links.txt +0 -0
  55. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet.egg-info/requires.txt +0 -0
  56. {pywavelet-0.2.0 → pywavelet-0.2.2}/src/pywavelet.egg-info/top_level.txt +0 -0
  57. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/conftest.py +0 -0
  58. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_data/roundtrip_chirp_freq.npz +0 -0
  59. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_data/roundtrip_chirp_time.npz +0 -0
  60. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_data/roundtrip_pure_f0_freq.npz +0 -0
  61. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_data/roundtrip_sine_freq.npz +0 -0
  62. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_data/roundtrip_sine_time.npz +0 -0
  63. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_lnl.py +0 -0
  64. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_mask.py +0 -0
  65. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_phi.py +0 -0
  66. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_psd.py +0 -0
  67. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_roundtrip_conversion.py +0 -0
  68. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_snr.py +0 -0
  69. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_timefreq_type.py +0 -0
  70. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_version.py +0 -0
  71. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/test_wavelet_plot.py +0 -0
  72. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/utils/__init__.py +0 -0
  73. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/utils/generate_data.py +0 -0
  74. {pywavelet-0.2.0 → pywavelet-0.2.2}/tests/utils/plotting.py +0 -0
@@ -5,16 +5,66 @@ CHANGELOG
5
5
  =========
6
6
 
7
7
 
8
+ .. _changelog-v0.2.2:
9
+
10
+ v0.2.2 (2025-01-23)
11
+ ===================
12
+
13
+ Unknown
14
+ -------
15
+
16
+ * Merge branch 'main' of github.com:pywavelet/pywavelet (`e8e2115`_)
17
+
18
+ .. _e8e2115: https://github.com/pywavelet/pywavelet/commit/e8e2115e797a5001f236ff027a14ef226151dcc1
19
+
20
+
21
+ .. _changelog-v0.2.1:
22
+
23
+ v0.2.1 (2025-01-23)
24
+ ===================
25
+
26
+ Bug Fixes
27
+ ---------
28
+
29
+ * fix: fix readme path (`34e927d`_)
30
+
31
+ Chores
32
+ ------
33
+
34
+ * chore(release): 0.2.1 (`a55bce5`_)
35
+
36
+ Unknown
37
+ -------
38
+
39
+ * Merge branch 'main' of github.com:pywavelet/pywavelet (`b8c6d15`_)
40
+
41
+ .. _34e927d: https://github.com/pywavelet/pywavelet/commit/34e927d411ec8fde89f552bd5ec89b38820e07e0
42
+ .. _a55bce5: https://github.com/pywavelet/pywavelet/commit/a55bce518c3484543efada283399a41df3ecf001
43
+ .. _b8c6d15: https://github.com/pywavelet/pywavelet/commit/b8c6d1579d48ec5fa22130430267794ae8e54f6c
44
+
45
+
8
46
  .. _changelog-v0.2.0:
9
47
 
10
48
  v0.2.0 (2025-01-23)
11
49
  ===================
12
50
 
51
+ Bug Fixes
52
+ ---------
53
+
54
+ * fix: add temp inverse transform (`586f9ad`_)
55
+
56
+ Chores
57
+ ------
58
+
59
+ * chore(release): 0.2.0 (`fcc35e9`_)
60
+
13
61
  Features
14
62
  --------
15
63
 
16
64
  * feat: add jax as optional backend (`264613e`_)
17
65
 
66
+ .. _586f9ad: https://github.com/pywavelet/pywavelet/commit/586f9ad311f905f7d2fbbfd02fea8198eeda8237
67
+ .. _fcc35e9: https://github.com/pywavelet/pywavelet/commit/fcc35e973d906bd18e03204449564f35fc657b89
18
68
  .. _264613e: https://github.com/pywavelet/pywavelet/commit/264613e5a58042641eb6814530dab36bb54b3371
19
69
 
20
70
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pywavelet
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: WDM wavelet transform your time/freq series!
5
5
  Author-email: Pywavelet Team <avi.vajpeyi@gmail.com>
6
6
  Project-URL: Homepage, https://pywavelet.github.io/pywavelet/
@@ -12,7 +12,7 @@ Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python :: 3.8
14
14
  Requires-Python: >=3.8
15
- Description-Content-Type: text/markdown
15
+ Description-Content-Type: text/x-rst
16
16
  Requires-Dist: numpy
17
17
  Requires-Dist: numba
18
18
  Requires-Dist: scipy
@@ -34,3 +34,52 @@ Requires-Dist: isort; extra == "dev"
34
34
  Requires-Dist: mypy; extra == "dev"
35
35
  Requires-Dist: jupyter-book; extra == "dev"
36
36
  Requires-Dist: GitPython; extra == "dev"
37
+
38
+ pywavelet
39
+ #########
40
+
41
+ .. image:: https://badge.fury.io/py/pywavelet.svg
42
+ :target: https://badge.fury.io/py/pywavelet
43
+ .. image:: https://coveralls.io/repos/github/avivajpeyi/pywavelet/badge.svg?branch=main&kill_cache=1
44
+ :target: https://coveralls.io/github/avivajpeyi/pywavelet?branch=main
45
+
46
+
47
+
48
+
49
+
50
+ WDM Wavelet transform
51
+
52
+
53
+ Quickstart
54
+ ==========
55
+
56
+ pywavelet is available on PyPI and can be installed with `pip <https://pip.pypa.io>`_.
57
+
58
+ .. code-block:: console
59
+
60
+ $ pip install pywavelet
61
+
62
+ For developers
63
+ --------------
64
+
65
+ First set up a conda environment with the latest version of python.
66
+
67
+ .. code-block::
68
+
69
+ $ conda create -n pywavelet -c conda-forge python=3.12
70
+
71
+ .. code-block::
72
+
73
+ $ pip install -e ".[dev]"
74
+ $ pre-commit install
75
+
76
+ Test code
77
+ ---------
78
+
79
+ Locate directory /tests from root directory. run
80
+
81
+ .. code-block::
82
+
83
+ $ pytest .
84
+
85
+ Hopefully everything should run fine.
@@ -11,7 +11,7 @@ name = "pywavelet"
11
11
  dynamic = ["version"] # scm versioning (using tags)
12
12
  requires-python = ">=3.8"
13
13
  description = "WDM wavelet transform your time/freq series!"
14
- readme = "README.md"
14
+ readme = "README.rst"
15
15
  authors = [
16
16
  { name = "Pywavelet Team", email = "avi.vajpeyi@gmail.com" },
17
17
  ]
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.2.0'
16
- __version_tuple__ = version_tuple = (0, 2, 0)
15
+ __version__ = version = '0.2.2'
16
+ __version_tuple__ = version_tuple = (0, 2, 2)
@@ -0,0 +1,69 @@
1
+ import jax.numpy as jnp
2
+ from jax.numpy.fft import rfftfreq
3
+
4
+ from ...phi_computer import phi_vec, phitilde_vec_norm
5
+ from ....types import FrequencySeries, TimeSeries, Wavelet
6
+ from .to_freq import inverse_wavelet_freq_helper
7
+ # from .inverse_wavelet_time_funcs import inverse_wavelet_time_helper
8
+
9
+
10
+ def from_wavelet_to_time(
11
+ wave_in: Wavelet,
12
+ dt: float,
13
+ nx: float = 4.0,
14
+ mult: int = 32,
15
+ ) -> TimeSeries:
16
+ """Inverse wavelet transform to time domain.
17
+
18
+ Parameters
19
+ ----------
20
+ wave_in : Wavelet
21
+ input wavelet
22
+ dt : float
23
+ time step
24
+ nx : float, optional
25
+ parameter for phi_vec, by default 4.0
26
+ mult : int, optional
27
+ parameter for phi_vec, by default 32
28
+
29
+ Returns
30
+ -------
31
+ TimeSeries
32
+ Time domain signal
33
+ """
34
+ # Can we just do this?
35
+ freq = from_wavelet_to_freq(wave_in, dt=dt, nx=nx)
36
+ return freq.to_timeseries()
37
+
38
+
39
+ def from_wavelet_to_freq(
40
+ wave_in: Wavelet, dt: float, nx=4.0
41
+ ) -> FrequencySeries:
42
+ """Inverse wavelet transform to frequency domain.
43
+
44
+ Parameters
45
+ ----------
46
+ wave_in : Wavelet
47
+ input wavelet
48
+ dt : float
49
+ time step
50
+ nx : float, optional
51
+ parameter for phitilde_vec_norm, by default 4.0
52
+
53
+ Returns
54
+ -------
55
+ FrequencySeries
56
+ Frequency domain signal
57
+
58
+ """
59
+ phif = jnp.array(phitilde_vec_norm(wave_in.Nf, wave_in.Nt, dt=dt, d=nx))
60
+ freq_data = inverse_wavelet_freq_helper(
61
+ wave_in.data, phif=phif, Nf=wave_in.Nf, Nt=wave_in.Nt
62
+ )
63
+
64
+ freq_data *= 2 ** (
65
+ -1 / 2
66
+ ) # Normalise to get the proper backwards transformation
67
+
68
+ freqs = rfftfreq(wave_in.ND*2, d=dt)[1:]
69
+ return FrequencySeries(data=freq_data, freq=freqs)
@@ -0,0 +1,70 @@
1
+ import jax
2
+ import jax.numpy as jnp
3
+ from jax import jit
4
+ from jax.numpy.fft import fft
5
+
6
+ from functools import partial
7
+
8
+
9
+ @partial(jit, static_argnames=('Nf', 'Nt'))
10
+ def inverse_wavelet_freq_helper(
11
+ wave_in: jnp.ndarray, phif: jnp.ndarray, Nf: int, Nt: int
12
+ ) -> jnp.ndarray:
13
+ """JAX vectorized function for inverse_wavelet_freq"""
14
+ wave_in = wave_in.T
15
+ ND = Nf * Nt
16
+
17
+ m_range = jnp.arange(Nf + 1)
18
+ prefactor2s = jnp.zeros((Nf + 1, Nt), dtype=jnp.complex128)
19
+
20
+ n_range = jnp.arange(Nt)
21
+
22
+ # m == 0 case
23
+ prefactor2s = prefactor2s.at[0].set(2 ** (-1 / 2) * wave_in[(2 * n_range) % Nt, 0])
24
+
25
+ # m == Nf case
26
+ prefactor2s = prefactor2s.at[Nf].set(2 ** (-1 / 2) * wave_in[(2 * n_range) % Nt + 1, 0])
27
+
28
+ # Other m cases
29
+ m_mid = m_range[1:Nf]
30
+ n_grid, m_grid = jnp.meshgrid(n_range, m_mid)
31
+ val = wave_in[n_grid, m_grid]
32
+ mult2 = jnp.where((n_grid + m_grid) % 2, -1j, 1)
33
+ prefactor2s = prefactor2s.at[1:Nf].set(mult2 * val)
34
+
35
+ # Vectorized FFT
36
+ fft_prefactor2s = fft(prefactor2s, axis=1)
37
+
38
+ # Vectorized __unpack_wave_inverse
39
+ ## TODO: Check with Giorgio
40
+ # ND or ND // 2 + 1?
41
+ # https://github.com/pywavelet/pywavelet/blob/63151a47cde9edc14f1e7e0bf17f554e78ad257c/src/pywavelet/transforms/from_wavelets/inverse_wavelet_freq_funcs.py
42
+ res = jnp.zeros(ND, dtype=jnp.complex128)
43
+
44
+ # m == 0 or m == Nf cases
45
+ i_ind_range = jnp.arange(Nt // 2)
46
+ i_0 = jnp.abs(i_ind_range)
47
+ i_Nf = jnp.abs(Nf * Nt // 2 - i_ind_range)
48
+ ind3_0 = (2 * i_0) % Nt
49
+ ind3_Nf = (2 * i_Nf) % Nt
50
+
51
+ res = res.at[i_0].add(fft_prefactor2s[0, ind3_0] * phif[i_ind_range])
52
+ res = res.at[i_Nf].add(fft_prefactor2s[Nf, ind3_Nf] * phif[i_ind_range])
53
+
54
+ # Special case for m == Nf
55
+ res = res.at[Nf * Nt // 2].add(fft_prefactor2s[Nf, 0] * phif[Nt // 2])
56
+
57
+ # Other m cases
58
+ m_mid = m_range[1:Nf]
59
+ i_ind_range = jnp.arange(Nt // 2 + 1)
60
+ m_grid, i_ind_grid = jnp.meshgrid(m_mid, i_ind_range)
61
+
62
+ i1 = Nt // 2 * m_grid - i_ind_grid
63
+ i2 = Nt // 2 * m_grid + i_ind_grid
64
+ ind31 = (Nt // 2 * m_grid - i_ind_grid) % Nt
65
+ ind32 = (Nt // 2 * m_grid + i_ind_grid) % Nt
66
+
67
+ res = res.at[i1].add(fft_prefactor2s[m_grid, ind31] * phif[i_ind_grid])
68
+ res = res.at[i2].add(fft_prefactor2s[m_grid, ind32] * phif[i_ind_grid])
69
+
70
+ return res
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pywavelet
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: WDM wavelet transform your time/freq series!
5
5
  Author-email: Pywavelet Team <avi.vajpeyi@gmail.com>
6
6
  Project-URL: Homepage, https://pywavelet.github.io/pywavelet/
@@ -12,7 +12,7 @@ Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python :: 3.8
14
14
  Requires-Python: >=3.8
15
- Description-Content-Type: text/markdown
15
+ Description-Content-Type: text/x-rst
16
16
  Requires-Dist: numpy
17
17
  Requires-Dist: numba
18
18
  Requires-Dist: scipy
@@ -34,3 +34,52 @@ Requires-Dist: isort; extra == "dev"
34
34
  Requires-Dist: mypy; extra == "dev"
35
35
  Requires-Dist: jupyter-book; extra == "dev"
36
36
  Requires-Dist: GitPython; extra == "dev"
37
+
38
+ pywavelet
39
+ #########
40
+
41
+ .. image:: https://badge.fury.io/py/pywavelet.svg
42
+ :target: https://badge.fury.io/py/pywavelet
43
+ .. image:: https://coveralls.io/repos/github/avivajpeyi/pywavelet/badge.svg?branch=main&kill_cache=1
44
+ :target: https://coveralls.io/github/avivajpeyi/pywavelet?branch=main
45
+
46
+
47
+
48
+
49
+
50
+ WDM Wavelet transform
51
+
52
+
53
+ Quickstart
54
+ ==========
55
+
56
+ pywavelet is available on PyPI and can be installed with `pip <https://pip.pypa.io>`_.
57
+
58
+ .. code-block:: console
59
+
60
+ $ pip install pywavelet
61
+
62
+ For developers
63
+ --------------
64
+
65
+ First set up a conda environment with the latest version of python.
66
+
67
+ .. code-block::
68
+
69
+ $ conda create -n pywavelet -c conda-forge python=3.12
70
+
71
+ .. code-block::
72
+
73
+ $ pip install -e ".[dev]"
74
+ $ pre-commit install
75
+
76
+ Test code
77
+ ---------
78
+
79
+ Locate directory /tests from root directory. run
80
+
81
+ .. code-block::
82
+
83
+ $ pytest .
84
+
85
+ Hopefully everything should run fine.
@@ -33,6 +33,9 @@ src/pywavelet/transforms/jax/forward/__init__.py
33
33
  src/pywavelet/transforms/jax/forward/from_freq.py
34
34
  src/pywavelet/transforms/jax/forward/from_time.py
35
35
  src/pywavelet/transforms/jax/forward/main.py
36
+ src/pywavelet/transforms/jax/inverse/__init__.py
37
+ src/pywavelet/transforms/jax/inverse/main.py
38
+ src/pywavelet/transforms/jax/inverse/to_freq.py
36
39
  src/pywavelet/transforms/numpy/__init__.py
37
40
  src/pywavelet/transforms/numpy/forward/__init__.py
38
41
  src/pywavelet/transforms/numpy/forward/from_freq.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes