tomwer 1.3.5__py3-none-any.whl → 1.3.7__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.
Files changed (124) hide show
  1. orangecontrib/tomwer/test/TestAcquisition.py +246 -0
  2. orangecontrib/tomwer/widgets/cluster/test/test_future_supervisorow.py +87 -0
  3. orangecontrib/tomwer/widgets/cluster/test/test_slurm_clusterow.py +67 -0
  4. orangecontrib/tomwer/widgets/control/test/test_advancement.py +51 -0
  5. orangecontrib/tomwer/widgets/control/test/test_data_validator.py +55 -0
  6. orangecontrib/tomwer/widgets/control/test/test_datadiscovery.py +131 -0
  7. orangecontrib/tomwer/widgets/control/test/test_datalist.py +70 -0
  8. orangecontrib/tomwer/widgets/control/test/test_datalistener.py +137 -0
  9. orangecontrib/tomwer/widgets/control/test/test_dataselector.py +95 -0
  10. orangecontrib/tomwer/widgets/control/test/test_datawatcher.py +436 -0
  11. orangecontrib/tomwer/widgets/control/test/test_emailow.py +29 -0
  12. orangecontrib/tomwer/widgets/control/test/test_notifier.py +51 -0
  13. orangecontrib/tomwer/widgets/control/test/test_nxtomo_concatenate_ow.py +64 -0
  14. orangecontrib/tomwer/widgets/control/test/test_nxtomomill.py +160 -0
  15. orangecontrib/tomwer/widgets/control/test/test_reduce_dark_flat_selector.py +40 -0
  16. orangecontrib/tomwer/widgets/control/test/test_singletomoobj.py +40 -0
  17. orangecontrib/tomwer/widgets/control/test/test_timerow.py +51 -0
  18. orangecontrib/tomwer/widgets/control/test/test_tomoobj_serie.py +96 -0
  19. orangecontrib/tomwer/widgets/control/test/test_volume_selector.py +69 -0
  20. orangecontrib/tomwer/widgets/control/test/test_volumesymlink.py +51 -0
  21. orangecontrib/tomwer/widgets/debugtools/test/test_dataset_generator.py +57 -0
  22. orangecontrib/tomwer/widgets/debugtools/test/test_object_inspector.py +62 -0
  23. orangecontrib/tomwer/widgets/other/test/test_pythonscript.py +31 -0
  24. orangecontrib/tomwer/widgets/reconstruction/test/test_axis.py +224 -0
  25. orangecontrib/tomwer/widgets/reconstruction/test/test_cast_volumeow.py +85 -0
  26. orangecontrib/tomwer/widgets/reconstruction/test/test_dark_refs_widget.py +136 -0
  27. orangecontrib/tomwer/widgets/reconstruction/test/test_delta_beta_selector.py +15 -0
  28. orangecontrib/tomwer/widgets/reconstruction/test/test_i_norm.py +226 -0
  29. orangecontrib/tomwer/widgets/reconstruction/test/test_nabu_helical_prepare_weights_double.py +20 -0
  30. orangecontrib/tomwer/widgets/reconstruction/test/test_nabu_volume.py +100 -0
  31. orangecontrib/tomwer/widgets/reconstruction/test/test_nabu_widget.py +107 -0
  32. orangecontrib/tomwer/widgets/reconstruction/test/test_sa_delta_beta.py +194 -0
  33. orangecontrib/tomwer/widgets/reconstruction/test/test_saaxis.py +220 -0
  34. orangecontrib/tomwer/widgets/stitching/test/test_zstitching.py +308 -0
  35. orangecontrib/tomwer/widgets/test/test_conditions.py +111 -0
  36. orangecontrib/tomwer/widgets/test/test_darkref.py +251 -0
  37. orangecontrib/tomwer/widgets/test/test_foldertransfert.py +131 -0
  38. orangecontrib/tomwer/widgets/visualization/test/test_dataviewerow.py +83 -0
  39. orangecontrib/tomwer/widgets/visualization/test/test_diffviewerow.py +65 -0
  40. orangecontrib/tomwer/widgets/visualization/test/test_live_sliceow.py +63 -0
  41. orangecontrib/tomwer/widgets/visualization/test/test_nxtomo_metadata_viewer.py +29 -0
  42. orangecontrib/tomwer/widgets/visualization/test/test_radio_stackow.py +56 -0
  43. orangecontrib/tomwer/widgets/visualization/test/test_sample_movedow.py +72 -0
  44. orangecontrib/tomwer/widgets/visualization/test/test_sinogram_viewerow.py +56 -0
  45. orangecontrib/tomwer/widgets/visualization/test/test_slice_stackow.py +57 -0
  46. orangecontrib/tomwer/widgets/visualization/test/test_volume_viewerow.py +57 -0
  47. tomwer/core/log/test/test_processlog.py +41 -0
  48. tomwer/core/process/control/datalistener/datalistener.py +11 -11
  49. tomwer/core/process/edit/test/test_darkflatpatch.py +269 -0
  50. tomwer/core/process/edit/test/test_imagekey_editor.py +125 -0
  51. tomwer/core/process/icat/test/test_create_screenshots.py +98 -0
  52. tomwer/core/process/icat/test/test_gallery.py +170 -0
  53. tomwer/core/process/reconstruction/axis/axis.py +3 -3
  54. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +3 -2
  55. tomwer/core/process/reconstruction/nabu/nabucommon.py +3 -4
  56. tomwer/core/process/reconstruction/nabu/nabuslices.py +4 -4
  57. tomwer/core/process/reconstruction/nabu/nabuvolume.py +2 -5
  58. tomwer/core/process/reconstruction/nabu/test/test_castvolume.py +143 -0
  59. tomwer/core/process/reconstruction/nabu/test/test_nabu_utils.py +203 -0
  60. tomwer/core/process/reconstruction/nabu/test/test_nabunormalization.py +222 -0
  61. tomwer/core/process/script/test/test_script.py +68 -0
  62. tomwer/core/process/stitching/test/test_metadataholder.py +17 -0
  63. tomwer/core/process/task.py +3 -2
  64. tomwer/core/process/test/test_data_transfer.py +4 -3
  65. tomwer/core/process/visualization/test/test_data_viewer.py +39 -0
  66. tomwer/core/process/visualization/test/test_diff_viewer.py +39 -0
  67. tomwer/core/process/visualization/test/test_image_stack_viewer.py +41 -0
  68. tomwer/core/process/visualization/test/test_radio_stack.py +39 -0
  69. tomwer/core/process/visualization/test/test_sample_moved.py +41 -0
  70. tomwer/core/process/visualization/test/test_sinogram_viewer.py +39 -0
  71. tomwer/core/process/visualization/test/test_slice_stack.py +39 -0
  72. tomwer/core/process/visualization/test/test_volume_viewer.py +39 -0
  73. tomwer/core/scan/blissscan.py +3 -3
  74. tomwer/core/scan/nxtomoscan.py +2 -2
  75. tomwer/core/scan/scanbase.py +5 -6
  76. tomwer/core/utils/test/test_image.py +30 -0
  77. tomwer/core/utils/test/test_nxtomo.py +66 -0
  78. tomwer/core/utils/test/test_scan_utils.py +46 -0
  79. tomwer/core/utils/test/test_time.py +6 -0
  80. tomwer/core/volume/test/test_volumes.py +21 -0
  81. tomwer/gui/control/reducedarkflatselector.py +2 -2
  82. tomwer/gui/control/serie/test/test_creator.py +451 -0
  83. tomwer/gui/control/serie/test/test_nxtomo_concatenate.py +21 -0
  84. tomwer/gui/edit/dkrfpatch.py +4 -4
  85. tomwer/gui/edit/nxtomowarmer.py +3 -2
  86. tomwer/gui/icat/test/test_create_screenshots_gui.py +23 -0
  87. tomwer/gui/icat/test/test_gallery_gui.py +37 -0
  88. tomwer/gui/imagefromfile.py +2 -2
  89. tomwer/gui/reconstruction/nabu/test/test_check.py +92 -0
  90. tomwer/gui/reconstruction/nabu/test/test_ctf.py +46 -0
  91. tomwer/gui/reconstruction/nabu/test/test_helical.py +21 -0
  92. tomwer/gui/reconstruction/nabu/test/test_nabu_preprocessing.py +81 -0
  93. tomwer/gui/reconstruction/normalization/test/test_intensity.py +119 -0
  94. tomwer/gui/stitching/config/tests/test_axisparams.py +25 -0
  95. tomwer/gui/stitching/tests/test_axis_ordered_list.py +21 -0
  96. tomwer/gui/stitching/tests/test_normalization.py +27 -0
  97. tomwer/gui/stitching/tests/test_preview.py +85 -0
  98. tomwer/gui/stitching/tests/test_stitching_raw.py +110 -0
  99. tomwer/gui/stitching/tests/test_z_stitching.py +67 -0
  100. tomwer/gui/stitching/tests/utils.py +79 -0
  101. tomwer/gui/stitching/z_stitching/tests/test_fine_estimation.py +35 -0
  102. tomwer/gui/stitching/z_stitching/tests/test_raw_estimation.py +215 -0
  103. tomwer/gui/stitching/z_stitching/tests/test_stitching_window.py +51 -0
  104. tomwer/gui/utils/test/test_completer.py +67 -0
  105. tomwer/gui/utils/test/test_line_selector.py +21 -0
  106. tomwer/gui/utils/test/test_splashscreen.py +8 -0
  107. tomwer/gui/utils/test/test_vignettes.py +68 -0
  108. tomwer/io/utils/h5pyutils.py +3 -7
  109. tomwer/io/utils/test/test_raw_and_processed_data.py +10 -0
  110. tomwer/io/utils/test/test_utils.py +92 -0
  111. tomwer/io/utils/utils.py +3 -3
  112. tomwer/synctools/stacks/reconstruction/castvolume.py +20 -5
  113. tomwer/tests/test_ewoks/test_conversion.py +104 -0
  114. tomwer/tests/test_ewoks/test_single_node_execution.py +112 -0
  115. tomwer/tests/test_ewoks/test_workflows.py +160 -0
  116. tomwer/version.py +1 -1
  117. {tomwer-1.3.5.dist-info → tomwer-1.3.7.dist-info}/METADATA +1 -1
  118. {tomwer-1.3.5.dist-info → tomwer-1.3.7.dist-info}/RECORD +124 -27
  119. /tomwer-1.3.5-py3.11-nspkg.pth → /tomwer-1.3.7-py3.11-nspkg.pth +0 -0
  120. {tomwer-1.3.5.dist-info → tomwer-1.3.7.dist-info}/LICENSE +0 -0
  121. {tomwer-1.3.5.dist-info → tomwer-1.3.7.dist-info}/WHEEL +0 -0
  122. {tomwer-1.3.5.dist-info → tomwer-1.3.7.dist-info}/entry_points.txt +0 -0
  123. {tomwer-1.3.5.dist-info → tomwer-1.3.7.dist-info}/namespace_packages.txt +0 -0
  124. {tomwer-1.3.5.dist-info → tomwer-1.3.7.dist-info}/top_level.txt +0 -0
