Repository: amaranth-lang/amaranth
Pull request #1228 (2024-03-22 21:22:40)

Finish `lib.memory` documentation
This PR carries two "minor" semantic changes as well:
- Removing `Memory.Init.depth`.
- Renaming `Memory.{r,w}_ports` to `.{read,write}_ports`.

See the commit messages for details.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/1228?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) Report
Attention: Patch coverage is `98.83721%` with `2 lines` in your changes are missing coverage. Please review.
> Project coverage is 89.75%. Comparing base [(`8c65a79`)](https://app.codecov.io/gh/amaranth-lang/amaranth/commit/8c65a79cdd2d1841992bbc1c6b7948ba697ae712?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) to head [(`a338648`)](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/1228?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang).
> Report is 5 commits behind head on main.

| [Files](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/1228?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) | Patch % | Lines |
|---|---|---|
| [amaranth/lib/memory.py](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/1228?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang#diff-YW1hcmFudGgvbGliL21lbW9yeS5weQ==) | 98.83% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/1228?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1228      +/-   ##
==========================================
+ Coverage   89.65%   89.75%   +0.09%     
==========================================
  Files          43       43              
  Lines        9718     9923     +205     
  Branches     2331     2395      +64     
==========================================
+ Hits         8713     8906     +193     
- Misses        816      825       +9     
- Partials      189      192       +3     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/1228?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang).


Patch:
diff --git a/amaranth/lib/memory.py b/amaranth/lib/memory.py
index dc7aed3..298cb44 100644
--- a/amaranth/lib/memory.py
+++ b/amaranth/lib/memory.py
@@ -5,216 +5,301 @@ from collections.abc import MutableSequence
 from ..hdl import MemoryIdentity, MemoryInstance, Shape, ShapeCastable, Const
 from ..hdl._mem import MemorySimRead
 from ..utils import ceil_log2
-from .data import ArrayLayout
-from . import wiring
 from .. import tracer
+from . import wiring, data
 
 
-__all__ = ["WritePort", "ReadPort", "Memory"]
+__all__ = ["Memory", "ReadPort", "WritePort"]
 
 
-class WritePort:
-    """A memory write port.
+class Memory(wiring.Component):
+    """Addressable array of rows.
 
-    Parameters
-    ----------
-    signature : :class:`WritePort.Signature`
-        The signature of the port.
-    memory : :class:`Memory` or ``None``
-        Memory associated with the port.
-    domain : str
-        Clock domain. Defaults to ``"sync"``. Writes have a latency of 1 clock cycle.
+    This :ref:`component <wiring>` is used to construct a memory array by first specifying its
+    dimensions and initial contents using the :py:`shape`, :py:`depth`, and :py:`init` parameters,
+    and then adding memory ports using the :meth:`read_port` and :meth:`write_port` methods.
+    Because it is mutable, it should be created and used locally within
+    the :ref:`elaborate <lang-elaboration>` method.
 
-    Attributes
+    The :py:`init` parameter and assignment to the :py:`init` attribute have the same effect, with
+    :class:`Memory.Init` converting elements of the iterable to match :py:`shape` and using
+    a default value for rows that are not explicitly initialized.
+
+    .. warning::
+
+        Uninitialized memories (including ASIC memories and some FPGA memories) are
+        `not yet supported <https://github.com/amaranth-lang/amaranth/issues/270>`_, and
+        the :py:`init` parameter must be always provided, if only as :py:`init=[]`.
+
+    Parameters
     ----------
-    signature : :class:`WritePort.Signature`
-    memory : :class:`Memory`
-    domain : str
+    shape : :ref:`shape-like <lang-shapelike>` object
+        Shape of each memory row.
+    depth : :class:`int`
+        Number of memory rows.
+    init : iterable of initial values
+        Initial values for memory rows.
+
+    Platform overrides
+    ------------------
+    Define the :py:`get_memory()` platform method to override the implementation of
+    :class:`Memory`, e.g. to instantiate library cells directly.
     """
 
-    class Signature(wiring.Signature):
-        """A signature of a write port.
+    class Init(MutableSequence):
+        """Memory initialization data.
 
-        Parameters
-        ----------
-        addr_width : int
-            Address width in bits. If the port is associated with a :class:`Memory`,
-            it must be equal to :py:`ceil_log2(memory.depth)`.
-        shape : :ref:`shape-like <lang-shapelike>` object
-            The shape of the port data. If the port is associated with a :class:`Memory`,
-            it must be equal to its element shape.
-        granularity : int or ``None``
-            Port granularity. If ``None``, the entire storage element is written at once.
-            Otherwise, determines the size of access covered by a single bit of ``en``.
-            One of the following must hold:
+        This is a special container used only for the :attr:`Memory.init` attribute. It is similar
+        to :class:`list`, but does not support inserting or deleting elements; its length is always
+        the same as the depth of the memory it belongs to.
 
-            - ``granularity is None``, in which case ``en_width == 1``, or
-            - ``shape == unsigned(data_width)`` and ``data_width == 0 or data_width % granularity == 0`` in which case ``en_width == data_width // granularity`` (or 0 if ``data_width == 0``)
-            - ``shape == amaranth.lib.data.ArrayLayout(_, elem_count)`` and ``elem_count == 0 or elem_count % granularity == 0`` in which case ``en_width == elem_count // granularity`` (or 0 if ``elem_count == 0``)
+        If :py:`shape` is a :ref:`custom shape-castable object <lang-shapecustom>`, then:
 
-        Members
-        -------
-        addr: :py:`unsigned(data_width)`
-        data: ``shape``
-        en: :py:`unsigned(en_width)`
-        """
+        * Each element must be convertible to :py:`shape` via :meth:`.ShapeCastable.const`, and
+        * Elements that are not explicitly initialized default to :py:`shape.const(None)`.
 
-        def __init__(self, *, addr_width, shape, granularity=None):
-            if not isinstance(addr_width, int) or addr_width < 0:
-                raise TypeError(f"`addr_width` must be a non-negative int, not {addr_width!r}")
-            self._addr_width = addr_width
-            self._shape = shape
-            self._granularity = granularity
-            if granularity is None:
-                en_width = 1
-            elif not isinstance(granularity, int) or granularity < 0:
-                raise TypeError(f"Granularity must be a non-negative int or None, not {granularity!r}")
-            elif not isinstance(shape, ShapeCastable):
-                actual_shape = Shape.cast(shape)
-                if actual_shape.signed:
-                    raise ValueError("Granularity cannot be specified with signed shape")
-                elif actual_shape.width == 0:
-                    en_width = 0
-                elif granularity == 0:
-                    raise ValueError("Granularity must be positive")
-                elif actual_shape.width % granularity != 0:
-                    raise ValueError("Granularity must divide data width")
-                else:
-                    en_width = actual_shape.width // granularity
-            elif isinstance(shape, ArrayLayout):
-                if shape.length == 0:
-                    en_width = 0
-                elif granularity == 0:
-                    raise ValueError("Granularity must be positive")
-                elif shape.length % granularity != 0:
-                    raise ValueError("Granularity must divide data array length")
-                else:
-                    en_width = shape.length // granularity
-            else:
-                raise TypeError("Granularity can only be specified for plain unsigned `Shape` or `ArrayLayout`")
-            super().__init__({
-                "addr": wiring.In(addr_width),
-                "data": wiring.In(shape),
-                "en": wiring.In(en_width),
-            })
+        Otherwise (when :py:`shape` is a :class:`.Shape`):
 
-        def create(self, *, path=None, src_loc_at=0):
-            return WritePort(self, memory=None, domain="sync", path=path, src_loc_at=1 + src_loc_at)
+        * Each element must be an :class:`int`, and
+        * Elements that are not explicitly initialized default to :py:`0`.
+        """
+        def __init__(self, elems, *, shape, depth):
+            Shape.cast(shape)
+            if not isinstance(depth, int) or depth < 0:
+                raise TypeError("Memory depth must be a non-negative integer, not {!r}"
+                                .format(depth))
+            self._shape = shape
+            self._depth = depth
 
-        @property
-        def addr_width(self):
-            return self._addr_width
+            if isinstance(shape, ShapeCastable):
+                self._elems = [None] * depth
+                self._raw = [Const.cast(shape.const(None)).value] * depth
+            else:
+                self._elems = [0] * depth
+                self._raw = self._elems # intentionally mutably aliased
+            try:
+                for index, item in enumerate(elems):
+                    self[index] = item
+            except (TypeError, ValueError) as e:
+                raise type(e)("Memory initialization value at address {:x}: {}"
+                              .format(index, e)) from None
 
         @property
         def shape(self):
             return self._shape
 
-        @property
-        def granularity(self):
-            return self._granularity
+        def __getitem__(self, index):
+            return self._elems[index]
 
-        def __eq__(self, other):
-            return (type(self) is type(other) and
-                    self.addr_width == other.addr_width and
-                    self.shape == other.shape and
-                    self.granularity == other.granularity)
+        def __setitem__(self, index, value):
+            if isinstance(index, slice):
+                indices = range(*index.indices(len(self._elems)))
+                if len(value) != len(indices):
+                    raise ValueError("Changing length of Memory.init is not allowed")
+                for actual_index, actual_value in zip(indices, value):
+                    self[actual_index] = actual_value
+            else:
+                if isinstance(self._shape, ShapeCastable):
+                    self._raw[index] = Const.cast(self._shape.const(value)).value
+                else:
+                    value = operator.index(value)
+                    # self._raw[index] assigned by the following line
+                self._elems[index] = value
+
+        def __delitem__(self, index):
+            raise TypeError("Deleting elements from Memory.init is not allowed")
+
+        def insert(self, index, value):
+            """:meta private:"""
+            raise TypeError("Inserting elements into Memory.init is not allowed")
+
+        def __len__(self):
+            return self._depth
 
         def __repr__(self):
-            granularity = f", granularity={self.granularity}" if self.granularity is not None else ""
-            return f"WritePort.Signature(addr_width={self.addr_width}, shape={self.shape}{granularity})"
+            return f"Memory.Init({self._elems!r}, shape={self._shape!r}, depth={self._depth})"
 
 
-    def __init__(self, signature, *, memory, domain, path=None, src_loc_at=0):
-        if not isinstance(signature, WritePort.Signature):
-            raise TypeError(f"Expected `WritePort.Signature`, not {signature!r}")
-        if memory is not None:
-            if not isinstance(memory, Memory):
-                raise TypeError(f"Expected `Memory` or `None`, not {memory!r}")
-            if signature.shape != memory.shape or Shape.cast(signature.shape) != Shape.cast(memory.shape):
-                raise ValueError(f"Memory shape {memory.shape!r} doesn't match port shape {signature.shape!r}")
-            if signature.addr_width != ceil_log2(memory.depth):
-                raise ValueError(f"Memory address width {ceil_log2(memory.depth)!r} doesn't match port address width {signature.addr_width!r}")
-        if not isinstance(domain, str):
-            raise TypeError(f"Domain has to be a string, not {domain!r}")
-        if domain == "comb":
-            raise ValueError("Write port domain cannot be \"comb\"")
-        self._signature = signature
-        self._memory = memory
-        self._domain = domain
-        self.__dict__.update(signature.members.create(path=path, src_loc_at=1 + src_loc_at))
-        if memory is not None:
-            memory._w_ports.append(self)
+    def __init__(self, *, shape, depth, init, attrs=None, src_loc_at=0):
+        # shape and depth validation is performed in Memory.Init()
+        self._shape = shape
+        self._depth = depth
+        self._init = Memory.Init(init, shape=shape, depth=depth)
+        self._attrs = {} if attrs is None else dict(attrs)
+        self.src_loc = tracer.get_src_loc(src_loc_at=src_loc_at)
+
+        self._identity = MemoryIdentity()
+        self._read_ports: "list[ReadPort]" = []
+        self._write_ports: "list[WritePort]" = []
+
+        super().__init__(wiring.Signature({}))
 
     @property
-    def signature(self):
-        return self._signature
+    def shape(self):
+        return self._shape
 
     @property
-    def memory(self):
-        return self._memory
+    def depth(self):
+        return self._depth
 
     @property
-    def domain(self):
-        return self._domain
+    def init(self):
+        return self._init
+
+    @init.setter
+    def init(self, init):
+        self._init = Memory.Init(init, shape=self._shape, depth=self._depth)
+
+    @property
+    def attrs(self):
+        return self._attrs
+
+    def read_port(self, *, domain="sync", transparent_for=(), src_loc_at=0):
+        """Request a read port.
+
+        If :py:`domain` is :py:`"comb"`, the created read port is asynchronous and always enabled
+        (with its enable input is tied to :py:`Const(1)`), and its data output always reflects
+        the contents of the selected row. Otherwise, the created read port is synchronous,
+        and its data output is updated with the contents of the selected row at each
+        :ref:`active edge <lang-sync>` of :py:`domain` where the enable input is asserted.
+
+        The :py:`transparent_for` parameter specifies the *transparency set* of this port: zero or
+        more :class:`WritePort`\\ s, all of which must belong to the same memory and clock domain.
+        If another port writes to a memory row at the same time as this port reads from the same
+        memory row, and that write port is a part of the transparency set, then this port retrieves
+        the new contents of the row; otherwise, this port retrieves the old contents of the row.
+
+        If another write port belonging to a different clock domain updates a memory row that this
+        port is reading at the same time, the behavior is undefined.
+
+        The signature of the returned port is
+        :py:`ReadPort.Signature(shape=self.shape, addr_width=ceil_log2(self.depth))`.
+
+        Returns
+        -------
+        :class:`ReadPort`
+        """
+        signature = ReadPort.Signature(shape=self.shape, addr_width=ceil_log2(self.depth))
+        return ReadPort(signature, memory=self, domain=domain, transparent_for=transparent_for,
+                        src_loc_at=1 + src_loc_at)
+
+    def write_port(self, *, domain="sync", granularity=None, src_loc_at=0):
+        """Request a write port.
+
+        The created write port is synchronous, updating the contents of the selected row at each
+        :ref:`active edge <lang-sync>` of :py:`domain` where the enable input is asserted.
+
+        Specifying a *granularity* when :py:`shape` is :func:`unsigned(width) <.unsigned>` or
+        :class:`data.ArrayLayout(_, width) <.data.ArrayLayout>` makes it possible to partially
+        update a memory row. In this case, :py:`granularity` must be an integer that evenly divides
+        :py:`width`, and the memory row is split into :py:`width // granularity` equally sized
+        parts, each of which is updated if the corresponding bit of the enable input is asserted.
+
+        The signature of the new port is
+        :py:`WritePort.Signature(shape=self.shape, addr_width=ceil_log2(self.depth), granularity=granularity)`.
+
+        Returns
+        -------
+        :class:`WritePort`
+        """
+        signature = WritePort.Signature(
+            shape=self.shape, addr_width=ceil_log2(self.depth), granularity=granularity)
+        return WritePort(signature, memory=self, domain=domain,
+                         src_loc_at=1 + src_loc_at)
+
+    @property
+    def read_ports(self):
+        """All read ports defined so far.
+
+        This property is provided for the :py:`platform.get_memory()` override.
+        """
+        return tuple(self._read_ports)
+
+    @property
+    def write_ports(self):
+        """All write ports defined so far.
+
+        This property is provided for the :py:`platform.get_memory()` override.
+        """
+        return tuple(self._write_ports)
+
+    def elaborate(self, platform):
+        if hasattr(platform, "get_memory"):
+            return platform.get_memory(self)
+
+        shape = Shape.cast(self.shape)
+        instance = MemoryInstance(identity=self._identity, width=shape.width, depth=self.depth,
+                                  init=self.init._raw, attrs=self.attrs, src_loc=self.src_loc)
+        write_ports = {}
+        for port in self._write_ports:
+            write_ports[port] = instance.write_port(
+                domain=port.domain, addr=port.addr, data=port.data, en=port.en)
+        for port in self._read_ports:
+            transparent_for = tuple(write_ports[write_port] for write_port in port.transparent_for)
+            instance.read_port(
+                domain=port.domain, data=port.data, addr=port.addr, en=port.en,
+                transparent_for=transparent_for)
+        return instance
+
+    def __getitem__(self, index):
+        """Simulation only."""
+        return MemorySimRead(self._identity, index)
 
 
 class ReadPort:
-    """A memory read port.
+    """A read memory port.
 
-    Parameters
-    ----------
-    signature : :class:`ReadPort.Signature`
-        The signature of the port.
-    memory : :class:`Memory`
-        Memory associated with the port.
-    domain : str
-        Clock domain. Defaults to ``"sync"``. If set to ``"comb"``, the port is asynchronous.
-        Otherwise, the read data becomes available on the next clock cycle.
-    transparent_for : iterable of :class:`WritePort`
-        The set of write ports that this read port should be transparent with. All ports
-        must belong to the same memory and the same clock domain.
+    Memory read ports, which are :ref:`interface objects <wiring>`, can be constructed by calling
+    :meth:`Memory.read_port` or via :meth:`ReadPort.Signature.create() <.Signature.create>`.
+
+    An asynchronous (:py:`"comb"` domain) memory read port is always enabled. The :py:`en` input of
+    such a port is tied to :py:`Const(1)`.
 
     Attributes
     ----------
     signature : :class:`ReadPort.Signature`
-    memory : :class:`Memory`
-    domain : str
-    transparent_for : tuple of :class:`WritePort`
+        Signature of this memory port.
+    memory : :class:`Memory` or :py:`None`
+        Memory associated with this memory port.
+    domain : :class:`str`
+        Name of this memory port's clock domain. For asynchronous ports, :py:`"comb"`.
+    transparent_for : :class:`tuple` of :class:`WritePort`
+        Transparency set of this memory port.
     """
 
     class Signature(wiring.Signature):
-        """A signature of a read port.
+        """Signature of a memory read port.
 
         Parameters
         ----------
-        addr_width : int
-            Address width in bits. If the port is associated with a :class:`Memory`,
-            it must be equal to :py:`ceil_log2(memory.depth)`.
+        addr_width : :class:`int`
+            Width of the address port.
         shape : :ref:`shape-like <lang-shapelike>` object
-            The shape of the port data. If the port is associated with a :class:`Memory`,
-            it must be equal to its element shape.
+            Shape of the data port.
 
         Members
         -------
-        addr: :py:`unsigned(data_width)`
-        data: ``shape``
-        en: :py:`unsigned(1)`
-            The enable signal. If ``domain == "comb"``, this is tied to ``Const(1)``.
-            Otherwise it is a signal with ``init=1``.
+        en: :py:`In(1, init=1)`
+            Enable input.
+        addr: :py:`In(addr_width)`
+            Address input.
+        data: :py:`Out(shape)`
+            Data output.
         """
 
         def __init__(self, *, addr_width, shape):
             if not isinstance(addr_width, int) or addr_width < 0:
-                raise TypeError(f"`addr_width` must be a non-negative int, not {addr_width!r}")
+                raise TypeError(f"Address width must be a non-negative integer, not {addr_width!r}")
             self._addr_width = addr_width
             self._shape = shape
             super().__init__({
+                "en": wiring.In(1, init=1),
                 "addr": wiring.In(addr_width),
                 "data": wiring.Out(shape),
-                "en": wiring.In(1, init=1),
             })
 
         def create(self, *, path=None, src_loc_at=0):
+            """:meta private:""" # work around Sphinx bug
             return ReadPort(self, memory=None, domain="sync", path=path, src_loc_at=1 + src_loc_at)
 
         @property
@@ -236,24 +321,27 @@ class ReadPort:
 
     def __init__(self, signature, *, memory, domain, transparent_for=(), path=None, src_loc_at=0):
         if not isinstance(signature, ReadPort.Signature):
-            raise TypeError(f"Expected `ReadPort.Signature`, not {signature!r}")
-        if memory is not None:
+            raise TypeError(f"Expected signature to be ReadPort.Signature, not {signature!r}")
+        if memory is not None: # may be None if created via `Signature.create()`
             if not isinstance(memory, Memory):
-                raise TypeError(f"Expected `Memory` or `None`, not {memory!r}")
-            if signature.shape != memory.shape or Shape.cast(signature.shape) != Shape.cast(memory.shape):
-                raise ValueError(f"Memory shape {memory.shape!r} doesn't match port shape {signature.shape!r}")
+                raise TypeError(f"Expected memory to be Memory or None, not {memory!r}")
+            if (signature.shape != memory.shape or
+                    Shape.cast(signature.shape) != Shape.cast(memory.shape)):
+                raise ValueError(f"Memory shape {memory.shape!r} doesn't match "
+                                 f"port shape {signature.shape!r}")
             if signature.addr_width != ceil_log2(memory.depth):
-                raise ValueError(f"Memory address width {ceil_log2(memory.depth)!r} doesn't match port address width {signature.addr_width!r}")
+                raise ValueError(f"Memory address width {ceil_log2(memory.depth)!r} doesn't match "
+                                 f"port address width {signature.addr_width!r}")
         if not isinstance(domain, str):
-            raise TypeError(f"Domain has to be a string, not {domain!r}")
+            raise TypeError(f"Domain must be a string, not {domain!r}")
         transparent_for = tuple(transparent_for)
         for port in transparent_for:
             if not isinstance(port, WritePort):
-                raise TypeError("`transparent_for` must contain only `WritePort` instances")
-            if memory is not None and port not in memory._w_ports:
-                raise ValueError("Transparent write ports must belong to the same memory")
+                raise TypeError("Transparency set must contain only WritePort instances")
+            if memory is not None and port not in memory._write_ports:
+                raise ValueError("Ports in transparency set must belong to the same memory")
             if port.domain != domain:
-                raise ValueError("Transparent write ports must belong to the same domain")
+                raise ValueError("Ports in transparency set must belong to the same domain")
         self._signature = signature
         self._memory = memory
         self._domain = domain
@@ -262,7 +350,7 @@ class ReadPort:
         if domain == "comb":
             self.en = Const(1)
         if memory is not None:
-            memory._r_ports.append(self)
+            memory._read_ports.append(self)
 
     @property
     def signature(self):
@@ -281,171 +369,154 @@ class ReadPort:
         return self._transparent_for
 
 
-class Memory(wiring.Component):
-    """A word addressable storage.
+class WritePort:
+    """A write memory port.
 
-    Parameters
-    ----------
-    shape : :ref:`shape-like <lang-shapelike>` object
-        The shape of a single element of the storage.
-    depth : int
-        Word count. This memory contains ``depth`` storage elements.
-    init : iterable of int or of any objects accepted by ``shape.const()``
-        Initial values. At power on, each storage element in this memory is initialized to
-        the corresponding element of ``init``, if any, or to the default value of ``shape`` otherwise.
-        Uninitialized memories are not currently supported.
-    attrs : dict
-        Dictionary of synthesis attributes.
+    Memory write ports, which are :ref:`interface objects <wiring>`, can be constructed by calling
+    :meth:`Memory.write_port` or via :meth:`WritePort.Signature.create() <.Signature.create>`.
 
     Attributes
     ----------
-    shape : :ref:`shape-like <lang-shapelike>`
-    depth : int
-    init : :class:`Memory.Init`
-    attrs : dict
-    r_ports : tuple of :class:`ReadPort`
-    w_ports : tuple of :class:`WritePort`
+    signature : :class:`WritePort.Signature`
+        Signature of this memory port.
+    memory : :class:`Memory` or :py:`None`
+        Memory associated with this memory port.
+    domain : :class:`str`
+        Name of this memory port's clock domain. Never :py:`"comb"`.
     """
 
-    class Init(MutableSequence):
-        """Initial data of a :class:`Memory`.
-
-        This is a container implementing the ``MutableSequence`` protocol, enforcing two constraints:
+    class Signature(wiring.Signature):
+        """Signature of a memory write port.
 
-        - the length is immutable and must equal ``depth``
-        - if ``shape`` is a :class:`ShapeCastable`, each element can be cast to ``shape`` via :py:`shape.const()`
-        - otherwise, each element is an :py:`int`
-        """
-        def __init__(self, items, *, shape, depth):
-            Shape.cast(shape)
-            if not isinstance(depth, int) or depth < 0:
-                raise TypeError("Memory depth must be a non-negative integer, not {!r}"
-                                .format(depth))
-            self._shape = shape
-            self._depth = depth
-            if isinstance(shape, ShapeCastable):
-                self._items = [None] * depth
-                default = Const.cast(shape.const(None)).value
-                self._raw = [default] * depth
-            else:
-                self._raw = self._items = [0] * depth
-            try:
-                for idx, item in enumerate(items):
-                    self[idx] = item
-            except (TypeError, ValueError) as e:
-                raise type(e)("Memory initialization value at address {:x}: {}"
-                                .format(idx, e)) from None
+        Width of the enable input is determined as follows:
 
-        def __getitem__(self, index):
-            return self._items[index]
+        * If :py:`granularity` is :py:`None`,
+          then :py:`en_width == 1`.
+        * If :py:`shape` is :func:`unsigned(data_width) <.unsigned>`,
+          then :py:`en_width == data_width // granularity`.
+        * If :py:`shape` is :class:`data.ArrayLayout(_, elem_count) <.data.ArrayLayout>`,
+          then :py:`en_width == elem_count // granularity`.
 
-        def __setitem__(self, index, value):
-            if isinstance(index, slice):
-                start, stop, step = index.indices(len(self._items))
-                indices = range(start, stop, step)
-                if len(value) != len(indices):
-                    raise ValueError("Changing length of Memory.init is not allowed")
-                for actual_index, actual_value in zip(indices, value):
-                    self[actual_index] = actual_value
-            else:
-                if isinstance(self._shape, ShapeCastable):
-                    self._raw[index] = Const.cast(self._shape.const(value)).value
-                else:
-                    value = operator.index(value)
-                self._items[index] = value
+        Parameters
+        ----------
+        addr_width : :class:`int`
+            Width of the address port.
+        shape : :ref:`shape-like <lang-shapelike>` object
+            Shape of the data port.
+        granularity : :class:`int` or :py:`None`
+            Granularity of memory access.
 
-        def __delitem__(self, index):
-            raise TypeError("Deleting items from Memory.init is not allowed")
+        Members
+        -------
+        en: :py:`In(en_width)`
+            Enable input.
+        addr: :py:`In(addr_width)`
+            Address input.
+        data: :py:`In(shape)`
+            Data input.
+        """
 
-        def insert(self, index, value):
-            raise TypeError("Inserting items into Memory.init is not allowed")
+        def __init__(self, *, addr_width, shape, granularity=None):
+            if not isinstance(addr_width, int) or addr_width < 0:
+                raise TypeError(f"Address width must be a non-negative integer, not {addr_width!r}")
+            self._addr_width = addr_width
+            self._shape = shape
+            self._granularity = granularity
+            if granularity is None:
+                en_width = 1
+            elif not isinstance(granularity, int) or granularity < 0:
+                raise TypeError(f"Granularity must be a non-negative integer or None, "
+                                f"not {granularity!r}")
+            elif not isinstance(shape, ShapeCastable):
+                actual_shape = Shape.cast(shape)
+                if actual_shape.signed:
+                    raise ValueError("Granularity cannot be specified for a memory with "
+                                     "a signed shape")
+                elif actual_shape.width == 0:
+                    en_width = 0
+                elif granularity == 0:
+                    raise ValueError("Granularity must be positive")
+                elif actual_shape.width % granularity != 0:
+                    raise ValueError("Granularity must evenly divide data width")
+                else:
+                    en_width = actual_shape.width // granularity
+            elif isinstance(shape, data.ArrayLayout):
+                if shape.length == 0:
+                    en_width = 0
+                elif granularity == 0:
+                    raise ValueError("Granularity must be positive")
+                elif shape.length % granularity != 0:
+                    raise ValueError("Granularity must evenly divide data array length")
+                else:
+                    en_width = shape.length // granularity
+            else:
+                raise TypeError("Granularity can only be specified for memories whose shape "
+                                "is unsigned or data.ArrayLayout")
+            super().__init__({
+                "en": wiring.In(en_width),
+                "addr": wiring.In(addr_width),
+                "data": wiring.In(shape),
+            })
 
-        def __len__(self):
-            return self._depth
+        def create(self, *, path=None, src_loc_at=0):
+            """:meta private:""" # work around Sphinx bug
+            return WritePort(self, memory=None, domain="sync", path=path, src_loc_at=1 + src_loc_at)
 
         @property
-        def depth(self):
-            return self._depth
+        def addr_width(self):
+            return self._addr_width
 
         @property
         def shape(self):
             return self._shape
 
-        def __repr__(self):
-            return f"Memory.Init({self._items!r})"
-
-    def __init__(self, *, depth, shape, init, attrs=None, src_loc_at=0, src_loc=None):
-        # shape and depth validation performed in Memory.Init constructor.
-        self._depth = depth
-        self._shape = shape
-        self._init = Memory.Init(init, shape=shape, depth=depth)
-        self._attrs = {} if attrs is None else dict(attrs)
-        self.src_loc = src_loc or tracer.get_src_loc(src_loc_at=src_loc_at)
-        self._identity = MemoryIdentity()
-        self._r_ports: "list[ReadPort]" = []
-        self._w_ports: "list[WritePort]" = []
-        super().__init__(wiring.Signature({}))
-
-    def read_port(self, *, domain="sync", transparent_for=()):
-        """Adds a new read port and returns it.
-
-        Equivalent to creating a :class:`ReadPort` with a signature of :py:`ReadPort.Signature(addr_width=ceil_log2(self.depth), shape=self.shape)`
-        """
-        signature = ReadPort.Signature(addr_width=ceil_log2(self.depth), shape=self.shape)
-        return ReadPort(signature, memory=self, domain=domain, transparent_for=transparent_for)
-
-    def write_port(self, *, domain="sync", granularity=None):
-        """Adds a new write port and returns it.
-
-        Equivalent to creating a :class:`WritePort` with a signature of :py:`WritePort.Signature(addr_width=ceil_log2(self.depth), shape=self.shape, granularity=granularity)`
-        """
-        signature = WritePort.Signature(addr_width=ceil_log2(self.depth), shape=self.shape, granularity=granularity)
-        return WritePort(signature, memory=self, domain=domain)
+        @property
+        def granularity(self):
+            return self._granularity
 
-    @property
-    def depth(self):
-        return self._depth
+        def __eq__(self, other):
+            return (type(self) is type(other) and
+                    self.addr_width == other.addr_width and
+                    self.shape == other.shape and
+                    self.granularity == other.granularity)
 
-    @property
-    def shape(self):
-        return self._shape
+        def __repr__(self):
+            granularity = f", granularity={self.granularity}" if self.granularity is not None else ""
+            return f"WritePort.Signature(addr_width={self.addr_width}, shape={self.shape}{granularity})"
 
-    @property
-    def init(self):
-        return self._init
 
-    @init.setter
-    def init(self, init):
-        self._init = Memory.Init(init, shape=self._shape, depth=self._depth)
+    def __init__(self, signature, *, memory, domain, path=None, src_loc_at=0):
+        if not isinstance(signature, WritePort.Signature):
+            raise TypeError(f"Expected signature to be WritePort.Signature, not {signature!r}")
+        if memory is not None: # may be None if created via `Signature.create()`
+            if not isinstance(memory, Memory):
+                raise TypeError(f"Expected memory to be Memory or None, not {memory!r}")
+            if (signature.shape != memory.shape or
+                    Shape.cast(signature.shape) != Shape.cast(memory.shape)):
+                raise ValueError(f"Memory shape {memory.shape!r} doesn't match "
+                                 f"port shape {signature.shape!r}")
+            if signature.addr_width != ceil_log2(memory.depth):
+                raise ValueError(f"Memory address width {ceil_log2(memory.depth)!r} doesn't match "
+                                 f"port address width {signature.addr_width!r}")
+        if not isinstance(domain, str):
+            raise TypeError(f"Domain must be a string, not {domain!r}")
+        if domain == "comb":
+            raise ValueError("Write ports cannot be asynchronous")
+        self._signature = signature
+        self._memory = memory
+        self._domain = domain
+        self.__dict__.update(signature.members.create(path=path, src_loc_at=1 + src_loc_at))
+        if memory is not None:
+            memory._write_ports.append(self)
 
     @property
-    def attrs(self):
-        return self._attrs
+    def signature(self):
+        return self._signature
 
     @property
-    def w_ports(self):
-        """Returns a tuple of all write ports defined so far."""
-        return tuple(self._w_ports)
+    def memory(self):
+        return self._memory
 
     @property
-    def r_ports(self):
-        """Returns a tuple of all read ports defined so far."""
-        return tuple(self._r_ports)
-
-    def elaborate(self, platform):
-        if hasattr(platform, "get_memory"):
-            return platform.get_memory(self)
-        shape = Shape.cast(self.shape)
-        instance = MemoryInstance(identity=self._identity, width=shape.width, depth=self.depth, init=self.init._raw, attrs=self.attrs, src_loc=self.src_loc)
-        w_ports = {}
-        for port in self._w_ports:
-            idx = instance.write_port(domain=port.domain, addr=port.addr, data=port.data, en=port.en)
-            w_ports[port] = idx
-        for port in self._r_ports:
-            transparent_for = [w_ports[write_port] for write_port in port.transparent_for]
-            instance.read_port(domain=port.domain, data=port.data, addr=port.addr, en=port.en, transparent_for=transparent_for)
-        return instance
-
-    def __getitem__(self, index):
-        """Simulation only."""
-        return MemorySimRead(self._identity, index)
+    def domain(self):
+        return self._domain
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
index 69a6288..053d59e 100644
--- a/docs/_static/custom.css
+++ b/docs/_static/custom.css
@@ -20,3 +20,6 @@ a { text-decoration: underline; }
 
 /* Many of our diagnostics are even longer */
 .rst-content pre.literal-block, .rst-content div[class^="highlight"] pre, .rst-content .linenodiv pre { white-space: pre-wrap; }
+
+/* Work around https://github.com/readthedocs/sphinx_rtd_theme/issues/1301 */
+.py.property { display: block !important; }
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index 71b71d8..177f454 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -39,7 +39,11 @@ napoleon_numpy_docstring = True
 napoleon_use_ivar = True
 napoleon_include_init_with_doc = True
 napoleon_include_special_with_doc = True
-napoleon_custom_sections = ["Platform overrides"]
+napoleon_custom_sections = [
+    ("Attributes", "params_style"), # by default displays as "Variables", which is confusing
+    ("Members", "params_style"), # `lib.wiring` signature members
+    "Platform overrides"
+]
 
 html_theme = "sphinx_rtd_theme"
 html_static_path = ["_static"]
diff --git a/docs/stdlib/_images/memory/example_fifo.svg b/docs/stdlib/_images/memory/example_fifo.svg
new file mode 100644
index 0000000..2b3222c
--- /dev/null
+++ b/docs/stdlib/_images/memory/example_fifo.svg
@@ -0,0 +1,41 @@
+<!--
+Render the following with wavedrom:
+
+{
+  	'signal': [
+  		{'name': 'clk',
+         'wave': 'P........'},
+  		{'name': 'push',
+         'wave': '01..0.10.',
+         'node': '.x'},
+  		{'name': 'wr_port.addr',
+         'wave': '=.===..=.',
+         'data': ['0', '1', '2', '3', '4', '5']},
+  		{'name': 'wr_port.data',
+         'wave': '====..=..',
+         'data': ['00', 'AA', 'BB', 'CC', 'DD'],
+         'node': '.w....t'},
+        {'name': 'memory[0]',
+         'wave': '=.=......',
+         'data': ['00', 'AA'],
+         'node': '..G'},
+        {'name': 'memory[3]',
+         'wave': '=......=.',
+         'data': ['00', 'DD'],
+         'node': '.......H'},
+  		{'name': 'pop',
+         'wave': '0..1...0.',
+         'node': '...y'},
+  		{'name': 'rd_port.addr',
+         'wave': '=...====.',
+         'data': ['0', '1', '2', '3', '4', '5']},
+  		{'name': 'rd_port.data',
+         'wave': '=...====.',
+         'data': ['00', 'AA', 'BB', 'CC', 'DD'],
+         'node': '....r..u'},
+    ], 'edge': [
+        'x-~>G', 'w->G', 'y-~>r', 't->H', 't->u'
+    ]
+}
+-->
+<svg id="svgcontent_0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="270" width="500" viewBox="0 0 500 270" overflow="hidden" class="WaveDrom"><style type="text/css">text{font-size:11pt;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;fill-opacity:1;font-family:Helvetica}.h1{font-size:33pt;font-weight:bold}.h2{font-size:27pt;font-weight:bold}.h3{font-size:20pt;font-weight:bold}.h4{font-size:14pt;font-weight:bold}.h5{font-size:11pt;font-weight:bold}.h6{font-size:8pt;font-weight:bold}.muted{fill:#aaa}.warning{fill:#f6b900}.error{fill:#f60000}.info{fill:#0041c4}.success{fill:#00ab00}.s1{fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}.s2{fill:none;stroke:#000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}.s3{color:#000;fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 3;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible}.s4{color:#000;fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible}.s5{fill:#fff;stroke:none}.s6{fill:#000;fill-opacity:1;stroke:none}.s7{color:#000;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s8{color:#000;fill:#ffffb4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s9{color:#000;fill:#ffe0b9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s10{color:#000;fill:#b9e0ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s11{color:#000;fill:#ccfdfe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s12{color:#000;fill:#cdfdc5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s13{color:#000;fill:#f0c1fb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s14{color:#000;fill:#f5c2c0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s15{fill:#0041c4;fill-opacity:1;stroke:none}.s16{fill:none;stroke:#0041c4;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}</style><defs><g id="socket"><rect y="15" x="6" height="20" width="20"/></g><g id="pclk"><path d="M0,20 0,0 20,0" class="s1"/></g><g id="nclk"><path d="m0,0 0,20 20,0" class="s1"/></g><g id="000"><path d="m0,20 20,0" class="s1"/></g><g id="0m0"><path d="m0,20 3,0 3,-10 3,10 11,0" class="s1"/></g><g id="0m1"><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="0mx"><path d="M3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 5,20" class="s2"/><path d="M20,0 4,16" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 9,1" class="s2"/><path d="m0,20 20,0" class="s1"/></g><g id="0md"><path d="m8,20 10,0" class="s3"/><path d="m0,20 5,0" class="s1"/></g><g id="0mu"><path d="m0,20 3,0 C 7,10 10.107603,0 20,0" class="s1"/></g><g id="0mz"><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="111"><path d="M0,0 20,0" class="s1"/></g><g id="1m0"><path d="m0,0 3,0 6,20 11,0" class="s1"/></g><g id="1m1"><path d="M0,0 3,0 6,10 9,0 20,0" class="s1"/></g><g id="1mx"><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 5,5" class="s2"/><path d="M3.5,1.5 5,0" class="s2"/></g><g id="1md"><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/></g><g id="1mu"><path d="M0,0 5,0" class="s1"/><path d="M8,0 18,0" class="s3"/></g><g id="1mz"><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/></g><g id="xxx"><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 10,0" class="s2"/><path d="M0,15 15,0" class="s2"/><path d="M0,20 20,0" class="s2"/><path d="M5,20 20,5" class="s2"/><path d="M10,20 20,10" class="s2"/><path d="m15,20 5,-5" class="s2"/></g><g id="xm0"><path d="M0,0 4,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/><path d="M0,5 4,1" class="s2"/><path d="M0,10 5,5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 7,13" class="s2"/><path d="M5,20 8,17" class="s2"/></g><g id="xm1"><path d="M0,0 20,0" class="s1"/><path d="M0,20 4,20 9,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 9,1" class="s2"/><path d="M0,15 7,8" class="s2"/><path d="M0,20 5,15" class="s2"/></g><g id="xmx"><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 10,0" class="s2"/><path d="M0,15 15,0" class="s2"/><path d="M0,20 20,0" class="s2"/><path d="M5,20 20,5" class="s2"/><path d="M10,20 20,10" class="s2"/><path d="m15,20 5,-5" class="s2"/></g><g id="xmd"><path d="m0,0 4,0 c 3,10 6,20 16,20" class="s1"/><path d="m0,20 20,0" class="s1"/><path d="M0,5 4,1" class="s2"/><path d="M0,10 5.5,4.5" class="s2"/><path d="M0,15 6.5,8.5" class="s2"/><path d="M0,20 8,12" class="s2"/><path d="m5,20 5,-5" class="s2"/><path d="m10,20 2.5,-2.5" class="s2"/></g><g id="xmu"><path d="M0,0 20,0" class="s1"/><path d="m0,20 4,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 10,0" class="s2"/><path d="M0,15 10,5" class="s2"/><path d="M0,20 6,14" class="s2"/></g><g id="xmz"><path d="m0,0 4,0 c 6,10 11,10 16,10" class="s1"/><path d="m0,20 4,0 C 10,10 15,10 20,10" class="s1"/><path d="M0,5 4.5,0.5" class="s2"/><path d="M0,10 6.5,3.5" class="s2"/><path d="M0,15 8.5,6.5" class="s2"/><path d="M0,20 11.5,8.5" class="s2"/></g><g id="ddd"><path d="m0,20 20,0" class="s3"/></g><g id="dm0"><path d="m0,20 10,0" class="s3"/><path d="m12,20 8,0" class="s1"/></g><g id="dm1"><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="dmx"><path d="M3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 5,20" class="s2"/><path d="M20,0 4,16" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 9,1" class="s2"/><path d="m0,20 20,0" class="s1"/></g><g id="dmd"><path d="m0,20 20,0" class="s3"/></g><g id="dmu"><path d="m0,20 3,0 C 7,10 10.107603,0 20,0" class="s1"/></g><g id="dmz"><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="uuu"><path d="M0,0 20,0" class="s3"/></g><g id="um0"><path d="m0,0 3,0 6,20 11,0" class="s1"/></g><g id="um1"><path d="M0,0 10,0" class="s3"/><path d="m12,0 8,0" class="s1"/></g><g id="umx"><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 5,5" class="s2"/><path d="M3.5,1.5 5,0" class="s2"/></g><g id="umd"><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/></g><g id="umu"><path d="M0,0 20,0" class="s3"/></g><g id="umz"><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s4"/></g><g id="zzz"><path d="m0,10 20,0" class="s1"/></g><g id="zm0"><path d="m0,10 6,0 3,10 11,0" class="s1"/></g><g id="zm1"><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="zmx"><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 6.5,8.5" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="zmd"><path d="m0,10 7,0 c 3,5 8,10 13,10" class="s1"/></g><g id="zmu"><path d="m0,10 7,0 C 10,5 15,0 20,0" class="s1"/></g><g id="zmz"><path d="m0,10 20,0" class="s1"/></g><g id="gap"><path d="m7,-2 -4,0 c -5,0 -5,24 -10,24 l 4,0 C 2,22 2,-2 7,-2 z" class="s5"/><path d="M-7,22 C -2,22 -2,-2 3,-2" class="s1"/><path d="M-3,22 C 2,22 2,-2 7,-2" class="s1"/></g><g id="Pclk"><path d="M-3,12 0,3 3,12 C 1,11 -1,11 -3,12 z" class="s6"/><path d="M0,20 0,0 20,0" class="s1"/></g><g id="Nclk"><path d="M-3,8 0,17 3,8 C 1,9 -1,9 -3,8 z" class="s6"/><path d="m0,0 0,20 20,0" class="s1"/></g><g id="0mv-2"><path d="M9,0 20,0 20,20 3,20 z" class="s7"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-2"><path d="M2.875,0 20,0 20,20 9,20 z" class="s7"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-2"><path d="M9,0 20,0 20,20 3,20 z" class="s7"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-2"><path d="M3,0 20,0 20,20 9,20 z" class="s7"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-2"><path d="M20,20 0,20 0,0 20,0" class="s7"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-2"><path d="M0,20 0,0 3,0 9,20" class="s7"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-2"><path d="M0,0 0,20 3,20 9,0" class="s7"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-2"><path d="M0,0 0,20 3,20 6,10 3,0" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-2"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s7"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-2"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s7"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-2"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s7"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-3"><path d="M9,0 20,0 20,20 3,20 z" class="s8"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-3"><path d="M2.875,0 20,0 20,20 9,20 z" class="s8"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-3"><path d="M9,0 20,0 20,20 3,20 z" class="s8"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-3"><path d="M3,0 20,0 20,20 9,20 z" class="s8"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-3"><path d="M20,20 0,20 0,0 20,0" class="s8"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-3"><path d="M0,20 0,0 3,0 9,20" class="s8"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-3"><path d="M0,0 0,20 3,20 9,0" class="s8"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-3"><path d="M0,0 0,20 3,20 6,10 3,0" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-3"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s8"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-3"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s8"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-3"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s8"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-4"><path d="M9,0 20,0 20,20 3,20 z" class="s9"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-4"><path d="M2.875,0 20,0 20,20 9,20 z" class="s9"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-4"><path d="M9,0 20,0 20,20 3,20 z" class="s9"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-4"><path d="M3,0 20,0 20,20 9,20 z" class="s9"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-4"><path d="M20,20 0,20 0,0 20,0" class="s9"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-4"><path d="M0,20 0,0 3,0 9,20" class="s9"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-4"><path d="M0,0 0,20 3,20 9,0" class="s9"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-4"><path d="M0,0 0,20 3,20 6,10 3,0" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-4"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s9"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-4"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s9"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-4"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s9"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-5"><path d="M9,0 20,0 20,20 3,20 z" class="s10"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-5"><path d="M2.875,0 20,0 20,20 9,20 z" class="s10"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-5"><path d="M9,0 20,0 20,20 3,20 z" class="s10"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-5"><path d="M3,0 20,0 20,20 9,20 z" class="s10"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-5"><path d="M20,20 0,20 0,0 20,0" class="s10"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-5"><path d="M0,20 0,0 3,0 9,20" class="s10"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-5"><path d="M0,0 0,20 3,20 9,0" class="s10"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-5"><path d="M0,0 0,20 3,20 6,10 3,0" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-5"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s10"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-5"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s10"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-5"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s10"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-6"><path d="M9,0 20,0 20,20 3,20 z" class="s11"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-6"><path d="M2.875,0 20,0 20,20 9,20 z" class="s11"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-6"><path d="M9,0 20,0 20,20 3,20 z" class="s11"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-6"><path d="M3,0 20,0 20,20 9,20 z" class="s11"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-6"><path d="M20,20 0,20 0,0 20,0" class="s11"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-6"><path d="M0,20 0,0 3,0 9,20" class="s11"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-6"><path d="M0,0 0,20 3,20 9,0" class="s11"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-6"><path d="M0,0 0,20 3,20 6,10 3,0" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-6"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s11"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-6"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s11"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-6"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s11"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-7"><path d="M9,0 20,0 20,20 3,20 z" class="s12"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-7"><path d="M2.875,0 20,0 20,20 9,20 z" class="s12"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-7"><path d="M9,0 20,0 20,20 3,20 z" class="s12"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-7"><path d="M3,0 20,0 20,20 9,20 z" class="s12"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-7"><path d="M20,20 0,20 0,0 20,0" class="s12"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-7"><path d="M0,20 0,0 3,0 9,20" class="s12"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-7"><path d="M0,0 0,20 3,20 9,0" class="s12"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-7"><path d="M0,0 0,20 3,20 6,10 3,0" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-7"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s12"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-7"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s12"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-7"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s12"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-8"><path d="M9,0 20,0 20,20 3,20 z" class="s13"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-8"><path d="M2.875,0 20,0 20,20 9,20 z" class="s13"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-8"><path d="M9,0 20,0 20,20 3,20 z" class="s13"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-8"><path d="M3,0 20,0 20,20 9,20 z" class="s13"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-8"><path d="M20,20 0,20 0,0 20,0" class="s13"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-8"><path d="M0,20 0,0 3,0 9,20" class="s13"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-8"><path d="M0,0 0,20 3,20 9,0" class="s13"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-8"><path d="M0,0 0,20 3,20 6,10 3,0" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-8"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s13"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-8"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s13"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-8"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s13"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-9"><path d="M9,0 20,0 20,20 3,20 z" class="s14"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-9"><path d="M2.875,0 20,0 20,20 9,20 z" class="s14"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-9"><path d="M9,0 20,0 20,20 3,20 z" class="s14"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-9"><path d="M3,0 20,0 20,20 9,20 z" class="s14"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-9"><path d="M20,20 0,20 0,0 20,0" class="s14"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-9"><path d="M0,20 0,0 3,0 9,20" class="s14"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-9"><path d="M0,0 0,20 3,20 9,0" class="s14"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-9"><path d="M0,0 0,20 3,20 6,10 3,0" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-9"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s14"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-9"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s14"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-9"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s14"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="vmv-2-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="arrow0"><path d="m-12,-3 9,3 -9,3 c 1,-2 1,-4 0,-6 z" class="s15"/><path d="M0,0 -15,0" class="s16"/></g><marker id="arrowhead" style="fill:#0041c4" markerHeight="7" markerWidth="10" markerUnits="strokeWidth" viewBox="0 -4 11 8" refX="15" refY="0" orient="auto"><path d="M0 -4 11 0 0 4z"/></marker><marker id="arrowtail" style="fill:#0041c4" markerHeight="7" markerWidth="10" markerUnits="strokeWidth" viewBox="-11 -4 11 8" refX="-15" refY="0" orient="auto"><path d="M0 -4 -11 0 0 4z"/></marker><marker id="tee" style="fill:#0041c4" markerHeight="6" markerWidth="1" markerUnits="strokeWidth" viewBox="0 0 1 6" refX="0" refY="3" orient="auto"><path d="M 0 0 L 0 6" style="stroke:#0041c4;stroke-width:2"/></marker></defs><g id="waves_0"><rect width="500" height="270" style="stroke:none;fill:white"/><g transform="translate(120.5,0.5)" id="lanes_0"><g id="gmarks_0"><g style="stroke:#888;stroke-width:0.5;stroke-dasharray:1,3"><line id="gmark_0_0" x1="0" y1="0" x2="0" y2="270"/><line id="gmark_1_0" x1="40" y1="0" x2="40" y2="270"/><line id="gmark_2_0" x1="80" y1="0" x2="80" y2="270"/><line id="gmark_3_0" x1="120" y1="0" x2="120" y2="270"/><line id="gmark_4_0" x1="160" y1="0" x2="160" y2="270"/><line id="gmark_5_0" x1="200" y1="0" x2="200" y2="270"/><line id="gmark_6_0" x1="240" y1="0" x2="240" y2="270"/><line id="gmark_7_0" x1="280" y1="0" x2="280" y2="270"/><line id="gmark_8_0" x1="320" y1="0" x2="320" y2="270"/><line id="gmark_9_0" x1="360" y1="0" x2="360" y2="270"/></g></g><g transform="translate(0,5)" id="wavelane_0_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>clk</tspan></text><g id="wavelane_draw_0_0"><use xlink:href="#Pclk"/><use transform="translate(20)" xlink:href="#nclk"/><use transform="translate(40)" xlink:href="#Pclk"/><use transform="translate(60)" xlink:href="#nclk"/><use transform="translate(80)" xlink:href="#Pclk"/><use transform="translate(100)" xlink:href="#nclk"/><use transform="translate(120)" xlink:href="#Pclk"/><use transform="translate(140)" xlink:href="#nclk"/><use transform="translate(160)" xlink:href="#Pclk"/><use transform="translate(180)" xlink:href="#nclk"/><use transform="translate(200)" xlink:href="#Pclk"/><use transform="translate(220)" xlink:href="#nclk"/><use transform="translate(240)" xlink:href="#Pclk"/><use transform="translate(260)" xlink:href="#nclk"/><use transform="translate(280)" xlink:href="#Pclk"/><use transform="translate(300)" xlink:href="#nclk"/><use transform="translate(320)" xlink:href="#Pclk"/><use transform="translate(340)" xlink:href="#nclk"/></g></g><g transform="translate(0,35)" id="wavelane_1_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>push</tspan></text><g id="wavelane_draw_1_0"><use xlink:href="#000"/><use transform="translate(20)" xlink:href="#000"/><use transform="translate(40)" xlink:href="#0m1"/><use transform="translate(60)" xlink:href="#111"/><use transform="translate(80)" xlink:href="#111"/><use transform="translate(100)" xlink:href="#111"/><use transform="translate(120)" xlink:href="#111"/><use transform="translate(140)" xlink:href="#111"/><use transform="translate(160)" xlink:href="#1m0"/><use transform="translate(180)" xlink:href="#000"/><use transform="translate(200)" xlink:href="#000"/><use transform="translate(220)" xlink:href="#000"/><use transform="translate(240)" xlink:href="#0m1"/><use transform="translate(260)" xlink:href="#111"/><use transform="translate(280)" xlink:href="#1m0"/><use transform="translate(300)" xlink:href="#000"/><use transform="translate(320)" xlink:href="#000"/><use transform="translate(340)" xlink:href="#000"/></g></g><g transform="translate(0,65)" id="wavelane_2_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>wr_port.addr</tspan></text><g id="wavelane_draw_2_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vvv-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vmv-2-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vmv-2-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vmv-2-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vvv-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vvv-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vmv-2-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vvv-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><text x="36" y="15" text-anchor="middle" xml:space="preserve"><tspan>0</tspan></text><text x="106" y="15" text-anchor="middle" xml:space="preserve"><tspan>1</tspan></text><text x="146" y="15" text-anchor="middle" xml:space="preserve"><tspan>2</tspan></text><text x="226" y="15" text-anchor="middle" xml:space="preserve"><tspan>3</tspan></text><text x="326" y="15" text-anchor="middle" xml:space="preserve"><tspan>4</tspan></text></g></g><g transform="translate(0,95)" id="wavelane_3_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>wr_port.data</tspan></text><g id="wavelane_draw_3_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vmv-2-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vmv-2-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vmv-2-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vvv-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vvv-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vmv-2-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vvv-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vvv-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><text x="16" y="15" text-anchor="middle" xml:space="preserve"><tspan>00</tspan></text><text x="66" y="15" text-anchor="middle" xml:space="preserve"><tspan>AA</tspan></text><text x="106" y="15" text-anchor="middle" xml:space="preserve"><tspan>BB</tspan></text><text x="186" y="15" text-anchor="middle" xml:space="preserve"><tspan>CC</tspan></text><text x="306" y="15" text-anchor="middle" xml:space="preserve"><tspan>DD</tspan></text></g></g><g transform="translate(0,125)" id="wavelane_4_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>memory[0]</tspan></text><g id="wavelane_draw_4_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vvv-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vmv-2-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vvv-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vvv-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vvv-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vvv-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vvv-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vvv-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><text x="36" y="15" text-anchor="middle" xml:space="preserve"><tspan>00</tspan></text><text x="226" y="15" text-anchor="middle" xml:space="preserve"><tspan>AA</tspan></text></g></g><g transform="translate(0,155)" id="wavelane_5_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>memory[3]</tspan></text><g id="wavelane_draw_5_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vvv-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vvv-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vvv-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vvv-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vvv-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vvv-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vmv-2-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vvv-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><text x="136" y="15" text-anchor="middle" xml:space="preserve"><tspan>00</tspan></text><text x="326" y="15" text-anchor="middle" xml:space="preserve"><tspan>DD</tspan></text></g></g><g transform="translate(0,185)" id="wavelane_6_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>pop</tspan></text><g id="wavelane_draw_6_0"><use xlink:href="#000"/><use transform="translate(20)" xlink:href="#000"/><use transform="translate(40)" xlink:href="#000"/><use transform="translate(60)" xlink:href="#000"/><use transform="translate(80)" xlink:href="#000"/><use transform="translate(100)" xlink:href="#000"/><use transform="translate(120)" xlink:href="#0m1"/><use transform="translate(140)" xlink:href="#111"/><use transform="translate(160)" xlink:href="#111"/><use transform="translate(180)" xlink:href="#111"/><use transform="translate(200)" xlink:href="#111"/><use transform="translate(220)" xlink:href="#111"/><use transform="translate(240)" xlink:href="#111"/><use transform="translate(260)" xlink:href="#111"/><use transform="translate(280)" xlink:href="#1m0"/><use transform="translate(300)" xlink:href="#000"/><use transform="translate(320)" xlink:href="#000"/><use transform="translate(340)" xlink:href="#000"/></g></g><g transform="translate(0,215)" id="wavelane_7_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>rd_port.addr</tspan></text><g id="wavelane_draw_7_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vvv-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vvv-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vvv-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vmv-2-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vmv-2-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vmv-2-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vmv-2-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vvv-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><text x="76" y="15" text-anchor="middle" xml:space="preserve"><tspan>0</tspan></text><text x="186" y="15" text-anchor="middle" xml:space="preserve"><tspan>1</tspan></text><text x="226" y="15" text-anchor="middle" xml:space="preserve"><tspan>2</tspan></text><text x="266" y="15" text-anchor="middle" xml:space="preserve"><tspan>3</tspan></text><text x="326" y="15" text-anchor="middle" xml:space="preserve"><tspan>4</tspan></text></g></g><g transform="translate(0,245)" id="wavelane_8_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>rd_port.data</tspan></text><g id="wavelane_draw_8_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vvv-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vvv-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vvv-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vmv-2-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vmv-2-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vmv-2-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vmv-2-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vvv-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><text x="76" y="15" text-anchor="middle" xml:space="preserve"><tspan>00</tspan></text><text x="186" y="15" text-anchor="middle" xml:space="preserve"><tspan>AA</tspan></text><text x="226" y="15" text-anchor="middle" xml:space="preserve"><tspan>BB</tspan></text><text x="266" y="15" text-anchor="middle" xml:space="preserve"><tspan>CC</tspan></text><text x="326" y="15" text-anchor="middle" xml:space="preserve"><tspan>DD</tspan></text></g></g><g id="wavearcs_0"><path id="gmark_x_G" d="M 46,45 c 28, 0 40, 90 40, 90" style="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none"/><path id="gmark_w_G" d="M 46,105 86,135" style="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none"/><path id="gmark_y_r" d="M 126,195 c 28, 0 40, 60 40, 60" style="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none"/><path id="gmark_t_H" d="M 246,105 286,165" style="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none"/><path id="gmark_t_u" d="M 246,105 286,255" style="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none"/><g transform="translate(46,45)"><rect x="-4" y="-5" width="9.26" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>x</tspan></text></g><g transform="translate(46,105)"><rect x="-6" y="-5" width="12.45" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>w</tspan></text></g><g transform="translate(246,105)"><rect x="-3" y="-5" width="6.07" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>t</tspan></text></g><g transform="translate(126,195)"><rect x="-4" y="-5" width="9.15" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>y</tspan></text></g><g transform="translate(166,255)"><rect x="-3" y="-5" width="6.84" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>r</tspan></text></g><g transform="translate(286,255)"><rect x="-5" y="-5" width="10.14" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>u</tspan></text></g></g><g id="wavegaps_0"><g transform="translate(0,5)" id="wavegap_0_0"/><g transform="translate(0,35)" id="wavegap_1_0"/><g transform="translate(0,65)" id="wavegap_2_0"/><g transform="translate(0,95)" id="wavegap_3_0"/><g transform="translate(0,125)" id="wavegap_4_0"/><g transform="translate(0,155)" id="wavegap_5_0"/><g transform="translate(0,185)" id="wavegap_6_0"/><g transform="translate(0,215)" id="wavegap_7_0"/><g transform="translate(0,245)" id="wavegap_8_0"/></g><g/></g><g id="groups_0"><g/></g></g></svg>
diff --git a/docs/stdlib/_images/memory/example_hello.svg b/docs/stdlib/_images/memory/example_hello.svg
new file mode 100644
index 0000000..0a43f2f
--- /dev/null
+++ b/docs/stdlib/_images/memory/example_hello.svg
@@ -0,0 +1,22 @@
+<!--
+Render the following with wavedrom:
+
+{
+    'signal': [
+        {'name': 'clk',
+         'wave': '0P............'},
+        {'name': 'rd_port.addr',
+         'wave': '==============',
+         'data': [0,1,2,3,4,5,6,7,8,9,10,11,0,1],
+         'node': '.a'},
+        {'name': 'rd_port.data',
+         'wave': '==============',
+         'data': ['H','e','l','l','o',' ','w','o','r','l','d','\\n','H','e'],
+         'node': '.d'}
+    ],
+    'edge': [
+        'a-|d'
+    ]
+}
+-->
+<svg id="svgcontent_0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="90" width="680" viewBox="0 0 680 90" overflow="hidden" class="WaveDrom"><style type="text/css">text{font-size:11pt;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;fill-opacity:1;font-family:Helvetica}.h1{font-size:33pt;font-weight:bold}.h2{font-size:27pt;font-weight:bold}.h3{font-size:20pt;font-weight:bold}.h4{font-size:14pt;font-weight:bold}.h5{font-size:11pt;font-weight:bold}.h6{font-size:8pt;font-weight:bold}.muted{fill:#aaa}.warning{fill:#f6b900}.error{fill:#f60000}.info{fill:#0041c4}.success{fill:#00ab00}.s1{fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}.s2{fill:none;stroke:#000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}.s3{color:#000;fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 3;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible}.s4{color:#000;fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible}.s5{fill:#fff;stroke:none}.s6{fill:#000;fill-opacity:1;stroke:none}.s7{color:#000;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s8{color:#000;fill:#ffffb4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s9{color:#000;fill:#ffe0b9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s10{color:#000;fill:#b9e0ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s11{color:#000;fill:#ccfdfe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s12{color:#000;fill:#cdfdc5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s13{color:#000;fill:#f0c1fb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s14{color:#000;fill:#f5c2c0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible}.s15{fill:#0041c4;fill-opacity:1;stroke:none}.s16{fill:none;stroke:#0041c4;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}</style><defs><g id="socket"><rect y="15" x="6" height="20" width="20"/></g><g id="pclk"><path d="M0,20 0,0 20,0" class="s1"/></g><g id="nclk"><path d="m0,0 0,20 20,0" class="s1"/></g><g id="000"><path d="m0,20 20,0" class="s1"/></g><g id="0m0"><path d="m0,20 3,0 3,-10 3,10 11,0" class="s1"/></g><g id="0m1"><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="0mx"><path d="M3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 5,20" class="s2"/><path d="M20,0 4,16" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 9,1" class="s2"/><path d="m0,20 20,0" class="s1"/></g><g id="0md"><path d="m8,20 10,0" class="s3"/><path d="m0,20 5,0" class="s1"/></g><g id="0mu"><path d="m0,20 3,0 C 7,10 10.107603,0 20,0" class="s1"/></g><g id="0mz"><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="111"><path d="M0,0 20,0" class="s1"/></g><g id="1m0"><path d="m0,0 3,0 6,20 11,0" class="s1"/></g><g id="1m1"><path d="M0,0 3,0 6,10 9,0 20,0" class="s1"/></g><g id="1mx"><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 5,5" class="s2"/><path d="M3.5,1.5 5,0" class="s2"/></g><g id="1md"><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/></g><g id="1mu"><path d="M0,0 5,0" class="s1"/><path d="M8,0 18,0" class="s3"/></g><g id="1mz"><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/></g><g id="xxx"><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 10,0" class="s2"/><path d="M0,15 15,0" class="s2"/><path d="M0,20 20,0" class="s2"/><path d="M5,20 20,5" class="s2"/><path d="M10,20 20,10" class="s2"/><path d="m15,20 5,-5" class="s2"/></g><g id="xm0"><path d="M0,0 4,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/><path d="M0,5 4,1" class="s2"/><path d="M0,10 5,5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 7,13" class="s2"/><path d="M5,20 8,17" class="s2"/></g><g id="xm1"><path d="M0,0 20,0" class="s1"/><path d="M0,20 4,20 9,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 9,1" class="s2"/><path d="M0,15 7,8" class="s2"/><path d="M0,20 5,15" class="s2"/></g><g id="xmx"><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 10,0" class="s2"/><path d="M0,15 15,0" class="s2"/><path d="M0,20 20,0" class="s2"/><path d="M5,20 20,5" class="s2"/><path d="M10,20 20,10" class="s2"/><path d="m15,20 5,-5" class="s2"/></g><g id="xmd"><path d="m0,0 4,0 c 3,10 6,20 16,20" class="s1"/><path d="m0,20 20,0" class="s1"/><path d="M0,5 4,1" class="s2"/><path d="M0,10 5.5,4.5" class="s2"/><path d="M0,15 6.5,8.5" class="s2"/><path d="M0,20 8,12" class="s2"/><path d="m5,20 5,-5" class="s2"/><path d="m10,20 2.5,-2.5" class="s2"/></g><g id="xmu"><path d="M0,0 20,0" class="s1"/><path d="m0,20 4,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,5 5,0" class="s2"/><path d="M0,10 10,0" class="s2"/><path d="M0,15 10,5" class="s2"/><path d="M0,20 6,14" class="s2"/></g><g id="xmz"><path d="m0,0 4,0 c 6,10 11,10 16,10" class="s1"/><path d="m0,20 4,0 C 10,10 15,10 20,10" class="s1"/><path d="M0,5 4.5,0.5" class="s2"/><path d="M0,10 6.5,3.5" class="s2"/><path d="M0,15 8.5,6.5" class="s2"/><path d="M0,20 11.5,8.5" class="s2"/></g><g id="ddd"><path d="m0,20 20,0" class="s3"/></g><g id="dm0"><path d="m0,20 10,0" class="s3"/><path d="m12,20 8,0" class="s1"/></g><g id="dm1"><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="dmx"><path d="M3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 5,20" class="s2"/><path d="M20,0 4,16" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 9,1" class="s2"/><path d="m0,20 20,0" class="s1"/></g><g id="dmd"><path d="m0,20 20,0" class="s3"/></g><g id="dmu"><path d="m0,20 3,0 C 7,10 10.107603,0 20,0" class="s1"/></g><g id="dmz"><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="uuu"><path d="M0,0 20,0" class="s3"/></g><g id="um0"><path d="m0,0 3,0 6,20 11,0" class="s1"/></g><g id="um1"><path d="M0,0 10,0" class="s3"/><path d="m12,0 8,0" class="s1"/></g><g id="umx"><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 6,9" class="s2"/><path d="M10,0 5,5" class="s2"/><path d="M3.5,1.5 5,0" class="s2"/></g><g id="umd"><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/></g><g id="umu"><path d="M0,0 20,0" class="s3"/></g><g id="umz"><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s4"/></g><g id="zzz"><path d="m0,10 20,0" class="s1"/></g><g id="zm0"><path d="m0,10 6,0 3,10 11,0" class="s1"/></g><g id="zm1"><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="zmx"><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 6.5,8.5" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="zmd"><path d="m0,10 7,0 c 3,5 8,10 13,10" class="s1"/></g><g id="zmu"><path d="m0,10 7,0 C 10,5 15,0 20,0" class="s1"/></g><g id="zmz"><path d="m0,10 20,0" class="s1"/></g><g id="gap"><path d="m7,-2 -4,0 c -5,0 -5,24 -10,24 l 4,0 C 2,22 2,-2 7,-2 z" class="s5"/><path d="M-7,22 C -2,22 -2,-2 3,-2" class="s1"/><path d="M-3,22 C 2,22 2,-2 7,-2" class="s1"/></g><g id="Pclk"><path d="M-3,12 0,3 3,12 C 1,11 -1,11 -3,12 z" class="s6"/><path d="M0,20 0,0 20,0" class="s1"/></g><g id="Nclk"><path d="M-3,8 0,17 3,8 C 1,9 -1,9 -3,8 z" class="s6"/><path d="m0,0 0,20 20,0" class="s1"/></g><g id="0mv-2"><path d="M9,0 20,0 20,20 3,20 z" class="s7"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-2"><path d="M2.875,0 20,0 20,20 9,20 z" class="s7"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-2"><path d="M9,0 20,0 20,20 3,20 z" class="s7"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-2"><path d="M3,0 20,0 20,20 9,20 z" class="s7"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-2"><path d="M20,20 0,20 0,0 20,0" class="s7"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-2"><path d="M0,20 0,0 3,0 9,20" class="s7"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-2"><path d="M0,0 0,20 3,20 9,0" class="s7"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-2"><path d="M0,0 0,20 3,20 6,10 3,0" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-2"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s7"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-2"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s7"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-2"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s7"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-3"><path d="M9,0 20,0 20,20 3,20 z" class="s8"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-3"><path d="M2.875,0 20,0 20,20 9,20 z" class="s8"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-3"><path d="M9,0 20,0 20,20 3,20 z" class="s8"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-3"><path d="M3,0 20,0 20,20 9,20 z" class="s8"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-3"><path d="M20,20 0,20 0,0 20,0" class="s8"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-3"><path d="M0,20 0,0 3,0 9,20" class="s8"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-3"><path d="M0,0 0,20 3,20 9,0" class="s8"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-3"><path d="M0,0 0,20 3,20 6,10 3,0" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-3"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s8"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-3"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s8"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-3"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s8"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-4"><path d="M9,0 20,0 20,20 3,20 z" class="s9"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-4"><path d="M2.875,0 20,0 20,20 9,20 z" class="s9"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-4"><path d="M9,0 20,0 20,20 3,20 z" class="s9"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-4"><path d="M3,0 20,0 20,20 9,20 z" class="s9"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-4"><path d="M20,20 0,20 0,0 20,0" class="s9"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-4"><path d="M0,20 0,0 3,0 9,20" class="s9"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-4"><path d="M0,0 0,20 3,20 9,0" class="s9"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-4"><path d="M0,0 0,20 3,20 6,10 3,0" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-4"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s9"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-4"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s9"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-4"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s9"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-5"><path d="M9,0 20,0 20,20 3,20 z" class="s10"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-5"><path d="M2.875,0 20,0 20,20 9,20 z" class="s10"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-5"><path d="M9,0 20,0 20,20 3,20 z" class="s10"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-5"><path d="M3,0 20,0 20,20 9,20 z" class="s10"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-5"><path d="M20,20 0,20 0,0 20,0" class="s10"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-5"><path d="M0,20 0,0 3,0 9,20" class="s10"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-5"><path d="M0,0 0,20 3,20 9,0" class="s10"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-5"><path d="M0,0 0,20 3,20 6,10 3,0" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-5"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s10"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-5"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s10"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-5"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s10"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-6"><path d="M9,0 20,0 20,20 3,20 z" class="s11"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-6"><path d="M2.875,0 20,0 20,20 9,20 z" class="s11"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-6"><path d="M9,0 20,0 20,20 3,20 z" class="s11"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-6"><path d="M3,0 20,0 20,20 9,20 z" class="s11"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-6"><path d="M20,20 0,20 0,0 20,0" class="s11"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-6"><path d="M0,20 0,0 3,0 9,20" class="s11"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-6"><path d="M0,0 0,20 3,20 9,0" class="s11"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-6"><path d="M0,0 0,20 3,20 6,10 3,0" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-6"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s11"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-6"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s11"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-6"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s11"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-7"><path d="M9,0 20,0 20,20 3,20 z" class="s12"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-7"><path d="M2.875,0 20,0 20,20 9,20 z" class="s12"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-7"><path d="M9,0 20,0 20,20 3,20 z" class="s12"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-7"><path d="M3,0 20,0 20,20 9,20 z" class="s12"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-7"><path d="M20,20 0,20 0,0 20,0" class="s12"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-7"><path d="M0,20 0,0 3,0 9,20" class="s12"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-7"><path d="M0,0 0,20 3,20 9,0" class="s12"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-7"><path d="M0,0 0,20 3,20 6,10 3,0" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-7"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s12"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-7"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s12"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-7"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s12"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-8"><path d="M9,0 20,0 20,20 3,20 z" class="s13"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-8"><path d="M2.875,0 20,0 20,20 9,20 z" class="s13"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-8"><path d="M9,0 20,0 20,20 3,20 z" class="s13"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-8"><path d="M3,0 20,0 20,20 9,20 z" class="s13"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-8"><path d="M20,20 0,20 0,0 20,0" class="s13"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-8"><path d="M0,20 0,0 3,0 9,20" class="s13"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-8"><path d="M0,0 0,20 3,20 9,0" class="s13"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-8"><path d="M0,0 0,20 3,20 6,10 3,0" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-8"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s13"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-8"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s13"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-8"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s13"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="0mv-9"><path d="M9,0 20,0 20,20 3,20 z" class="s14"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="1mv-9"><path d="M2.875,0 20,0 20,20 9,20 z" class="s14"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="xmv-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,5 3.5,1.5" class="s2"/><path d="M0,10 4.5,5.5" class="s2"/><path d="M0,15 6,9" class="s2"/><path d="M0,20 4,16" class="s2"/></g><g id="dmv-9"><path d="M9,0 20,0 20,20 3,20 z" class="s14"/><path d="M3,20 9,0 20,0" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="umv-9"><path d="M3,0 20,0 20,20 9,20 z" class="s14"/><path d="m3,0 6,20 11,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="zmv-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="m6,10 3,10 11,0" class="s1"/><path d="M0,10 6,10 9,0 20,0" class="s1"/></g><g id="vvv-9"><path d="M20,20 0,20 0,0 20,0" class="s14"/><path d="m0,20 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vm0-9"><path d="M0,20 0,0 3,0 9,20" class="s14"/><path d="M0,0 3,0 9,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vm1-9"><path d="M0,0 0,20 3,20 9,0" class="s14"/><path d="M0,0 20,0" class="s1"/><path d="M0,20 3,20 9,0" class="s1"/></g><g id="vmx-9"><path d="M0,0 0,20 3,20 6,10 3,0" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/><path d="m20,15 -5,5" class="s2"/><path d="M20,10 10,20" class="s2"/><path d="M20,5 8,17" class="s2"/><path d="M20,0 7,13" class="s2"/><path d="M15,0 7,8" class="s2"/><path d="M10,0 9,1" class="s2"/></g><g id="vmd-9"><path d="m0,0 0,20 20,0 C 10,20 7,10 3,0" class="s14"/><path d="m0,0 3,0 c 4,10 7,20 17,20" class="s1"/><path d="m0,20 20,0" class="s1"/></g><g id="vmu-9"><path d="m0,0 0,20 3,0 C 7,10 10,0 20,0" class="s14"/><path d="m0,20 3,0 C 7,10 10,0 20,0" class="s1"/><path d="M0,0 20,0" class="s1"/></g><g id="vmz-9"><path d="M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20" class="s14"/><path d="m0,0 3,0 c 7,10 12,10 17,10" class="s1"/><path d="m0,20 3,0 C 10,10 15,10 20,10" class="s1"/></g><g id="vmv-2-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-2"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s7"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-3"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s8"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-4"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s9"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-5"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s10"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-6"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s11"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-7"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s12"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-8"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s13"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-2-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s7"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-3-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s8"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-4-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s9"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-5-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s10"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-6-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s11"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-7-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s12"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-8-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s13"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="vmv-9-9"><path d="M9,0 20,0 20,20 9,20 6,10 z" class="s14"/><path d="M3,0 0,0 0,20 3,20 6,10 z" class="s14"/><path d="m0,0 3,0 6,20 11,0" class="s1"/><path d="M0,20 3,20 9,0 20,0" class="s1"/></g><g id="arrow0"><path d="m-12,-3 9,3 -9,3 c 1,-2 1,-4 0,-6 z" class="s15"/><path d="M0,0 -15,0" class="s16"/></g><marker id="arrowhead" style="fill:#0041c4" markerHeight="7" markerWidth="10" markerUnits="strokeWidth" viewBox="0 -4 11 8" refX="15" refY="0" orient="auto"><path d="M0 -4 11 0 0 4z"/></marker><marker id="arrowtail" style="fill:#0041c4" markerHeight="7" markerWidth="10" markerUnits="strokeWidth" viewBox="-11 -4 11 8" refX="-15" refY="0" orient="auto"><path d="M0 -4 -11 0 0 4z"/></marker><marker id="tee" style="fill:#0041c4" markerHeight="6" markerWidth="1" markerUnits="strokeWidth" viewBox="0 0 1 6" refX="0" refY="3" orient="auto"><path d="M 0 0 L 0 6" style="stroke:#0041c4;stroke-width:2"/></marker></defs><g id="waves_0"><rect width="680" height="90" style="stroke:none;fill:white"/><g transform="translate(100.5,0.5)" id="lanes_0"><g id="gmarks_0"><g style="stroke:#888;stroke-width:0.5;stroke-dasharray:1,3"><line id="gmark_0_0" x1="0" y1="0" x2="0" y2="90"/><line id="gmark_1_0" x1="40" y1="0" x2="40" y2="90"/><line id="gmark_2_0" x1="80" y1="0" x2="80" y2="90"/><line id="gmark_3_0" x1="120" y1="0" x2="120" y2="90"/><line id="gmark_4_0" x1="160" y1="0" x2="160" y2="90"/><line id="gmark_5_0" x1="200" y1="0" x2="200" y2="90"/><line id="gmark_6_0" x1="240" y1="0" x2="240" y2="90"/><line id="gmark_7_0" x1="280" y1="0" x2="280" y2="90"/><line id="gmark_8_0" x1="320" y1="0" x2="320" y2="90"/><line id="gmark_9_0" x1="360" y1="0" x2="360" y2="90"/><line id="gmark_10_0" x1="400" y1="0" x2="400" y2="90"/><line id="gmark_11_0" x1="440" y1="0" x2="440" y2="90"/><line id="gmark_12_0" x1="480" y1="0" x2="480" y2="90"/><line id="gmark_13_0" x1="520" y1="0" x2="520" y2="90"/><line id="gmark_14_0" x1="560" y1="0" x2="560" y2="90"/></g></g><g transform="translate(0,5)" id="wavelane_0_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>clk</tspan></text><g id="wavelane_draw_0_0"><use xlink:href="#000"/><use transform="translate(20)" xlink:href="#000"/><use transform="translate(40)" xlink:href="#Pclk"/><use transform="translate(60)" xlink:href="#nclk"/><use transform="translate(80)" xlink:href="#Pclk"/><use transform="translate(100)" xlink:href="#nclk"/><use transform="translate(120)" xlink:href="#Pclk"/><use transform="translate(140)" xlink:href="#nclk"/><use transform="translate(160)" xlink:href="#Pclk"/><use transform="translate(180)" xlink:href="#nclk"/><use transform="translate(200)" xlink:href="#Pclk"/><use transform="translate(220)" xlink:href="#nclk"/><use transform="translate(240)" xlink:href="#Pclk"/><use transform="translate(260)" xlink:href="#nclk"/><use transform="translate(280)" xlink:href="#Pclk"/><use transform="translate(300)" xlink:href="#nclk"/><use transform="translate(320)" xlink:href="#Pclk"/><use transform="translate(340)" xlink:href="#nclk"/><use transform="translate(360)" xlink:href="#Pclk"/><use transform="translate(380)" xlink:href="#nclk"/><use transform="translate(400)" xlink:href="#Pclk"/><use transform="translate(420)" xlink:href="#nclk"/><use transform="translate(440)" xlink:href="#Pclk"/><use transform="translate(460)" xlink:href="#nclk"/><use transform="translate(480)" xlink:href="#Pclk"/><use transform="translate(500)" xlink:href="#nclk"/><use transform="translate(520)" xlink:href="#Pclk"/><use transform="translate(540)" xlink:href="#nclk"/></g></g><g transform="translate(0,35)" id="wavelane_1_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>rd_port.addr</tspan></text><g id="wavelane_draw_1_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vmv-2-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vmv-2-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vmv-2-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vmv-2-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vmv-2-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vmv-2-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vmv-2-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vmv-2-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><use transform="translate(360)" xlink:href="#vmv-2-2"/><use transform="translate(380)" xlink:href="#vvv-2"/><use transform="translate(400)" xlink:href="#vmv-2-2"/><use transform="translate(420)" xlink:href="#vvv-2"/><use transform="translate(440)" xlink:href="#vmv-2-2"/><use transform="translate(460)" xlink:href="#vvv-2"/><use transform="translate(480)" xlink:href="#vmv-2-2"/><use transform="translate(500)" xlink:href="#vvv-2"/><use transform="translate(520)" xlink:href="#vmv-2-2"/><use transform="translate(540)" xlink:href="#vvv-2"/><text x="16" y="15" text-anchor="middle" xml:space="preserve">0</text><text x="66" y="15" text-anchor="middle" xml:space="preserve">1</text><text x="106" y="15" text-anchor="middle" xml:space="preserve">2</text><text x="146" y="15" text-anchor="middle" xml:space="preserve">3</text><text x="186" y="15" text-anchor="middle" xml:space="preserve">4</text><text x="226" y="15" text-anchor="middle" xml:space="preserve">5</text><text x="266" y="15" text-anchor="middle" xml:space="preserve">6</text><text x="306" y="15" text-anchor="middle" xml:space="preserve">7</text><text x="346" y="15" text-anchor="middle" xml:space="preserve">8</text><text x="386" y="15" text-anchor="middle" xml:space="preserve">9</text><text x="426" y="15" text-anchor="middle" xml:space="preserve">10</text><text x="466" y="15" text-anchor="middle" xml:space="preserve">11</text><text x="506" y="15" text-anchor="middle" xml:space="preserve">0</text><text x="546" y="15" text-anchor="middle" xml:space="preserve">1</text></g></g><g transform="translate(0,65)" id="wavelane_2_0"><text x="-10" y="15" class="info" text-anchor="end" xml:space="preserve"><tspan>rd_port.data</tspan></text><g id="wavelane_draw_2_0"><use xlink:href="#vvv-2"/><use transform="translate(20)" xlink:href="#vvv-2"/><use transform="translate(40)" xlink:href="#vmv-2-2"/><use transform="translate(60)" xlink:href="#vvv-2"/><use transform="translate(80)" xlink:href="#vmv-2-2"/><use transform="translate(100)" xlink:href="#vvv-2"/><use transform="translate(120)" xlink:href="#vmv-2-2"/><use transform="translate(140)" xlink:href="#vvv-2"/><use transform="translate(160)" xlink:href="#vmv-2-2"/><use transform="translate(180)" xlink:href="#vvv-2"/><use transform="translate(200)" xlink:href="#vmv-2-2"/><use transform="translate(220)" xlink:href="#vvv-2"/><use transform="translate(240)" xlink:href="#vmv-2-2"/><use transform="translate(260)" xlink:href="#vvv-2"/><use transform="translate(280)" xlink:href="#vmv-2-2"/><use transform="translate(300)" xlink:href="#vvv-2"/><use transform="translate(320)" xlink:href="#vmv-2-2"/><use transform="translate(340)" xlink:href="#vvv-2"/><use transform="translate(360)" xlink:href="#vmv-2-2"/><use transform="translate(380)" xlink:href="#vvv-2"/><use transform="translate(400)" xlink:href="#vmv-2-2"/><use transform="translate(420)" xlink:href="#vvv-2"/><use transform="translate(440)" xlink:href="#vmv-2-2"/><use transform="translate(460)" xlink:href="#vvv-2"/><use transform="translate(480)" xlink:href="#vmv-2-2"/><use transform="translate(500)" xlink:href="#vvv-2"/><use transform="translate(520)" xlink:href="#vmv-2-2"/><use transform="translate(540)" xlink:href="#vvv-2"/><text x="16" y="15" text-anchor="middle" xml:space="preserve"><tspan>H</tspan></text><text x="66" y="15" text-anchor="middle" xml:space="preserve"><tspan>e</tspan></text><text x="106" y="15" text-anchor="middle" xml:space="preserve"><tspan>l</tspan></text><text x="146" y="15" text-anchor="middle" xml:space="preserve"><tspan>l</tspan></text><text x="186" y="15" text-anchor="middle" xml:space="preserve"><tspan>o</tspan></text><text x="226" y="15" text-anchor="middle" xml:space="preserve"><tspan></tspan></text><text x="266" y="15" text-anchor="middle" xml:space="preserve"><tspan>w</tspan></text><text x="306" y="15" text-anchor="middle" xml:space="preserve"><tspan>o</tspan></text><text x="346" y="15" text-anchor="middle" xml:space="preserve"><tspan>r</tspan></text><text x="386" y="15" text-anchor="middle" xml:space="preserve"><tspan>l</tspan></text><text x="426" y="15" text-anchor="middle" xml:space="preserve"><tspan>d</tspan></text><text x="466" y="15" text-anchor="middle" xml:space="preserve"><tspan>\n</tspan></text><text x="506" y="15" text-anchor="middle" xml:space="preserve"><tspan>H</tspan></text><text x="546" y="15" text-anchor="middle" xml:space="preserve"><tspan>e</tspan></text></g></g><g id="wavearcs_0"><path id="gmark_a_d" d="m 46,45 0,0 0,30" style="fill:none;stroke:#00F;stroke-width:1"/><g transform="translate(46,45)"><rect x="-5" y="-5" width="10.14" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>a</tspan></text></g><g transform="translate(46,75)"><rect x="-5" y="-5" width="10.14" height="11" style="fill:#FFF;"/><text text-anchor="middle" y="3" style="font-size:11px;"><tspan>d</tspan></text></g></g><g id="wavegaps_0"><g transform="translate(0,5)" id="wavegap_0_0"/><g transform="translate(0,35)" id="wavegap_1_0"/><g transform="translate(0,65)" id="wavegap_2_0"/></g><g/></g><g id="groups_0"><g/></g></g></svg>
diff --git a/docs/stdlib/memory.rst b/docs/stdlib/memory.rst
index 0508d56..2927a2c 100644
--- a/docs/stdlib/memory.rst
+++ b/docs/stdlib/memory.rst
@@ -1,10 +1,144 @@
-Memories
---------
+Memory arrays
+-------------
 
 .. py:module:: amaranth.lib.memory
 
-The :mod:`amaranth.lib.memory` module provides a way to efficiently store data organized as an array of identically shaped rows, which may be addressed (read and/or written) one at a time.
+The :mod:`amaranth.lib.memory` module provides a way to efficiently store data organized as an array of identically shaped rows, which may be addressed (read and/or written) one at a time. This organization is well suited for an efficient implementation in hardware.
+
+
+Introduction
+============
+
+A memory :ref:`component <wiring>` is accessed through read and write *memory ports*, which are :ref:`interface objects <wiring>` with address, data, and enable ports. The address input selects the memory row to be accessed, the enable input determines whether an access will be made, and the data output (for read ports) or data input (for write ports) transfers data between the memory row and the rest of the design. Read ports can be synchronous (where the memory row access is triggered by the :ref:`active edge <lang-sync>` of a clock) or asynchronous (where the memory row is accessed continuously); write ports are always synchronous.
+
+.. note::
+
+    Unfortunately, the terminology related to memories has an ambiguity: a "port" could refer to either an *interface port* (:class:`.Signal` objects created by the :mod:`amaranth.lib.wiring` module) or to a *memory port* (:class:`ReadPort` or :class:`WritePort` object created by :class:`amaranth.lib.memory.Memory`).
+
+    Amaranth documentation always uses the term "memory port" when referring to the latter.
+
+To use a memory, first create a :class:`Memory` object, providing a shape, depth (the number of rows), and initial contents. Then, request as many memory ports as the number of concurrent accesses you need to perform by using the :meth:`Memory.read_port` and :meth:`Memory.write_port` methods.
+
+.. warning::
+
+    While :class:`Memory` will provide virtually any memory configuration on request and all will simulate correctly, only a subset of configurations can implemented in hardware efficiently or `at all`. Exactly what any given hardware platform supports is specific to both the device and the toolchain used.
+
+    However, the following two configurations are well-supported on most platforms:
+
+    1. Zero or one write ports and any amount of read ports. Almost all devices include one or two read ports in a hardware memory block, but the toolchain will replicate memory blocks as needed to meet the requested amount of read ports, using more resources.
+    2. Two write ports and any amount of read ports whose address input always matches that of the either write port. Most devices include two combined read/write ports in a hardware memory block (known as "true dual-port", or "TDP", block RAM), and the toolchain will replicate memory blocks to meet the requested amount of read ports. However, some devices include one read-only and one write-only port in a hardware memory block (known as "simple dual-port", or "SDP", block RAM), making this configuration unavailable. Also, the combined (synchronous) read/write port of a TDP block RAM usually does not have independent read enable and write enable inputs; in this configuration, the read enable input should usually be left in the (default if not driven) asserted state.
+
+    Most devices include hardware primitives that can efficiently implement memories with asynchronous read ports (known as "LUT RAM", "distributed RAM", or "DRAM"; not to be confused with "dynamic RAM", also abbreviated as "DRAM"). On devices without these hardware primitives, memories with asynchronous read ports are implemented using logic resources, which are consumed at an extremely high rate. Synchronous read ports should be always preferred over asynchronous ones.
+
+    Additionally, some memory configurations (that seem like they should be supported by the device and the toolchain) may fail to be recognized, or may use much more resources than they should. This can happen due to device and/or toolchain errata (including defects in Amaranth). Unfortunately, such issues can only be handled on a case-by-case basis; in general, simpler memory configurations are better and more widely supported.
+
+
+Examples
+========
+
+.. testsetup::
+
+    from amaranth import *
+
+First, import the :class:`Memory` class.
+
+.. testcode::
+
+    from amaranth.lib.memory import Memory
+
+
+Read-only memory
+++++++++++++++++
+
+In the following example, a read-only memory is used to output a fixed message in a loop:
+
+.. testcode::
+    :hide:
+
+    m = Module()
+
+.. testcode::
+
+    message = b"Hello world\n"
+    m.submodules.memory = memory = \
+        Memory(shape=unsigned(8), depth=len(message), init=message)
+
+    rd_port = memory.read_port(domain="comb")
+    with m.If(rd_port.addr == memory.depth - 1):
+        m.d.sync += rd_port.addr.eq(0)
+    with m.Else():
+        m.d.sync += rd_port.addr.eq(rd_port.addr + 1)
+
+    character = Signal(8)
+    m.d.comb += character.eq(rd_port.data)
+
+In this example, the memory read port is asynchronous, and a change of the address input (labelled `a` on the diagram below) results in an immediate change of the data output (labelled `d`).
+
+.. image:: _images/memory/example_hello.svg
+
+
+First-in, first-out queue
++++++++++++++++++++++++++
+
+In a more complex example, a power-of-2 sized writable memory is used to implement a first-in, first-out queue:
+
+.. testcode::
+    :hide:
+
+    m = Module()
+
+.. testcode::
+
+    push = Signal()
+    pop  = Signal()
+
+    m.submodules.memory = memory = \
+        Memory(shape=unsigned(8), depth=16, init=[])
+
+    wr_port = memory.write_port()
+    m.d.comb += wr_port.en.eq(push)
+    with m.If(push):
+        m.d.sync += wr_port.addr.eq(wr_port.addr + 1)
+
+    rd_port = memory.read_port(transparent_for=(wr_port,))
+    m.d.comb += rd_port.en.eq(pop)
+    with m.If(pop):
+        m.d.sync += rd_port.addr.eq(rd_port.addr + 1)
+
+    # Data can be shifted in via `wr_port.data` and out via `rd_port.data`.
+    # This example assumes that empty queue will be never popped from.
+
+In this example, the memory read and write ports are synchronous. A write operation (labelled `x`, `w`) updates the addressed row 0 on the next clock cycle, and a read operation (labelled `y`, `r`) retrieves the contents of the same addressed row 0 on the next clock cycle as well.
+
+However, the memory read port is also configured to be *transparent* relative to the memory write port. This means that if a write and a read operation (labelled `t`, `u` respectively) access the same row with address 3, the new contents will be read out, reducing the minimum push-to-pop latency to one cycle, down from two cycles that would be required without the use of transparency.
+
+.. image:: _images/memory/example_fifo.svg
+
+
+Memories
+========
+
+..
+    attributes are not documented because they can be easily used to break soundness and we don't
+    document them for signals either; they are rarely necessary for interoperability
+
+.. autoclass:: Memory(*, depth, shape, init, src_loc_at=0)
+    :no-members:
+
+    .. autoclass:: amaranth.lib.memory::Memory.Init(...)
+
+    .. automethod:: read_port
+
+    .. automethod:: write_port
+
+    .. autoproperty:: read_ports
+
+    .. autoproperty:: write_ports
+
+
+Memory ports
+============
 
-.. todo::
+.. autoclass:: ReadPort(...)
 
-    Write the rest of this document.
+.. autoclass:: WritePort(...)
diff --git a/docs/stdlib/wiring.rst b/docs/stdlib/wiring.rst
index c40a07f..910cfcc 100644
--- a/docs/stdlib/wiring.rst
+++ b/docs/stdlib/wiring.rst
@@ -1,3 +1,5 @@
+.. _wiring:
+
 Interfaces and connections
 ##########################
 
diff --git a/pyproject.toml b/pyproject.toml
index 3edd906..4844d64 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -71,7 +71,7 @@ test = [
 docs = [
   "sphinx~=7.1",
   "sphinxcontrib-platformpicker~=1.3",
-  "sphinx-rtd-theme~=1.2",
+  "sphinx-rtd-theme~=2.0",
   "sphinx-autobuild",
 ]
 examples = [


Test patch:
diff --git a/tests/test_lib_memory.py b/tests/test_lib_memory.py
index 401b4bb..91eef93 100644
--- a/tests/test_lib_memory.py
+++ b/tests/test_lib_memory.py
@@ -58,28 +58,29 @@ class WritePortTestCase(FHDLTestCase):
 
     def test_signature_wrong(self):
         with self.assertRaisesRegex(TypeError,
-                "^`addr_width` must be a non-negative int, not -2$"):
+                r"^Address width must be a non-negative integer, not -2$"):
             memory.WritePort.Signature(addr_width=-2, shape=8)
         with self.assertRaisesRegex(TypeError,
-                "^Granularity must be a non-negative int or None, not -2$"):
+                r"^Granularity must be a non-negative integer or None, not -2$"):
             memory.WritePort.Signature(addr_width=4, shape=8, granularity=-2)
         with self.assertRaisesRegex(ValueError,
-                "^Granularity cannot be specified with signed shape$"):
+                r"^Granularity cannot be specified for a memory with a signed shape$"):
             memory.WritePort.Signature(addr_width=2, shape=signed(8), granularity=2)
         with self.assertRaisesRegex(TypeError,
-                "^Granularity can only be specified for plain unsigned `Shape` or `ArrayLayout`$"):
+                r"^Granularity can only be specified for memories whose shape is unsigned or "
+                r"data.ArrayLayout$"):
             memory.WritePort.Signature(addr_width=2, shape=MyStruct, granularity=2)
         with self.assertRaisesRegex(ValueError,
-                "^Granularity must be positive$"):
+                r"^Granularity must be positive$"):
             memory.WritePort.Signature(addr_width=2, shape=8, granularity=0)
         with self.assertRaisesRegex(ValueError,
-                "^Granularity must be positive$"):
+                r"^Granularity must be positive$"):
             memory.WritePort.Signature(addr_width=2, shape=data.ArrayLayout(8, 8), granularity=0)
         with self.assertRaisesRegex(ValueError,
-                "^Granularity must divide data width$"):
+                r"^Granularity must evenly divide data width$"):
             memory.WritePort.Signature(addr_width=2, shape=8, granularity=3)
         with self.assertRaisesRegex(ValueError,
-                "^Granularity must divide data array length$"):
+                r"^Granularity must evenly divide data array length$"):
             memory.WritePort.Signature(addr_width=2, shape=data.ArrayLayout(8, 8), granularity=3)
 
     def test_signature_eq(self):
