dendrotweaks 0.4.1__py3-none-any.whl → 0.4.3__py3-none-any.whl
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.
- dendrotweaks/__init__.py +1 -1
- dendrotweaks/analysis/ephys_analysis.py +4 -2
- dendrotweaks/biophys/distributions.py +2 -2
- dendrotweaks/biophys/mechanisms.py +1 -1
- dendrotweaks/model.py +63 -21
- dendrotweaks/morphology/domains.py +3 -0
- dendrotweaks/morphology/sec_trees.py +60 -26
- dendrotweaks/simulators.py +1 -1
- dendrotweaks/stimuli/populations.py +1 -1
- {dendrotweaks-0.4.1.dist-info → dendrotweaks-0.4.3.dist-info}/METADATA +1 -1
- {dendrotweaks-0.4.1.dist-info → dendrotweaks-0.4.3.dist-info}/RECORD +14 -14
- {dendrotweaks-0.4.1.dist-info → dendrotweaks-0.4.3.dist-info}/WHEEL +1 -1
- {dendrotweaks-0.4.1.dist-info → dendrotweaks-0.4.3.dist-info}/licenses/LICENSE +0 -0
- {dendrotweaks-0.4.1.dist-info → dendrotweaks-0.4.3.dist-info}/top_level.txt +0 -0
dendrotweaks/__init__.py
CHANGED
@@ -24,7 +24,8 @@ def get_somatic_data(model):
|
|
24
24
|
tuple
|
25
25
|
A tuple containing the voltage, time, time step, and injected current.
|
26
26
|
"""
|
27
|
-
|
27
|
+
soma = model.sec_tree.root
|
28
|
+
seg = soma(0.5)
|
28
29
|
iclamp = model.iclamps[seg]
|
29
30
|
|
30
31
|
v = np.array(model.simulator.recordings['v'][seg])
|
@@ -163,7 +164,8 @@ def detect_somatic_spikes(model, **kwargs):
|
|
163
164
|
Returns:
|
164
165
|
dict: A dictionary containing spike metrics.
|
165
166
|
"""
|
166
|
-
|
167
|
+
soma = model.sec_tree.root
|
168
|
+
seg = soma(0.5)
|
167
169
|
|
168
170
|
v = np.array(model.simulator.recordings['v'][seg])
|
169
171
|
t = np.array(model.simulator.t)
|
@@ -20,7 +20,7 @@ def constant(position, value=0):
|
|
20
20
|
The value of the constant function at the given position.
|
21
21
|
"""
|
22
22
|
if isinstance(position, ndarray):
|
23
|
-
return full_like(position, value)
|
23
|
+
return full_like(position, value, dtype=float)
|
24
24
|
else:
|
25
25
|
return value
|
26
26
|
|
@@ -42,7 +42,7 @@ def uniform(position, value=0):
|
|
42
42
|
The value of the constant function at the given position.
|
43
43
|
"""
|
44
44
|
if isinstance(position, ndarray):
|
45
|
-
return full_like(position, value)
|
45
|
+
return full_like(position, value, dtype=float)
|
46
46
|
else:
|
47
47
|
return value
|
48
48
|
|
dendrotweaks/model.py
CHANGED
@@ -473,6 +473,25 @@ class Model():
|
|
473
473
|
# SEGMENTATION
|
474
474
|
# ========================================================================
|
475
475
|
|
476
|
+
# TODO Make a context manager for this
|
477
|
+
def _temp_clear_stimuli(self):
|
478
|
+
"""
|
479
|
+
Temporarily save and clear stimuli.
|
480
|
+
"""
|
481
|
+
self.export_stimuli(file_name='_temp_stimuli')
|
482
|
+
self.remove_all_stimuli()
|
483
|
+
self.remove_all_recordings()
|
484
|
+
|
485
|
+
def _temp_reload_stimuli(self):
|
486
|
+
"""
|
487
|
+
Load stimuli from a temporary file and clean up.
|
488
|
+
"""
|
489
|
+
self.load_stimuli(file_name='_temp_stimuli')
|
490
|
+
for ext in ['json', 'csv']:
|
491
|
+
temp_path = self.path_manager.get_file_path('stimuli', '_temp_stimuli', extension=ext)
|
492
|
+
if os.path.exists(temp_path):
|
493
|
+
os.remove(temp_path)
|
494
|
+
|
476
495
|
def set_segmentation(self, d_lambda=0.1, f=100):
|
477
496
|
"""
|
478
497
|
Set the number of segments in each section based on the geometry.
|
@@ -486,22 +505,26 @@ class Model():
|
|
486
505
|
"""
|
487
506
|
self.d_lambda = d_lambda
|
488
507
|
|
508
|
+
# Temporarily save and clear stimuli
|
509
|
+
self._temp_clear_stimuli()
|
510
|
+
|
489
511
|
# Pre-distribute parameters needed for lambda_f calculation
|
490
512
|
for param_name in ['cm', 'Ra']:
|
491
513
|
self.distribute(param_name)
|
492
514
|
|
493
|
-
# Calculate lambda_f for each section
|
515
|
+
# Calculate lambda_f and set nseg for each section
|
494
516
|
for sec in self.sec_tree.sections:
|
495
517
|
lambda_f = calculate_lambda_f(sec.distances, sec.diameters, sec.Ra, sec.cm, f)
|
496
|
-
nseg = int((sec.L / (d_lambda * lambda_f) + 0.9) / 2) * 2 + 1
|
497
|
-
|
498
|
-
sec._ref.nseg = nseg
|
499
|
-
# Rebuild the segment tree
|
500
|
-
self.seg_tree = create_segment_tree(self.sec_tree)
|
518
|
+
nseg = max(1, int((sec.L / (d_lambda * lambda_f) + 0.9) / 2) * 2 + 1)
|
519
|
+
sec._nseg = sec._ref.nseg = nseg
|
501
520
|
|
502
|
-
#
|
521
|
+
# Rebuild the segment tree and redistribute parameters
|
522
|
+
self.seg_tree = create_segment_tree(self.sec_tree)
|
503
523
|
self.distribute_all()
|
504
524
|
|
525
|
+
# Reload stimuli and clean up temporary files
|
526
|
+
self._temp_reload_stimuli()
|
527
|
+
|
505
528
|
|
506
529
|
# ========================================================================
|
507
530
|
# MECHANISMS
|
@@ -674,17 +697,26 @@ class Model():
|
|
674
697
|
|
675
698
|
def define_domain(self, domain_name: str, sections, distribute=True):
|
676
699
|
"""
|
677
|
-
Adds a new domain to the
|
678
|
-
the section tree graph.
|
700
|
+
Adds a new domain to the cell and ensures proper partitioning
|
701
|
+
of the section tree graph.
|
702
|
+
|
703
|
+
This method does not automatically insert mechanisms into the newly
|
704
|
+
created domain. It is the user's responsibility to insert mechanisms
|
705
|
+
into the domain after its creation. However, if the domain already
|
706
|
+
exists and is being extended, mechanisms will be inserted automatically
|
707
|
+
into the newly added sections.
|
679
708
|
|
680
709
|
Parameters
|
681
710
|
----------
|
682
711
|
domain_name : str
|
683
|
-
The name of the domain.
|
712
|
+
The name of the domain to be added or extended.
|
684
713
|
sections : list[Section] or Callable
|
685
|
-
The sections to include in the domain. If a callable is provided,
|
686
|
-
it should be a filter function applied to the list of all sections
|
687
|
-
|
714
|
+
The sections to include in the domain. If a callable is provided,
|
715
|
+
it should be a filter function applied to the list of all sections
|
716
|
+
in the model.
|
717
|
+
distribute : bool, optional
|
718
|
+
Whether to re-distribute the parameters after defining the domain.
|
719
|
+
Default is True.
|
688
720
|
"""
|
689
721
|
if isinstance(sections, Callable):
|
690
722
|
sections = self.get_sections(sections)
|
@@ -714,14 +746,22 @@ class Model():
|
|
714
746
|
sec.uninsert_mechanism(mech)
|
715
747
|
|
716
748
|
|
749
|
+
# Add sections to the new domain
|
717
750
|
for sec in sections_to_move:
|
718
751
|
domain.add_section(sec)
|
752
|
+
# Important: here we insert mechanisms only if we extend the domain,
|
753
|
+
# i.e. the domain already exists and has mechanisms.
|
754
|
+
# If the domain is new, we DO NOT insert mechanisms automatically
|
755
|
+
# and leave it to the user to do so.
|
719
756
|
for mech_name in self.domains_to_mechs.get(domain.name, set()):
|
720
757
|
mech = self.mechanisms[mech_name]
|
721
|
-
sec.insert_mechanism(mech
|
758
|
+
sec.insert_mechanism(mech)
|
722
759
|
|
723
760
|
self._remove_empty()
|
724
761
|
|
762
|
+
if distribute:
|
763
|
+
self.distribute_all()
|
764
|
+
|
725
765
|
|
726
766
|
def _add_domain_groups(self, domain_name):
|
727
767
|
"""
|
@@ -1023,7 +1063,6 @@ class Model():
|
|
1023
1063
|
distribution = Distribution(distr_type, **distr_params)
|
1024
1064
|
self.params[param_name][group_name] = distribution
|
1025
1065
|
|
1026
|
-
|
1027
1066
|
def distribute_all(self):
|
1028
1067
|
"""
|
1029
1068
|
Distribute all parameters to the segments.
|
@@ -1065,8 +1104,9 @@ class Model():
|
|
1065
1104
|
value = seg.parent.get_param_value(param_name)
|
1066
1105
|
seg.set_param_value(param_name, value)
|
1067
1106
|
else:
|
1068
|
-
for seg in filtered_segments
|
1069
|
-
|
1107
|
+
dists = np.array([seg.path_distance() for seg in filtered_segments])
|
1108
|
+
values = distribution(dists)
|
1109
|
+
for seg, value in zip(filtered_segments, values):
|
1070
1110
|
seg.set_param_value(param_name, value)
|
1071
1111
|
|
1072
1112
|
|
@@ -1470,6 +1510,8 @@ class Model():
|
|
1470
1510
|
|
1471
1511
|
|
1472
1512
|
# Reinsert active mechanisms after creating the new domain
|
1513
|
+
# The new domain by default has no mechanisms. Here we re-insert the
|
1514
|
+
# exact same mechanisms as in the original domain of the root section.
|
1473
1515
|
for mech_name in inserted_mechs:
|
1474
1516
|
mech = self.mechanisms[mech_name]
|
1475
1517
|
root.insert_mechanism(mech)
|
@@ -1888,10 +1930,10 @@ class Model():
|
|
1888
1930
|
segments = [self.sec_tree.sections[sec_idx](loc)
|
1889
1931
|
for sec_idx, loc in zip(df_pop['sec_idx'], df_pop['loc'])]
|
1890
1932
|
|
1891
|
-
pop = Population(i,
|
1892
|
-
segments,
|
1893
|
-
pop_data['N'],
|
1894
|
-
|
1933
|
+
pop = Population(idx=i,
|
1934
|
+
segments=segments,
|
1935
|
+
N=pop_data['N'],
|
1936
|
+
syn_type=syn_type)
|
1895
1937
|
|
1896
1938
|
syn_locs = [(self.sec_tree.sections[sec_idx], loc) for sec_idx, loc in zip(df_pop['sec_idx'].tolist(), df_pop['loc'].tolist())]
|
1897
1939
|
|
@@ -11,6 +11,7 @@ from dendrotweaks.morphology.trees import Node, Tree
|
|
11
11
|
from dendrotweaks.morphology.domains import Domain
|
12
12
|
from dataclasses import dataclass, field
|
13
13
|
from bisect import bisect_left
|
14
|
+
from functools import cached_property
|
14
15
|
|
15
16
|
import warnings
|
16
17
|
|
@@ -271,11 +272,55 @@ class Section(Node):
|
|
271
272
|
seg_values = [seg.get_param_value(param_name) for seg in self.segments]
|
272
273
|
return round(np.mean(seg_values), 16)
|
273
274
|
|
275
|
+
@cached_property
|
276
|
+
def path_distance_to_root(self) -> float:
|
277
|
+
"""
|
278
|
+
Calculate the total distance from the section start to the root.
|
279
|
+
|
280
|
+
Returns
|
281
|
+
-------
|
282
|
+
float
|
283
|
+
The distance from the section start to the root.
|
284
|
+
"""
|
285
|
+
distance = 0
|
286
|
+
node = self.parent # Start from the parent node
|
287
|
+
|
288
|
+
if node is None:
|
289
|
+
return 0
|
290
|
+
|
291
|
+
while node.parent:
|
292
|
+
distance += node.length
|
293
|
+
node = node.parent
|
294
|
+
|
295
|
+
return distance
|
296
|
+
|
297
|
+
@cached_property
|
298
|
+
def path_distance_within_domain(self) -> float:
|
299
|
+
"""
|
300
|
+
Calculate the distance from the section start to the root within the same domain.
|
301
|
+
|
302
|
+
Returns
|
303
|
+
-------
|
304
|
+
float
|
305
|
+
The distance from the section start to the root within the same domain.
|
306
|
+
"""
|
307
|
+
distance = 0
|
308
|
+
node = self.parent # Start from the parent node
|
309
|
+
|
310
|
+
if node is None:
|
311
|
+
return 0
|
274
312
|
|
275
|
-
|
276
|
-
|
313
|
+
while node.parent:
|
314
|
+
if node.domain != self.domain:
|
315
|
+
break
|
316
|
+
distance += node.length
|
317
|
+
node = node.parent
|
318
|
+
|
319
|
+
return distance
|
320
|
+
|
321
|
+
def path_distance(self, relative_position: float = 0, within_domain: bool = False) -> float:
|
277
322
|
"""
|
278
|
-
|
323
|
+
Get the distance from the section to the root at a given relative position.
|
279
324
|
|
280
325
|
Parameters
|
281
326
|
----------
|
@@ -288,29 +333,16 @@ class Section(Node):
|
|
288
333
|
-------
|
289
334
|
float
|
290
335
|
The distance from the section to the root.
|
291
|
-
|
292
|
-
Important
|
293
|
-
---------
|
294
|
-
Assumes that we always attach the 0 end of the child.
|
295
336
|
"""
|
296
337
|
if not (0 <= relative_position <= 1):
|
297
338
|
raise ValueError('Relative position must be between 0 and 1.')
|
298
339
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
while node.parent:
|
340
|
+
if self.parent is None: # Soma section
|
341
|
+
# relative_position = abs(relative_position - 0.5)
|
342
|
+
return 0
|
304
343
|
|
305
|
-
|
306
|
-
|
307
|
-
if within_domain and node.parent.domain != node.domain:
|
308
|
-
break
|
309
|
-
|
310
|
-
node = node.parent
|
311
|
-
factor = 1
|
312
|
-
|
313
|
-
return distance
|
344
|
+
base_distance = self.path_distance_within_domain if within_domain else self.path_distance_to_root
|
345
|
+
return base_distance + relative_position * self.length
|
314
346
|
|
315
347
|
|
316
348
|
def disconnect_from_parent(self):
|
@@ -671,11 +703,13 @@ class NeuronSection(Section):
|
|
671
703
|
first_segment = self.segments[0]
|
672
704
|
parent = first_segment.parent
|
673
705
|
|
674
|
-
for
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
706
|
+
for seg in new_segments:
|
707
|
+
seg_tree.insert_node_before(seg, first_segment)
|
708
|
+
# for i, seg in enumerate(new_segments[:]):
|
709
|
+
# if i == 0:
|
710
|
+
# seg_tree.insert_node_before(seg, first_segment)
|
711
|
+
# else:
|
712
|
+
# seg_tree.insert_node_after(seg, new_segments[i-1])
|
679
713
|
|
680
714
|
for seg in old_segments:
|
681
715
|
seg_tree.remove_node(seg)
|
dendrotweaks/simulators.py
CHANGED
@@ -178,7 +178,7 @@ class NeuronSimulator(Simulator):
|
|
178
178
|
The location along the normalized section length to remove the recording from.
|
179
179
|
"""
|
180
180
|
seg = sec(loc)
|
181
|
-
if self._recordings[var]
|
181
|
+
if seg in self._recordings[var]:
|
182
182
|
self._recordings[var][seg] = None
|
183
183
|
self._recordings[var].pop(seg)
|
184
184
|
if not self._recordings[var]:
|
@@ -255,7 +255,7 @@ class Population():
|
|
255
255
|
'syn_type': [self.syn_type] * len(flat_synapses),
|
256
256
|
'name': [self.name] * len(flat_synapses),
|
257
257
|
'sec_idx': [syn.sec.idx for syn in flat_synapses],
|
258
|
-
'loc': [syn.loc for syn in flat_synapses],
|
258
|
+
'loc': [round(syn.loc, 8) for syn in flat_synapses],
|
259
259
|
}
|
260
260
|
|
261
261
|
|
@@ -1,16 +1,16 @@
|
|
1
|
-
dendrotweaks/__init__.py,sha256=
|
2
|
-
dendrotweaks/model.py,sha256=
|
1
|
+
dendrotweaks/__init__.py,sha256=qo6haBW1LRSOhXRjByeruiXSnrnjxrUxqRbDymWSLaQ,384
|
2
|
+
dendrotweaks/model.py,sha256=Dm_KJ8KbvHLCFKB3LwBHhgSrdiQteamqgda1J0p898w,71053
|
3
3
|
dendrotweaks/model_io.py,sha256=xwXKMcUle-Y0HoWFYVZu3G8v4pdQXmeaDfl2Xi65eHw,2137
|
4
4
|
dendrotweaks/path_manager.py,sha256=dai5o6UA0nk-ubwKWRu4LFdDBO77zW_SsMf6k0MLBiI,8703
|
5
|
-
dendrotweaks/simulators.py,sha256=
|
5
|
+
dendrotweaks/simulators.py,sha256=OscZ4H6z9YiNloDtMAgpPx9n2e-9WJyLEWtcSD1YZR8,7411
|
6
6
|
dendrotweaks/utils.py,sha256=jaUJNb39Bsevg3WJByP56bO7CLj1wzlh-uGZl-lxi1I,7131
|
7
7
|
dendrotweaks/analysis/__init__.py,sha256=SEYpoQ5iXiQXyHB20-IAdDHYI-7CR5GYFXIwr-O05Ug,858
|
8
|
-
dendrotweaks/analysis/ephys_analysis.py,sha256=
|
8
|
+
dendrotweaks/analysis/ephys_analysis.py,sha256=PqT3aBCuxQbvdm9jnXjlTJ3R5n7_Vwp4fLrHGgtfoWw,14362
|
9
9
|
dendrotweaks/analysis/morphometric_analysis.py,sha256=5zohjGssyx-wezI-yY3Q-kYM_wzAQLLFBJ9Xk950_JY,3571
|
10
10
|
dendrotweaks/biophys/__init__.py,sha256=k0o2xwyoaJUb1lfO9OHtqxheNP6R-Ya5o0g-bJOdCZg,360
|
11
|
-
dendrotweaks/biophys/distributions.py,sha256=
|
11
|
+
dendrotweaks/biophys/distributions.py,sha256=XGczxBYJ0-vkIfXbfzvlIqlH9OpBuT9J95Kzyd2zL5A,10325
|
12
12
|
dendrotweaks/biophys/groups.py,sha256=Q4kBIqL1-piIgrpsVq6ojinAWHiEJ1GzMjSAQ7Ni_E8,3212
|
13
|
-
dendrotweaks/biophys/mechanisms.py,sha256=
|
13
|
+
dendrotweaks/biophys/mechanisms.py,sha256=IxKcyYftEbupJWPqGAV20Ox5a3uPbnXs3gYSlC3z-E0,18520
|
14
14
|
dendrotweaks/biophys/default_mod/AMPA.mod,sha256=HY_pWzYvaSDV-w7qruenG2mnll8v79s40HFHjUCIi4U,980
|
15
15
|
dendrotweaks/biophys/default_mod/AMPA_NMDA.mod,sha256=ztv2ePUiEQZ93-23FTkGO2DC91rehQuqo0NUIbHZ368,2318
|
16
16
|
dendrotweaks/biophys/default_mod/CaDyn.mod,sha256=gwc69K_rxu2w_mV7CnOSOnVaCMc8Z-MfdBFf6lAj4kg,1298
|
@@ -33,9 +33,9 @@ dendrotweaks/biophys/io/loader.py,sha256=Wv9ZkEDyA3MkCdV0sMeRnBffg2WAI7yTV3r6C41
|
|
33
33
|
dendrotweaks/biophys/io/parser.py,sha256=boT27lFrn5LYrJnkZFs0SwrZZrkSkwO8efqGPJ4Qj0I,17914
|
34
34
|
dendrotweaks/biophys/io/reader.py,sha256=JWm5WM9illvSfDkhWEmWBcj8Y7PSi8zeZX9j1ARUHVU,6576
|
35
35
|
dendrotweaks/morphology/__init__.py,sha256=JwXmSmdn9e_jqslITEdiU9kWvzxcxT9Aw_kUkXLbm5o,353
|
36
|
-
dendrotweaks/morphology/domains.py,sha256=
|
36
|
+
dendrotweaks/morphology/domains.py,sha256=l57KVR5eo1LlH_fCd1AOMiG_SsYLBPBTGQ5R78BHfdM,2545
|
37
37
|
dendrotweaks/morphology/point_trees.py,sha256=5dUPaQXYPdJbWoD3pFI2DV2XnuFRhB5d0wTBlfmmIeI,21600
|
38
|
-
dendrotweaks/morphology/sec_trees.py,sha256=
|
38
|
+
dendrotweaks/morphology/sec_trees.py,sha256=eKLC-yNhsn_rPdTE7w7p6STa1onYkBTGcpBKBpEWZUI,36957
|
39
39
|
dendrotweaks/morphology/seg_trees.py,sha256=-XeSJuD7ZixBJYQDzvmSEiNvOWbVmX_DanyAPkkR-NA,4042
|
40
40
|
dendrotweaks/morphology/trees.py,sha256=NrNvPMR-U0clt63eqwVJqU0H8NJgY53QGA_BkdcwkQI,16033
|
41
41
|
dendrotweaks/morphology/io/__init__.py,sha256=gAZqZdf5VKPb6ksK8Lwt7MbTAq8TDP8uq3Vs_ebNFEY,324
|
@@ -47,10 +47,10 @@ dendrotweaks/morphology/reduce/reduce.py,sha256=5czZDrG3xsvHn3c_tbYhUOlXgST989-R
|
|
47
47
|
dendrotweaks/morphology/reduce/reduced_cylinder.py,sha256=jGJ4J-amukRr-3DPirVR5pzNO-6H7_sZF1N_X57ZGdw,5132
|
48
48
|
dendrotweaks/stimuli/__init__.py,sha256=bFfSEZhCVpwOVEBgLe65iiY3SdpjKPhyLemC1z5OX9I,153
|
49
49
|
dendrotweaks/stimuli/iclamps.py,sha256=NjkhhwZKJR1f_g3N9BVxMVoO9ubBk5WkQ6h9Bnf9xgA,1681
|
50
|
-
dendrotweaks/stimuli/populations.py,sha256=
|
50
|
+
dendrotweaks/stimuli/populations.py,sha256=vq2NUOaxaltUwlcT7wuCe8z1JWc8rk6HfPmfEd1kK68,8335
|
51
51
|
dendrotweaks/stimuli/synapses.py,sha256=g4MgWTske2TZ2i9FIIOE8-KXNx_3dWa3zEhB2rcqYig,5470
|
52
|
-
dendrotweaks-0.4.
|
53
|
-
dendrotweaks-0.4.
|
54
|
-
dendrotweaks-0.4.
|
55
|
-
dendrotweaks-0.4.
|
56
|
-
dendrotweaks-0.4.
|
52
|
+
dendrotweaks-0.4.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
53
|
+
dendrotweaks-0.4.3.dist-info/METADATA,sha256=KV5Ev0CC98m0JrqsvOG5szTc8Yqi_RjcKuOK4C7yHZo,2740
|
54
|
+
dendrotweaks-0.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
55
|
+
dendrotweaks-0.4.3.dist-info/top_level.txt,sha256=OzT_2BSI5j5zxC447K6Y-0W-GHbued7iX-_hFGAKMxY,13
|
56
|
+
dendrotweaks-0.4.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|