internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: napari/napari

Issue:
Docs version is incorrect, and is missing pages
## 📚 Documentation

The current documentation shows napari version 0.1.dev742+g0adb11c:

<img width="849" alt="Screen Shot 2020-06-16 at 1 50 53 pm" src="https://user-images.githubusercontent.com/492549/84730044-822ceb00-afd8-11ea-9445-eadcd82155cd.png">

I suspect this might be an issue with #1324 interacting badly with how we build docs.

Additionally, the pages under [`docs/explanations`](https://github.com/napari/napari/tree/0adb11c53dbd29ae82fbf46b68c61bda63128775/docs/explanations) don't appear in the docs. I think the fix is to add `explanations` to the toctree here:

https://github.com/napari/napari/blob/0adb11c53dbd29ae82fbf46b68c61bda63128775/docs/source/index.rst


Discussion:

Resolving patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -14,6 +14,8 @@
 import sys
 import re
 import fileinput
+import recommonmark  # noqa: F401
+from recommonmark.transform import AutoStructify
 
 sys.path.insert(0, os.path.abspath('../..'))
 from napari import __version__  # noqa: E402
@@ -128,3 +130,21 @@ def clean_release_notes():
 
 # If true, `todo` and `todoList` produce output, else they produce nothing.
 todo_include_todos = True
+
+
+# app setup hook
+github_doc_root = 'https://github.com/napari/napari/tree/master/docs/'
+
+
+def setup(app):
+    app.add_config_value(
+        'recommonmark_config',
+        {
+            'url_resolver': lambda url: github_doc_root + url,
+            'enable_auto_toc_tree': True,
+            'auto_toc_tree_section': 'Contents',
+            'enable_eval_rst': True,
+        },
+        True,
+    )
+    app.add_transform(AutoStructify)


Test patch:
Repository: napari/napari

Issue:
RuntimeWarning when creating empty points
## 🐛 Bug
not a bug exactly, but a runtime warning that users will now see when creating empty points after #1360 

```python
In [1]: viewer = napari.Viewer()     

In [2]: viewer.add_points()    
napari/napari/components/layerlist.py:209: RuntimeWarning: All-NaN axis encountered
  min_v = np.nanmin(
napari/napari/components/layerlist.py:212: RuntimeWarning: All-NaN axis encountered
  max_v = np.nanmax(
```

@sofroniewn, would it be possible to catch the all-NaN cases before running np.nanmax/min here?

https://github.com/napari/napari/blob/2315443d9bd4fb6b8cc30967cf433d017f7c8ca2/napari/components/layerlist.py#L209-L214

 


Discussion:
Yup, that shouldn't have happened. I'll look into fixing. Thanks for the catch.

Resolving patch:
diff --git a/napari/components/layerlist.py b/napari/components/layerlist.py
--- a/napari/components/layerlist.py
+++ b/napari/components/layerlist.py
@@ -1,6 +1,7 @@
 import numpy as np
 import itertools
 from typing import Optional, List
+import warnings
 from ..layers import Layer
 from ..utils.naming import inc_name_count
 from ..utils.list import ListModel
@@ -206,12 +207,23 @@ def _extent_world(self) -> np.ndarray:
             mins = [e[0][::-1] for e in extrema]
             maxs = [e[1][::-1] for e in extrema]
 
-            min_v = np.nanmin(
-                list(itertools.zip_longest(*mins, fillvalue=np.nan)), axis=1
-            )
-            max_v = np.nanmax(
-                list(itertools.zip_longest(*maxs, fillvalue=np.nan)), axis=1
-            )
+            with warnings.catch_warnings():
+                # Taking the nanmin and nanmax of an axis of all nan
+                # raises a warning and returns nan for that axis
+                # as we have do an explict nan_to_num below this
+                # behaviour is acceptable and we can filter the
+                # warning
+                warnings.filterwarnings(
+                    'ignore', message='All-NaN axis encountered'
+                )
+                min_v = np.nanmin(
+                    list(itertools.zip_longest(*mins, fillvalue=np.nan)),
+                    axis=1,
+                )
+                max_v = np.nanmax(
+                    list(itertools.zip_longest(*maxs, fillvalue=np.nan)),
+                    axis=1,
+                )
 
         min_vals = np.nan_to_num(min_v[::-1], nan=0)
         max_vals = np.nan_to_num(max_v[::-1], nan=512)
@@ -318,8 +330,6 @@ def save(
         layers = self.selected if selected else list(self)
 
         if not layers:
-            import warnings
-
             warnings.warn(f"No layers {'selected' if selected else 'to save'}")
             return []
 


Test patch:
Repository: napari/napari

Issue:
interaction between visibility and contrast limits
## 🐛 Bug

I've just found a funny bug where there appears to be an interaction between the visibility and contrast limits

## To Reproduce

Steps to reproduce the behavior:

1.  napari.view_image(np.random.random((200, 200)), contrast_limits=[0, 10])
2. move clim slider to be [0, 2]
3. toggle visibility off/ on
4. notice that after toggle image is very saturated. adjusting slider a tiny bit causes image to look good again

![clim_bug](https://user-images.githubusercontent.com/6531703/91382297-4be0c880-e7de-11ea-9812-9a82e3c92a65.gif)

## Environment

last commit https://github.com/napari/napari/commit/62a046de2e5009f0c9137e3fd3999fbf902928ca



Discussion:
How do you control visibility on the vispy level? Does anything change on the underlying ImageVisual object?
> How do you control visibility on the vispy level? Does anything change on the underlying ImageVisual object?

Just with the setting the `visible` property - see here https://github.com/napari/napari/blob/master/napari/_vispy/vispy_base_layer.py#L142 where `node` is the `ImageVisual`

Not sure if it affects both `ImageVisual` and `VolumeVisual`, need to check.

If I had to guess, I'd guess with > 90% confidence this problem would reproduce in vispy alone just by toggling visibility too

Resolving patch:
diff --git a/napari/_vispy/vendored/image.py b/napari/_vispy/vendored/image.py
--- a/napari/_vispy/vendored/image.py
+++ b/napari/_vispy/vendored/image.py
@@ -513,6 +513,7 @@ def _build_texture(self):
                 self._clim = (0, 1)
 
         self._texture_limits = np.array(self._clim)
+        self._need_colortransform_update = True
         self._texture.set_data(data)
         self._need_texture_upload = False
 


Test patch:
diff --git a/napari/_vispy/_tests/test_image_rendering.py b/napari/_vispy/_tests/test_image_rendering.py
--- a/napari/_vispy/_tests/test_image_rendering.py
+++ b/napari/_vispy/_tests/test_image_rendering.py
@@ -1,4 +1,8 @@
+import os
+import sys
+
 import numpy as np
+import pytest
 
 
 def test_image_rendering(make_test_viewer):
@@ -35,3 +39,25 @@ def test_image_rendering(make_test_viewer):
     # Change rendering property
     layer.rendering = 'additive'
     assert layer.rendering == 'additive'
+
+
+@pytest.mark.skipif(
+    sys.platform.startswith('win') or not os.getenv("CI"),
+    reason='Screenshot tests are not supported on napari windows CI.',
+)
+def test_visibility_consistency(qtbot, make_test_viewer):
+    """Make sure toggling visibility maintains image contrast.
+
+    see #1622 for details.
+    """
+    viewer = make_test_viewer(show=True)
+
+    layer = viewer.add_image(
+        np.random.random((200, 200)), contrast_limits=[0, 10]
+    )
+    qtbot.wait(10)
+    layer.contrast_limits = (0, 2)
+    screen1 = viewer.screenshot().astype('float')
+    layer.visible = True
+    screen2 = viewer.screenshot().astype('float')
+    assert np.max(np.abs(screen2 - screen1)) < 5
Repository: napari/napari

Issue:
pan/zoom broken for multiscale images with scale= parameter
## 🐛 Bug

When viewing a multiscale image, setting the scale on the viewed dimensions to anything other than 1, 1 causes the pan/zoom to break.

```python
import napari
import dask.array as da

with napari.gui_qt():
    v = napari.view_image(
        [da.random.random((10000, 10000)), da.random.random((5000, 5000))],
        scale=[10, 10],
    )
```

See the screengrab video. The jumps are just me scrolling.

https://imgur.com/a/UpzyOC6

When you click and drag, the coordinates displayed in the status bar change while dragging. 🤷 


Discussion:

Resolving patch:
diff --git a/napari/layers/image/image.py b/napari/layers/image/image.py
--- a/napari/layers/image/image.py
+++ b/napari/layers/image/image.py
@@ -555,9 +555,7 @@ def _set_view_slice(self):
                         corner_pixels[0, d], corner_pixels[1, d] + 1, 1
                     )
                 self._transforms['tile2data'].translate = (
-                    corner_pixels[0]
-                    * self._transforms['data2world'].scale
-                    * self._transforms['tile2data'].scale
+                    corner_pixels[0] * self._transforms['tile2data'].scale
                 )
 
             image = self.data[level][tuple(indices)]


Test patch:
Repository: napari/napari

Issue:
3D scale bar length when adjusting window size
## 🐛 Bug

The scale bar no longer has the right length when adjusting the window size in 3D rendering mode, due to a hard coded value in #1720 that is wrong. This should be fixed in #854 by an accurate camera model.
3D scale bar length when adjusting window size
## 🐛 Bug

The scale bar no longer has the right length when adjusting the window size in 3D rendering mode, due to a hard coded value in #1720 that is wrong. This should be fixed in #854 by an accurate camera model.


Discussion:

Resolving patch:
diff --git a/examples/scale_bar.py b/examples/scale_bar.py
new file mode 100644
--- /dev/null
+++ b/examples/scale_bar.py
@@ -0,0 +1,12 @@
+"""
+Display a 3D volume and the scale bar
+"""
+import numpy as np
+import napari
+
+
+with napari.gui_qt():
+    np.random.seed(0)
+    viewer = napari.Viewer()
+    viewer.add_image(np.random.random((5, 5, 5)), colormap='red', opacity=0.8)
+    viewer.scale_bar.visible = True
diff --git a/napari/_qt/qt_viewer.py b/napari/_qt/qt_viewer.py
--- a/napari/_qt/qt_viewer.py
+++ b/napari/_qt/qt_viewer.py
@@ -520,16 +520,20 @@ def show_key_bindings_dialog(self, event=None):
     def _canvas2world_scale(self):
         """list: Scale factors from canvas pixels to world coordinates."""
         if isinstance(self.view.camera, PanZoomCamera):
-            return 1 / self.view.camera.transform.scale[0]
+            scale_factor = 1 / self.view.camera.transform.scale[0]
         elif isinstance(self.view.camera, ArcballCamera):
-            # Note magic number 598 is empirically determined so that
-            # Arcball camera and PanZoomCamera match
-            scale_factor = self.view.camera.scale_factor / 598
-            return scale_factor
+            # For fov = 0.0 normalize scale factor by canvas size to get scale factor.
+            # Note that the scaling is stored in the `_projection` property of the
+            # camera which is updated in vispy here
+            # https://github.com/vispy/vispy/blob/v0.6.5/vispy/scene/cameras/perspective.py#L301-L313
+            scale_factor = self.view.camera.scale_factor / np.min(
+                self.view.canvas.size
+            )
         else:
             raise ValueError(
                 f'Camera type {type(self.view.camera)} not recognized'
             )
+        return scale_factor
 
     def _map_canvas2world(self, position):
         """Map position from canvas pixels into world coordinates.


Test patch:
Repository: napari/napari

Issue:
World coordinates origin off in 3D
## 🐛 Bug

The world coordinates origin is off when toggling between 2D and 3D. This can be really clearly seen when running 
`python examples/affine_transforms.py` and toggling the number of displayed dimensions. See how the images move relative to the squares.

![world_coordinates_volume](https://user-images.githubusercontent.com/6531703/96400337-4de35a00-1185-11eb-93e6-c60c3b936843.gif)



Discussion:

Resolving patch:
diff --git a/napari/_vispy/vispy_base_layer.py b/napari/_vispy/vispy_base_layer.py
--- a/napari/_vispy/vispy_base_layer.py
+++ b/napari/_vispy/vispy_base_layer.py
@@ -127,9 +127,11 @@ def _on_matrix_change(self, event=None):
         affine_matrix[: matrix.shape[0], : matrix.shape[1]] = matrix
         affine_matrix[-1, : len(translate)] = translate
 
-        if self._array_like:
+        if self._array_like and self.layer.dims.ndisplay == 2:
             # Perform pixel offset to shift origin from top left corner
-            # of pixel to center of pixel
+            # of pixel to center of pixel.
+            # Note this offset is only required for array like data in
+            # 2D.
             offset_matrix = (
                 self.layer._transforms['data2world']
                 .set_slice(self.layer.dims.displayed)


Test patch:
Repository: napari/napari

Issue:
Text in welcome visual is too dark on non-macOS platforms
## 🐛 Bug

The welcome text is almost unreadable on Windows or Linux:

![signal-2020-10-28-132757](https://user-images.githubusercontent.com/492549/97384445-d3080500-1923-11eb-99d3-9a3e495635c3.jpeg)

![IMG_5476](https://user-images.githubusercontent.com/492549/97384469-ddc29a00-1923-11eb-9038-81124f3dd874.jpg)

It's fine on mac:

<img width="1200" alt="Screen-Shot-2020-10-27-at-7 12 45-PM" src="https://user-images.githubusercontent.com/492549/97384494-e7e49880-1923-11eb-9286-90f141bd0827.png">

@sofroniewn noted that he had to make the text darker for it to appear normal on mac. It appears that this is due to some bug in macOS graphics — the workaround should be applied only on mac.


Discussion:

Resolving patch:
diff --git a/napari/_vispy/vispy_welcome_visual.py b/napari/_vispy/vispy_welcome_visual.py
--- a/napari/_vispy/vispy_welcome_visual.py
+++ b/napari/_vispy/vispy_welcome_visual.py
@@ -1,3 +1,4 @@
+import sys
 from os.path import dirname, join
 
 import numpy as np
@@ -64,9 +65,17 @@ def _on_palette_change(self, event):
             background_color = np.divide(
                 str_to_rgb(darken(self._viewer.palette['background'], 70)), 255
             )
-            # Note this unsual scaling is done to preserve color balance on
-            # rendering by VisPy, which appears to be off when opacity < 1
-            text_color = np.multiply(foreground_color, [0.4, 0.65, 0.9])
+            text_color = foreground_color
+            if sys.platform == 'darwin':
+                # Note this unsual scaling is done to preserve color balance on
+                # rendering by VisPy, which appears to be off when opacity < 1.
+                # It only needs to be done on a mac, where we need opacity < 1
+                # to achieve good blending.
+                text_color = np.multiply(text_color, [0.4, 0.65, 0.9])
+                text_color = list(text_color) + [0.7]
+            else:
+                text_color = list(text_color) + [1]
+
         else:
             foreground_color = np.divide(
                 str_to_rgb(lighten(self._viewer.palette['foreground'], 30)),
@@ -92,9 +101,8 @@ def _on_palette_change(self, event):
 
         self._logo = new_logo
         self.node.set_data(self._logo)
-        # Having opacity < 1 improves blending but throws color balance
-        # off which needs to be adjusted if desired
-        self.text_node.color = list(text_color) + [0.7]
+
+        self.text_node.color = text_color
 
     def _on_visible_change(self, event):
         """Change visibiliy of axes."""


Test patch:
Repository: napari/napari

Issue:
Scale bar and axes are not scaled properly on init
## 🐛 Bug

When I make the scale bar and/or the axes visible via the GUI, they initially show up super tiny. Then when I zoom a bit (one "click" on my mouse wheel), they become normally sized.

## To Reproduce

Steps to reproduce the behavior:

1. Open the `nD_labeyls.py` example
2. From menus make the axes and scale bar visible (View -> Axes / Scale bar-> Visible). They now visible, but tiny in the upper left hand and lower right hand corners, respectively.
![axes](https://user-images.githubusercontent.com/1120672/97414310-1ce0f300-1904-11eb-98a3-995f31a02c89.png)


3. Change the zoom. The axes and scale bar are now "normally" sized. If I return to the original zoom level, they maintain their size.
![axes_2](https://user-images.githubusercontent.com/1120672/97414337-266a5b00-1904-11eb-8a50-2aefa321adaf.png)

## Expected behavior
They are initialized with the correct size.

## Environment

napari: 0.3.9.dev27+g52cc6cd.d20201006
Platform: Linux-5.4.0-52-generic-x86_64-with-debian-bullseye-sid
Python: 3.7.7 (default, May 7 2020, 21:25:33) [GCC 7.3.0]
Qt: 5.14.2
PyQt5: 5.14.2
NumPy: 1.19.1
SciPy: 1.5.2
Dask: 2.24.0
VisPy: 0.6.4

GL version: 4.6.0 NVIDIA 450.66
MAX_TEXTURE_SIZE: 32768

Plugins:
- napari_plugin_engine: 0.1.7
- svg: 0.1.4




Discussion:

Resolving patch:
diff --git a/napari/_vispy/vispy_axes_visual.py b/napari/_vispy/vispy_axes_visual.py
--- a/napari/_vispy/vispy_axes_visual.py
+++ b/napari/_vispy/vispy_axes_visual.py
@@ -66,6 +66,7 @@ def __init__(self, axes, camera, dims, parent=None, order=0):
         self._axes = axes
         self._dims = dims
         self._camera = camera
+        self._scale = 1
 
         # note order is z, y, x
         self._default_data = np.array(
@@ -127,11 +128,10 @@ def __init__(self, axes, camera, dims, parent=None, order=0):
         self._on_visible_change(None)
         self._on_data_change(None)
 
-        self._scale = 1
-
     def _on_visible_change(self, event):
         """Change visibiliy of axes."""
         self.node.visible = self._axes.visible
+        self._on_zoom_change(None)
 
     def _on_data_change(self, event):
         """Change style of axes."""
diff --git a/napari/_vispy/vispy_scale_bar_visual.py b/napari/_vispy/vispy_scale_bar_visual.py
--- a/napari/_vispy/vispy_scale_bar_visual.py
+++ b/napari/_vispy/vispy_scale_bar_visual.py
@@ -115,6 +115,7 @@ def _on_visible_change(self, event):
         """Change visibiliy of scale bar."""
         self.node.visible = self._scale_bar.visible
         self.text_node.visible = self._scale_bar.visible
+        self._on_zoom_change(None)
 
     def _on_position_change(self, event):
         """Change position of scale bar."""


Test patch:
Repository: napari/napari

Issue:
GUI package installer shows each package twice after installation
## 🐛 Bug

The installed packages list in the GUI displays each installed plugin twice, including the built in SVG.

![image](https://user-images.githubusercontent.com/17995243/98755453-b20fdb80-241c-11eb-9b21-006e18e44028.png)

Removing one of the plugins removes both instances from the list.

## To Reproduce

Steps to reproduce the behavior:

1. Install latest version of napari from master
2. Install some plugins, whether through GUI or command line (behaviour is the same)
3. Click Plugins>Install/Uninstall Package(s)...

## Expected behavior

Each plugin only shows up once when installed.

## Environment

 ```
napari: 0.4.1a1
Platform: Windows-10-10.0.18362-SP0
Python: 3.8.5 (default, Sep 3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)]
Qt: 5.15.1
PySide2: 5.15.1
NumPy: 1.19.4
SciPy: 1.5.4
Dask: 2.30.0
VisPy: 0.6.5

GL version: 4.6.0 - Build 26.20.100.7870
MAX_TEXTURE_SIZE: 16384

Plugins:
- napari_plugin_engine: 0.1.8
- nd2-reader: 0.0.3
- svg: 0.1.4
- tracks: 0.0.1
```

 - I've reproduced this in three fresh conda environments on Windows



Discussion:

Resolving patch:
diff --git a/napari/plugins/__init__.py b/napari/plugins/__init__.py
--- a/napari/plugins/__init__.py
+++ b/napari/plugins/__init__.py
@@ -11,12 +11,6 @@
     sys.path.append(user_site_packages())
 
 
-if os.name == 'nt':
-    # This is where plugins will be in bundled apps on windows
-    exe_dir = os.path.dirname(sys.executable)
-    winlib = os.path.join(exe_dir, "Lib", "site-packages")
-    sys.path.append(winlib)
-
 # the main plugin manager instance for the `napari` plugin namespace.
 plugin_manager = PluginManager(
     'napari', discover_entry_point='napari.plugin', discover_prefix='napari_'


Test patch:
Repository: napari/napari

Issue:
Paintbrush size off on initialization
## 🐛 Bug

Merging #1955 has caused a regression where paintbrush size is off on initialization. If you move the brush size once then everything works. I will look into fixing! Sorry about that!! Not sure if other initializations are off right now too.



Discussion:

Resolving patch:
diff --git a/napari/components/viewer_model.py b/napari/components/viewer_model.py
--- a/napari/components/viewer_model.py
+++ b/napari/components/viewer_model.py
@@ -411,6 +411,7 @@ def _update_active_layer(self, event):
             self.status = active_layer.status
             self.help = active_layer.help
             self.cursor.style = active_layer.cursor
+            self.cursor.size = active_layer.cursor_size
             self.interactive = active_layer.interactive
             self.active_layer = active_layer
 
diff --git a/napari/layers/base/base.py b/napari/layers/base/base.py
--- a/napari/layers/base/base.py
+++ b/napari/layers/base/base.py
@@ -186,7 +186,7 @@ def __init__(
         self._status = 'Ready'
         self._help = ''
         self._cursor = 'standard'
-        self._cursor_size = None
+        self._cursor_size = 1
         self._interactive = True
         self._value = None
         self.scale_factor = 1


Test patch:
diff --git a/napari/components/_tests/test_viewer_model.py b/napari/components/_tests/test_viewer_model.py
--- a/napari/components/_tests/test_viewer_model.py
+++ b/napari/components/_tests/test_viewer_model.py
@@ -534,6 +534,24 @@ def test_active_layer_status_update():
     assert viewer.status == viewer.active_layer.status
 
 
+def test_active_layer_cursor_size():
+    """Test cursor size update on active layer."""
+    viewer = ViewerModel()
+    np.random.seed(0)
+    viewer.add_image(np.random.random((10, 10)))
+    # Base layer has a default cursor size of 1
+    assert viewer.cursor.size == 1
+
+    viewer.add_labels(np.random.random((10, 10)))
+    assert len(viewer.layers) == 2
+    assert viewer.active_layer == viewer.layers[1]
+
+    viewer.layers[1].mode = 'paint'
+    # Labels layer has a default cursor size of 10
+    # due to paintbrush
+    assert viewer.cursor.size == 10
+
+
 def test_sliced_world_extent():
     """Test world extent after adding layers and slicing."""
     np.random.seed(0)
Repository: napari/napari

Issue:
Console no longer working
## 🐛 Bug

I think we've got a regression, from #2036 where the console is no longer visible when opened with the button in the viewer.

<img width="1200" alt="Screen Shot 2021-01-01 at 3 34 47 PM" src="https://user-images.githubusercontent.com/6531703/103447936-ec407100-4c46-11eb-8487-76cf63aca2a6.png">

I just see the above. I havn't looked into fix yet, but was able to narrow to that commit. I imagine it will be pretty simple fix. cc @tlambert03 



Discussion:

Resolving patch:
diff --git a/napari/_qt/qt_viewer.py b/napari/_qt/qt_viewer.py
--- a/napari/_qt/qt_viewer.py
+++ b/napari/_qt/qt_viewer.py
@@ -295,7 +295,7 @@ def console(self):
     @console.setter
     def console(self, console):
         self._console = console
-        self.dockConsole.widget = console
+        self.dockConsole.setWidget(console)
         self._update_theme()
 
     def _constrain_width(self, event):


Test patch:
diff --git a/napari/_qt/_tests/test_qt_viewer.py b/napari/_qt/_tests/test_qt_viewer.py
--- a/napari/_qt/_tests/test_qt_viewer.py
+++ b/napari/_qt/_tests/test_qt_viewer.py
@@ -39,7 +39,7 @@ def test_qt_viewer_with_console(make_test_viewer):
     assert view._console is None
     # Check console is created when requested
     assert view.console is not None
-    assert view.dockConsole.widget == view.console
+    assert view.dockConsole.widget() is view.console
 
 
 def test_qt_viewer_toggle_console(make_test_viewer):
@@ -51,7 +51,7 @@ def test_qt_viewer_toggle_console(make_test_viewer):
     # Check console has been created when it is supposed to be shown
     view.toggle_console_visibility(None)
     assert view._console is not None
-    assert view.dockConsole.widget == view.console
+    assert view.dockConsole.widget() is view.console
 
 
 @pytest.mark.parametrize('layer_class, data, ndim', layer_test_data)