spatial-graph 0.0.5__tar.gz → 0.0.6__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.
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/.github/workflows/ci.yml +16 -4
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/.pre-commit-config.yaml +3 -3
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/PKG-INFO +9 -4
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/pyproject.toml +15 -5
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_graph/graph_base.py +29 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_graph/wrapper_template.pyx +7 -7
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/tests/test_attributes.py +55 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/.github/workflows/docs.yaml +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/.gitignore +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/Dockerfile +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/LICENSE +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/README.md +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/docs/index.md +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/examples/basic_usage.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/examples/query_nearest_vispy.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/mkdocs.yml +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/__init__.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_dtypes.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_graph/__init__.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_graph/graph.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_graph/src/LICENSE.txt +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_graph/src/graph_lite.h +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_graph/views.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/__init__.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/line_rtree.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/point_rtree.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/rtree.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/src/ARCHITECTURE.md +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/src/LICENSE +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/src/config.h +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/src/rtree.c +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/src/rtree.h +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/tree_base.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_rtree/wrapper_template.pyx +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_spatial_graph.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/_util.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/src/spatial_graph/py.typed +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/tests/test_bench.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/tests/test_dtype.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/tests/test_graph.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/tests/test_invalid_inputs.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/tests/test_rtree.py +0 -0
- {spatial_graph-0.0.5 → spatial_graph-0.0.6}/tests/test_spatial_graph.py +0 -0
|
@@ -22,22 +22,34 @@ jobs:
|
|
|
22
22
|
run: pipx run pre-commit run --all-files
|
|
23
23
|
|
|
24
24
|
test:
|
|
25
|
+
name: ${{ matrix.os }} py${{ matrix.python-version }} ${{ matrix.resolution }}
|
|
25
26
|
runs-on: ${{ matrix.os }}
|
|
26
27
|
strategy:
|
|
27
28
|
fail-fast: false
|
|
28
29
|
matrix:
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
# ubuntu: full python range x both resolutions
|
|
31
|
+
os: [ubuntu-latest]
|
|
32
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
33
|
+
resolution: [lowest-direct, highest]
|
|
34
|
+
# windows/macos: only the endpoints, highest resolution
|
|
35
|
+
include:
|
|
36
|
+
- { os: windows-latest, python-version: "3.10", resolution: highest }
|
|
37
|
+
- { os: windows-latest, python-version: "3.14", resolution: highest }
|
|
38
|
+
- { os: macos-latest, python-version: "3.10", resolution: highest }
|
|
39
|
+
- { os: macos-latest, python-version: "3.14", resolution: highest }
|
|
40
|
+
|
|
41
|
+
env:
|
|
42
|
+
UV_RESOLUTION: ${{ matrix.resolution }}
|
|
31
43
|
|
|
32
44
|
steps:
|
|
33
|
-
- uses: actions/checkout@
|
|
45
|
+
- uses: actions/checkout@v4
|
|
34
46
|
- uses: astral-sh/setup-uv@v6
|
|
35
47
|
with:
|
|
36
48
|
python-version: ${{ matrix.python-version }}
|
|
37
49
|
enable-cache: true
|
|
38
50
|
cache-dependency-glob: "**/pyproject.toml"
|
|
39
51
|
- name: Test with coverage
|
|
40
|
-
run: uv run pytest -v --cov=spatial_graph --cov-report=xml
|
|
52
|
+
run: uv run --no-dev --group test pytest -v --cov=spatial_graph --cov-report=xml
|
|
41
53
|
- uses: codecov/codecov-action@v5
|
|
42
54
|
with:
|
|
43
55
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -7,7 +7,7 @@ default_install_hook_types: [pre-commit, commit-msg]
|
|
|
7
7
|
|
|
8
8
|
repos:
|
|
9
9
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
10
|
-
rev:
|
|
10
|
+
rev: v6.0.0
|
|
11
11
|
hooks:
|
|
12
12
|
- id: trailing-whitespace
|
|
13
13
|
- id: end-of-file-fixer
|
|
@@ -15,13 +15,13 @@ repos:
|
|
|
15
15
|
- id: check-added-large-files
|
|
16
16
|
|
|
17
17
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
18
|
-
rev: v0.
|
|
18
|
+
rev: v0.15.16
|
|
19
19
|
hooks:
|
|
20
20
|
- id: ruff-check
|
|
21
21
|
args: [--fix, --unsafe-fixes]
|
|
22
22
|
- id: ruff-format
|
|
23
23
|
|
|
24
24
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
25
|
-
rev:
|
|
25
|
+
rev: v2.1.0
|
|
26
26
|
hooks:
|
|
27
27
|
- id: mypy
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatial-graph
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: A spatial graph datastructure for python.
|
|
5
5
|
Project-URL: homepage, https://github.com/funkelab/spatial_graph
|
|
6
6
|
Project-URL: repository, https://github.com/funkelab/spatial_graph
|
|
@@ -10,16 +10,21 @@ License-File: LICENSE
|
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
20
|
Requires-Dist: ct3>=3.3.3
|
|
21
|
-
Requires-Dist: numpy
|
|
21
|
+
Requires-Dist: numpy>=1.21.2
|
|
22
|
+
Requires-Dist: numpy>=1.23.2; python_version >= '3.11'
|
|
23
|
+
Requires-Dist: numpy>=1.26.0; python_version >= '3.12'
|
|
24
|
+
Requires-Dist: numpy>=2.1.0; python_version >= '3.13'
|
|
25
|
+
Requires-Dist: numpy>=2.3.2; python_version >= '3.14'
|
|
22
26
|
Requires-Dist: setuptools>=75.8.0
|
|
27
|
+
Requires-Dist: typing-extensions>=4.5.0
|
|
23
28
|
Requires-Dist: witty>=v0.3.0
|
|
24
29
|
Description-Content-Type: text/markdown
|
|
25
30
|
|
|
@@ -10,7 +10,7 @@ name = "spatial-graph"
|
|
|
10
10
|
dynamic = ["version"]
|
|
11
11
|
description = "A spatial graph datastructure for python."
|
|
12
12
|
readme = "README.md"
|
|
13
|
-
requires-python = ">=3.
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
14
|
license = { text = "MIT" }
|
|
15
15
|
authors = [
|
|
16
16
|
{ email = "funkej@janelia.hhmi.org", name = "Jan Funke" },
|
|
@@ -20,14 +20,24 @@ classifiers = [
|
|
|
20
20
|
"Development Status :: 3 - Alpha",
|
|
21
21
|
"Intended Audience :: Developers",
|
|
22
22
|
"License :: OSI Approved :: MIT License",
|
|
23
|
-
"Programming Language :: Python :: 3.9",
|
|
24
23
|
"Programming Language :: Python :: 3.10",
|
|
25
24
|
"Programming Language :: Python :: 3.11",
|
|
26
25
|
"Programming Language :: Python :: 3.12",
|
|
27
26
|
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
28
|
"Typing :: Typed",
|
|
29
29
|
]
|
|
30
|
-
dependencies = [
|
|
30
|
+
dependencies = [
|
|
31
|
+
"witty>=v0.3.0",
|
|
32
|
+
"CT3>=3.3.3",
|
|
33
|
+
"numpy>=2.3.2; python_version >= '3.14'",
|
|
34
|
+
"numpy>=2.1.0; python_version >= '3.13'",
|
|
35
|
+
"numpy>=1.26.0; python_version >= '3.12'",
|
|
36
|
+
"numpy>=1.23.2; python_version >= '3.11'",
|
|
37
|
+
"numpy>=1.21.2",
|
|
38
|
+
"setuptools>=75.8.0",
|
|
39
|
+
"typing_extensions>=4.5.0", # witty<=0.3.1 imports it without declaring it
|
|
40
|
+
]
|
|
31
41
|
|
|
32
42
|
[dependency-groups]
|
|
33
43
|
test = ["pytest>=8.3.5", "pytest-cov>=6.1.1"]
|
|
@@ -36,7 +46,7 @@ dev = [
|
|
|
36
46
|
{ include-group = "test" },
|
|
37
47
|
"ipython>=8.18.1",
|
|
38
48
|
"mypy>=1.15.0",
|
|
39
|
-
"
|
|
49
|
+
"prek>=0.4.4",
|
|
40
50
|
"pytest-benchmark>=5.1.0", # specifically excluded from test group for ci
|
|
41
51
|
"ruff>=0.11.10",
|
|
42
52
|
]
|
|
@@ -54,7 +64,7 @@ homepage = "https://github.com/funkelab/spatial_graph"
|
|
|
54
64
|
repository = "https://github.com/funkelab/spatial_graph"
|
|
55
65
|
|
|
56
66
|
[tool.ruff]
|
|
57
|
-
target-version = "
|
|
67
|
+
target-version = "py310"
|
|
58
68
|
line-length = 88
|
|
59
69
|
fix = true
|
|
60
70
|
unsafe-fixes = true
|
|
@@ -33,6 +33,33 @@ else:
|
|
|
33
33
|
SRC_DIR = Path(__file__).parent
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _cpp_member_name(name: str) -> str:
|
|
37
|
+
"""C++ struct member name for a user-provided node/edge attribute.
|
|
38
|
+
|
|
39
|
+
User attribute names are used verbatim as the Python-facing API (property
|
|
40
|
+
names, ``get_*``/``set_*`` methods), but they cannot be used directly as
|
|
41
|
+
C++ identifiers: a name like ``int16`` collides with the MSVC built-in
|
|
42
|
+
``__int16`` (Microsoft extensions treat ``int8``/``int16``/``int32``/
|
|
43
|
+
``int64`` *and* their single-underscore forms ``_int8``/... as synonyms for
|
|
44
|
+
the ``__intN`` keywords), and others may clash with C++ keywords (``class``,
|
|
45
|
+
``new``, ...). We therefore mangle the C++ member with a fixed prefix and
|
|
46
|
+
alias it back to the user name on the Cython side, so the public API is
|
|
47
|
+
unchanged while the generated C++ always compiles. The prefix matters: a
|
|
48
|
+
bare ``_`` prefix would turn ``int8`` into ``_int8``, which is itself an
|
|
49
|
+
MSVC keyword, so the prefix must not be ``_`` followed by a digit.
|
|
50
|
+
"""
|
|
51
|
+
return "_attr_" + name
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _cpp_arg_name(name: str) -> str:
|
|
55
|
+
"""C++ constructor parameter name for a user-provided attribute.
|
|
56
|
+
|
|
57
|
+
Must be distinct from :func:`_cpp_member_name` (the constructor initializes
|
|
58
|
+
``member(arg)``) and, like it, must avoid the MSVC ``_intN`` keyword forms.
|
|
59
|
+
"""
|
|
60
|
+
return "_arg_" + name
|
|
61
|
+
|
|
62
|
+
|
|
36
63
|
def _build_wrapper(
|
|
37
64
|
node_dtype: str,
|
|
38
65
|
node_attr_dtypes: Mapping[str, str] | None = None,
|
|
@@ -60,6 +87,8 @@ def _build_wrapper(
|
|
|
60
87
|
name: DType(dtype) for name, dtype in edge_attr_dtypes.items()
|
|
61
88
|
}
|
|
62
89
|
wrapper_template.directed = directed
|
|
90
|
+
wrapper_template.cpp_member = _cpp_member_name
|
|
91
|
+
wrapper_template.cpp_arg = _cpp_arg_name
|
|
63
92
|
|
|
64
93
|
return str(wrapper_template)
|
|
65
94
|
|
|
@@ -95,7 +95,7 @@ cdef extern from *:
|
|
|
95
95
|
${class_name}(
|
|
96
96
|
%set $sep=""
|
|
97
97
|
%for name, dtype in $dtypes.items()
|
|
98
|
-
$sep$dtype.to_c_decl(
|
|
98
|
+
$sep$dtype.to_c_decl($cpp_arg(name))
|
|
99
99
|
%set $sep=", "
|
|
100
100
|
%end for
|
|
101
101
|
) :
|
|
@@ -103,15 +103,15 @@ cdef extern from *:
|
|
|
103
103
|
%for name, dtype in $dtypes.items()
|
|
104
104
|
$sep
|
|
105
105
|
%if $dtype.is_array
|
|
106
|
-
${name}{
|
|
106
|
+
${cpp_member(name)}{
|
|
107
107
|
%set $isep=""
|
|
108
108
|
%for i in range($dtype.size)
|
|
109
|
-
${isep}
|
|
109
|
+
${isep}${cpp_arg(name)}[$i]
|
|
110
110
|
%set $isep=", "
|
|
111
111
|
%end for
|
|
112
112
|
}
|
|
113
113
|
%else
|
|
114
|
-
${name}(
|
|
114
|
+
${cpp_member(name)}(${cpp_arg(name)})%slurp
|
|
115
115
|
%end if
|
|
116
116
|
%set $sep=", "
|
|
117
117
|
%end for
|
|
@@ -119,7 +119,7 @@ cdef extern from *:
|
|
|
119
119
|
%end if
|
|
120
120
|
|
|
121
121
|
%for name, dtype in $dtypes.items()
|
|
122
|
-
$dtype.to_c_decl($name);
|
|
122
|
+
$dtype.to_c_decl($cpp_member(name));
|
|
123
123
|
%end for
|
|
124
124
|
};
|
|
125
125
|
"""
|
|
@@ -129,13 +129,13 @@ cdef extern from *:
|
|
|
129
129
|
${class_name}(
|
|
130
130
|
%set $sep=""
|
|
131
131
|
%for name, dtype in $dtypes.items()
|
|
132
|
-
$sep$dtype.to_c_decl(
|
|
132
|
+
$sep$dtype.to_c_decl($cpp_arg(name))
|
|
133
133
|
%set $sep=", "
|
|
134
134
|
%end for
|
|
135
135
|
) except +
|
|
136
136
|
|
|
137
137
|
%for name, dtype in $dtypes.items()
|
|
138
|
-
$dtype.to_pyxtype() $name
|
|
138
|
+
$dtype.to_pyxtype() $name "$cpp_member(name)"
|
|
139
139
|
%end for
|
|
140
140
|
|
|
141
141
|
cdef class ${class_name}View:
|
|
@@ -95,3 +95,58 @@ def test_attr_dtypes(dtype):
|
|
|
95
95
|
graph.add_node(1, position=np.array([0.0, 0.0, 0.0]), **{f"node_attr_{dtype}": 0})
|
|
96
96
|
graph.add_node(2, position=np.array([0.0, 0.0, 0.0]), **{f"node_attr_{dtype}": 1})
|
|
97
97
|
graph.add_edge([1, 2], **{f"edge_attr_{dtype}": 0})
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Attribute names that collide with C++ keywords or MSVC built-in type
|
|
101
|
+
# specifiers (but are not Python keywords, so they are valid attribute names).
|
|
102
|
+
# These cannot appear verbatim as C++ struct members or constructor parameters.
|
|
103
|
+
# In particular MSVC (with Microsoft extensions) treats ``int8``/``int16``/
|
|
104
|
+
# ``int32``/``int64`` *and* their single-underscore forms ``_int8``/... as
|
|
105
|
+
# aliases for the ``__intN`` keywords, so a member named ``int16`` (or a
|
|
106
|
+
# constructor parameter ``_int16``) expands to ``__int16`` and fails to compile.
|
|
107
|
+
@pytest.mark.parametrize(
|
|
108
|
+
"attr_name", ["int8", "int16", "int32", "int64", "new", "double"]
|
|
109
|
+
)
|
|
110
|
+
def test_attr_name_collides_with_cpp_keyword(attr_name):
|
|
111
|
+
graph = sg.SpatialGraph(
|
|
112
|
+
ndims=3,
|
|
113
|
+
node_dtype="uint64",
|
|
114
|
+
node_attr_dtypes={attr_name: "int16", "position": "double[3]"},
|
|
115
|
+
edge_attr_dtypes={attr_name: "int16"},
|
|
116
|
+
position_attr="position",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
graph.add_node(1, position=np.array([1.0, 1.0, 1.0]), **{attr_name: 5})
|
|
120
|
+
graph.add_node(2, position=np.array([2.0, 2.0, 2.0]), **{attr_name: 7})
|
|
121
|
+
graph.add_edge([1, 2], **{attr_name: 9})
|
|
122
|
+
|
|
123
|
+
# The user-provided name stays the public attribute (the colliding name only
|
|
124
|
+
# affects the internal C++ identifier). Reading it back through every public
|
|
125
|
+
# access path must return the stored value -- a regression in the C++ member
|
|
126
|
+
# mangling / Cython cname-aliasing would silently corrupt these.
|
|
127
|
+
nodes = np.array([1, 2], dtype="uint64")
|
|
128
|
+
|
|
129
|
+
# single node
|
|
130
|
+
assert getattr(graph.node_attrs[1], attr_name) == 5
|
|
131
|
+
assert getattr(graph.node_attrs[2], attr_name) == 7
|
|
132
|
+
# array of nodes
|
|
133
|
+
np.testing.assert_array_equal(
|
|
134
|
+
getattr(graph.node_attrs[nodes], attr_name), np.array([5, 7], dtype="int16")
|
|
135
|
+
)
|
|
136
|
+
# all nodes (order is unspecified, so compare sorted)
|
|
137
|
+
np.testing.assert_array_equal(
|
|
138
|
+
np.sort(getattr(graph.node_attrs, attr_name)), np.array([5, 7], dtype="int16")
|
|
139
|
+
)
|
|
140
|
+
# edge
|
|
141
|
+
assert getattr(graph.edge_attrs[(1, 2)], attr_name) == 9
|
|
142
|
+
|
|
143
|
+
# the position attribute is mangled the same way and must be unaffected
|
|
144
|
+
np.testing.assert_array_equal(
|
|
145
|
+
graph.node_attrs[1].position, np.array([1.0, 1.0, 1.0])
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# setting through the public attribute round-trips too
|
|
149
|
+
setattr(graph.node_attrs[1], attr_name, 11)
|
|
150
|
+
assert getattr(graph.node_attrs[1], attr_name) == 11
|
|
151
|
+
setattr(graph.edge_attrs[(1, 2)], attr_name, 13)
|
|
152
|
+
assert getattr(graph.edge_attrs[(1, 2)], attr_name) == 13
|
|
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
|