@@ -54,7 +54,7 @@ import logging
54
54
  from typing import Optional
55
55
 
56
56
  from silx.io.utils import h5py_read_dataset
57
- from tomoscan.io import HDF5File, get_swmr_mode
57
+ from silx.io.utils import open as open_hdf5
58
58
 
59
59
  _logger = logging.getLogger(__name__)
60
60
 
@@ -203,7 +203,7 @@ class BlissScan:
203
203
  return True
204
204
  return False
205
205
 
206
- with HDF5File(file_path, mode="r", swmr=get_swmr_mode()) as h5s:
206
+ with open_hdf5(file_path) as h5s:
207
207
  if not isinstance(h5s, h5py.Group) or not isinstance(
208
208
  h5s[entry], h5py.Group
209
209
  ):
@@ -227,7 +227,7 @@ class BlissScan:
227
227
  return tuple()
228
228
  else:
229
229
  res = []
230
- with HDF5File(file_path, mode="r", swmr=get_swmr_mode()) as h5s:
230
+ with open_hdf5(file_path) as h5s:
231
231
  for entry in h5s:
232
232
  if BlissScan.is_bliss_valid_entry(file_path=file_path, entry=entry):
233
233
  res.append(entry)
@@ -40,12 +40,12 @@ from urllib.parse import urlparse
40
40
 
