cuslines 2.2__tar.gz → 2.2.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.
- {cuslines-2.2 → cuslines-2.2.1}/PKG-INFO +1 -1
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/generic_tracker.py +3 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal/mt_tractography.py +2 -90
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/numba/nu_tractography.py +0 -2
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/webgpu/wg_tractography.py +2 -91
- {cuslines-2.2 → cuslines-2.2.1}/cuslines.egg-info/PKG-INFO +1 -1
- {cuslines-2.2 → cuslines-2.2.1}/.github/workflows/dockerbuild.yml +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/.github/workflows/publish_pypi.yml +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/.gitignore +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/.pre-commit-config.yaml +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/CLAUDE.md +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/Dockerfile +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/LICENSE +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/README.md +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/__init__.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/boot_utils.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/boot.cu +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/cudamacro.h +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/cuwsort.cuh +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/disc.h +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/generate_streamlines_cuda.cu +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/globals.h +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/ptt.cu +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/ptt.cuh +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/ptt_init.cu +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/tracking_helpers.cu +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_c/utils.cu +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_python/__init__.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_python/_globals.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_python/cu_direction_getters.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_python/cu_propagate_seeds.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_python/cu_tractography.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/cuda_python/cutils.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal/README.md +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal/__init__.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal/mt_direction_getters.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal/mt_propagate_seeds.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal/mutils.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/boot.metal +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/disc.h +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/generate_streamlines_metal.metal +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/globals.h +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/philox_rng.h +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/ptt.metal +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/tracking_helpers.metal +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/types.h +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/utils.metal +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/metal_shaders/warp_sort.metal +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/numba/__init__.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/numba/nu_globals.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/numba_njit/generate_streamlines_numba.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/numba_njit/num_streamlines_numba.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/numba_njit/tracking_helpers.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/webgpu/README.md +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/webgpu/__init__.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/webgpu/benchmark.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/webgpu/wg_direction_getters.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/webgpu/wg_propagate_seeds.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/webgpu/wgutils.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/boot.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/disc.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/generate_streamlines.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/globals.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/philox_rng.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/ptt.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/tracking_helpers.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/types.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/utils.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines/wgsl_shaders/warp_sort.wgsl +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines.egg-info/SOURCES.txt +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines.egg-info/dependency_links.txt +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines.egg-info/requires.txt +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/cuslines.egg-info/top_level.txt +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/pyproject.toml +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/run_gpu_streamlines.py +0 -0
- {cuslines-2.2 → cuslines-2.2.1}/setup.cfg +0 -0
|
@@ -5,25 +5,16 @@ we wrap numpy arrays as Metal shared buffers with zero copies.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
|
-
from tqdm import tqdm
|
|
9
8
|
import logging
|
|
10
9
|
from math import radians
|
|
11
10
|
|
|
12
11
|
from cuslines.metal.mutils import (
|
|
13
|
-
REAL_SIZE,
|
|
14
12
|
REAL_DTYPE,
|
|
15
|
-
aligned_array,
|
|
16
|
-
PAGE_SIZE,
|
|
17
|
-
checkMetalError,
|
|
18
13
|
)
|
|
19
14
|
|
|
20
15
|
from cuslines.metal.mt_direction_getters import MetalGPUDirectionGetter, MetalBootDirectionGetter
|
|
21
16
|
from cuslines.metal.mt_propagate_seeds import MetalSeedBatchPropagator
|
|
22
|
-
|
|
23
|
-
from trx.trx_file_memmap import TrxFile
|
|
24
|
-
from nibabel.streamlines.tractogram import Tractogram
|
|
25
|
-
from nibabel.streamlines.array_sequence import ArraySequence, MEGABYTE
|
|
26
|
-
from dipy.io.stateful_tractogram import Space, StatefulTractogram
|
|
17
|
+
from cuslines.generic_tracker import GenericTracker
|
|
27
18
|
|
|
28
19
|
logger = logging.getLogger("GPUStreamlines")
|
|
29
20
|
|
|
@@ -64,7 +55,7 @@ def _buffer_as_array(buf, dtype, shape):
|
|
|
64
55
|
return np.frombuffer(memview, dtype=dtype, count=count).reshape(shape)
|
|
65
56
|
|
|
66
57
|
|
|
67
|
-
class MetalGPUTracker:
|
|
58
|
+
class MetalGPUTracker(GenericTracker):
|
|
68
59
|
def __init__(
|
|
69
60
|
self,
|
|
70
61
|
dg: MetalGPUDirectionGetter,
|
|
@@ -177,82 +168,3 @@ class MetalGPUTracker:
|
|
|
177
168
|
self.dg.gen_pipeline = None
|
|
178
169
|
self._allocated = False
|
|
179
170
|
return False
|
|
180
|
-
|
|
181
|
-
def _divide_chunks(self, seeds):
|
|
182
|
-
global_chunk_sz = self.chunk_size # single GPU
|
|
183
|
-
nchunks = (seeds.shape[0] + global_chunk_sz - 1) // global_chunk_sz
|
|
184
|
-
return global_chunk_sz, nchunks
|
|
185
|
-
|
|
186
|
-
def generate_sft(self, seeds, ref_img):
|
|
187
|
-
global_chunk_sz, nchunks = self._divide_chunks(seeds)
|
|
188
|
-
buffer_size = 0
|
|
189
|
-
generators = []
|
|
190
|
-
|
|
191
|
-
with tqdm(total=seeds.shape[0]) as pbar:
|
|
192
|
-
for idx in range(nchunks):
|
|
193
|
-
chunk = seeds[idx * global_chunk_sz : (idx + 1) * global_chunk_sz]
|
|
194
|
-
self.seed_propagator.propagate(chunk)
|
|
195
|
-
buffer_size += self.seed_propagator.get_buffer_size()
|
|
196
|
-
generators.append(self.seed_propagator.as_generator())
|
|
197
|
-
pbar.update(chunk.shape[0])
|
|
198
|
-
|
|
199
|
-
array_sequence = ArraySequence(
|
|
200
|
-
(item for gen in generators for item in gen), buffer_size
|
|
201
|
-
)
|
|
202
|
-
return StatefulTractogram(array_sequence, ref_img, Space.VOX)
|
|
203
|
-
|
|
204
|
-
def generate_trx(self, seeds, ref_img):
|
|
205
|
-
global_chunk_sz, nchunks = self._divide_chunks(seeds)
|
|
206
|
-
|
|
207
|
-
sl_len_guess = 100
|
|
208
|
-
sl_per_seed_guess = 2
|
|
209
|
-
n_sls_guess = sl_per_seed_guess * seeds.shape[0]
|
|
210
|
-
|
|
211
|
-
trx_reference = TrxFile(reference=ref_img)
|
|
212
|
-
trx_reference.streamlines._data = trx_reference.streamlines._data.astype(np.float32)
|
|
213
|
-
trx_reference.streamlines._offsets = trx_reference.streamlines._offsets.astype(np.uint64)
|
|
214
|
-
|
|
215
|
-
trx_file = TrxFile(
|
|
216
|
-
nb_streamlines=n_sls_guess,
|
|
217
|
-
nb_vertices=n_sls_guess * sl_len_guess,
|
|
218
|
-
init_as=trx_reference,
|
|
219
|
-
)
|
|
220
|
-
offsets_idx = 0
|
|
221
|
-
sls_data_idx = 0
|
|
222
|
-
|
|
223
|
-
with tqdm(total=seeds.shape[0]) as pbar:
|
|
224
|
-
for idx in range(int(nchunks)):
|
|
225
|
-
chunk = seeds[idx * global_chunk_sz : (idx + 1) * global_chunk_sz]
|
|
226
|
-
self.seed_propagator.propagate(chunk)
|
|
227
|
-
tractogram = Tractogram(
|
|
228
|
-
self.seed_propagator.as_array_sequence(),
|
|
229
|
-
affine_to_rasmm=ref_img.affine,
|
|
230
|
-
)
|
|
231
|
-
tractogram.to_world()
|
|
232
|
-
sls = tractogram.streamlines
|
|
233
|
-
|
|
234
|
-
new_offsets_idx = offsets_idx + len(sls._offsets)
|
|
235
|
-
new_sls_data_idx = sls_data_idx + len(sls._data)
|
|
236
|
-
|
|
237
|
-
if (
|
|
238
|
-
new_offsets_idx > trx_file.header["NB_STREAMLINES"]
|
|
239
|
-
or new_sls_data_idx > trx_file.header["NB_VERTICES"]
|
|
240
|
-
):
|
|
241
|
-
logger.info("TRX resizing...")
|
|
242
|
-
trx_file.resize(
|
|
243
|
-
nb_streamlines=new_offsets_idx * 2,
|
|
244
|
-
nb_vertices=new_sls_data_idx * 2,
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
trx_file.streamlines._data[sls_data_idx:new_sls_data_idx] = sls._data
|
|
248
|
-
trx_file.streamlines._offsets[offsets_idx:new_offsets_idx] = (
|
|
249
|
-
sls_data_idx + sls._offsets
|
|
250
|
-
)
|
|
251
|
-
trx_file.streamlines._lengths[offsets_idx:new_offsets_idx] = sls._lengths
|
|
252
|
-
|
|
253
|
-
offsets_idx = new_offsets_idx
|
|
254
|
-
sls_data_idx = new_sls_data_idx
|
|
255
|
-
pbar.update(chunk.shape[0])
|
|
256
|
-
|
|
257
|
-
trx_file.resize()
|
|
258
|
-
return trx_file
|
|
@@ -2,9 +2,7 @@ import math
|
|
|
2
2
|
from math import radians
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
|
-
from dipy.io.stateful_tractogram import Space, StatefulTractogram
|
|
6
5
|
from nibabel.streamlines.array_sequence import ArraySequence, MEGABYTE
|
|
7
|
-
from tqdm import tqdm
|
|
8
6
|
|
|
9
7
|
from cuslines.generic_tracker import GenericTracker
|
|
10
8
|
from cuslines.numba_njit.num_streamlines_numba import getNumStreamlinesProb_generator
|
|
@@ -5,28 +5,22 @@ readbacks via device.queue.read_buffer() (similar to CUDA's cudaMemcpy).
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
|
-
from tqdm import tqdm
|
|
9
8
|
import logging
|
|
10
9
|
from math import radians
|
|
11
10
|
|
|
12
11
|
from cuslines.webgpu.wgutils import (
|
|
13
|
-
REAL_SIZE,
|
|
14
12
|
REAL_DTYPE,
|
|
15
13
|
create_buffer_from_data,
|
|
16
14
|
)
|
|
17
15
|
|
|
18
16
|
from cuslines.webgpu.wg_direction_getters import WebGPUDirectionGetter, WebGPUBootDirectionGetter
|
|
19
17
|
from cuslines.webgpu.wg_propagate_seeds import WebGPUSeedBatchPropagator
|
|
20
|
-
|
|
21
|
-
from trx.trx_file_memmap import TrxFile
|
|
22
|
-
from nibabel.streamlines.tractogram import Tractogram
|
|
23
|
-
from nibabel.streamlines.array_sequence import ArraySequence, MEGABYTE
|
|
24
|
-
from dipy.io.stateful_tractogram import Space, StatefulTractogram
|
|
18
|
+
from cuslines.generic_tracker import GenericTracker
|
|
25
19
|
|
|
26
20
|
logger = logging.getLogger("GPUStreamlines")
|
|
27
21
|
|
|
28
22
|
|
|
29
|
-
class WebGPUTracker:
|
|
23
|
+
class WebGPUTracker(GenericTracker):
|
|
30
24
|
def __init__(
|
|
31
25
|
self,
|
|
32
26
|
dg: WebGPUDirectionGetter,
|
|
@@ -207,86 +201,3 @@ class WebGPUTracker:
|
|
|
207
201
|
self.device = None
|
|
208
202
|
self._allocated = False
|
|
209
203
|
return False
|
|
210
|
-
|
|
211
|
-
def _divide_chunks(self, seeds):
|
|
212
|
-
global_chunk_sz = self.chunk_size # single GPU
|
|
213
|
-
nchunks = (seeds.shape[0] + global_chunk_sz - 1) // global_chunk_sz
|
|
214
|
-
return global_chunk_sz, nchunks
|
|
215
|
-
|
|
216
|
-
def generate_sft(self, seeds, ref_img):
|
|
217
|
-
global_chunk_sz, nchunks = self._divide_chunks(seeds)
|
|
218
|
-
buffer_size = 0
|
|
219
|
-
generators = []
|
|
220
|
-
|
|
221
|
-
with tqdm(total=seeds.shape[0]) as pbar:
|
|
222
|
-
for idx in range(nchunks):
|
|
223
|
-
chunk = seeds[idx * global_chunk_sz : (idx + 1) * global_chunk_sz]
|
|
224
|
-
self.seed_propagator.propagate(chunk)
|
|
225
|
-
buffer_size += self.seed_propagator.get_buffer_size()
|
|
226
|
-
generators.append(self.seed_propagator.as_generator())
|
|
227
|
-
pbar.update(chunk.shape[0])
|
|
228
|
-
|
|
229
|
-
array_sequence = ArraySequence(
|
|
230
|
-
(item for gen in generators for item in gen), buffer_size
|
|
231
|
-
)
|
|
232
|
-
return StatefulTractogram(array_sequence, ref_img, Space.VOX)
|
|
233
|
-
|
|
234
|
-
def generate_trx(self, seeds, ref_img):
|
|
235
|
-
global_chunk_sz, nchunks = self._divide_chunks(seeds)
|
|
236
|
-
|
|
237
|
-
sl_len_guess = 100
|
|
238
|
-
sl_per_seed_guess = 2
|
|
239
|
-
n_sls_guess = sl_per_seed_guess * seeds.shape[0]
|
|
240
|
-
|
|
241
|
-
trx_reference = TrxFile(reference=ref_img)
|
|
242
|
-
trx_reference.streamlines._data = trx_reference.streamlines._data.astype(
|
|
243
|
-
np.float32
|
|
244
|
-
)
|
|
245
|
-
trx_reference.streamlines._offsets = trx_reference.streamlines._offsets.astype(
|
|
246
|
-
np.uint64
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
trx_file = TrxFile(
|
|
250
|
-
nb_streamlines=n_sls_guess,
|
|
251
|
-
nb_vertices=n_sls_guess * sl_len_guess,
|
|
252
|
-
init_as=trx_reference,
|
|
253
|
-
)
|
|
254
|
-
offsets_idx = 0
|
|
255
|
-
sls_data_idx = 0
|
|
256
|
-
|
|
257
|
-
with tqdm(total=seeds.shape[0]) as pbar:
|
|
258
|
-
for idx in range(int(nchunks)):
|
|
259
|
-
chunk = seeds[idx * global_chunk_sz : (idx + 1) * global_chunk_sz]
|
|
260
|
-
self.seed_propagator.propagate(chunk)
|
|
261
|
-
tractogram = Tractogram(
|
|
262
|
-
self.seed_propagator.as_array_sequence(),
|
|
263
|
-
affine_to_rasmm=ref_img.affine,
|
|
264
|
-
)
|
|
265
|
-
tractogram.to_world()
|
|
266
|
-
sls = tractogram.streamlines
|
|
267
|
-
|
|
268
|
-
new_offsets_idx = offsets_idx + len(sls._offsets)
|
|
269
|
-
new_sls_data_idx = sls_data_idx + len(sls._data)
|
|
270
|
-
|
|
271
|
-
if (
|
|
272
|
-
new_offsets_idx > trx_file.header["NB_STREAMLINES"]
|
|
273
|
-
or new_sls_data_idx > trx_file.header["NB_VERTICES"]
|
|
274
|
-
):
|
|
275
|
-
logger.info("TRX resizing...")
|
|
276
|
-
trx_file.resize(
|
|
277
|
-
nb_streamlines=new_offsets_idx * 2,
|
|
278
|
-
nb_vertices=new_sls_data_idx * 2,
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
trx_file.streamlines._data[sls_data_idx:new_sls_data_idx] = sls._data
|
|
282
|
-
trx_file.streamlines._offsets[offsets_idx:new_offsets_idx] = (
|
|
283
|
-
sls_data_idx + sls._offsets
|
|
284
|
-
)
|
|
285
|
-
trx_file.streamlines._lengths[offsets_idx:new_offsets_idx] = sls._lengths
|
|
286
|
-
|
|
287
|
-
offsets_idx = new_offsets_idx
|
|
288
|
-
sls_data_idx = new_sls_data_idx
|
|
289
|
-
pbar.update(chunk.shape[0])
|
|
290
|
-
|
|
291
|
-
trx_file.resize()
|
|
292
|
-
return trx_file
|
|
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
|