numba-mpi 1.0.1__tar.gz → 1.1.1__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.
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.github/workflows/tests+pypi.yml +6 -3
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/CITATION.cff +3 -3
- {numba_mpi-1.0.1/numba_mpi.egg-info → numba_mpi-1.1.1}/PKG-INFO +2 -2
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/README.md +1 -1
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/__init__.py +1 -0
- numba_mpi-1.1.1/numba_mpi/api/reduce.py +112 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1/numba_mpi.egg-info}/PKG-INFO +2 -2
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi.egg-info/SOURCES.txt +2 -0
- numba_mpi-1.1.1/tests/api/test_reduce.py +54 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.github/numba_mpi_logo.png +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.github/numba_mpi_logo.svg +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.github/workflows/readme_snippets.yml +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.github/workflows/stale.yml +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.gitignore +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.pre-commit-config.yaml +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.vscode/settings.json +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/.zenodo.json +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/CODE_OF_CONDUCT.md +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/LICENSE +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/__init__.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/allreduce.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/barrier.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/bcast.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/initialized.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/irecv.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/isend.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/operator.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/rank.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/recv.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/requests.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/scatter_gather.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/send.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/size.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/api/wtime.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/common.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi/utils.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi.egg-info/dependency_links.txt +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi.egg-info/requires.txt +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/numba_mpi.egg-info/top_level.txt +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/setup.cfg +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/setup.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/__init__.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_allreduce.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_barrier.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_bcast.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_init.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_isend_irecv.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_rank.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_scatter_gather.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_send_recv.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_size.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/api/test_wtime.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/common.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/paper_listings/exchange.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/paper_listings/hello.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/paper_listings/mpi4py_with_error.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/paper_listings/numba_mpi.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/paper_listings/py-pde.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/paper_listings/test.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/paper_listings/timing.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/test_paper_listings.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/test_version.py +0 -0
- {numba_mpi-1.0.1 → numba_mpi-1.1.1}/tests/utils.py +0 -0
@@ -33,7 +33,7 @@ jobs:
|
|
33
33
|
pip install -r numba_mpi.egg-info/requires.txt
|
34
34
|
- name: Analysing the code with pylint
|
35
35
|
run: |
|
36
|
-
pylint --unsafe-load-any-extension=y --disable=fixme $(git ls-files '*.py' | grep -v -e ^tests/paper_listings)
|
36
|
+
pylint --unsafe-load-any-extension=y --disable=fixme,duplicate-code $(git ls-files '*.py' | grep -v -e ^tests/paper_listings)
|
37
37
|
|
38
38
|
precommit:
|
39
39
|
runs-on: ubuntu-latest
|
@@ -165,9 +165,12 @@ jobs:
|
|
165
165
|
python -We -c "import numba_mpi"
|
166
166
|
|
167
167
|
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
168
|
-
uses: pypa/gh-action-pypi-publish@
|
168
|
+
uses: pypa/gh-action-pypi-publish@release/v1.12
|
169
169
|
with:
|
170
|
+
attestations: false
|
170
171
|
repository_url: https://test.pypi.org/legacy/
|
171
172
|
|
172
173
|
- if: startsWith(github.ref, 'refs/tags')
|
173
|
-
uses: pypa/gh-action-pypi-publish@
|
174
|
+
uses: pypa/gh-action-pypi-publish@release/v1.12
|
175
|
+
with:
|
176
|
+
attestations: false
|
@@ -14,7 +14,7 @@ preferred-citation:
|
|
14
14
|
given-names: "David"
|
15
15
|
- family-names: "Arabas"
|
16
16
|
given-names: "Sylwester"
|
17
|
-
doi: "10.
|
18
|
-
journal: "
|
19
|
-
title: " Enabling MPI communication within Numba/LLVM JIT-compiled Python code
|
17
|
+
doi: "10.1016/j.softx.2024.101897"
|
18
|
+
journal: "SoftwareX"
|
19
|
+
title: "Numba-MPI v1.0: Enabling MPI communication within Numba/LLVM JIT-compiled Python code"
|
20
20
|
year: 2024
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: numba-mpi
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.1.1
|
4
4
|
Summary: Numba @jittable MPI wrappers tested on Linux, macOS and Windows
|
5
5
|
Home-page: https://github.com/numba-mpi/numba-mpi
|
6
6
|
Author: https://github.com/numba-mpi/numba-mpi/graphs/contributors
|
@@ -37,7 +37,7 @@ Requires-Dist: py-pde; extra == "tests"
|
|
37
37
|
### Overview
|
38
38
|
numba-mpi provides Python wrappers to the C MPI API callable from within [Numba JIT-compiled code](https://numba.readthedocs.io/en/stable/user/jit.html) (@jit mode). For an outline of the project, rationale, architecture, and features, refer to: [numba-mpi arXiv e-print](https://doi.org/10.48550/arXiv.2407.13712) (please cite if numba-mpi is used in your research).
|
39
39
|
|
40
|
-
Support is provided for a subset of MPI routines covering: `size`/`rank`, `send`/`recv`, `allreduce`, `bcast`, `scatter`/`gather` & `allgather`, `barrier`, `wtime`
|
40
|
+
Support is provided for a subset of MPI routines covering: `size`/`rank`, `send`/`recv`, `allreduce`, `reduce`, `bcast`, `scatter`/`gather` & `allgather`, `barrier`, `wtime`
|
41
41
|
and basic asynchronous communication with `isend`/`irecv` (only for contiguous arrays); for request handling including `wait`/`waitall`/`waitany` and `test`/`testall`/`testany`.
|
42
42
|
|
43
43
|
The API uses NumPy and supports both numeric and character datatypes (e.g., `broadcast`).
|
@@ -16,7 +16,7 @@
|
|
16
16
|
### Overview
|
17
17
|
numba-mpi provides Python wrappers to the C MPI API callable from within [Numba JIT-compiled code](https://numba.readthedocs.io/en/stable/user/jit.html) (@jit mode). For an outline of the project, rationale, architecture, and features, refer to: [numba-mpi arXiv e-print](https://doi.org/10.48550/arXiv.2407.13712) (please cite if numba-mpi is used in your research).
|
18
18
|
|
19
|
-
Support is provided for a subset of MPI routines covering: `size`/`rank`, `send`/`recv`, `allreduce`, `bcast`, `scatter`/`gather` & `allgather`, `barrier`, `wtime`
|
19
|
+
Support is provided for a subset of MPI routines covering: `size`/`rank`, `send`/`recv`, `allreduce`, `reduce`, `bcast`, `scatter`/`gather` & `allgather`, `barrier`, `wtime`
|
20
20
|
and basic asynchronous communication with `isend`/`irecv` (only for contiguous arrays); for request handling including `wait`/`waitall`/`waitany` and `test`/`testall`/`testany`.
|
21
21
|
|
22
22
|
The API uses NumPy and supports both numeric and character datatypes (e.g., `broadcast`).
|
@@ -13,6 +13,7 @@ from .api.isend import isend
|
|
13
13
|
from .api.operator import Operator
|
14
14
|
from .api.rank import rank
|
15
15
|
from .api.recv import recv
|
16
|
+
from .api.reduce import reduce
|
16
17
|
from .api.requests import test, testall, testany, wait, waitall, waitany
|
17
18
|
from .api.scatter_gather import allgather, gather, scatter
|
18
19
|
from .api.send import send
|
@@ -0,0 +1,112 @@
|
|
1
|
+
"""file contains MPI_Reduce() implementations"""
|
2
|
+
|
3
|
+
import ctypes
|
4
|
+
from numbers import Number
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
from numba.core import types
|
8
|
+
from numba.extending import overload
|
9
|
+
|
10
|
+
from numba_mpi.common import _MPI_Comm_World_ptr, _MpiComm, _MpiDatatype, _MpiOp, libmpi
|
11
|
+
from numba_mpi.utils import _mpi_addr, _mpi_dtype
|
12
|
+
|
13
|
+
_MPI_Reduce = libmpi.MPI_Reduce
|
14
|
+
_MPI_Reduce.restype = ctypes.c_int
|
15
|
+
_MPI_Reduce.argtypes = [
|
16
|
+
ctypes.c_void_p,
|
17
|
+
ctypes.c_void_p,
|
18
|
+
ctypes.c_int,
|
19
|
+
_MpiDatatype,
|
20
|
+
_MpiOp,
|
21
|
+
ctypes.c_int,
|
22
|
+
_MpiComm,
|
23
|
+
]
|
24
|
+
|
25
|
+
|
26
|
+
def reduce(sendobj, recvobj, operator, root): # pylint: disable=unused-argument
|
27
|
+
"""wrapper for MPI_Reduce
|
28
|
+
Note that complex datatypes and user-defined functions are not properly supported.
|
29
|
+
Returns integer status code (0 == MPI_SUCCESS)
|
30
|
+
"""
|
31
|
+
if isinstance(sendobj, Number):
|
32
|
+
# reduce a single number
|
33
|
+
sendobj = np.array([sendobj])
|
34
|
+
status = _MPI_Reduce(
|
35
|
+
sendobj.ctypes.data,
|
36
|
+
recvobj.ctypes.data,
|
37
|
+
sendobj.size,
|
38
|
+
_mpi_dtype(sendobj),
|
39
|
+
_mpi_addr(operator),
|
40
|
+
root,
|
41
|
+
_mpi_addr(_MPI_Comm_World_ptr),
|
42
|
+
)
|
43
|
+
|
44
|
+
elif isinstance(sendobj, np.ndarray):
|
45
|
+
# reduce an array
|
46
|
+
sendobj = np.ascontiguousarray(sendobj)
|
47
|
+
status = _MPI_Reduce(
|
48
|
+
sendobj.ctypes.data,
|
49
|
+
recvobj.ctypes.data,
|
50
|
+
sendobj.size,
|
51
|
+
_mpi_dtype(sendobj),
|
52
|
+
_mpi_addr(operator),
|
53
|
+
root,
|
54
|
+
_mpi_addr(_MPI_Comm_World_ptr),
|
55
|
+
)
|
56
|
+
|
57
|
+
else:
|
58
|
+
raise TypeError(f"Unsupported type {sendobj.__class__.__name__}")
|
59
|
+
|
60
|
+
return status
|
61
|
+
|
62
|
+
|
63
|
+
@overload(reduce)
|
64
|
+
def ol_reduce(sendobj, recvobj, operator, root): # pylint: disable=unused-argument
|
65
|
+
"""wrapper for MPI_Reduce
|
66
|
+
Note that complex datatypes and user-defined functions are not properly supported.
|
67
|
+
Returns integer status code (0 == MPI_SUCCESS)
|
68
|
+
"""
|
69
|
+
if isinstance(sendobj, types.Number):
|
70
|
+
# reduce a single number
|
71
|
+
|
72
|
+
def impl(sendobj, recvobj, operator, root):
|
73
|
+
sendobj = np.array([sendobj])
|
74
|
+
|
75
|
+
status = _MPI_Reduce(
|
76
|
+
sendobj.ctypes.data,
|
77
|
+
recvobj.ctypes.data,
|
78
|
+
sendobj.size,
|
79
|
+
_mpi_dtype(sendobj),
|
80
|
+
_mpi_addr(operator),
|
81
|
+
root,
|
82
|
+
_mpi_addr(_MPI_Comm_World_ptr),
|
83
|
+
)
|
84
|
+
|
85
|
+
# The following no-op prevents numba from too aggressive optimizations
|
86
|
+
# This looks like a bug in numba (tested for version 0.55)
|
87
|
+
sendobj[0] # pylint: disable=pointless-statement
|
88
|
+
|
89
|
+
return status
|
90
|
+
|
91
|
+
elif isinstance(sendobj, types.Array):
|
92
|
+
# reduce an array
|
93
|
+
|
94
|
+
def impl(sendobj, recvobj, operator, root):
|
95
|
+
sendobj = np.ascontiguousarray(sendobj)
|
96
|
+
|
97
|
+
status = _MPI_Reduce(
|
98
|
+
sendobj.ctypes.data,
|
99
|
+
recvobj.ctypes.data,
|
100
|
+
sendobj.size,
|
101
|
+
_mpi_dtype(sendobj),
|
102
|
+
_mpi_addr(operator),
|
103
|
+
root,
|
104
|
+
_mpi_addr(_MPI_Comm_World_ptr),
|
105
|
+
)
|
106
|
+
|
107
|
+
return status
|
108
|
+
|
109
|
+
else:
|
110
|
+
raise TypeError(f"Unsupported type {sendobj.__class__.__name__}")
|
111
|
+
|
112
|
+
return impl
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: numba-mpi
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.1.1
|
4
4
|
Summary: Numba @jittable MPI wrappers tested on Linux, macOS and Windows
|
5
5
|
Home-page: https://github.com/numba-mpi/numba-mpi
|
6
6
|
Author: https://github.com/numba-mpi/numba-mpi/graphs/contributors
|
@@ -37,7 +37,7 @@ Requires-Dist: py-pde; extra == "tests"
|
|
37
37
|
### Overview
|
38
38
|
numba-mpi provides Python wrappers to the C MPI API callable from within [Numba JIT-compiled code](https://numba.readthedocs.io/en/stable/user/jit.html) (@jit mode). For an outline of the project, rationale, architecture, and features, refer to: [numba-mpi arXiv e-print](https://doi.org/10.48550/arXiv.2407.13712) (please cite if numba-mpi is used in your research).
|
39
39
|
|
40
|
-
Support is provided for a subset of MPI routines covering: `size`/`rank`, `send`/`recv`, `allreduce`, `bcast`, `scatter`/`gather` & `allgather`, `barrier`, `wtime`
|
40
|
+
Support is provided for a subset of MPI routines covering: `size`/`rank`, `send`/`recv`, `allreduce`, `reduce`, `bcast`, `scatter`/`gather` & `allgather`, `barrier`, `wtime`
|
41
41
|
and basic asynchronous communication with `isend`/`irecv` (only for contiguous arrays); for request handling including `wait`/`waitall`/`waitany` and `test`/`testall`/`testany`.
|
42
42
|
|
43
43
|
The API uses NumPy and supports both numeric and character datatypes (e.g., `broadcast`).
|
@@ -30,6 +30,7 @@ numba_mpi/api/isend.py
|
|
30
30
|
numba_mpi/api/operator.py
|
31
31
|
numba_mpi/api/rank.py
|
32
32
|
numba_mpi/api/recv.py
|
33
|
+
numba_mpi/api/reduce.py
|
33
34
|
numba_mpi/api/requests.py
|
34
35
|
numba_mpi/api/scatter_gather.py
|
35
36
|
numba_mpi/api/send.py
|
@@ -46,6 +47,7 @@ tests/api/test_bcast.py
|
|
46
47
|
tests/api/test_init.py
|
47
48
|
tests/api/test_isend_irecv.py
|
48
49
|
tests/api/test_rank.py
|
50
|
+
tests/api/test_reduce.py
|
49
51
|
tests/api/test_scatter_gather.py
|
50
52
|
tests/api/test_send_recv.py
|
51
53
|
tests/api/test_size.py
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# pylint: disable=missing-function-docstring,missing-class-docstring,missing-module-docstring
|
2
|
+
import numpy as np
|
3
|
+
import pytest
|
4
|
+
from numba import njit
|
5
|
+
|
6
|
+
import numba_mpi as mpi
|
7
|
+
from tests.common import data_types_real
|
8
|
+
from tests.utils import get_random_array
|
9
|
+
|
10
|
+
|
11
|
+
@njit
|
12
|
+
def jit_reduce(sendobj, recvobj, operator, root):
|
13
|
+
"""helper function to produce a jitted version of `reduce`"""
|
14
|
+
return mpi.reduce(sendobj, recvobj, operator, root)
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.mark.parametrize("reduce", (mpi.reduce, jit_reduce))
|
18
|
+
@pytest.mark.parametrize(
|
19
|
+
"op_mpi, op_np",
|
20
|
+
(
|
21
|
+
(mpi.Operator.SUM, np.sum),
|
22
|
+
(mpi.Operator.MIN, np.min),
|
23
|
+
(mpi.Operator.MAX, np.max),
|
24
|
+
),
|
25
|
+
)
|
26
|
+
@pytest.mark.parametrize("data_type", data_types_real)
|
27
|
+
@pytest.mark.parametrize("root", range(mpi.size()))
|
28
|
+
def test_reduce(reduce, op_mpi, op_np, data_type, root):
|
29
|
+
# test arrays
|
30
|
+
src = get_random_array((3,), data_type)
|
31
|
+
rcv = np.empty_like(src)
|
32
|
+
status = reduce(src, rcv, operator=op_mpi, root=root)
|
33
|
+
assert status == mpi.SUCCESS
|
34
|
+
if mpi.rank() == root:
|
35
|
+
expect = op_np(np.tile(src, [mpi.size(), 1]), axis=0)
|
36
|
+
np.testing.assert_equal(rcv, expect)
|
37
|
+
|
38
|
+
# test scalars
|
39
|
+
src = src[0]
|
40
|
+
rcv = np.empty(1, dtype=src.dtype)
|
41
|
+
status = reduce(src, rcv, operator=op_mpi, root=root)
|
42
|
+
assert status == mpi.SUCCESS
|
43
|
+
if mpi.rank() == root:
|
44
|
+
expect = op_np(np.tile(src, [mpi.size(), 1]), axis=0)
|
45
|
+
np.testing.assert_equal(rcv, expect)
|
46
|
+
|
47
|
+
# test 0d arrays
|
48
|
+
src = get_random_array((), data_type)
|
49
|
+
rcv = np.empty_like(src)
|
50
|
+
status = reduce(src, rcv, operator=op_mpi, root=root)
|
51
|
+
assert status == mpi.SUCCESS
|
52
|
+
if mpi.rank() == root:
|
53
|
+
expect = op_np(np.tile(src, [mpi.size(), 1]), axis=0)
|
54
|
+
np.testing.assert_equal(rcv, expect)
|
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
|
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
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|