phasorpy 0.1__cp310-cp310-win_amd64.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.
- phasorpy/__init__.py +10 -0
- phasorpy/__main__.py +7 -0
- phasorpy/_phasorpy.cp310-win_amd64.pyd +0 -0
- phasorpy/_phasorpy.pyx +1811 -0
- phasorpy/_typing.py +77 -0
- phasorpy/_utils.py +441 -0
- phasorpy/cli.py +87 -0
- phasorpy/color.py +581 -0
- phasorpy/components.py +313 -0
- phasorpy/conftest.py +36 -0
- phasorpy/cursors.py +502 -0
- phasorpy/datasets.py +433 -0
- phasorpy/io.py +1671 -0
- phasorpy/phasor.py +3135 -0
- phasorpy/plot.py +2074 -0
- phasorpy/py.typed +0 -0
- phasorpy/utils.py +68 -0
- phasorpy/version.py +71 -0
- phasorpy-0.1.dist-info/LICENSE.txt +21 -0
- phasorpy-0.1.dist-info/METADATA +78 -0
- phasorpy-0.1.dist-info/RECORD +24 -0
- phasorpy-0.1.dist-info/WHEEL +5 -0
- phasorpy-0.1.dist-info/entry_points.txt +2 -0
- phasorpy-0.1.dist-info/top_level.txt +1 -0
phasorpy/datasets.py
ADDED
@@ -0,0 +1,433 @@
|
|
1
|
+
"""Manage sample data files for testing and tutorials.
|
2
|
+
|
3
|
+
The ``phasorpy.datasets`` module provides a :py:func:`fetch` function to
|
4
|
+
download data files from remote repositories and cache them in a local
|
5
|
+
directory. The cache location can be changed by setting the
|
6
|
+
``PHASORPY_DATA_DIR`` environment variable.
|
7
|
+
|
8
|
+
Datasets from the following repositories are available:
|
9
|
+
|
10
|
+
- `PhasorPy tests <https://zenodo.org/record/8417894>`_
|
11
|
+
- `LFD Workshop <https://zenodo.org/record/8411056>`_
|
12
|
+
- `FLUTE <https://zenodo.org/record/8046636>`_
|
13
|
+
- `napari-flim-phasor-plotter
|
14
|
+
<https://github.com/zoccoler/napari-flim-phasor-plotter/tree/0.0.6/src/napari_flim_phasor_plotter/data>`_
|
15
|
+
- `Phasor-based multi-harmonic unmixing for in-vivo hyperspectral imaging
|
16
|
+
<https://zenodo.org/records/13625087>`_
|
17
|
+
|
18
|
+
The implementation is based on the `Pooch <https://www.fatiando.org/pooch>`_
|
19
|
+
library.
|
20
|
+
|
21
|
+
"""
|
22
|
+
|
23
|
+
from __future__ import annotations
|
24
|
+
|
25
|
+
__all__ = ['fetch', 'REPOSITORIES']
|
26
|
+
|
27
|
+
import os
|
28
|
+
from typing import TYPE_CHECKING
|
29
|
+
|
30
|
+
if TYPE_CHECKING:
|
31
|
+
from ._typing import Any, Iterable
|
32
|
+
|
33
|
+
import pooch
|
34
|
+
|
35
|
+
ENV = 'PHASORPY_DATA_DIR'
|
36
|
+
|
37
|
+
DATA_ON_GITHUB = bool(
|
38
|
+
os.environ.get('PHASORPY_DATA_ON_GITHUB', False)
|
39
|
+
) or bool(os.environ.get('GITHUB_ACTIONS', False))
|
40
|
+
|
41
|
+
TESTS = pooch.create(
|
42
|
+
path=pooch.os_cache('phasorpy'),
|
43
|
+
base_url=(
|
44
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/tests'
|
45
|
+
if DATA_ON_GITHUB
|
46
|
+
else 'doi:10.5281/zenodo.8417894'
|
47
|
+
),
|
48
|
+
env=ENV,
|
49
|
+
registry={
|
50
|
+
'flimage.int.bin': (
|
51
|
+
'sha256:'
|
52
|
+
'5d470ed31ed0611b43270261341bc1c41f55fda665eaf529d848a139fcae5fc8'
|
53
|
+
),
|
54
|
+
'flimage.int.bin.zip': (
|
55
|
+
'sha256:'
|
56
|
+
'51062322891b4c22d577100395d8c02297c5494030d2c550a0fd6c90f73cc211'
|
57
|
+
),
|
58
|
+
'flimage.mod.bin': (
|
59
|
+
'sha256:'
|
60
|
+
'b0312f6f9f1f24e228b2c3a3cb07e18d80b35f31109f8c771b088b54419c5200'
|
61
|
+
),
|
62
|
+
'flimage.phi.bin': (
|
63
|
+
'sha256:'
|
64
|
+
'd593acc698a1226c7a4571fa61f6751128584dcca6ed4449d655283cd231b125'
|
65
|
+
),
|
66
|
+
'flimfast.flif': (
|
67
|
+
'sha256:'
|
68
|
+
'b12cedf299831a46baf80dcfe7bfb9f366fee30fb3e2b3039d4346f1bbaf3e2c'
|
69
|
+
),
|
70
|
+
'flimfast.flif.zip': (
|
71
|
+
'sha256:'
|
72
|
+
'b25642b1a2dbc547f0cdaadc13ce89ecaf372f90a20978910c02b13beb138c2e'
|
73
|
+
),
|
74
|
+
'frequency_domain.ifli': (
|
75
|
+
'sha256:'
|
76
|
+
'56015b98a2edaf4ee1262b5e1034305aa29dd8b20e301ced9cd7a109783cd171'
|
77
|
+
),
|
78
|
+
'frequency_domain.ifli.zip': (
|
79
|
+
'sha256:'
|
80
|
+
'93066cc48028360582f6e3380d09d2c5a6f540a8f931639da3cfca158926df9b'
|
81
|
+
),
|
82
|
+
'paramecium.lsm': (
|
83
|
+
'sha256:'
|
84
|
+
'b3b3b80be244a41352c56390191a50e4010d52e5ca341dc51bd1d7c89f10cedf'
|
85
|
+
),
|
86
|
+
'paramecium.lsm.zip': (
|
87
|
+
'sha256:'
|
88
|
+
'7828a80e878ee7ab88f9bd9a6cda72e5d698394d37f69a7bee5b0b31b3856919'
|
89
|
+
),
|
90
|
+
'simfcs.b64': (
|
91
|
+
'sha256:'
|
92
|
+
'5ccccd0bcd46c66ea174b6074975f631bdf163fcb047e35f9310aaf67c320fb8'
|
93
|
+
),
|
94
|
+
'simfcs.b64.zip': (
|
95
|
+
'sha256:'
|
96
|
+
'b176761905fc5916d0770bd6baaa0e31af5981f92ec323e588f9ce398324818e'
|
97
|
+
),
|
98
|
+
'simfcs.b&h': (
|
99
|
+
'sha256:'
|
100
|
+
'6a406c4fd862318a51370461c7607390f022afdcb2ce27c4600df4b5af83c26e'
|
101
|
+
),
|
102
|
+
'simfcs.b&h.zip': (
|
103
|
+
'sha256:'
|
104
|
+
'ec14666be76bd5bf2dcaee63435332857795173c8dc94be8778697f32b041aa1'
|
105
|
+
),
|
106
|
+
'simfcs.bhz': (
|
107
|
+
'sha256:'
|
108
|
+
'14f8b5287e257514945ca17a8398425fc040c00bfad2d8a3c6adb4790862d211'
|
109
|
+
),
|
110
|
+
'simfcs.r64': (
|
111
|
+
'sha256:'
|
112
|
+
'ead3b2df45c1dff91e91a325f97225f4837c372db04c2e49437ee9ec68532946'
|
113
|
+
),
|
114
|
+
'simfcs.ref': (
|
115
|
+
'sha256:'
|
116
|
+
'697dad17fb3a3cf7329a45b43ba9ae5f7220c1f3d5f08749f2ce3eadb0598420'
|
117
|
+
),
|
118
|
+
'simfcs.ref.zip': (
|
119
|
+
'sha256:'
|
120
|
+
'482331ec5586973e9eb5c2e4f793d9621cc756ce8f4093f5e21906a7ce5726f8'
|
121
|
+
),
|
122
|
+
'simfcs.z64': (
|
123
|
+
'sha256:'
|
124
|
+
'f1dd861f80528c77bb581023c05c7bf7293d6ad3c4a3d10f9a50b8b5618a8099'
|
125
|
+
),
|
126
|
+
'simfcs_1000.int': (
|
127
|
+
'sha256:'
|
128
|
+
'bb5bde0ecf24243865cdbc2b065358fe8c557696de18567dbb3f75adfb2ab51a'
|
129
|
+
),
|
130
|
+
'simfcs_1000.int.zip': (
|
131
|
+
'sha256:'
|
132
|
+
'f75e211dc344f194a871f0b3af3f6d8a9e4850e9718526f2bfad87ef16c1c377'
|
133
|
+
),
|
134
|
+
'simfcs_1000.mod': (
|
135
|
+
'sha256:'
|
136
|
+
'84265df08d48ff56f6844d55392fccac9fa429c481d1ac81b07c23738075d336'
|
137
|
+
),
|
138
|
+
'simfcs_1000.phs': (
|
139
|
+
'sha256:'
|
140
|
+
'8a39d2abd3999ab73c34db2476849cddf303ce389b35826850f9a700589b4a90'
|
141
|
+
),
|
142
|
+
'tcspc.sdt': (
|
143
|
+
'sha256:'
|
144
|
+
'0ff0b25b36cb9a7657112a3b081ff479bcae487ce8100b8756e5780e0957708d'
|
145
|
+
),
|
146
|
+
'tcspc.sdt.zip': (
|
147
|
+
'sha256:'
|
148
|
+
'57a772bc413e85e0f13eb996f8c2484dfb3d15df67ffa6c3b968d3a03c27fdc3'
|
149
|
+
),
|
150
|
+
},
|
151
|
+
)
|
152
|
+
|
153
|
+
LFD_WORKSHOP = pooch.create(
|
154
|
+
path=pooch.os_cache('phasorpy'),
|
155
|
+
base_url=(
|
156
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/lfd_workshop'
|
157
|
+
if DATA_ON_GITHUB
|
158
|
+
else 'doi:10.5281/zenodo.8411056'
|
159
|
+
),
|
160
|
+
env=ENV,
|
161
|
+
registry={
|
162
|
+
'4-22-03-2-A5-CHO-CELL3B.tif': (
|
163
|
+
'sha256:'
|
164
|
+
'015d4b5a4cbb6cc40ac0c39f7a0b57ff31173df5b3112627b551e4d8ce8c3b02'
|
165
|
+
),
|
166
|
+
'1011rac1002.ref': (
|
167
|
+
'sha256:'
|
168
|
+
'b8eb374d21ba74519342187aa0b6f67727983c1e9d02a9b86bde7e323f5545ac'
|
169
|
+
),
|
170
|
+
'CFP and CFP-YFp.ref': (
|
171
|
+
'sha256:'
|
172
|
+
'f4f494d5e71836aeacfa8796dcf9b92bbc0f62b8176d6c10d5ab9ce202313257'
|
173
|
+
),
|
174
|
+
'CFP-YFP many cells with background.ref': (
|
175
|
+
'sha256:'
|
176
|
+
'7cb88018be807144edbb2746d0ec6548eeb3ddc4aa176f3fba4223990aa21754'
|
177
|
+
),
|
178
|
+
'CFPpax8651866.ref': (
|
179
|
+
'sha256:'
|
180
|
+
'eda9177f2841229d120782862779e2db295ad773910b308bc2c360c22c75f391'
|
181
|
+
),
|
182
|
+
'Paxillins013.bin': (
|
183
|
+
'sha256:'
|
184
|
+
'b979e3112dda2fa1cc34a351dab7b0c82009ef07eaa34829503fb79f7a6bb7d2'
|
185
|
+
),
|
186
|
+
'capillaries1001.ref': (
|
187
|
+
'sha256:'
|
188
|
+
'27f071ae31032ed4e79c365bb2076044876f7fc10ef622aff458945f33e60984'
|
189
|
+
),
|
190
|
+
'pax1023.bin': (
|
191
|
+
'sha256:'
|
192
|
+
'f467e8264bb10fc12a19506693837f384e32ca01c0cac0b25704c19ceb8d7d5a'
|
193
|
+
),
|
194
|
+
},
|
195
|
+
)
|
196
|
+
|
197
|
+
FLUTE = pooch.create(
|
198
|
+
path=pooch.os_cache('phasorpy'),
|
199
|
+
base_url='doi:10.5281/zenodo.8046636',
|
200
|
+
env=ENV,
|
201
|
+
registry={
|
202
|
+
'Embryo.tif': (
|
203
|
+
'sha256:'
|
204
|
+
'd1107de8d0f3da476e90bcb80ddf40231df343ed9f28340c873cf858ca869e20'
|
205
|
+
),
|
206
|
+
'Fluorescein_Embryo.tif': (
|
207
|
+
'sha256:'
|
208
|
+
'53cb66439a6e921aef1aa7f57ef542260c51cdb8fe56a643f80ea88fe2230bc8'
|
209
|
+
),
|
210
|
+
'Fluorescein_hMSC.tif': (
|
211
|
+
'sha256:'
|
212
|
+
'a3f22076e8dc89b639f690146e46ff8a068388cbf381c2f3a9225cdcbbcec605'
|
213
|
+
),
|
214
|
+
'hMSC control.tif': (
|
215
|
+
'sha256:'
|
216
|
+
'725570373ee51ee226560ec5ebb57708e2fac53effc94774c03b71c67a42c9f8'
|
217
|
+
),
|
218
|
+
'hMSC-ZOOM.tif': (
|
219
|
+
'sha256:'
|
220
|
+
'6ff4be17e9d98a94b44ef13ec57af3c520f8deaeef72a7210ea371b84617ce92'
|
221
|
+
),
|
222
|
+
'hMSC_rotenone.tif': (
|
223
|
+
'sha256:'
|
224
|
+
'cd0d2bd3baddc0f82c84c9624692e51bbbc56a80ac20b5936be0898d619c2bf2'
|
225
|
+
),
|
226
|
+
},
|
227
|
+
)
|
228
|
+
|
229
|
+
NAPARI_FLIM_PHASOR_PLOTTER = pooch.create(
|
230
|
+
path=pooch.os_cache('phasorpy'),
|
231
|
+
base_url='https://github.com/zoccoler/napari-flim-phasor-plotter/'
|
232
|
+
'raw/0.0.6/src/napari_flim_phasor_plotter/data',
|
233
|
+
env=ENV,
|
234
|
+
registry={
|
235
|
+
'hazelnut_FLIM_single_image.ptu': (
|
236
|
+
'sha256:'
|
237
|
+
'262f60ebc0054ba985fdda3032b58419aac07720e5f157800616c864d15fc2d3'
|
238
|
+
),
|
239
|
+
'hazelnut_FLIM_z_stack.zip': (
|
240
|
+
'sha256:'
|
241
|
+
'8d26ebc7c758a70ee256d95c06f7921baa3cecbcdde82c7bb54b66bcb8db156e'
|
242
|
+
),
|
243
|
+
'lifetime_cat.tif': (
|
244
|
+
'sha256:'
|
245
|
+
'5f2a2d20284a6f32fa3d1d13cb0c535cea5c2ec99c23148d9ee2d1e22d121a34'
|
246
|
+
),
|
247
|
+
'lifetime_cat_labels.tif': (
|
248
|
+
'sha256:'
|
249
|
+
'102d74c202171f0ce2821dfbf1c92ead578bafebf99830e0cfa766e7407aadf9'
|
250
|
+
),
|
251
|
+
'lifetime_cat_metadata.yml': (
|
252
|
+
'sha256:'
|
253
|
+
'20c447c1251598f255309fa866e58fc0e4abc2b73e824d18727833a05467d8bc'
|
254
|
+
),
|
255
|
+
'seminal_receptacle_FLIM_single_image.sdt': (
|
256
|
+
'sha256:'
|
257
|
+
'2ba169495e533235cffcad953e76c7969286aad9181b946f5167390b8ff1a44a'
|
258
|
+
),
|
259
|
+
},
|
260
|
+
)
|
261
|
+
|
262
|
+
ZENODO_13625087 = pooch.create(
|
263
|
+
path=pooch.os_cache('phasorpy'),
|
264
|
+
base_url=(
|
265
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/zenodo_13625087'
|
266
|
+
# if DATA_ON_GITHUB
|
267
|
+
# else 'doi:10.1088/2050-6120/ac9ae9'
|
268
|
+
),
|
269
|
+
env=ENV,
|
270
|
+
registry={
|
271
|
+
'33_Hoechst_Golgi_Mito_Lyso_CellMAsk_404_488_561_633_SP.lsm': (
|
272
|
+
'sha256:'
|
273
|
+
'68fcefcad4e750e9ec7068820e455258c986f6a9b724e66744a28bbbb689f986'
|
274
|
+
),
|
275
|
+
'34_Hoechst_Golgi_Mito_Lyso_CellMAsk_404_488_561_633_SP.lsm': (
|
276
|
+
'sha256:'
|
277
|
+
'5c0b7d76c274fd64891fca2507013b7c8c9979d8131ce282fac55fd24fbb38bd'
|
278
|
+
),
|
279
|
+
'35_Hoechst_Golgi_Mito_Lyso_CellMAsk_404_488_561_633_SP.lsm': (
|
280
|
+
'sha256:'
|
281
|
+
'df57c178c185f6e271a66e2664dcc09d6f5abf923ee7d9c33add41bafc15214c'
|
282
|
+
),
|
283
|
+
'38_Hoechst_Golgi_Mito_Lyso_CellMAsk_404_488_561_633_SP.lsm': (
|
284
|
+
'sha256:'
|
285
|
+
'092ac050edf55e26dcda8cba10122408c6f1b81d19accf07214385d6eebfcf3e'
|
286
|
+
),
|
287
|
+
},
|
288
|
+
)
|
289
|
+
|
290
|
+
REPOSITORIES: dict[str, pooch.Pooch] = {
|
291
|
+
'tests': TESTS,
|
292
|
+
'lfd-workshop': LFD_WORKSHOP,
|
293
|
+
'flute': FLUTE,
|
294
|
+
'napari-flim-phasor-plotter': NAPARI_FLIM_PHASOR_PLOTTER,
|
295
|
+
'zenodo-13625087': ZENODO_13625087,
|
296
|
+
}
|
297
|
+
"""Pooch repositories."""
|
298
|
+
|
299
|
+
|
300
|
+
def fetch(
|
301
|
+
*args: str | Iterable[str | pooch.Pooch] | pooch.Pooch,
|
302
|
+
extract_dir: str | None = '.',
|
303
|
+
return_scalar: bool = True,
|
304
|
+
**kwargs: Any,
|
305
|
+
) -> Any: # str | tuple[str, ...]
|
306
|
+
"""Return absolute path(s) to sample file(s) in local storage.
|
307
|
+
|
308
|
+
The files are downloaded from a remote repository if they do not already
|
309
|
+
exist in the local storage.
|
310
|
+
|
311
|
+
Parameters
|
312
|
+
----------
|
313
|
+
*args: str or iterable of str, optional
|
314
|
+
Name(s) of file(s) or repositories to fetch from local storage.
|
315
|
+
If omitted, return files in all repositories.
|
316
|
+
extract_dir : str or None, optional
|
317
|
+
Path, relative to cache location, where ZIP files will be unpacked.
|
318
|
+
return_scalar : bool, optional
|
319
|
+
If true (default), return single path as string, else tuple of string.
|
320
|
+
**kwargs : optional
|
321
|
+
Additional arguments passed to ``pooch.fetch()``.
|
322
|
+
For example, ``progressbar=True``.
|
323
|
+
|
324
|
+
Returns
|
325
|
+
-------
|
326
|
+
str or tuple of str
|
327
|
+
Absolute path(s) of file(s) in local storage.
|
328
|
+
|
329
|
+
Examples
|
330
|
+
--------
|
331
|
+
>>> fetch('simfcs.r64')
|
332
|
+
'...simfcs.r64'
|
333
|
+
>>> fetch('simfcs.r64', 'simfcs.ref')
|
334
|
+
('...simfcs.r64', '...simfcs.ref')
|
335
|
+
|
336
|
+
"""
|
337
|
+
filenames: list[str] = []
|
338
|
+
if not args:
|
339
|
+
args = tuple(REPOSITORIES.keys())
|
340
|
+
for arg in args:
|
341
|
+
if isinstance(arg, str):
|
342
|
+
if arg in REPOSITORIES:
|
343
|
+
# fetch all files in repository
|
344
|
+
filenames.extend(
|
345
|
+
fetch(
|
346
|
+
*REPOSITORIES[arg].registry.keys(),
|
347
|
+
extract_dir=extract_dir,
|
348
|
+
return_scalar=False,
|
349
|
+
**kwargs,
|
350
|
+
)
|
351
|
+
)
|
352
|
+
continue
|
353
|
+
for repo in REPOSITORIES.values():
|
354
|
+
if arg + '.zip' in repo.registry:
|
355
|
+
# fetch single file in ZIP
|
356
|
+
filenames.append(
|
357
|
+
repo.fetch(
|
358
|
+
arg + '.zip',
|
359
|
+
processor=_Unzip([arg], extract_dir),
|
360
|
+
**kwargs,
|
361
|
+
)
|
362
|
+
)
|
363
|
+
break
|
364
|
+
if arg in repo.registry:
|
365
|
+
if arg.endswith('.zip'):
|
366
|
+
# fetch and extract all files in ZIP
|
367
|
+
filenames.extend(
|
368
|
+
repo.fetch(
|
369
|
+
arg,
|
370
|
+
processor=pooch.processors.Unzip(
|
371
|
+
extract_dir=extract_dir
|
372
|
+
),
|
373
|
+
**kwargs,
|
374
|
+
)
|
375
|
+
)
|
376
|
+
else:
|
377
|
+
# fetch single file
|
378
|
+
filenames.append(repo.fetch(arg, **kwargs))
|
379
|
+
break
|
380
|
+
else:
|
381
|
+
raise ValueError(f'file {arg!r} not found')
|
382
|
+
elif isinstance(arg, pooch.Pooch):
|
383
|
+
# fetch all files in repository
|
384
|
+
filenames.extend(
|
385
|
+
fetch(
|
386
|
+
*arg.registry.keys(),
|
387
|
+
extract_dir=extract_dir,
|
388
|
+
return_scalar=False,
|
389
|
+
**kwargs,
|
390
|
+
)
|
391
|
+
)
|
392
|
+
else:
|
393
|
+
# fetch all files in iterable
|
394
|
+
filenames.extend(
|
395
|
+
fetch(
|
396
|
+
*arg,
|
397
|
+
extract_dir=extract_dir,
|
398
|
+
return_scalar=False,
|
399
|
+
**kwargs,
|
400
|
+
)
|
401
|
+
)
|
402
|
+
if return_scalar and len(filenames) == 1:
|
403
|
+
return filenames[0]
|
404
|
+
return tuple(filenames)
|
405
|
+
|
406
|
+
|
407
|
+
class _Unzip(pooch.processors.ExtractorProcessor): # type: ignore[misc]
|
408
|
+
"""Pooch processor that unpacks ZIP archive and returns single file."""
|
409
|
+
|
410
|
+
def __call__(self, fname: str, action: str, pooch_: pooch.Pooch) -> str:
|
411
|
+
pooch.processors.ExtractorProcessor.__call__(
|
412
|
+
self, fname, action, pooch_
|
413
|
+
)
|
414
|
+
return os.path.splitext(fname)[0]
|
415
|
+
|
416
|
+
@property
|
417
|
+
def suffix(self) -> str:
|
418
|
+
"""String appended to unpacked archive folder name."""
|
419
|
+
return '.unzip'
|
420
|
+
|
421
|
+
def _all_members(self, fname: str) -> list[str]:
|
422
|
+
"""Return all members from archive."""
|
423
|
+
from zipfile import ZipFile
|
424
|
+
|
425
|
+
with ZipFile(fname, 'r') as zip_file:
|
426
|
+
return zip_file.namelist()
|
427
|
+
|
428
|
+
def _extract_file(self, fname: str, extract_dir: str) -> None:
|
429
|
+
"""Extract all files from ZIP archive."""
|
430
|
+
from zipfile import ZipFile
|
431
|
+
|
432
|
+
with ZipFile(fname, 'r') as zip_file:
|
433
|
+
zip_file.extractall(path=extract_dir)
|