41
41
  import h5py
42
42
  from processview.core.dataset import DatasetIdentifier
43
+ from silx.io.utils import open as open_hdf5
43
44
  from tomoscan.esrf.identifier.hdf5Identifier import (
44
45
  NXtomoScanIdentifier as _NXtomoScanIdentifier,
45
46
  )
46
47
  from tomoscan.esrf.identifier.url_utils import UrlSettings, split_path, split_query
47
48
  from tomoscan.esrf.scan.nxtomoscan import NXtomoScan as _tsNXtomoScan
48
- from tomoscan.io import HDF5File, get_swmr_mode
49
49
  from nxtomo.nxobject.nxdetector import ImageKey
50
50
 
51
51
  from tomwer.utils import docstring
@@ -424,7 +424,7 @@ class NXtomoScan(_tsNXtomoScan, TomwerScanBase):
424
424
  return tuple()
425
425
  else:
426
426
  res = []
427
- with HDF5File(file_path, mode="r", swmr=get_swmr_mode()) as h5s:
427
+ with open_hdf5(file_path) as h5s:
428
428
  for entry_name, node in h5s.items():
429
429
  if isinstance(node, h5py.Group):
430
430
  if NXtomoScan.entry_is_nx_tomo(node):
@@ -40,8 +40,9 @@ import functools
40
40
  import numpy
