slide2vec 5.1.0__tar.gz → 5.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.
- {slide2vec-5.1.0 → slide2vec-5.1.1}/PKG-INFO +1 -1
- {slide2vec-5.1.0 → slide2vec-5.1.1}/pyproject.toml +2 -2
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/__init__.py +1 -1
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/dense_regions.py +35 -6
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec.egg-info/PKG-INFO +1 -1
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_dense_regions.py +43 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/LICENSE +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/README.md +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/setup.cfg +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/__main__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/api.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/artifacts.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/cli.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/configs/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/configs/default.yaml +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/configs/resources.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/data/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/data/dataset.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/data/tile_reader.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/data/tile_store.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/distributed/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/distributed/direct_embed_worker.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/distributed/pipeline_worker.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/base.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/conch.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/gigapath.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/hibou.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/hoptimus.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/lunit.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/midnight.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/moozy/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/moozy/blocks.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/moozy/case.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/moozy/loading.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/moozy/slide.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/moozy/types.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/musk.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/phikon.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/prism.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/prost40m.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/titan.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/uni.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/models/virchow.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/registry.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/encoders/validation.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/inference.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/progress.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/artifacts_collect.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/batching.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/cpu_budget.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/dense_sliding.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/distributed.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/distributed_stage.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/embedding.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/embedding_persist.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/embedding_pipeline.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/hierarchical.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/manifest.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/model_settings.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/patient_pipeline.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/persist_callbacks.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/persistence.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/process_list.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/progress_bridge.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/registry.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/serialization.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/slide_encode.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/tiling.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/tiling_pipeline.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/types.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/runtime/worker_io.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/utils/__init__.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/utils/config.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/utils/coordinates.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/utils/log_utils.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/utils/tiling_io.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec/utils/utils.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec.egg-info/SOURCES.txt +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec.egg-info/dependency_links.txt +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec.egg-info/entry_points.txt +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec.egg-info/not-zip-safe +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec.egg-info/requires.txt +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/slide2vec.egg-info/top_level.txt +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_architecture_runtime_split.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_attention_extraction.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_dense_extraction.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_dense_sliding.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_encoder_registry.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_hs2p_package_cutover.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_output_consistency.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_progress.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_regression_core.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_regression_inference.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_regression_models.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_runtime_batching.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_tile_store.py +0 -0
- {slide2vec-5.1.0 → slide2vec-5.1.1}/tests/test_tiling_pipeline.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "slide2vec"
|
|
7
|
-
version = "5.1.
|
|
7
|
+
version = "5.1.1"
|
|
8
8
|
description = "Embedding of whole slide images with Foundation Models"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -167,7 +167,7 @@ no_implicit_reexport = true
|
|
|
167
167
|
max-line-length = 160
|
|
168
168
|
|
|
169
169
|
[tool.bumpver]
|
|
170
|
-
current_version = "5.1.
|
|
170
|
+
current_version = "5.1.1"
|
|
171
171
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
172
172
|
commit = false # We do version bumping in CI, not as a commit
|
|
173
173
|
tag = false # Git tag already exists — we don't auto-tag
|
|
@@ -39,9 +39,31 @@ import torch
|
|
|
39
39
|
import torch.nn.functional as F
|
|
40
40
|
from PIL import Image
|
|
41
41
|
|
|
42
|
+
from slide2vec.runtime.batching import autocast_dtype
|
|
42
43
|
from slide2vec.runtime.dense_sliding import encode_dense_sliding
|
|
43
44
|
from slide2vec.runtime.slide_encode import slide_encode_autocast_ctx
|
|
44
45
|
|
|
46
|
+
|
|
47
|
+
def _resolve_output_dtype(output_dtype: "torch.dtype | None", precision: str) -> "torch.dtype":
|
|
48
|
+
"""Resolve the dtype emitted grids are materialized in.
|
|
49
|
+
|
|
50
|
+
Defaults (``output_dtype is None``) to the model's compute precision — fp16 runs
|
|
51
|
+
yield fp16 grids — so the engine no longer force-upcasts everything to float32.
|
|
52
|
+
numpy has no bfloat16, so a bf16 compute precision widens to float32 (its lossless
|
|
53
|
+
container) and an *explicit* ``torch.bfloat16`` request is rejected: the grid is
|
|
54
|
+
materialized via ``.numpy()`` and bfloat16 cannot cross that boundary.
|
|
55
|
+
"""
|
|
56
|
+
if output_dtype is None:
|
|
57
|
+
compute = autocast_dtype(torch, precision) # float16 | bfloat16 | None(fp32)
|
|
58
|
+
return torch.float16 if compute == torch.float16 else torch.float32
|
|
59
|
+
if output_dtype == torch.bfloat16:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
"output_dtype=torch.bfloat16 cannot be materialized as a numpy grid; "
|
|
62
|
+
"request torch.float16 or torch.float32"
|
|
63
|
+
)
|
|
64
|
+
return output_dtype
|
|
65
|
+
|
|
66
|
+
|
|
45
67
|
_PAD_MODES = {"reflect", "constant", "zero", "replicate"}
|
|
46
68
|
|
|
47
69
|
|
|
@@ -161,14 +183,16 @@ def iter_regions_dense(
|
|
|
161
183
|
attention_include_registers: bool = False,
|
|
162
184
|
batch_size: int = 1,
|
|
163
185
|
precision: str = "fp32",
|
|
186
|
+
output_dtype: "torch.dtype | None" = None,
|
|
164
187
|
dense_transform: Callable | None = None,
|
|
165
188
|
) -> Iterator[np.ndarray]:
|
|
166
189
|
"""Stream slide regions at ``coordinates`` into dense grids, one per coordinate.
|
|
167
190
|
|
|
168
|
-
Yields one ``(d, grid_h, grid_w)``
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
191
|
+
Yields one ``(d, grid_h, grid_w)`` grid per coordinate, in coordinate order, in the
|
|
192
|
+
model's compute ``precision`` by default (fp16 runs yield fp16 grids; see
|
|
193
|
+
``output_dtype``). Regions are read and encoded one ``batch_size`` chunk at a time, so
|
|
194
|
+
resident host memory is bounded by ``batch_size`` rather than by a slide's ROI count
|
|
195
|
+
(the loop holds at most one batch of grids resident — no per-slide accumulation).
|
|
172
196
|
|
|
173
197
|
Injectable core: takes a constructed dense-capable ``model`` (with
|
|
174
198
|
``encode_tiles_dense`` / ``encode_tiles_attention`` / ``patch_size`` /
|
|
@@ -194,8 +218,12 @@ def iter_regions_dense(
|
|
|
194
218
|
sliding is internal to extraction.
|
|
195
219
|
overlap: fractional window overlap in ``[0, 1)`` for the sliding path (ignored when
|
|
196
220
|
``window_size is None``); the stride is ``window * (1 - overlap)``.
|
|
221
|
+
output_dtype: torch dtype the grids are materialized in. ``None`` (default) follows
|
|
222
|
+
the compute ``precision`` — fp16 → fp16, fp32 → fp32, bf16 → fp32 (numpy has no
|
|
223
|
+
bfloat16). Pass e.g. ``torch.float32`` to force a lossless cache regardless of
|
|
224
|
+
precision; an explicit ``torch.bfloat16`` is rejected (cannot cross ``.numpy()``).
|
|
197
225
|
|
|
198
|
-
Yields
|
|
226
|
+
Yields grids in coordinate order in ``output_dtype``; empty ``coordinates`` yields nothing.
|
|
199
227
|
``feature_kind`` selects ``encode_tiles_dense`` (patch grid) vs
|
|
200
228
|
``encode_tiles_attention`` (CLS-attention grid); both produce a ``(C, gh, gw)`` grid and
|
|
201
229
|
share this path. Each yielded grid is a standalone contiguous copy, so it does not pin
|
|
@@ -203,6 +231,7 @@ def iter_regions_dense(
|
|
|
203
231
|
"""
|
|
204
232
|
if pad_mode not in _PAD_MODES:
|
|
205
233
|
raise ValueError(f"unsupported pad_mode {pad_mode!r}; expected one of {sorted(_PAD_MODES)}")
|
|
234
|
+
resolved_output_dtype = _resolve_output_dtype(output_dtype, precision)
|
|
206
235
|
geometry = compute_dense_geometry(target_size=target_size, patch_size=model.patch_size)
|
|
207
236
|
if dense_transform is None:
|
|
208
237
|
dense_transform = model.get_dense_transform()
|
|
@@ -262,7 +291,7 @@ def iter_regions_dense(
|
|
|
262
291
|
raise ValueError(
|
|
263
292
|
f"{feature_kind} encode returned a {out.ndim}-D tensor; expected (B, d, gh, gw)."
|
|
264
293
|
)
|
|
265
|
-
batch_np = out.detach().
|
|
294
|
+
batch_np = out.detach().to(resolved_output_dtype).cpu().numpy()
|
|
266
295
|
for i in range(batch_np.shape[0]):
|
|
267
296
|
# Standalone C-contiguous copy: a per-row view would pin the whole
|
|
268
297
|
# batch alive (the blended sliding output is contiguous, so a view of
|
|
@@ -18,6 +18,7 @@ timm = pytest.importorskip("timm")
|
|
|
18
18
|
|
|
19
19
|
from slide2vec.encoders.base import TimmTileEncoder # noqa: E402
|
|
20
20
|
from slide2vec.runtime.dense_regions import ( # noqa: E402
|
|
21
|
+
_resolve_output_dtype,
|
|
21
22
|
compute_dense_geometry,
|
|
22
23
|
iter_regions_dense,
|
|
23
24
|
pad_image_to_encoded,
|
|
@@ -219,3 +220,45 @@ def test_iter_regions_dense_validates_eagerly_before_any_read(kwargs):
|
|
|
219
220
|
requested_spacing_um=0.5, target_size=target_size, **kwargs,
|
|
220
221
|
)
|
|
221
222
|
assert wsi.calls == []
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@pytest.mark.parametrize(
|
|
226
|
+
"precision,expected",
|
|
227
|
+
[("fp16", torch.float16), ("fp32", torch.float32), ("bf16", torch.float32)],
|
|
228
|
+
ids=["fp16->fp16", "fp32->fp32", "bf16->fp32(numpy-safe)"],
|
|
229
|
+
)
|
|
230
|
+
def test_resolve_output_dtype_defaults_follow_precision(precision, expected):
|
|
231
|
+
# output_dtype=None tracks the compute precision; bf16 widens to fp32 (numpy has no
|
|
232
|
+
# bfloat16). An explicit dtype overrides; an explicit bfloat16 is rejected.
|
|
233
|
+
assert _resolve_output_dtype(None, precision) is expected
|
|
234
|
+
assert _resolve_output_dtype(torch.float32, precision) is torch.float32
|
|
235
|
+
assert _resolve_output_dtype(torch.float16, precision) is torch.float16
|
|
236
|
+
with pytest.raises(ValueError):
|
|
237
|
+
_resolve_output_dtype(torch.bfloat16, precision)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@pytest.mark.parametrize("dtype,np_dtype", [(torch.float16, np.float16), (torch.float32, np.float32)])
|
|
241
|
+
def test_iter_regions_dense_honours_output_dtype(dtype, np_dtype):
|
|
242
|
+
"""An explicit output_dtype materializes the grids in that dtype, deterministically."""
|
|
243
|
+
enc = _encoder()
|
|
244
|
+
target_size = 64
|
|
245
|
+
wsi = _FakeWSI(target_h=target_size, target_w=target_size)
|
|
246
|
+
grids = list(iter_regions_dense(
|
|
247
|
+
model=enc, device="cpu", wsi=wsi, coordinates=[(0, 0)],
|
|
248
|
+
requested_spacing_um=0.5, target_size=target_size, output_dtype=dtype,
|
|
249
|
+
))
|
|
250
|
+
assert len(grids) == 1
|
|
251
|
+
assert grids[0].dtype == np_dtype
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def test_iter_regions_dense_rejects_bfloat16_output_eagerly():
|
|
255
|
+
"""output_dtype=bfloat16 (uncrossable by .numpy()) raises at the call site, no read."""
|
|
256
|
+
enc = _encoder()
|
|
257
|
+
target_size = 64
|
|
258
|
+
wsi = _FakeWSI(target_h=target_size, target_w=target_size)
|
|
259
|
+
with pytest.raises(ValueError):
|
|
260
|
+
iter_regions_dense(
|
|
261
|
+
model=enc, device="cpu", wsi=wsi, coordinates=[(0, 0)],
|
|
262
|
+
requested_spacing_um=0.5, target_size=target_size, output_dtype=torch.bfloat16,
|
|
263
|
+
)
|
|
264
|
+
assert wsi.calls == []
|
|
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
|
|
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
|