@@ -120,7 +121,7 @@ class WritePortTestCase(FHDLTestCase):
         m = memory.Memory(depth=16, shape=8, init=[])
         port = memory.WritePort(signature, memory=m, domain="sync")
         self.assertIs(port.memory, m)
-        self.assertEqual(m.w_ports, (port,))
+        self.assertEqual(m.write_ports, (port,))
 
         signature = memory.WritePort.Signature(shape=MyStruct, addr_width=4)
         port = signature.create()
@@ -134,17 +135,17 @@ class WritePortTestCase(FHDLTestCase):
     def test_constructor_wrong(self):
         signature = memory.ReadPort.Signature(shape=8, addr_width=4)
         with self.assertRaisesRegex(TypeError,
-                r"^Expected `WritePort.Signature`, not ReadPort.Signature\(.*\)$"):
+                r"^Expected signature to be WritePort.Signature, not ReadPort.Signature\(.*\)$"):
             memory.WritePort(signature, memory=None, domain="sync")
         signature = memory.WritePort.Signature(shape=8, addr_width=4, granularity=2)
         with self.assertRaisesRegex(TypeError,
-                r"^Domain has to be a string, not None$"):
+                r"^Domain must be a string, not None$"):
             memory.WritePort(signature, memory=None, domain=None)
         with self.assertRaisesRegex(TypeError,
-                r"^Expected `Memory` or `None`, not 'a'$"):
+                r"^Expected memory to be Memory or None, not 'a'$"):
             memory.WritePort(signature, memory="a", domain="sync")
         with self.assertRaisesRegex(ValueError,
-                r"^Write port domain cannot be \"comb\"$"):
+                r"^Write ports cannot be asynchronous$"):
             memory.WritePort(signature, memory=None, domain="comb")
         signature = memory.WritePort.Signature(shape=8, addr_width=4)
         m = memory.Memory(depth=8, shape=8, init=[])