41
41
  from silx.io.url import DataUrl
42
42
  from silx.utils.enum import Enum as _Enum
43
+ from silx.io.utils import open as open_hdf5
44
+
43
45
  from tomoscan.identifier import VolumeIdentifier
44
- from tomoscan.io import HDF5File, get_swmr_mode
45
46
  from tomoscan.normalization import IntensityNormalization
46
47
  from tomoscan.volumebase import VolumeBase
47
48
  from tomoscan.identifier import BaseIdentifier
@@ -131,7 +132,7 @@ class TomwerScanBase(TomwerObject):
131
132
  and self.process_file is not None
132
133
  and os.path.exists(self.process_file)
133
134
  ):
134
- with HDF5File(self.process_file, mode="r", swmr=get_swmr_mode()) as h5s:
135
+ with open_hdf5(self.process_file) as h5s:
135
136
  self._process_index = len(h5s.items())
136
137
  else:
137
138
  self._process_index = 0
@@ -756,7 +757,7 @@ class TomwerScanBase(TomwerObject):
756
757
  from tomwer.core.process.task import Task
757
758
 
758
759
  if os.path.exists(self.process_file):
759
- with HDF5File(self.process_file, mode="r", swmr=get_swmr_mode()) as h5s:
760
+ with open_hdf5(self.process_file) as h5s:
760
761
  if not hasattr(self, "entry"):
761
762
  entry = "entry"
762
763
  else:
