braindecode 0.1.9__tar.gz → 0.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.
- {Braindecode-0.1.9 → Braindecode-0.2.1}/Braindecode.egg-info/PKG-INFO +1 -1
- {Braindecode-0.1.9 → Braindecode-0.2.1}/Braindecode.egg-info/SOURCES.txt +1 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/PKG-INFO +1 -1
- Braindecode-0.2.1/braindecode/__init__.py +1 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datautil/iterators.py +9 -4
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datautil/splitters.py +14 -9
- Braindecode-0.2.1/braindecode/torch_ext/losses.py +83 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/util.py +1 -0
- Braindecode-0.2.1/braindecode/version.py +1 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/Makefile +10 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/conf.py +6 -21
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/index.rst +1 -1
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/notebooks/Cropped_Decoding.ipynb +3 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.rst +8 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/setup.cfg +1 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/setup.py +7 -4
- Braindecode-0.1.9/braindecode/__init__.py +0 -0
- Braindecode-0.1.9/braindecode/torch_ext/losses.py +0 -41
- {Braindecode-0.1.9 → Braindecode-0.2.1}/Braindecode.egg-info/dependency_links.txt +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/Braindecode.egg-info/not-zip-safe +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/Braindecode.egg-info/requires.txt +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/Braindecode.egg-info/top_level.txt +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/LICENSE.txt +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/MANIFEST.in +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/README.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datasets/__init__.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datasets/bbci.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datasets/bcic_iv_2a.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datasets/multiple.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datasets/sensor_positions.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datautil/__init__.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datautil/signal_target.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datautil/signalproc.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datautil/trial_segment.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/datautil/util.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/experiments/__init__.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/experiments/experiment.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/experiments/monitors.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/experiments/stopcriteria.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/mne_ext/__init__.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/mne_ext/signalproc.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/models/__init__.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/models/deep4.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/models/eegnet.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/models/shallow_fbcsp.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/models/util.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/torch_ext/__init__.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/torch_ext/constraints.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/torch_ext/functions.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/torch_ext/init.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/torch_ext/modules.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/torch_ext/util.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/visualization/__init__.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/visualization/perturbation.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/braindecode/visualization/plot.py +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/notebooks/BBCI_Data.ipynb +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/notebooks/BBCI_Data_Start_Stop.ipynb +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/notebooks/BBCI_Data_Trialwise.ipynb +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/notebooks/Experiment_Class.ipynb +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/notebooks/TrialWise_Decoding.ipynb +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/notebooks/visualization/Perturbation.ipynb +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.datasets.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.datautil.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.experiments.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.mne_ext.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.models.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.torch_ext.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/braindecode.visualization.rst +0 -0
- {Braindecode-0.1.9 → Braindecode-0.2.1}/docs/source/modules.rst +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from braindecode.version import __version__
|
|
@@ -218,7 +218,7 @@ class CropsFromTrialsIterator(object):
|
|
|
218
218
|
for i_blocks in blocks_per_batch:
|
|
219
219
|
start_stop_blocks = i_trial_start_stop_block[i_blocks]
|
|
220
220
|
batch = _create_batch_from_i_trial_start_stop_blocks(
|
|
221
|
-
X, y, start_stop_blocks)
|
|
221
|
+
X, y, start_stop_blocks, self.n_preds_per_input)
|
|
222
222
|
yield batch
|
|
223
223
|
|
|
224
224
|
|
|
@@ -231,7 +231,7 @@ def _compute_start_stop_block_inds(i_trial_starts, i_trial_stops,
|
|
|
231
231
|
----------
|
|
232
232
|
i_trial_starts: 1darray/list of int
|
|
233
233
|
Indices of first samples to predict(!).
|
|
234
|
-
i_trial_stops:
|
|
234
|
+
i_trial_stops: 1darray/list of int
|
|
235
235
|
Indices one past last sample to predict.
|
|
236
236
|
input_time_length: int
|
|
237
237
|
n_preds_per_input: int
|
|
@@ -299,12 +299,17 @@ def _get_start_stop_blocks_for_trial(i_trial_start, i_trial_stop,
|
|
|
299
299
|
return start_stop_blocks
|
|
300
300
|
|
|
301
301
|
|
|
302
|
-
def _create_batch_from_i_trial_start_stop_blocks(X, y, i_trial_start_stop_block
|
|
302
|
+
def _create_batch_from_i_trial_start_stop_blocks(X, y, i_trial_start_stop_block,
|
|
303
|
+
n_preds_per_input=None):
|
|
303
304
|
Xs = []
|
|
304
305
|
ys = []
|
|
305
306
|
for i_trial, start, stop in i_trial_start_stop_block:
|
|
306
307
|
Xs.append(X[i_trial][:,start:stop])
|
|
307
|
-
|
|
308
|
+
if not hasattr(y[i_trial], '__len__'):
|
|
309
|
+
ys.append(y[i_trial])
|
|
310
|
+
else:
|
|
311
|
+
assert n_preds_per_input is not None
|
|
312
|
+
ys.append(y[i_trial][stop-n_preds_per_input:stop])
|
|
308
313
|
batch_X = np.array(Xs)
|
|
309
314
|
batch_y = np.array(ys)
|
|
310
315
|
# add empty fourth dimension if necessary
|
|
@@ -33,17 +33,22 @@ def concatenate_two_sets(set_a, set_b):
|
|
|
33
33
|
-------
|
|
34
34
|
concatenated_set: :class:`.SignalAndTarget`
|
|
35
35
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
else:
|
|
39
|
-
if hasattr(set_a.X, 'ndim'):
|
|
40
|
-
set_a.X = set_a.X.tolist()
|
|
41
|
-
if hasattr(set_b.X, 'ndim'):
|
|
42
|
-
set_b.X = set_b.X.tolist()
|
|
43
|
-
new_X = set_a.X + set_b.X
|
|
44
|
-
new_y = np.concatenate((set_a.y, set_b.y), axis=0)
|
|
36
|
+
new_X = concatenate_np_array_or_add_lists(set_a.X, set_b.X)
|
|
37
|
+
new_y = concatenate_np_array_or_add_lists(set_a.y, set_b.y)
|
|
45
38
|
return SignalAndTarget(new_X, new_y)
|
|
46
39
|
|
|
40
|
+
def concatenate_np_array_or_add_lists(a, b):
|
|
41
|
+
if hasattr(a, 'ndim') and hasattr(b, 'ndim'):
|
|
42
|
+
new = np.concatenate((a, b), axis=0)
|
|
43
|
+
else:
|
|
44
|
+
if hasattr(a, 'ndim'):
|
|
45
|
+
a = a.tolist()
|
|
46
|
+
if hasattr(b, 'ndim'):
|
|
47
|
+
b = b.tolist()
|
|
48
|
+
new = a + b
|
|
49
|
+
return new
|
|
50
|
+
|
|
51
|
+
|
|
47
52
|
|
|
48
53
|
def split_into_two_sets(dataset, first_set_fraction=None, n_first_set=None):
|
|
49
54
|
"""
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import torch as th
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def log_categorical_crossentropy_1_hot(logpreds, targets, dims=None):
|
|
5
|
+
"""
|
|
6
|
+
Returns log categorical crossentropy for given log-predictions and targets,
|
|
7
|
+
targets should be one-hot-encoded.
|
|
8
|
+
|
|
9
|
+
Computes :math:`-\mathrm{logpreds} \cdot \mathrm{targets}`
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
logpreds: `torch.autograd.Variable`
|
|
14
|
+
Logarithm of softmax output.
|
|
15
|
+
targets: `torch.autograd.Variable`
|
|
16
|
+
One-hot encoded targets
|
|
17
|
+
dims: int or iterable of int, optional.
|
|
18
|
+
Compute sum across these dims
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
loss: `torch.autograd.Variable`
|
|
23
|
+
:math:`-\mathrm{logpreds} \cdot \mathrm{targets}`
|
|
24
|
+
"""
|
|
25
|
+
assert logpreds.size() == targets.size()
|
|
26
|
+
result = -logpreds * targets
|
|
27
|
+
# Sum across dims if axis given or more than 1 dim
|
|
28
|
+
if dims is not None:
|
|
29
|
+
if not hasattr(dims, '__len__'):
|
|
30
|
+
dims = [dims]
|
|
31
|
+
for dim in dims:
|
|
32
|
+
result = th.sum(result, dim=int(dim))
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def log_categorical_crossentropy(log_preds, targets, class_weights=None):
|
|
37
|
+
"""
|
|
38
|
+
Returns log categorical crossentropy for given log-predictions and targets.
|
|
39
|
+
|
|
40
|
+
Computes :math:`-\mathrm{logpreds} \cdot \mathrm{targets}` if you assume
|
|
41
|
+
targets to be one-hot-encoded. Also works for targets that are not
|
|
42
|
+
one-hot-encoded, in this case only uses targets that are in the range
|
|
43
|
+
of the expected class labels, i.e., [0,log_preds.size()[1]-1].
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
log_preds: torch.autograd.Variable`
|
|
48
|
+
Logarithm of softmax output.
|
|
49
|
+
targets: `torch.autograd.Variable`
|
|
50
|
+
class_weights: list of int, optional
|
|
51
|
+
Weights given to loss of different classes
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
|
|
56
|
+
loss: `torch.autograd.Variable`
|
|
57
|
+
"""
|
|
58
|
+
if log_preds.size() == targets.size():
|
|
59
|
+
assert class_weights is None, (
|
|
60
|
+
"Class weights not implemented for one-hot")
|
|
61
|
+
return log_categorical_crossentropy_1_hot(log_preds, targets)
|
|
62
|
+
n_classes = log_preds.size()[1]
|
|
63
|
+
n_elements = 0
|
|
64
|
+
losses = []
|
|
65
|
+
for i_class in range(n_classes):
|
|
66
|
+
mask = targets == i_class
|
|
67
|
+
mask = mask.type_as(log_preds)
|
|
68
|
+
n_elements -= th.sum(mask)
|
|
69
|
+
this_loss = th.sum(mask * log_preds[:,i_class])
|
|
70
|
+
if class_weights is not None:
|
|
71
|
+
this_loss = this_loss * class_weights[i_class]
|
|
72
|
+
losses.append(this_loss)
|
|
73
|
+
return th.sum(th.stack(losses)) / n_elements
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def l2_loss(model):
|
|
77
|
+
losses = [th.sum(p * p) for p in model.parameters()]
|
|
78
|
+
return sum(losses)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def l1_loss(model):
|
|
82
|
+
losses = [th.sum(th.abs(p)) for p in model.parameters()]
|
|
83
|
+
return sum(losses)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.1"
|
|
@@ -21,3 +21,13 @@ apidoc:
|
|
|
21
21
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
22
22
|
%: Makefile
|
|
23
23
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
removeipynbcheckpoints: Makefile
|
|
27
|
+
rm -rf notebooks/.ipynb_checkpoints/ notebooks/visualization/.ipynb_checkpoints/
|
|
28
|
+
|
|
29
|
+
removesource: Makefile
|
|
30
|
+
rm -rf source/
|
|
31
|
+
|
|
32
|
+
rmanddoc: removesource removeipynbcheckpoints apidoc html
|
|
33
|
+
echo "Done"
|
|
@@ -82,8 +82,9 @@ autodoc_member_order = 'bysource'
|
|
|
82
82
|
## Default flags used by autodoc directives
|
|
83
83
|
autodoc_default_flags = ['members', 'show-inheritance']
|
|
84
84
|
|
|
85
|
-
exclude_patterns = ['_build', '_templates'
|
|
86
|
-
|
|
85
|
+
exclude_patterns = ['_build', '_templates']
|
|
86
|
+
|
|
87
|
+
|
|
87
88
|
napoleon_google_docstring = False
|
|
88
89
|
napoleon_use_param = False
|
|
89
90
|
napoleon_use_ivar = True
|
|
@@ -110,9 +111,10 @@ author = 'Robin Tibor Schirrmeister'
|
|
|
110
111
|
# built documents.
|
|
111
112
|
#
|
|
112
113
|
# The short X.Y version.
|
|
113
|
-
|
|
114
|
+
import braindecode
|
|
115
|
+
version = braindecode.__version__
|
|
114
116
|
# The full version, including alpha/beta/rc tags.
|
|
115
|
-
release =
|
|
117
|
+
release = version
|
|
116
118
|
|
|
117
119
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
118
120
|
# for a list of supported languages.
|
|
@@ -211,20 +213,3 @@ texinfo_documents = [
|
|
|
211
213
|
author, 'Braindecode', 'One line description of project.',
|
|
212
214
|
'Miscellaneous'),
|
|
213
215
|
]
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
## mock stuff
|
|
217
|
-
"""
|
|
218
|
-
import sys
|
|
219
|
-
from mock import Mock as MagicMock
|
|
220
|
-
#from unittest.mock import MagicMock
|
|
221
|
-
|
|
222
|
-
class Mock(MagicMock):
|
|
223
|
-
@classmethod
|
|
224
|
-
def __getattr__(cls, name):
|
|
225
|
-
return MagicMock()
|
|
226
|
-
|
|
227
|
-
MOCK_MODULES = ['torch', 'h5py',]
|
|
228
|
-
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
|
|
229
|
-
|
|
230
|
-
"""
|
|
@@ -3,7 +3,7 @@ Welcome to Braindecode
|
|
|
3
3
|
|
|
4
4
|
A deep learning toolbox to decode raw time-domain EEG.
|
|
5
5
|
|
|
6
|
-
For EEG researchers that want to
|
|
6
|
+
For EEG researchers that want to work with deep learning and
|
|
7
7
|
deep learning researchers that want to work with EEG data.
|
|
8
8
|
For now focussed on convolutional networks.
|
|
9
9
|
|
|
@@ -9,13 +9,16 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
|
|
|
9
9
|
long_description = f.read()
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
# This will add __version__ to version dict
|
|
13
|
+
version = {}
|
|
14
|
+
with open(path.join(here, 'braindecode/version.py'), encoding='utf-8') as (
|
|
15
|
+
version_file):
|
|
16
|
+
exec(version_file.read(), version)
|
|
17
|
+
|
|
12
18
|
setup(
|
|
13
19
|
name='Braindecode',
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
# the version across setup.py and the project code, see
|
|
17
|
-
# http://packaging.python.org/en/latest/tutorial.html#version
|
|
18
|
-
version='0.1.9', # TODO: read from __init__.py?
|
|
21
|
+
version=version['__version__'],
|
|
19
22
|
|
|
20
23
|
description='A deep learning toolbox to decode raw time-domain EEG.',
|
|
21
24
|
long_description=long_description,
|
|
File without changes
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import torch as th
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def log_categorical_crossentropy(logpreds, targets, dims=None):
|
|
5
|
-
"""
|
|
6
|
-
Returns log categorical crossentropy for given log-predictions and targets.
|
|
7
|
-
|
|
8
|
-
Computes :math:`-\mathrm{logpreds} \cdot \mathrm{targets}`
|
|
9
|
-
|
|
10
|
-
Parameters
|
|
11
|
-
----------
|
|
12
|
-
logpreds: `torch.autograd.Variable`
|
|
13
|
-
Logarithm of softmax output.
|
|
14
|
-
targets: `torch.autograd.Variable`
|
|
15
|
-
dims: int or iterable of int, optional.
|
|
16
|
-
Compute sum across these dims
|
|
17
|
-
|
|
18
|
-
Returns
|
|
19
|
-
-------
|
|
20
|
-
loss: `torch.autograd.Variable`
|
|
21
|
-
:math:`-\mathrm{logpreds} \cdot \mathrm{targets}`
|
|
22
|
-
"""
|
|
23
|
-
assert logpreds.size() == targets.size()
|
|
24
|
-
result = -logpreds * targets
|
|
25
|
-
# Sum across dims if axis given or more than 1 dim
|
|
26
|
-
if dims is not None:
|
|
27
|
-
if not hasattr(dims, '__len__'):
|
|
28
|
-
dims = [dims]
|
|
29
|
-
for dim in dims:
|
|
30
|
-
result = th.sum(result, dim=int(dim))
|
|
31
|
-
return result
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def l2_loss(model):
|
|
35
|
-
losses = [th.sum(p * p) for p in model.parameters()]
|
|
36
|
-
return sum(losses)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def l1_loss(model):
|
|
40
|
-
losses = [th.sum(th.abs(p)) for p in model.parameters()]
|
|
41
|
-
return sum(losses)
|
|
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
|