@@ -186,7 +187,7 @@ class ReadPortTestCase(FHDLTestCase):
 
     def test_signature_wrong(self):
         with self.assertRaisesRegex(TypeError,
-                "^`addr_width` must be a non-negative int, not -2$"):
+                "^Address width must be a non-negative integer, not -2$"):
             memory.ReadPort.Signature(addr_width=-2, shape=8)
 
     def test_signature_eq(self):
@@ -227,7 +228,7 @@ class ReadPortTestCase(FHDLTestCase):
         m = memory.Memory(depth=16, shape=8, init=[])
         port = memory.ReadPort(signature, memory=m, domain="sync")
         self.assertIs(port.memory, m)
-        self.assertEqual(m.r_ports, (port,))
+        self.assertEqual(m.read_ports, (port,))
         write_port = m.write_port()
         port = memory.ReadPort(signature, memory=m, domain="sync", transparent_for=[write_port])
         self.assertIs(port.memory, m)
@@ -245,14 +246,14 @@ class ReadPortTestCase(FHDLTestCase):
     def test_constructor_wrong(self):
         signature = memory.WritePort.Signature(shape=8, addr_width=4)
         with self.assertRaisesRegex(TypeError,
-                r"^Expected `ReadPort.Signature`, not WritePort.Signature\(.*\)$"):
+                r"^Expected signature to be ReadPort.Signature, not WritePort.Signature\(.*\)$"):
             memory.ReadPort(signature, memory=None, domain="sync")
         signature = memory.ReadPort.Signature(shape=8, addr_width=4)
         with self.assertRaisesRegex(TypeError,
-                r"^Domain has to be a string, not None$"):
+                r"^Domain must be a string, not None$"):
             memory.ReadPort(signature, memory=None, domain=None)
         with self.assertRaisesRegex(TypeError,
-                r"^Expected `Memory` or `None`, not 'a'$"):
+                r"^Expected memory to be Memory or None, not 'a'$"):
             memory.ReadPort(signature, memory="a", domain="sync")
         signature = memory.ReadPort.Signature(shape=8, addr_width=4)
         m = memory.Memory(depth=8, shape=8, init=[])