@@ -864,9 +865,7 @@ def _get_reconstructed_single_file_volume(
864
865
 
865
866
  if check_url is True:
866
867
  try:
867
- with HDF5File(
868
- os.path.abspath(file_), "r", swmr=get_swmr_mode()
869
- ) as h5f:
868
+ with open_hdf5(os.path.abspath(file_)) as h5f:
870
869
  if entry not in h5f:
871
870
  logger.info("{volume} does not exists")
872
871
  return None
@@ -0,0 +1,30 @@
1
+ import numpy
2
+ import pytest
3
+
4
+ from tomwer.core.utils.image import ImageScaleMethod, scale_img2_to_img1, shift_img
5
+
6
+
7
+ @pytest.mark.parametrize(
8
+ "use_scipy, dx, dy, cval", ((True, 1.0, -2.3, 0), (False, 0.0, 3.6, 2))
9
+ )
10
+ def test_shift_image(use_scipy, dx, dy, cval):
11
+ data = numpy.linspace(0, 12, 100 * 100).reshape(100, 100)
12
+ assert isinstance(
13
+ shift_img(data=data, dx=dx, dy=dy, cval=cval, use_scipy=use_scipy),
14
+ numpy.ndarray,
15
+ )
16
+
17
+
18
+ def test_scale_img2_to_img1():
19
+ """
20
+ test scale_img2_to_img1 function
21
+ """
22
+ img_1 = numpy.linspace(0, 10, 100 * 100, dtype=numpy.float16).reshape(100, 100)
23
+ img_2 = numpy.linspace(20, 100, 100 * 100, dtype=numpy.float16).reshape(100, 100)
24
+
25
+ rescale_img = scale_img2_to_img1(img_2, img_1, method=ImageScaleMethod.MEAN)
26
+ numpy.testing.assert_array_almost_equal(
27
+ img_2,
28
+ rescale_img,
29
+ decimal=1,
30
+ )
@@ -0,0 +1,66 @@
1
+ # coding: utf-8
2
+ # /*##########################################################################
3
+ #
4
+ # Copyright (c) 2016-2017 European Synchrotron Radiation Facility
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+ #
24
+ # ###########################################################################*/
25
+
26
+ __authors__ = ["H. Payno"]
27
+ __license__ = "MIT"
28
+ __date__ = "02/12/2021"
29
+
30
+
31
+ import pytest
32
+ from nxtomo.nxobject.nxdetector import ImageKey
33
+
34
+ from tomwer.core.utils.nxtomoutils import get_n_series
35
+
36
+
37
+ def test_get_n_series():
38
+ """test tomwer.core.utils.nxtomoutils.get_n_series function"""
39
+ array_1 = (
40
+ [ImageKey.DARK_FIELD] * 2
41
+ + [ImageKey.FLAT_FIELD]
42
+ + [ImageKey.PROJECTION] * 4
43
+ + [ImageKey.FLAT_FIELD]
44
+ )
45
+
46
+ with pytest.raises(ValueError):
47
+ get_n_series(array_1, ImageKey.INVALID)
48
+ with pytest.raises(ValueError):
49
+ get_n_series(array_1, 3)
50
+
51
+ assert len(array_1) == 8
52
+ assert get_n_series(array_1, ImageKey.DARK_FIELD) == 1
53
+ assert get_n_series(array_1, ImageKey.FLAT_FIELD) == 2
54
+ assert get_n_series(array_1, ImageKey.PROJECTION) == 1
55
+ assert get_n_series(array_1, 0) == 1
56
+
57
+ array_2 = (
58
+ [ImageKey.FLAT_FIELD.value]
59
+ + [ImageKey.PROJECTION.value] * 4
60
+ + [ImageKey.INVALID.value] * 2
61
+ + [ImageKey.PROJECTION.value] * 3
62
+ + [ImageKey.FLAT_FIELD.value]
63
+ )
64
+ assert get_n_series(array_2, ImageKey.DARK_FIELD) == 0
65
+ assert get_n_series(array_2, ImageKey.PROJECTION) == 1
66
+ assert get_n_series(array_2, ImageKey.FLAT_FIELD) == 2
@@ -0,0 +1,46 @@
1
+ import os
2
+
3
+ from tomwer.core.scan.blissscan import BlissScan
4
+ from tomwer.core.scan.edfscan import EDFTomoScan
5
+ from tomwer.core.scan.nxtomoscan import NXtomoScan
6
+ from tomwer.core.utils.scanutils import format_output_location
7
+
8
+
9
+ def test_format_output_location(tmp_path):
10
+ """
11
+ test different use cases of 'format_output_location'
12
+ """
13
+ bliss_raw_dir = tmp_path / "raw"
14
+ bliss_raw_dir.mkdir()
15
+ bliss_sample_dir = bliss_raw_dir / "sample"
16
+ bliss_sample_dir.mkdir()
17
+ bliss_master_file = bliss_sample_dir / "dataset.h5"
18
+
19
+ bliss_proposal_file = bliss_raw_dir / "ihsample.h5"
20
+ bliss_proposal_file = os.path.abspath(bliss_proposal_file)
21
+
22
+ bliss_scan = BlissScan(
23
+ master_file=bliss_master_file, entry="1.1", proposal_file=bliss_proposal_file
24
+ )
25
+ expected_path = os.path.join(tmp_path, "reduced", "sample")
26
+ assert (
27
+ format_output_location(
28
+ location="{scan_parent_dir_basename}/../reduced/{scan_dir_name}",
29
+ scan=bliss_scan,
30
+ )
31
+ == expected_path
32
+ )
33
+
34
+ edf_scan = EDFTomoScan("/test/my/folder/")
35
+ assert (
36
+ format_output_location(
37
+ location="{scan_parent_dir_basename}/output", scan=edf_scan
38
+ )
39
+ == "/test/my/output"
40
+ )
41
+
42
+ hdf5_scan = NXtomoScan("/ddsad/my/file.hdf5", entry="entry0000")
43
+ assert (
44
+ format_output_location(location="{scan_basename}/output.nx", scan=hdf5_scan)
45
+ == "/ddsad/my/output.nx"
46
+ )
@@ -0,0 +1,6 @@
1
+ from tomwer.core.utils.time import Timer
2
+
3
+
4
+ def test_timer():
5
+ with Timer("timer name"):
6
+ pass
@@ -0,0 +1,21 @@
1
+ from tomwer.core.volume import EDFVolume, HDF5Volume, RawVolume
2
+ from tomwer.core.volume.rawvolume import RawVolumeIdentifier
3
+
4
+
5
+ def test_volume_data_parent_folder():
6
+ edf_volume = EDFVolume(folder="/my/folder/path")
7
+ assert edf_volume.volume_data_parent_folder() == "/my/folder"
8
+
9
+ hdf5_volume = HDF5Volume(file_path="/path/to/hdf5/file.hdf5", data_path="entry")
10
+ assert hdf5_volume.volume_data_parent_folder() == "/path/to/hdf5"
11
+
12
+ raw_volume = RawVolume(file_path="/path/to/raw.vol")
13
+ assert raw_volume.volume_data_parent_folder() == "/path/to"
14
+
15
+
16
+ def test_raw_identifier():
17
+ raw_volume = RawVolume(file_path="/path/to/raw.vol")
18
+ assert (
19
+ RawVolumeIdentifier.from_str(raw_volume.get_identifier().to_str())
20
+ == raw_volume.get_identifier()
21
+ )
@@ -8,11 +8,11 @@ from silx.gui import qt
8
8
  from silx.gui.plot import Plot2D
9
9
  from silx.io.dictdump import h5todict
10
10
  from silx.io.url import DataUrl
11
+ from silx.io.utils import open as open_hdf5
11
12
  from silx.gui.dialog.DataFileDialog import DataFileDialog
12
13
 
13
14
  from tomoscan.esrf.scan.utils import cwd_context
14
15
  from tomoscan.framereducer.target import REDUCER_TARGET
15
- from tomoscan.io import HDF5File, get_swmr_mode
16
16
 
17
17
  from tomwer.io.utils import get_default_directory
18
18
 
@@ -249,7 +249,7 @@ class ReduceDarkFlatSelectorTableWidget(qt.QWidget):
249
249
  if not os.path.exists(file_path):
250
250
  _logger.error(f"file doesn't exists ({file_path})")
251
251
 
252
- with HDF5File(file_path, mode="r", swmr=get_swmr_mode()) as h5f:
252
+ with open_hdf5(file_path) as h5f:
253
253
  entries = tuple(h5f.keys())
254
254
 
255
255
  res = []