@@ -266,15 +267,15 @@ class ReadPortTestCase(FHDLTestCase):
         m = memory.Memory(depth=16, shape=8, init=[])
         port = m.read_port()
         with self.assertRaisesRegex(TypeError,
-                r"^`transparent_for` must contain only `WritePort` instances$"):
+                r"^Transparency set must contain only WritePort instances$"):
             memory.ReadPort(signature, memory=m, domain="sync", transparent_for=[port])
         write_port = m.write_port()
         m2 = memory.Memory(depth=16, shape=8, init=[])
         with self.assertRaisesRegex(ValueError,
-                r"^Transparent write ports must belong to the same memory$"):
+                r"^Ports in transparency set must belong to the same memory$"):
             memory.ReadPort(signature, memory=m2, domain="sync", transparent_for=[write_port])
         with self.assertRaisesRegex(ValueError,
-                r"^Transparent write ports must belong to the same domain$"):
+                r"^Ports in transparency set must belong to the same domain$"):
             memory.ReadPort(signature, memory=m, domain="other", transparent_for=[write_port])
 
 
@@ -284,14 +285,14 @@ class MemoryTestCase(FHDLTestCase):
         self.assertEqual(m.shape, 8)
         self.assertEqual(m.depth, 4)
         self.assertEqual(m.init.shape, 8)
-        self.assertEqual(m.init.depth, 4)
+        self.assertEqual(len(m.init), 4)
         self.assertEqual(m.attrs, {})
         self.assertIsInstance(m.init, memory.Memory.Init)
         self.assertEqual(list(m.init), [1, 2, 3, 0])
         self.assertEqual(m.init._raw, [1, 2, 3, 0])
-        self.assertRepr(m.init, "Memory.Init([1, 2, 3, 0])")
-        self.assertEqual(m.r_ports, ())
-        self.assertEqual(m.w_ports, ())
+        self.assertRepr(m.init, "Memory.Init([1, 2, 3, 0], shape=8, depth=4)")
+        self.assertEqual(m.read_ports, ())
+        self.assertEqual(m.write_ports, ())
 
     def test_constructor_shapecastable(self):
         init = [
@@ -353,10 +354,10 @@ class MemoryTestCase(FHDLTestCase):
                 r"^Changing length of Memory.init is not allowed$"):
             m.init[1:] = [1, 2]
         with self.assertRaisesRegex(TypeError,
-                r"^Deleting items from Memory.init is not allowed$"):
+                r"^Deleting elements from Memory.init is not allowed$"):
             del m.init[1:2]
         with self.assertRaisesRegex(TypeError,
-                r"^Inserting items into Memory.init is not allowed$"):
+                r"^Inserting elements into Memory.init is not allowed$"):
             m.init.insert(1, 3)
 
     def test_port(self):
@@ -374,8 +375,8 @@ class MemoryTestCase(FHDLTestCase):
             wp = m.write_port()
             self.assertEqual(wp.signature.addr_width, addr_width)
             self.assertEqual(wp.signature.shape, 8)
-            self.assertEqual(m.r_ports, (rp,))
-            self.assertEqual(m.w_ports, (wp,))
+            self.assertEqual(m.read_ports, (rp,))
+            self.assertEqual(m.write_ports, (wp,))
 
     def test_elaborate(self):
         m = memory.Memory(shape=MyStruct, depth=4, init=[{"a": 1, "b": 2}])